admin
2025-01-22 7e31442f0e9b07764e9c6a9680d3d4aeba5fe1de
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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' }
}