| 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() ); | 
|   *] | 
| } |