| Quintiq file version 2.0 | 
| #parent: #root | 
| StaticMethod AddSalesDemandsBeforeScope ( | 
|   LibOpt_Scope scope, | 
|   RunContextForCapacityPlanning runcontext | 
| ) | 
| { | 
|   TextBody: | 
|   [* | 
|     asdips := construct( AggregatedSalesDemandInPeriods ); | 
|     if ( runcontext.IsMetaIteration() )  | 
|     { | 
|       SelectorMeta::ComputeFirstLastPISPIPInScope( scope ); // make sure to set these relations - we can limit to nonlead because leaf has already been done in smartplan gap fill | 
|     } | 
|      | 
|     traverse( scope.GetProductInStockingPointInOptimizerRun(), Elements, pisp ) | 
|     { | 
|       maxpostponementperiod := pisp.OptimizerMaxPostponementPeriod(); | 
|        | 
|       pispipstart := null( ProductInStockingPointInPeriodPlanning ); | 
|        | 
|       if( runcontext.IsMetaIteration() ) | 
|       { | 
|         pispipstart := pisp.EarliestPISPIPInScope(); | 
|       } | 
|       else | 
|       { | 
|         pispipstart := minselect( pisp, ProductInStockingPointInPeriodPlanning, pispip, scope.Contains( pispip.PISPIPInOptimizerRun() ), pispip.Start() ); | 
|       } | 
|        | 
|       periodstart := pispipstart.Period_MP(); | 
|        | 
|       previouspispip := pispipstart.PreviousPlanningPISPIP(); | 
|        | 
|       for( i := 1; | 
|            i <= maxpostponementperiod         // within the maximum number of postponement periods | 
|            and not isnull( previouspispip );  // the previous pispip exists | 
|            i++ | 
|          ) | 
|       { | 
|         // For non-aggregated demand, we can select directly | 
|         traverse( previouspispip, SalesDemandInPeriodBase.astype( LeafSalesDemandInPeriod ), lsdip, | 
|                   guard( lsdip.PostponementSpecification().IsValidData(), false ) | 
|                   and not lsdip.IsPostponed() | 
|                   and lsdip.CanOptimizeWhenPostponed( periodstart ) | 
|                   and lsdip.NeedsToBePlanned() | 
|                   and lsdip.IsWithinThresholdQuantity() | 
|                   and lsdip.GetIsPISPAndProductInOptimizerRun( scope, runcontext.IsPostProcessing() ) ) | 
|         { | 
|           scope.AddSDIPBeforeScope( lsdip ); | 
|           lsdip.SDIPBeforeScopeInRun().OptMinPostponementPeriod( periodstart.SequenceNrInPeriodSpecification() | 
|                                                                 - lsdip.AsSalesDemandInPeriodBase().Period_MP().SequenceNrInPeriodSpecification() | 
|                                                                ); | 
|         } | 
|          | 
|         // For aggregated demand, we need to select from the disaggregated demands, since the aggregated demand might have a different horizon | 
|         traverse( previouspispip, SalesDemandInPeriodBase.astype( DisaggregatedSalesDemandInPeriod ).AggregatedSalesDemandInPeriod, asdip, | 
|                   guard( asdip.PostponementSpecification().IsValidData(), false ) | 
|                   and not asdip.IsPostponed() | 
|                   and asdip.CanOptimizeWhenPostponed( periodstart ) | 
|                   and asdip.NeedsToBePlanned() | 
|                   and asdip.IsWithinThresholdQuantity() ) | 
|         { | 
|           asdips.Add( asdip ); | 
|         } | 
|          | 
|         previouspispip := previouspispip.PreviousPlanningPISPIP(); | 
|       } | 
|     } | 
|      | 
|     // The same aggregated SDIP may be selected multiple times from different disaggregated SDIPs, so get the unique ones. | 
|     uniqueasdips := asdips.Unique(); | 
|      | 
|     traverse( uniqueasdips, Elements, asdip ) | 
|     { | 
|       scope.AddSDIPBeforeScope( asdip ); | 
|        | 
|       // Since the horizons of the disaggregated sales demand PISPs may differ, we set the minimum value | 
|       asdip.SDIPBeforeScopeInRun().OptMinPostponementPeriod( 1 ); | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |