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
84
85
Quintiq file version 2.0
#parent: #root
Method HandlePostPeriodRollPeriodTaskOperations
{
  Description: 'Finalize the period roll, sychronize periods, aggregate/disaggregate the period task operations.'
  TextBody:
  [*
    // ODE2 May-29-2017 (updated)
    /* IMPORTANT NOTE
    This aggregation/disaggregation is performed with the following assumptions:
    1) Only Planning periods have PeriodTaskOperation
    2) PeriodTaskOperation AsPeriodTaskOperation relation is procedural
    Any of the above item changed, this is expected to be broken.
    */
    
    deadperiods := selectset( this, Period_MP, period, period.IsDeleted() or not period.IsPlanning() );
    affectedoperations := selectset( deadperiods, Elements.UnitPeriod.PeriodTaskOperation.Operation, operation, true ); 
    
    // PeriodTaskOperation
    traverse( affectedoperations, Elements, operation )
    {
      // Get all the dead period task operation
      deadptos := selectset( operation, PeriodTaskOperation, pto,
                             pto.AsPeriodTaskOperationOfUnitPeriod().Period_MP().IsDeleted()
                             or not pto.AsPeriodTaskOperationOfUnitPeriod().Period_MP().IsPlanning() );
    
      traverse( deadptos, Elements, deadpto )
      {
        // Use the procedural relation
        period := deadpto.AsPeriodTaskOperationOfUnitPeriod().Period_MP();
        // Get all planning up that is within the range
        planningups := selectsortedset( operation, Unit.PlanningUnitPeriod, pup,
                                        not pup.Period_MP().IsDeleted()
                                        and pup.Period_MP().IsInPeriod( deadpto.AsPeriodTaskOperationOfUnitPeriod().Start(),
                                                                        deadpto.AsPeriodTaskOperationOfUnitPeriod().End() ),
                                        pup.Start() );
    
        // Aggregate to planning period
        // If there is no planning unit period, no aggregation needs to be done (means out of planning & historical scope)
        if( planningups.Size() = 1 )
        {
          planningup := planningups.Element( 0 );
          oldpto := PeriodTaskOperation::FindPeriodTaskOperationTypeIndex( planningup.Start(), operation.ID() ); 
          // There is a chance that aggregation  already performed on this pto. Propagation is required as quantity is not calculated yet.
          Transaction::Transaction().Propagate( attribute( PeriodTaskOperation, Quantity ) );
          
          newqty := ifexpr( not isnull( oldpto ) and oldpto <> deadpto,
                            oldpto.Quantity() + deadpto.Quantity(),
                            deadpto.Quantity() );
    
          pto := PeriodTaskOperation::CreateOrUpdate( operation, planningup, newqty, deadpto.HasUserQuantity() );
          
          traverse( deadpto, PeriodTaskInCampaign, ptc )
          {
            pto.PeriodTaskInCampaign( relmove, ptc )
          }
          traverse( deadpto, PeriodTaskInTransition, ptt )
          {
            pto.PeriodTaskInTransition( relmove, ptt )
          }
          
          deadptos.Remove( pto ); // Remove from the objects after having updated the period task.
        }
        // Disaggregate to base period
        else if( planningups.Size() > 1 )
        {
          // The dead pto might get updated in the loop, so keep the old value out side the loop.
          quantity := deadpto.Quantity()
          duration := period.Duration();
        
          traverse( planningups, Elements, planningup )
          {
            // Disaggregate by factor
            factor := minvalue( planningup.Period_MP().Duration() / duration, 1.0 );
            newqty :=  quantity * factor;
            pto := PeriodTaskOperation::CreateOrUpdate( operation, planningup, newqty, deadpto.HasUserQuantity() );
            deadptos.Remove( pto ); // Remove from the objects after updated the period task.
          }
        }
      }
      // Delete all dead period task
      PeriodTaskOperation::Delete( deadptos );
    }
  *]
}