| 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 ); | 
|     } | 
|   *] | 
| } |