| Quintiq file version 2.0 | 
| #parent: #root | 
| Method InitConstraintsGoalsForPISPIPs ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   const RunContextForCapacityPlanning runcontext, | 
|   const LibOpt_Scope scope, | 
|   const constcontent ProductInStockingPointInPeriodPlanningLeafs leafpispipsinrun, | 
|   const constcontent ProductInStockingPoint_MPs pispsinrun | 
| ) const | 
| { | 
|   Description: 'Init constraints goals for PISPIPs' | 
|   TextBody: | 
|   [* | 
|     collectvaluesmodel := runcontext.IsMetaIteration();  | 
|      | 
|     // calculate customer satisfaction | 
|     // ffconst constraint UoM: Default | 
|     ffconst := program.TotalFulfillmentConstraints().New(); | 
|     ffconst.Sense( '=' ); | 
|     ffconst.RHSValue( this.ScaleConstraintRHS( typeofexpression( ffconst ), 0.0 )); | 
|     // Term UoM: Default | 
|     ffconst.NewTerm( 1.0 * this.ScaleConstraintTerm( typeof( MPTotalFulfillmentVariable ), typeofexpression( ffconst )) | 
|                      , program.TotalFulfillmentVariables().Get() ); | 
|      | 
|     // calculate target inventory level penalty | 
|     // ssconst constraint UoM: PISP --> please note that this means that if different product in stocking points have different UoM, | 
|     //                                their target inventory will have a different weight in the goal | 
|     ssconst := program.TotalTargetInvLevelConstraints().New(); | 
|     ssconst.Sense( '=' ); | 
|     ssconst.RHSValue( this.ScaleConstraintRHS( typeofexpression( ssconst ), 0.0 ) ); | 
|     // Term UoM: ~PISP | 
|     ssconst.NewTerm( 1.0 * this.ScaleConstraintTerm( typeof( MPTotalTargetInvLevelVariable ), typeofexpression( ssconst )) | 
|                      , program.TotalTargetInvLevelVariables().Get() ); | 
|      | 
|     // calculate minimum inventory level penalty | 
|     // minlevelconst constraint UoM: PISP --> please note that this means that if different product in stocking points have different UoM, | 
|     //                                      their minimum inventory will have a different weight in the goal | 
|     minlevelconst := program.TotalMinimumInventoryLevelConstraints().New(); | 
|     minlevelconst.Sense( '=' ); | 
|     minlevelconst.RHSValue( this.ScaleConstraintRHS( typeofexpression( minlevelconst ), 0.0 ) ); | 
|     // Term UoM: ~PISP | 
|     minlevelconst.NewTerm( 1.0 * this.ScaleConstraintTerm( typeof( MPTotalMinimumInventoryLevelVariable ), typeofexpression( minlevelconst ) ) | 
|                            , program.TotalMinimumInventoryLevelVariables().Get() ); | 
|      | 
|     // calculate maximum inventory level penalty | 
|     // maxlevelconst constraint UoM: PISP --> please note that this means that if different product in stocking points have different UoM, | 
|     //                                      their maximum inventory will have a different weight in the goal | 
|     maxlevelconst := program.TotalMaximumInventoryLevelConstraints().New(); | 
|     maxlevelconst.Sense( '=' ); | 
|     maxlevelconst.RHSValue( this.ScaleConstraintRHS( typeofexpression( maxlevelconst ), 0.0 ) ); | 
|     // Term UoM: ~PISP | 
|     maxlevelconst.NewTerm( 1.0 * this.ScaleConstraintTerm( typeof( MPTotalMaximumInventoryLevelVariable ), typeofexpression( maxlevelconst ) ) | 
|                            , program.TotalMaximumInventoryLevelVariables().Get() ); | 
|      | 
|     // calculate priority | 
|     // pconst constraint UoM: Default | 
|     pconst := program.TotalSalesDemandPriorityConstraints().New(); | 
|     pconst.Sense( '=' ); | 
|     pconst.RHSValue( this.ScaleConstraintRHS( typeofexpression( pconst ), 0.0 ) ); | 
|     // Term UoM: Default | 
|     pconst.NewTerm( 1.0 * this.ScaleConstraintTerm( typeof( MPTotalSalesDemandPriorityVariable ), typeofexpression( pconst )) | 
|                     , program.TotalSalesDemandPriorityVariables().Get() ); | 
|      | 
|     // calculate postponed sales demand penalty | 
|     // ppconst constraint UoM: Monetary | 
|     ppconst := program.TotalPostponementPenaltyConstraints().New(); | 
|     ppconst.Sense( '=' ); | 
|     ppconst.RHSValue( this.ScaleConstraintRHS( typeofexpression( ppconst ), 0.0 ) ); | 
|     // Term UoM: Monetary | 
|     ppconst.NewTerm( 1.0 * this.ScaleConstraintTerm( typeof( MPTotalPostponementPenaltyVariable ), typeofexpression( ppconst ) ) | 
|                      , program.TotalPostponementPenaltyVariables().Get() ); | 
|      | 
|     // Calculate the total inventory mix balancing KPI | 
|     // The total inventory mix balancing KPI is equal to sum of the overall maximum difference in days over all periods | 
|     // plus  the sum of all difference in demand days over all categories and periods | 
|     // TotalDiffInDays = ÃƒÂ¢Ã‹â€ Ã¢â‚¬Ëœ_period〖 OverallDiffInDays( period ) ÃƒÂ£Ã¢â€šÂ¬Ã¢â‚¬â€ + ÃƒÂ¢Ã‹â€ Ã¢â‚¬Ëœ_category ÃƒÂ¢Ã‹â€ Ã¢â‚¬Ëœ_period ÃƒÂ£Ã¢â€šÂ¬Ã¢â‚¬â€œ DifferenceInDays( category, period ) ÃƒÂ£Ã¢â€šÂ¬Ã¢â‚¬â€ | 
|      | 
|     // imbconstr UoM: Days | 
|     imbconstr := program.TotalInventoryMixBalancingConstraints().New(); | 
|     imbconstr.Sense( '=' ); | 
|     imbconstr.RHSValue( this.ScaleConstraintRHS( typeofexpression( imbconstr ), 0.0 ) ); | 
|     // term UoM: Days | 
|     imbconstr.NewTerm( this.ScaleConstraintTerm( typeof( MPTotalInventoryMixBalancingVariable ), typeofexpression( imbconstr ) ) | 
|                        , program.TotalInventoryMixBalancingVariables().Get() ); | 
|      | 
|     // Calculate total expiry                    | 
|     expconst := program.TotalExpiredQtyConstraints().New(); | 
|     expconst.Sense( '=' ); | 
|     expconst.RHSValue( this.ScaleConstraintRHS( typeofexpression( expconst ), 0.0 ) ); | 
|      | 
|     scalefactor_salesdemandqty_ffconst := this.ScaleConstraintTerm( typeof( MPSalesDemandQtyVariable ), typeofexpression( ffconst ) ); | 
|     scalefactor_salesdemandqty_pconst := this.ScaleConstraintTerm( typeof( MPSalesDemandQtyVariable ), typeofexpression( pconst ) ); | 
|     scalefactor_invqtyundertarget_ssconst := this.ScaleConstraintTerm( typeof( MPInvQtyUnderTargetVariable ), typeofexpression( ssconst ) ); | 
|     scalefactor_mininvqtyunder_minlevelconst := this.ScaleConstraintTerm( typeof( MPMinInvQtyUnderVariable ), typeofexpression( minlevelconst ) ); | 
|     scalefactor_maxinvqtyover_maxlevelconst := this.ScaleConstraintTerm( typeof( MPMaxInvQtyOverVariable ), typeofexpression( maxlevelconst ) ); | 
|     scalefactor_delayedsalesdemandqty_ffconst := this.ScaleConstraintTerm( typeof( MPDelayedSalesDemandQtyVariable ), typeofexpression( ffconst ) ); | 
|     scalefactor_delayedsalesdemandqty_ppconst := this.ScaleConstraintTerm( typeof( MPDelayedSalesDemandQtyVariable ), typeofexpression( ppconst ) ); | 
|     scalefactor_delayedsalesdemandqty_pconst := this.ScaleConstraintTerm( typeof( MPDelayedSalesDemandQtyVariable ), typeofexpression( pconst ) ); | 
|     scalefactor_diffindays_imbconstr := this.ScaleConstraintTerm( typeof( MPDifferenceInDemandDaysVariable ), typeofexpression( imbconstr ) ); | 
|     scalefactor_overalldd_imbconstr := this.ScaleConstraintTerm( typeof( MPOverallDiffInvInDemandDaysVariable ), typeofexpression( imbconstr ) ); | 
|     scalefactor_totalexpired_expconst := this.ScaleConstraintTerm( typeof( MPTotalExpiredQtyVariable ), typeofexpression( expconst )); | 
|     scalefactor_expired_expconst := this.ScaleConstraintTerm( typeof( MPExpiredVariable ), typeofexpression( expconst ) ); | 
|     scalefactor_salesdemandqtyvar := CapacityPlanningSuboptimizer::GetVariableScaleFactor( typeof( MPSalesDemandQtyVariable ) ); | 
|     scalefactor_undertargetvar := CapacityPlanningSuboptimizer::GetVariableScaleFactor( typeof( MPInvQtyUnderTargetVariable ) );  | 
|     scalefactor_underminvar := CapacityPlanningSuboptimizer::GetVariableScaleFactor( typeof( MPMinInvQtyUnderVariable ) );  | 
|     scalefactor_overmaxvar := CapacityPlanningSuboptimizer::GetVariableScaleFactor( typeof( MPMaxInvQtyOverVariable ) );  | 
|                                                                                             | 
|     // Consider the leaf pispips in this optimizer run | 
|     totalfulfillmentvariablesvalue := 0.0;  | 
|     totalsalesdemandpriovariablesvalue := 0.0;  | 
|     totalpostponementvariablesvalue := 0.0;  | 
|      | 
|     isdefinesdp := runcontext.WeightLevelNonFinancial().SalesDemandPriorityWeight() <> 0.0 and runcontext.WeightLevelNonFinancial().SalesDemandPriorityLevel() >= 1;  | 
|     isdefinedpostpenalty := runcontext.WeightLevelNonFinancial().PostponementPenaltyWeight() <> 0.0 and runcontext.WeightLevelNonFinancial().PostponementPenaltyLevel() >= 1;  | 
|     isdefinedff := runcontext.WeightLevelNonFinancial().FulfillmentWeight() <> 0.0 and runcontext.WeightLevelNonFinancial().FulfillmentLevel() >= 1;  | 
|      | 
|     traverse( leafpispipsinrun, Elements, pispip ) | 
|     { | 
|       // note the following relation path does not inlude postponed sales demand  | 
|       traverse( pispip, PlanningBaseSalesDemandInPeriodForOptimization, sd, not sd.MasterSalesDemand().IsExcludedFromFulfillmentKPI() ) | 
|       { | 
|         uomconversion := sd.DefaultUOMConversionFactor(); | 
|         ffconst_coefficient := ifexpr( isdefinedff, uomconversion * scalefactor_salesdemandqty_ffconst, 0.0 );  | 
|         pconst_coefficient := ifexpr(  isdefinesdp, uomconversion * guard( -sd.Priority().Weight(), 0 ) * scalefactor_salesdemandqty_pconst, 0.0 );  | 
|      | 
|         var := sd.GetSalesDemandQtyVariable( program );  | 
|         if ( not isnull( var ) )  | 
|         {   | 
|           // Term: uomconversion  * SalesDemandQty variable | 
|           // UoM: [PISP to Default] *         [PISP] | 
|           ffconst.NewTerm( -ffconst_coefficient, var ); | 
|           // Term: uomconversion  *  -sd.Priority.Weight * SalesDemandQty variable | 
|           // UoM: [PISP to Default] *        [-]           *       [PISP] | 
|           pconst.NewTerm( pconst_coefficient, var ); | 
|         } | 
|          | 
|         if ( collectvaluesmodel and not isnull( var ) )  | 
|         { | 
|           totalfulfillmentvariablesvalue := totalfulfillmentvariablesvalue + (sd.FulfilledQuantity() * ffconst_coefficient / scalefactor_salesdemandqtyvar );   | 
|           totalsalesdemandpriovariablesvalue := totalsalesdemandpriovariablesvalue + ( sd.FulfilledQuantity() * (-pconst_coefficient) / scalefactor_salesdemandqtyvar );  | 
|         } | 
|       } | 
|     } | 
|      | 
|     // Goal has to take postponed sales demands into account | 
|     traverse( leafpispipsinrun, Elements, pispip ) | 
|     { | 
|       maxpostponementperiod := pispip.ProductInStockingPoint_MP().OptimizerMaxPostponementPeriod(); | 
|       previouspispip := pispip.PreviousPlanningPISPIP(); | 
|        | 
|       for( i := 1; | 
|            i <= maxpostponementperiod         // within the maximum number of postponement periods | 
|            and not isnull( previouspispip );  // the previous pispip exists | 
|            i++ | 
|          ) | 
|       { | 
|         // this path contains postponable salesdemand, but excludes actual postponed sales demand | 
|         traverse( previouspispip, astype( ProductInStockingPointInPeriodPlanningLeaf ).PlanningBaseSalesDemandInPeriodForOptimizationPostponable, sd, | 
|                   not sd.MasterSalesDemand().IsExcludedFromFulfillmentKPI() ) | 
|         { | 
|           penalty := sd.GetPenalty(); | 
|                  | 
|           sdpostponed := select( sd, PostponedSalesDemand, p, true, p.ProductInStockingPointInPeriodPlanning() = pispip and not p.IsManuallyPostponed() ); | 
|           fulfilledqty := guard( sdpostponed.FulfilledQuantity(), 0.0 ); | 
|            | 
|           uomconversion := sd.DefaultUOMConversionFactor(); | 
|           coeffactor_ffconst := ifexpr( isdefinedff, uomconversion * scalefactor_delayedsalesdemandqty_ffconst, 0.0 ); | 
|           coeffactor_ppconst := ifexpr( isdefinedpostpenalty, penalty * i * scalefactor_delayedsalesdemandqty_ppconst, 0.0 ); | 
|           coeffactor_pconst := ifexpr( isdefinesdp, uomconversion * guard( sd.Priority().Weight(), 0 ) * scalefactor_delayedsalesdemandqty_pconst, 0.0 ); | 
|            | 
|           var := sd.GetDelayedSalesDemandQtyVariable( program, pispip.Period_MP() ); | 
|            | 
|           if( not isnull( var ) ) | 
|           { | 
|             // Term: uomconversion  * DelayedSalesDemandQty variable | 
|             // UoM: [PISP to Default] *         [PISP] | 
|             ffconst.NewTerm( -coeffactor_ffconst, var );           // Fulfillment | 
|             // Term:    penalty    *  i  * DelayedSalesDemandQty | 
|             // Uom:  [Monetary/PISP] * [-] *        [PISP] | 
|             ppconst.NewTerm( -coeffactor_ppconst, var );   // Penalty | 
|              | 
|             pconst.NewTerm( -coeffactor_pconst, var ); // sales demand priority | 
|              | 
|             if ( collectvaluesmodel ) | 
|             { | 
|               totalfulfillmentvariablesvalue := totalfulfillmentvariablesvalue + ( fulfilledqty * coeffactor_ffconst / CapacityPlanningSuboptimizer::GetVariableScaleFactor( typeofexpression( var ) ) ); | 
|               totalpostponementvariablesvalue := totalpostponementvariablesvalue + ( fulfilledqty * coeffactor_ppconst / CapacityPlanningSuboptimizer::GetVariableScaleFactor( typeofexpression( var ) ) ); | 
|               totalsalesdemandpriovariablesvalue := totalsalesdemandpriovariablesvalue + ( fulfilledqty * coeffactor_pconst / CapacityPlanningSuboptimizer::GetVariableScaleFactor( typeofexpression( var ) ) ); | 
|             } | 
|           } | 
|         } | 
|          | 
|         previouspispip := previouspispip.PreviousPlanningPISPIP(); | 
|       } | 
|     } | 
|      | 
|     this.StoreValueInProgram( program, 'collect_values_model_MPTotalFulfillmentVariables', totalfulfillmentvariablesvalue );  | 
|     this.StoreValueInProgram( program, 'collect_values_model_MPTotalSalesDemandPriorityVariable', totalsalesdemandpriovariablesvalue );  | 
|     this.StoreValueInProgram( program, 'collect_values_model_MPTotalPostponementPenaltyVariables', totalpostponementvariablesvalue );  | 
|      | 
|      | 
|     // Consider the non-leaf pispips for the high level inventory specifications | 
|     leafpispips := null( ProductInStockingPointInPeriodPlannings, constcontent, owning ); | 
|     pispips := this.GetPISPIPsForInventorySpecifications( scope, false, &leafpispips ); | 
|      | 
|     totalinvundertarget := 0.0;  | 
|     totalinvundermin := 0.0;  | 
|     totalinvvovermax := 0.0;  | 
|     traverse( pispips, Elements, pispip ) | 
|     { | 
|       // target inventory level | 
|       targetunder_defined := pispip.GetTargetUnderDefined( scope, runcontext );  | 
|       if( targetunder_defined ) | 
|       { | 
|         // Term UoM: PISP | 
|         coeffactor := scalefactor_invqtyundertarget_ssconst * pispip.ProductInStockingPoint_MP().DefaultUOMConversionFactor();  | 
|         targetundervar := program.InvQtyUnderTargetVariables().Get( pispip ) | 
|         ssconst.NewTerm( -1.0 * coeffactor, targetundervar ); | 
|      | 
|         violation := (coeffactor / scalefactor_undertargetvar ) * maxvalue( 0.0, pispip.TargetInventoryLevel() - pispip.InventoryLevelEnd() );   | 
|         totalinvundertarget := totalinvundertarget + violation;            | 
|          | 
|         if ( pispip.Start() < runcontext.FirstPeriod_MP().Start() and pispip.GetHasTargetInDays() ) // make hard constraint for pispip prior to optimizer horizon in meta  | 
|         { | 
|           violationatstartrun := (coeffactor / scalefactor_undertargetvar ) * pispip.ViolationTargetInventoryRunStart();  | 
|           boundcons := program.BoundTargetInventoryViolationPriorToHorizonConstraints().New( pispip ); // need constraint because of parallel thread init  | 
|           boundcons.Sense( '<=' );  | 
|           boundcons.RHSValue( violationatstartrun );  | 
|           boundcons.NewTerm( 1.0, targetundervar ); | 
|           boundcons.NewTerm( -1.0, program.InvQtySpecPriorToHorizonSlackVariables().Get( pispip ) );  | 
|         } | 
|       } | 
|      | 
|       // Penalty for not reaching the minimum inventory level | 
|       minunder_defined := pispip.GetMinUnderDefined( scope, runcontext );  | 
|       if( minunder_defined ) | 
|       { | 
|         // Term UoM: PISP | 
|         coeffactor := scalefactor_mininvqtyunder_minlevelconst * pispip.ProductInStockingPoint_MP().DefaultUOMConversionFactor();  | 
|         minundervar := program.MinInvQtyUnderVariables().Get( pispip );  | 
|         minlevelconst.NewTerm( -1.0 * coeffactor, minundervar ); | 
|      | 
|         violation := ( coeffactor/ scalefactor_underminvar ) * maxvalue( 0.0, pispip.MinInventoryLevel() - pispip.InventoryLevelEnd() ); | 
|         totalinvundermin := totalinvundermin + violation ; | 
|          | 
|         if ( pispip.Start() < runcontext.FirstPeriod_MP().Start() and pispip.GetHasMinLevelInDays() ) // make hard constraint for pispip prior to optimizer horizon in meta  | 
|         { | 
|           violationatstartrun := ( coeffactor/ scalefactor_underminvar ) * pispip.ViolationMinInventoryRunStart(); | 
|           boundcons := program.BoundMinInventoryViolationPriorToHorizonConstraints().New( pispip ); // need constraint because of parallel thread init  | 
|           boundcons.Sense( '<=' );  | 
|           boundcons.RHSValue( violationatstartrun );  | 
|           boundcons.NewTerm( 1.0, minundervar ); | 
|           boundcons.NewTerm( -1.0, program.InvQtySpecPriorToHorizonSlackVariables().Get( pispip ) );  | 
|         } | 
|       } | 
|      | 
|       // Penalty for exceeding the maximum inventory level | 
|       maxover_defined := pispip.GetMaxOverDefined( scope, runcontext );  | 
|       if( maxover_defined ) | 
|       { | 
|         // Term UoM: PISP | 
|         coeffactor := scalefactor_maxinvqtyover_maxlevelconst * pispip.ProductInStockingPoint_MP().DefaultUOMConversionFactor();  | 
|         maxovervar := program.MaxInvQtyOverVariables().Get( pispip );  | 
|         maxlevelconst.NewTerm( -1.0 * coeffactor, maxovervar ); | 
|      | 
|         violation := ( coeffactor/ scalefactor_overmaxvar ) * maxvalue( 0.0, pispip.InventoryLevelEnd() - pispip.MaxInventoryLevel() );  | 
|         totalinvvovermax := totalinvvovermax + violation ; | 
|          | 
|         if ( pispip.Start() < runcontext.FirstPeriod_MP().Start() and pispip.GetHasMaxLevelInDays() ) // make hard constraint for pispip prior to optimizer horizon in meta  | 
|         { | 
|           violationatstartrun := ( coeffactor/ scalefactor_overmaxvar ) * pispip.ViolationMaxInventoryRunStart();  | 
|           boundcons := program.BoundMaxInventoryViolationPriorToHorizonConstraints().New( pispip ); // need constraint because of parallel thread init  | 
|           boundcons.Sense( '<=' );  | 
|           boundcons.RHSValue( violationatstartrun );  | 
|           boundcons.NewTerm( 1.0, maxovervar ); | 
|           boundcons.NewTerm( -1.0, program.InvQtySpecPriorToHorizonSlackVariables().Get( pispip ) );  | 
|         } | 
|       } | 
|     } | 
|      | 
|     // workaround due to not being able to write to attribute | 
|     this.StoreValueInProgram( program, 'collect_values_model_MPTotalMaximumInventoryLevelVariable', totalinvvovermax );  | 
|     this.StoreValueInProgram( program, 'collect_values_model_MPTotalMinimumInventoryLevelVariable', totalinvundermin );  | 
|     this.StoreValueInProgram( program, 'collect_values_model_MPTotalTargetInventoryLevelVariable', totalinvundertarget );  | 
|      | 
|     if( runcontext.UseInventoryMixBalancing() ) | 
|     { | 
|       productcategories := Product_MP::GetProductCategoriesForOptimization( this.MacroPlan(), scope ); | 
|       isfirstcategory := true; | 
|        | 
|       traverse( productcategories, Elements, productcategory ) | 
|       { | 
|         // Retrieve all pispips that are part of this product category and that are part of the optimizer run | 
|         pispips := selectset( productcategory.GetLeavesConst(), Elements.ProductInStockingPoint_MP.ProductInStockingPointInPeriodPlanning, pispip, | 
|                               scope.Contains( pispip.PISPIPInOptimizerRun() ) ) | 
|         // Select all periods related to these pispips | 
|         periods := selectset( pispips, Elements.Period_MP, period, true ); | 
|        | 
|         traverse( periods, Elements, period ) | 
|         { | 
|           imbconstr.NewTerm( -1.0 * scalefactor_diffindays_imbconstr, program.DifferenceInDemandDaysVariables().Get( productcategory, period ) ); | 
|      | 
|           // The overall difference in demand days does not depend on the category | 
|           // Therefore, we should only add a term if this is the first category (without this check we would add one term per category) | 
|           if( isfirstcategory ) | 
|           { | 
|             imbconstr.NewTerm( -1.0 * scalefactor_overalldd_imbconstr, program.OverallDiffInvInDemandDaysVariables().Get( period ) ); | 
|           } | 
|         } | 
|         isfirstcategory := false; | 
|       } | 
|     } | 
|      | 
|     if( runcontext.UseExpiredQty() ) | 
|     { | 
|       // PISPExpire[PISP]:  PISPExpire[PISP] - SUM(( 1.0 + ( offset from last period * 0.01 ) ) * Expired[pispip]) = 0 | 
|       // TotalExpire: TotalExpire - SUM(PISPExpire[PISP]) = 0 | 
|       expconst.NewTerm( 1.0 * scalefactor_totalexpired_expconst | 
|                        , program.TotalExpiredQtyVariables().Get() ); | 
|      | 
|        | 
|       traverse( pispsinrun, Elements, pisp, pisp.IsOptShelfLife() ) | 
|       { | 
|          | 
|         pispexpvar := program.PISPTotalExpiryVariables().Get( pisp ); | 
|         expconst.NewTerm( -1.0 * scalefactor_expired_expconst, | 
|                           pispexpvar | 
|                         ); | 
|                          | 
|         pispexpconst := program.PISPTotalExpiryConstraints().New( pisp ); | 
|         pispexpconst.Sense("=" ); | 
|         pispexpconst.RHSValue( 0.0 ) | 
|         pispexpconst.NewTerm( 1.0 * scalefactor_expired_expconst, | 
|                           pispexpvar | 
|                         );  | 
|                          | 
|         traverse( pisp, ProductInStockingPointInPeriod.astype( ProductInStockingPointInPeriodPlanningLeaf ), pispip, scope.Contains( pispip.PISPIPInOptimizerRun() ) )  | 
|         // we can avoid scope.Contains(  pispip.PISPIPInOptimizerRun() ) ) - same transaction | 
|         { | 
|           pispexpconst.NewTerm( -1.0 * scalefactor_expired_expconst, | 
|                                 program.ExpiredVariables().Get( pispip ) | 
|                               ); | 
|         }       | 
|       } | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |