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