yanweiyuan3
2023-08-09 588bc7829387dfc761cc25f06f77d4c81818bd10
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
Quintiq file version 2.0
#parent: #root
Function CalcShelfLifeDDAgeQuantityIndexVectorAsBinaryValue
{
  TextBody:
  [*
    // Firstly, we get use the vector that stores the age, qty at the start of the PISPIPPL (ShelfLifeSupplyAge/QtyVectorAsBinaryValue)
    // Then we reserve the available product with the optimizer reserved qty, then we allocate the available product to dependent demand qty. 
    // *Available product: product that is not expired.
    // Then we set add back the reserved product for optimizer reserved qty.
    // Only then, we set the attribute to determine for each DD, which product will it get. As well as, what are the SLS after all DDs have been allocated.
    // The logic will try to allocate the product stock according to optimizer fulfilled quantity.
    
    // Example for product A, shelf life of 6 days.
    // At the start of PISPIP, we have: { Age: 4, Qty: 10; Age: 5, Qty: 15; Age: 6, Qty: 20; Age: 7, Qty: 10 } 
    // The available product are only those with age <=6, which are { Age: 4, Qty: 10; Age: 5, Qty: 15; Age: 6, Qty: 20 }
    // We have optimizer reserved qty of 5, so we'll reserve the youngest available product out first { Age: 4, Qty: 5 }, remaining to fulfill other demands { Age: 4, Qty: 5; Age: 5, Qty: 15; Age: 6, Qty: 20 }
    // Then we have total DD of 25, so we'll allocate the oldest available product to the DD, remaining to fulfill other demands { Age: 4, Qty: 5, Age: 5, Qty: 10 }
    // After these steps, we add back the optimizer reserved qty back to the vector, so the remaining vector including unavaiable product are { Age: 4, Qty: 10; Age: 5, Qty: 10; Age: 6, Qty: 0; Age: 7, Qty: 10 }
    // Then, we remove those elements in the vector with qty of 0. Result: { Age: 4, Qty: 10; Age: 5, Qty: 10; Age: 7, Qty: 10 }. This is then set to the attribute ShelfLifeSupplyAfterDDAge/QtyVectorAsBinaryValue
    // As a result from this method we also have attributes to determine for each DD, how many product of what age/qty will it get. E.g. DD 1 of Qty: 10 get { Age: 6, Qty: 10 }. DD2 of Qty 15 get { Age: 5, Qty: 5; Age: 6, Qty: 10 }
    // In this case we have { index: 0, Age: 6, Qty: 10; index: 1, Age: 5, Qty: 5; index: 1, Age: 6, Qty: 10 } Note: Index 0 = DD 1; Index 1 = DD 2
    
    // Construct the vectors for shelf-life dd
    quantityvector := RealVector::Construct();
    agevector := RealVector::Construct();
    indexvector := NumberVector::Construct();
    pispippl := this.ProductInStockingPointInPeriodPlanningLeaf();
    
    // This is used to calculate the the fulfillable quantity of trip dependent demands
    // Construct the vectors for shelf-life supply after dd
    source_quantity_after_dd_vector := RealVector::Construct();
    source_age_after_dd_vector := RealVector::Construct();
    
    // Product stock quantity and age tracking is only needed if the Product has shelf life or maturation
    product := pispippl.ProductInStockingPoint_MP().Product_MP();
    if( product.HasShelfLifeOrMaturation() )
    {
    
      // Get source quantity and age vectors
      source_quantityvector := RealVector::Construct( this.ShelfLifeSupplyQuantityVectorAsBinaryValue() );
      source_agevector := RealVector::Construct( this.ShelfLifeSupplyAgeVectorAsBinaryValue() );
    
      // The remaining supply that can be allocated to manual planning after deducting expired stock and optimizer fulfilled/reserved qty
      remainingsupplyafteroptfulfilled := source_quantityvector.Sum()
                                          - pispippl.ExpiredInPeriodShelfLifeSupplyQuantity()
                                          - pispippl.OptimizerFulfilledDemandQuantity()
                                          - pispippl.OptimizerReservedQuantity();
      
      // The quantity and age consumed by optimizer's quantity
      source_quantityconsumedvector := RealVector::Construct();
      source_ageconsumedvector := RealVector::Construct();
      
      // Consider optimizer fulfilled sales demand qty first before allocating supplies to dependent demands
      // such that sales demand will only consume the oldest stock
      optfulfilled_sdqty := pispippl.GetOptimizerFulfilledSalesDemandQuantity();
      if( optfulfilled_sdqty > 0 )
      {
        ShelfLife::SetAvailableAgeAndQuantityVectorAfterDemand( source_quantityvector, source_agevector,
                                                                source_quantityconsumedvector, source_ageconsumedvector,
                                                                optfulfilled_sdqty,
                                                                false, // do not ignore maturation
                                                                true, // reserve oldest product
                                                                pispippl,
                                                                0.0 // the passed in source_agevector has not been aged so there is need to un-age 
                                                                );
      }
    
      // Since this is used to calculate the fulfillable quantity of trip dependent demands, it should use the same logic as CalcSystemFulfilledQuantity
      optimizerreservedquantity := pispippl.OptimizerReservedQuantity();
      if( optimizerreservedquantity > 0 )
      {
        ShelfLife::SetAvailableAgeAndQuantityVectorAfterDemand( source_quantityvector, source_agevector,
                                                                source_quantityconsumedvector, source_ageconsumedvector,
                                                                optimizerreservedquantity,
                                                                true, // ignore maturation
                                                                false, // reserve youngest product
                                                                pispippl,
                                                                0.0 // the passed in source_agevector has not been aged so there is need to un-age 
                                                              );
      }
    
      source_agevectorsize := source_agevector.Size();
      // Sort the demands by period task lane leg first and then longest duration in trip
      dependentdemands := ProductInStockingPointInPeriod::GetSortedDependentDemands( pispippl );
      
      // Traverse the dependent demands
      traverse( dependentdemands, Elements, dependentdemand )
      {
        dependentdemand.AllocateShelfLifeSupplyToDependentDemand( product,
                                                                  dependentdemands,
                                                                  // remaining supply after opt fulfilled will be reduced (if remaining supply is used) and output it back
                                                                  remainingsupplyafteroptfulfilled,
                                                                  source_quantityvector,
                                                                  source_agevector,
                                                                  source_agevectorsize,
                                                                  quantityvector,
                                                                  agevector,
                                                                  indexvector );
      }
      
      // Filter out elements that is less than or equals to 0 in quantity vector
      isvalidvector := source_quantityvector.GreaterThan( 0.0 );
      source_quantityvector := source_quantityvector.GetSelection( isvalidvector );
      source_agevector := source_agevector.GetSelection( isvalidvector );
    
      // Reassign the optimizer reserved quantity back to the vectors
      source_quantityvector.Append( source_quantityconsumedvector );
      source_agevector.Append( source_ageconsumedvector );
      
      // Sort the vectors
      ShelfLife::SortAndMergeVectorAccordingToAge( source_quantityvector, source_agevector );
      source_quantity_after_dd_vector.Append( source_quantityvector );
      source_age_after_dd_vector.Append( source_agevector );
    }
    
    this.ShelfLifeDDAgeVectorAsBinaryValue( agevector.AsBinaryValue() );
    this.ShelfLifeDDQuantityVectorAsBinaryValue( quantityvector.AsBinaryValue() );
    this.ShelfLifeDDIndexVectorAsBinaryValue( indexvector.AsBinaryValue() );
    
    this.ShelfLifeSupplyAfterDDAgeVectorAsBinaryValue( source_age_after_dd_vector.AsBinaryValue() );
    this.ShelfLifeSupplyAfterDDQuantityVectorAsBinaryValue( source_quantity_after_dd_vector.AsBinaryValue() );
  *]
}