Quintiq file version 2.0
|
#parent: #root
|
Method GetInventoryRHSForShelfLifeSplitBalanceConstraint (
|
const ProductInStockingPointInPeriodPlanning pispip,
|
const IncomingShelfLifeDay islday,
|
DateTime firstpispipforsplitbalancestart,
|
const RunContextForCapacityPlanning runcontext,
|
const LibOpt_Scope scope
|
) const as Real
|
{
|
Description: 'Calculate and returns the rhs of the balance constraints (=starting inventory of a pispip considering all decisions outside the optimizer horizon)'
|
TextBody:
|
[*
|
// The RHS of this constraint is the initial inventory based on all decisions outside the scope of the optimizer, it includes:
|
// Inventory supply that is greater than 0. If the supply is less than 0, it will be treated as a "must be fulfilled" demands
|
|
rhs := 0.0;
|
positiveinvsupply := sum( pispip, astype( ProductInStockingPointInPeriodPlanningLeaf ).InventorySupply, isup, isup.Quantity() > 0.0, isup.Quantity() );
|
supplyquantity := ifexpr( islday.ShelfLifeDays() = 0, positiveinvsupply, 0.0 ); // optimizer figures out itself how much expires, so give total inventory supply quantity
|
|
// Inventory of the previous period if that period exists and is not part of the optimizer horizon
|
invstart := 0.0;
|
previouspispip := pispip.PreviousPlanningPISPIP();
|
if( not isnull( previouspispip )
|
and previouspispip.Start() < firstpispipforsplitbalancestart )
|
{
|
if ( islday.ShelfLifeDays() = 0 ) // we put inventory start for first pispip in ShelfLifeDays = 0 pispip.Note we assume we have shelflife extra periods before optimizer scope
|
{
|
invstart := pispip.InventoryLevelStart() // take start iventory of pispip to make sure expired qty is taken out (in this case previous pispip is completely outside of the optimizer so optimizer does not
|
// figure out itself what has expired.
|
}
|
}
|
|
|
product := pispip.ProductInStockingPoint_MP().Product_MP();
|
|
// The sum of the new supply that starts outside the optimizer scope
|
sumnewsupply := 0.0;
|
// Add the new supply quantity if it has a dd on a pispip outside the optimizer scope
|
traverse( pispip, astype( ProductInStockingPointInPeriodPlanningLeaf ).NewSupply, newsupply, not newsupply.Quantity() = 0.0 )
|
|
{
|
if ( newsupply.IsNewSupplyOfOperation()
|
and ( isnull( newsupply.PeriodTaskOperation().UnitPeriodWhenInScope() ) // either the period task is not in scope
|
or not newsupply.GetHasAllDDInOptimizerScope( runcontext, scope ) ) ) // or if it is check whether some dependent demand is taking from a pispip not in scope
|
{
|
if ( islday.ShelfLifeDays() = 0 )
|
{
|
sumnewsupply := sumnewsupply + newsupply.Quantity();
|
}
|
}
|
else
|
{
|
traverse( newsupply, PeriodTask_MP.astype( PeriodTaskLaneLeg ).Trip.ProductInTrip, pit,
|
pit.Product_MP() = product
|
and not scope.Contains( pit.ProductInTripInOptimizerRun() )
|
and pit.Trip().GetShelfLifeAgeToAdd() = islday.ShelfLifeDays()
|
)
|
{
|
sumnewsupply := sumnewsupply + pit.Quantity();
|
}
|
}
|
}
|
|
// If the user runs a downstream smart plan, then the user may specify a total available supply
|
// This total available supply will overrule the real total supply of the pispip
|
// Therefore, we will have to update the RHS of the balance constraint to account for the difference
|
magicalusersupply := 0.0;
|
if( pispip.GetHasMisMatchAvailableUserSupply() and islday.ShelfLifeDays() = 0 )
|
{
|
magicalusersupply := pispip.TotalAvailableSupplyUser() - pispip.SupplyQuantity();
|
}
|
|
// Calculate the total RHS
|
rhs := -( supplyquantity + invstart + sumnewsupply + magicalusersupply );
|
|
return rhs
|
*]
|
InterfaceProperties { Accessibility: 'Module' }
|
}
|