| Quintiq file version 2.0 | 
| #parent: #root | 
| Method OTSPrepareInventoryLevelTargetsBeforeScope ( | 
|   Date firstperioddate | 
| ) | 
| { | 
|   Description: 'Inventory targets ( min, max, safety ) prior to the optimizer scope are converted from target in days into absolute targets (having value equal to the current min / max / safety level).' | 
|   TextBody: | 
|   [* | 
|     traverse( this, MacroPlan.Product_MP.ProductInStockingPoint_MP.ProductInStockingPointInPeriodPlanning, pispip )  | 
|     { | 
|       pispip.DebugCheckOTSPreprocessingMax( pispip.MaxInventoryLevel() );  | 
|       pispip.DebugCheckOTSPreprocessingMin( pispip.MinInventoryLevel() );  | 
|       pispip.DebugCheckOTSPreprocessingSafety( pispip.TargetInventoryLevel() );    | 
|     } | 
|      | 
|     traverse( this, MacroPlan.Product_MP, product )  | 
|     { | 
|       traverse( product, ProductInStockingPoint_MP, pisp )  | 
|       { | 
|         istoprocess := selectsortedset(  pisp,  | 
|                                          InventorySpecification,  | 
|                                          is,  | 
|                                          ( is.HasMaxLevelInDays() or is.HasMinLevelInDays() ) and is.Start() < firstperioddate ,  | 
|                                          firstperioddate - is.Start() ); // go from later to earlier  | 
|      | 
|         firstinscopeis := InventorySpecification::FindInventorySpecificationTypeIndex( product.ID(), pisp.StockingPointID(), firstperioddate );  | 
|         if ( isnull( firstinscopeis ) and istoprocess.Size() > 0 )  | 
|         { | 
|           is := istoprocess.Element( 0 ); // carry over information from last istoprocess in time  | 
|           InventorySpecification::Create( product,  | 
|                                         pisp.StockingPoint_MP(),  | 
|                                         firstperioddate,  | 
|                                         false, // hastargetindays | 
|                                         0.0, // target in days | 
|                                         0.0, // target in qty | 
|                                         is.HasMinLevelInDays(), // hasminindays | 
|                                         is.MinLevelInDays(), // min in days  | 
|                                         is.MinLevelInQuantity(), // in in qty | 
|                                         is.HasMaxLevel(),  | 
|                                         is.HasMaxLevelInDays(), // has max level in days | 
|                                         is.MaxLevelInDays(), // max level in days  | 
|                                         is.MaxLevelInQuantity(), // max level in qty | 
|                                         is.IsCalculated(), // is calculated | 
|                                         is.IsManuallyConfigured() ); // is from db | 
|         } | 
|      | 
|         lastdate := firstperioddate;                                         | 
|         traverse( istoprocess, Elements, is )                              | 
|         { | 
|           sp := is.StockingPoint_MP();  | 
|           isstart := is.Start(); | 
|           hasmaxlevel := is.HasMaxLevel();  | 
|           manualconf := is.IsManuallyConfigured();  | 
|            | 
|           is.Delete();  | 
|        | 
|           traverse( this, MacroPlan.PlanningPeriod, period, period.StartDate() < lastdate and period.StartDate() >= isstart ) // up to where previous process is starts | 
|           { | 
|             pispip := select( pisp, ProductInStockingPointInPeriodPlanning, p, true, p.Start() = period.Start() );   | 
|             | 
|             mininqty := pispip.MinInventoryLevel();   | 
|             maxinqty := pispip.MaxInventoryLevel();  | 
|              | 
|             InventorySpecification::Create( product,  | 
|                                             sp,  | 
|                                             period.StartDate(),  | 
|                                             false, // hastargetindays | 
|                                             0.0, // target in days | 
|                                             0.0, // target in qty | 
|                                             false, // hasminindays | 
|                                             0.0, // min in days  | 
|                                             mininqty, // in in qty | 
|                                             hasmaxlevel,  | 
|                                             false, // has max level in days | 
|                                             0.0, // max level in days  | 
|                                             maxinqty, // max level in qty | 
|                                             false, // is calculated | 
|                                             manualconf ); // is from db | 
|              | 
|           }  | 
|           lastdate := isstart; // end point for next iteration of traverse for InventorySpecification object                               | 
|         }   | 
|        | 
|         safetytoprocess := selectsortedset(  pisp, SafetyStock, s, s.HasTargetInDays() and s.Start() < firstperioddate, firstperioddate - s.Start() ); // go from later to earlier  | 
|      | 
|         firstinscopess := SafetyStock::FindTypeIndexSafetyStock( product.ID(), pisp.StockingPointID(), firstperioddate );  | 
|         // check for safety stock starting on firsperiodate. If nothing then we should create a new one, otherwise ok | 
|         if ( isnull( firstinscopess ) and safetytoprocess.Size() > 0 )  | 
|         { | 
|           sstock := safetytoprocess.Element( 0 ); // carry over information from last safety stock before 'firstperioddate' | 
|           SafetyStock::Create( product,  | 
|                                pisp.StockingPoint_MP(),  | 
|                                firstperioddate,  | 
|                                sstock.HasTargetInDays(), // has target in days  | 
|                                sstock.TargetInDays(), // target in days  | 
|                                sstock.TargetInQuantity(), // target in qty | 
|                                sstock.IsCalculated(), // is calculated  | 
|                                sstock.HasUserTarget(), // has user target  | 
|                                sstock.IsManuallyConfigured() );  | 
|         } | 
|          | 
|         lastdate := firstperioddate;  | 
|         traverse( safetytoprocess, Elements, sstock )  | 
|         { | 
|           sp := sstock.StockingPoint_MP();  | 
|           pisp := select( product, ProductInStockingPoint_MP, p, true, p.StockingPoint_MP() = sp );  | 
|           ismanual := sstock.IsManuallyConfigured();  | 
|           sstockstart := sstock.Start(); | 
|           hasuser := sstock.HasUserTarget();  | 
|         | 
|           sstock.Delete();  | 
|            | 
|           traverse( this, MacroPlan.PlanningPeriod, period, period.StartDate() < lastdate and period.StartDate() >= sstockstart )  | 
|           { | 
|             pispip := select( pisp, ProductInStockingPointInPeriodPlanning, p, true, p.Start() = period.Start() );   | 
|              | 
|             slevel := pispip.TargetInventoryLevel();  | 
|      | 
|             SafetyStock::Create( product,  | 
|                                  sp,  | 
|                                  period.StartDate(),  | 
|                                  false, // has target in days  | 
|                                  0.0, // target in days  | 
|                                  slevel, // target in qty | 
|                                  false, // is calculated  | 
|                                  hasuser, // has user target  | 
|                                  ismanual ); | 
|           } | 
|           lastdate := sstockstart; // end point for next iteration of traverse on safety stock objects          | 
|         } | 
|       }     | 
|     }  | 
|      | 
|     Transaction::Transaction().Propagate( attribute( ProductInStockingPointInPeriodPlanning, MinInventoryLevel ) );  | 
|     Transaction::Transaction().Propagate( attribute( ProductInStockingPointInPeriodPlanning, MaxInventoryLevel ) );  | 
|     Transaction::Transaction().Propagate( attribute( ProductInStockingPointInPeriodPlanning, TargetInventoryLevel ) );  | 
|      | 
|     unchanged := true;  | 
|     traverse( this, MacroPlan.Product_MP.ProductInStockingPoint_MP.ProductInStockingPointInPeriodPlanning, pispip )  | 
|     { | 
|       if ( pispip.DebugCheckOTSPreprocessingMax() <> pispip.MaxInventoryLevel() )  | 
|       { | 
|         unchanged := false;  | 
|         debuginfo(  'Changed max', pispip.Start(), pispip.ProductInStockingPoint_MP().Name(), 'old=', pispip.DebugCheckOTSPreprocessingMax() , 'new=', pispip.MaxInventoryLevel() );  | 
|       }  | 
|       if ( pispip.DebugCheckOTSPreprocessingMin() <> pispip.MinInventoryLevel() )  | 
|       { | 
|         unchanged := false;  | 
|         debuginfo(  'Changed min', pispip.Start(), pispip.ProductInStockingPoint_MP().Name(), 'old=', pispip.DebugCheckOTSPreprocessingMin() , 'new=', pispip.MinInventoryLevel() );  | 
|       } | 
|       if ( pispip.DebugCheckOTSPreprocessingSafety() <> pispip.TargetInventoryLevel() ) | 
|       { | 
|         unchanged := false;  | 
|         debuginfo(  'Changed safety', pispip.Start(), pispip.ProductInStockingPoint_MP().Name(), 'old=', pispip.DebugCheckOTSPreprocessingSafety() , 'new=', pispip.TargetInventoryLevel() );  | 
|       }  | 
|     } | 
|     verify( unchanged, 'Failure to change inventory / safety stock in days' ); | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |