| Quintiq file version 2.0 | 
| #parent: #root | 
| Method InitConstraintsForBalanceSplitShelfLife ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   const constcontent ProductInStockingPointInPeriods smartplanpispips, | 
|   constcontent ProductInStockingPoint_MPs intermediatepisps, | 
|   const ProductInStockingPointInPeriodPlanning pispip, | 
|   DateTime firstpispipforbalancestart, | 
|   DateTime lastpispipstart, | 
|   const RunContextForCapacityPlanning runcontext, | 
|   const LibOpt_Scope scope, | 
|   Real scalefactor_demandslack_const, | 
|   Real scalefactor_tripnewsupply_const, | 
|   Real scalefactor_invqty_const, | 
|   Real scalefactor_salesdemandqty_const, | 
|   Real scalefactor_dependentdemandinpispip_const, | 
|   Real scalefactor_delayedsalesdemandqty_const, | 
|   Real scalefactor_periodtaskqty_const, | 
|   Real scalefactor_expired_const, | 
|   Real scalefactor_rhs_constr | 
| ) const | 
| { | 
|   Description: | 
|   [* | 
|     To balance the demands and supply in pispip for a pispip that has IsOptShelfLife or IsOptMaturation | 
|     Dependent demands must be fulfilled in full quantity. Sales demands can be fulfilled halfly. | 
|   *] | 
|   TextBody: | 
|   [* | 
|     traverse( pispip, ProductInStockingPoint_MP.IncomingShelfLifeDay, islday )  | 
|     { | 
|       constr := program.BalanceShelfLifeConstraints().New( pispip, islday ); | 
|       constr.Sense( '=' ); | 
|      | 
|       // RHS UoM: PISP | 
|       rhs := this.GetInventoryRHSForShelfLifeSplitBalanceConstraint( pispip, islday, firstpispipforbalancestart, runcontext, scope );   | 
|       rhs_scaled := rhs * scalefactor_rhs_constr | 
|      | 
|       constr.RHSValue( rhs_scaled ); | 
|      | 
|       // Inventory end for pispip | 
|       // Term UoM: PISP | 
|       constr.NewTerm( -1.0 * scalefactor_invqty_const, program.InvQtyShelfLifeVariables().Get( pispip, islday ) ); | 
|      | 
|       // Penalty for decreasing demand quantity in a pispip for balancing the constraint | 
|       // (A positive DemandSlack represents a supply that is "magically" created to even out the balance constraint) | 
|       // Term UoM: PISP | 
|       // Remove the slack in case we only plan 1 step upstream and it is an intermediary product.       | 
|       if( this.GetIsBalanceSlackAllowed( pispip, smartplanpispips, intermediatepisps, runcontext ) ) | 
|       {   | 
|         constr.NewTerm( 1.0 * scalefactor_demandslack_const, | 
|                        program.DemandSlackShelfLifeVariables().Get( pispip, islday ) );                   | 
|       } | 
|        | 
|       // New supplies from operations added to shelflife 0  | 
|       if ( islday.ShelfLifeDays() = 0 )  | 
|       { | 
|         ispostprocessing := runcontext.IsPostProcessing();  | 
|         traverse( pispip,  | 
|                   ProductInStockingPoint_MP.OperationOutputAvailableForOptimization,  | 
|                   output, | 
|                   output.HasRegularProductforOptimizer()  | 
|                   or output.GetIsProductInOptimizerRun( ispostprocessing ) ) | 
|         {              | 
|           // Term UoM: Output PISP | 
|           this.AddConstraintForOperationNewSupplies( output, pispip.Period_MP(), null( Period_MP ), | 
|                                                      program, 1.0, constr, scalefactor_periodtaskqty_const, scope ); | 
|         } | 
|       } | 
|        | 
|        | 
|       // New supplies from trips, for those productintrip that are part of the optimizer run and add islday.ShelfLifeDays to the shelf life | 
|       traverse( pispip, astype( ProductInStockingPointInPeriodPlanningLeaf ).NewSupply.ProductInTrip, productintrip, | 
|                 scope.Contains(  productintrip.ProductInTripInOptimizerRun() )  | 
|                 and productintrip.Trip().GetShelfLifeAgeToAdd() = islday.ShelfLifeDays() )  | 
|       { | 
|         // Term UoM: Output PISP | 
|         constr.NewTerm( 1.0 * scalefactor_tripnewsupply_const, program.TripNewSupplyVariables().Get( productintrip ) ); | 
|       } | 
|      | 
|       // Inventory carried forward, if the previous pispip exists and is part of the optimizer run | 
|       previouspispip := pispip.PreviousPlanningPISPIP(); | 
|       if( not isnull( previouspispip ) and previouspispip.Start() >= firstpispipforbalancestart )  | 
|       { | 
|            // Term UoM: PISP | 
|           constr.NewTerm( 1.0 * scalefactor_invqty_const,   program.InvQtyShelfLifeVariables().Get( previouspispip, islday ) ); | 
|       } | 
|      | 
|       // Sales demands added to balance constraint pispip salesdemands | 
|       traverse( pispip.GetLeafSalesDemandInPeriod(), Elements, lsdip, not lsdip.IsPostponed() or lsdip.IsManuallyPostponed() ) // analogous to non-shelflife balance  | 
|       { | 
|         // Leaf sales demands added to balance constraint | 
|         constr.NewTerm( -1.0 * scalefactor_salesdemandqty_const, | 
|                        program.SalesDemandShelfLifeQtyVariables().Get( lsdip, islday ) ); | 
|       } | 
|       traverse( pispip.GetDisaggregatedSalesDemandInPeriod(), Elements, dsdip, not dsdip.IsPostponed() ) // analogous to non-shelflife balance  | 
|       { | 
|         // Disaggregated sales demands added to balance constraint | 
|         constr.NewTerm( -1.0 * scalefactor_salesdemandqty_const, | 
|                        program.DisaggregatedSalesDemandShelfLifeQtyVariables().Get( dsdip, islday ) ); | 
|       } | 
|      | 
|       // Dependent demands, with slack to prevent infeasibility | 
|       // Term UoM: PISP | 
|       traverse( pispip, ProductInStockingPoint_MP.OutgoingShelfLifeDay, oslday )  | 
|       { | 
|         constr.NewTerm( -1.0 * scalefactor_dependentdemandinpispip_const, | 
|                        program.DependentDemandInPISPIPShelfLifeVariables().Get( pispip, islday, oslday ) ); | 
|       }   | 
|        | 
|       //subtract the number of products that have expired in this pispip | 
|       if ( pispip.ProductInStockingPoint_MP().IsOptShelfLife() )  | 
|       { | 
|         constr.NewTerm( -1.0 * scalefactor_expired_const, program.ExpiredForAgeVariables().Get( pispip, islday ) ); | 
|       } | 
|      | 
|       // Postponed demands which are postponed to this period | 
|       previouspispip := pispip.PreviousPlanningPISPIP(); | 
|        | 
|       maxnumberofpostponement := pispip.ProductInStockingPoint_MP().OptimizerMaxPostponementPeriod(); // set in init instance for performance  | 
|       for( i := 1; | 
|            i <= maxnumberofpostponement // within the maximum number of postponement periods | 
|            and not isnull( previouspispip ); // the previous pispip exists | 
|            i++ ) | 
|       { | 
|         traverse( previouspispip.astype( ProductInStockingPointInPeriodPlanningLeaf ), PlanningBaseSalesDemandInPeriodForOptimizationPostponable, sd ) | 
|         {                                              | 
|           var := sd.GetDelayedSalesDemandShelfLifeQtyVariable( program, pispip.Period_MP(), islday );  | 
|           if( not isnull( var ) ) | 
|           { | 
|             // Term UoM: PISP | 
|             constr.NewTerm( -1.0 * scalefactor_delayedsalesdemandqty_const, var ); | 
|           } | 
|         } | 
|         previouspispip := previouspispip.PreviousPlanningPISPIP(); | 
|       } | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |