| Quintiq file version 2.0 | 
| #parent: #root | 
| Method AddProductionTermMaturationSlackConstraint ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   MPConstraint matconstr, | 
|   const ProductInStockingPoint_MP pisp, | 
|   const ProductInStockingPointInPeriodPlanning pispip, | 
|   const OutgoingShelfLifeDay oslday, | 
|   Real scalefactor_mass, | 
|   Real scalefactor_rhs_const, | 
|   Boolean ismaturationforarrival | 
| ) const | 
| { | 
|   TextBody: | 
|   [* | 
|     extra_age := ifexpr( ismaturationforarrival, [Real]oslday.ShelfLifeDays(), 0.0 );        | 
|     pispipforexpired := constnull(  ProductInStockingPointInPeriodPlanning );  | 
|     if ( pisp.IsOptShelfLife() )  | 
|     { | 
|       product := pisp.Product_MP();  | 
|       periodpispip := pispip.Period_MP();  | 
|       // pick latest PISPIP prior to pispip where product will not be usable any more in 'pispip'. The cumulative waste there we need to discount  | 
|       pispipforexpired := maxselect( pispip,  | 
|                                      ProductInStockingPoint_MP.ProductInStockingPointInPeriodPlanning,  | 
|                                      p,  | 
|                                      p.Start() <= pispip.Start()  | 
|                                      and not CapacityPlanningSuboptimizer::GetIsUsableInTargetPeriod( product, p.Period_MP(), periodpispip ),  | 
|                                      p.Start() ); | 
|     } | 
|      | 
|     traverse( pisp, IncomingShelfLifeDay, islday )  | 
|     { | 
|       // compute latest period "pispipearlier"  so that production will be mature for pispip and also will have arrived | 
|       pispipearlier := maxselect( pisp,  | 
|                                   ProductInStockingPointInPeriodPlanning,  | 
|                                   p,  | 
|                                   pisp.Product_MP().GetIsMaturedInTargetPeriod( extra_age, p.Start(), pispip.Period_MP() ) // add outgoing trip days because ok to be mature on arrival only | 
|                                   and p.Start() + islday.ShelfLifeAsDuration() <= pispip.Start(), // product needs to be there | 
|                                   p.Start() );  | 
|        | 
|        | 
|       if ( not isnull( pispipearlier ) )  | 
|       { | 
|         cpvar := program.CumulativeProductionVariables().Find( pispipearlier, islday )  | 
|         if ( not isnull( cpvar ) )  | 
|         { | 
|           matconstr.NewTerm( -1.0 * scalefactor_mass, cpvar );  | 
|         } | 
|         else | 
|         { | 
|           matconstr.RHSValue( matconstr.RHSValue() + scalefactor_rhs_const* pispipearlier.GetCumulativeProduction( islday ) );  | 
|         } | 
|       } | 
|        | 
|       // take out expired quantity, because this cannot count towards how much matured product is there | 
|       if ( pisp.IsOptShelfLife() and not isnull( pispipforexpired ) )  | 
|       {   | 
|         wvar := program.CumulativeWasteVariables().Find( pispipforexpired, islday ) | 
|         if ( not isnull( wvar ) )  | 
|         { | 
|           matconstr.NewTerm(  1.0 * scalefactor_mass, wvar );  | 
|         } | 
|       } | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |