chenqinghong
2024-05-07 3ec06a830367465068963156dcc1d8e522571c13
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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' }
}