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' } 
 | 
} 
 |