| Quintiq file version 2.0 | 
| #parent: #root | 
| StaticMethod RefreshOfflinePlan ( | 
|   MacroPlan macroPlan | 
| ) | 
| { | 
|   TextBody: | 
|   [* | 
|     macroPlan.OfflinePlanTable( relflush ); | 
|      | 
|     opt := macroPlan.OfflinePlanTable( relnew, SaveDateTime := DateTime::ActualTime() ); | 
|      | 
|     // 生成下线计划表 | 
|     traverse ( macroPlan, Unit, u, u.HasCapacityTypeTime()  | 
|     //           and u.Name() = "eMotor Assy (France)" // 测试本地场景时可以过滤 | 
|     //           and u.Name() = "DL-MoMo"              // 测试实际场景时可以过滤 | 
|               ) { | 
|       // 创建产线行 | 
|       opt.OfflinePlanRow( relnew, ProductionLine := u.ID(), ProductID := "", Type := "0" ); | 
|        | 
|       traverse ( u, Operation, o ) { | 
|         traverse ( o, PeriodTaskOperation.NewSupply, ns ) { | 
|           // 找行 | 
|           oprQuantity := select( opt, OfflinePlanRow, tempOPR, tempOPR.ProductionLine() = u.ID() and tempOPR.ProductID() = ns.AsProductionSupply().ProductInStockingPoint_MP().ProductID() and tempOPR.Type() = "1" ); | 
|           oprOrder    := select( opt, OfflinePlanRow, tempOPR, tempOPR.ProductionLine() = u.ID() and tempOPR.ProductID() = ns.AsProductionSupply().ProductInStockingPoint_MP().ProductID() and tempOPR.Type() = "2" ); | 
|           if ( isnull( oprQuantity ) and isnull( oprOrder ) ) { | 
|             oprQuantity := opt.OfflinePlanRow( relnew, ProductionLine := u.ID(), ProductID := ns.AsProductionSupply().ProductInStockingPoint_MP().ProductID(), Type := "1", Notes := ns.AsProductionSupply().ProductInStockingPoint_MP().Product_MP().Notes() ); | 
|             oprOrder    := opt.OfflinePlanRow( relnew, ProductionLine := u.ID(), ProductID := ns.AsProductionSupply().ProductInStockingPoint_MP().ProductID(), Type := "2" ); | 
|           } | 
|            | 
|           // 找列 | 
|           opc := select( opt, OfflinePlanColumn, tempOPC, tempOPC.ColumnDate() = ns.Start().Date() ); | 
|           if ( isnull( opc ) ) { | 
|             opc := opt.OfflinePlanColumn( relnew, ColumnDate := ns.Start().Date() ); | 
|           } | 
|            | 
|           // 赋值单元格 | 
|           cellQuantity := oprQuantity.OfflinePlanCell( relnew, Value := [String]ns.Quantity().Round( 0 ), Shift := ns.PeriodTask_MP().UnitPeriod().astype( UnitPeriodTimeBase ).ShiftPattern().Name() ); | 
|           cellQuantity.OfflinePlanColumn( relset, opc ); | 
|           cellOrder := oprOrder.OfflinePlanCell( relnew,  | 
|     //                                             Value := "单号" + [String]ns.Quantity().Round( 0 ) + "_" + [String]( ns.ProductInStockingPointInPeriodPlanningLeaf().InventoryLevelEnd() - ns.ProductInStockingPointInPeriodPlanningLeaf().MinInventoryLevel() ), | 
|                                                  Value := [String]ns.Quantity().Round( 0 ), | 
|                                                  InventoryWeight := ns.ProductInStockingPointInPeriodPlanningLeaf().InventoryLevelEnd() - ns.ProductInStockingPointInPeriodPlanningLeaf().MinInventoryLevel() ); | 
|           cellOrder.OfflinePlanColumn( relset, opc ); | 
|         } | 
|       } | 
|        | 
|       // 创建总量行 | 
|       opt.OfflinePlanRow( relnew, ProductionLine := u.ID(), ProductID := "Z", Type := "3" ); | 
|       // 创建班次行 | 
|       opt.OfflinePlanRow( relnew, ProductionLine := u.ID(), ProductID := "Z", Type := "4" ); | 
|       // 创建班次开始时间行 | 
|       opt.OfflinePlanRow( relnew, ProductionLine := u.ID(), ProductID := "Z", Type := "5" ); | 
|       // 创建班次结束时间行 | 
|       opt.OfflinePlanRow( relnew, ProductionLine := u.ID(), ProductID := "Z", Type := "6" ); | 
|     } | 
|      | 
|     // 创建产品列&类型列 | 
|     productOPC := opt.OfflinePlanColumn( relnew, ColumnDate := macroPlan.StartOfPlanning().Date() - 2 ); | 
|     typeOPC    := opt.OfflinePlanColumn( relnew, ColumnDate := macroPlan.StartOfPlanning().Date() - 1 ); | 
|     traverse ( opt, OfflinePlanRow, opr ) { | 
|       if ( opr.Type() = "0" ) { | 
|         productLineCell := opr.OfflinePlanCell( relnew, Value := opr.ProductionLine() ); | 
|         productLineCell.OfflinePlanColumn( relset, productOPC ); | 
|       } else if( opr.Type() = "1" ) { | 
|         productCell := opr.OfflinePlanCell( relnew, Value := opr.ProductID() + ifexpr( opr.Notes() = "", "", "-" + opr.Notes() ) ); | 
|         productCell.OfflinePlanColumn( relset, productOPC ); | 
|         typeCell    := opr.OfflinePlanCell( relnew, Value :=  "Quantity" ); | 
|         typeCell.OfflinePlanColumn( relset, typeOPC ); | 
|       } else if ( opr.Type() = "2" ) { | 
|         typeCell    := opr.OfflinePlanCell( relnew, Value :=  "Order" ); | 
|         typeCell.OfflinePlanColumn( relset, typeOPC ); | 
|       } else if ( opr.Type() = "3" ) { | 
|         totalCell := opr.OfflinePlanCell( relnew, Value := "合计" ); | 
|         totalCell.OfflinePlanColumn( relset, productOPC ); | 
|         totalCell := opr.OfflinePlanCell( relnew, Value :=  "总量" ); | 
|         totalCell.OfflinePlanColumn( relset, typeOPC ); | 
|       } else if ( opr.Type() = "4" ) { | 
|         shiftCell := opr.OfflinePlanCell( relnew, Value :=  "班次" ); | 
|         shiftCell.OfflinePlanColumn( relset, typeOPC ); | 
|       } else if ( opr.Type() = "5" ) { | 
|         shiftStartDateCell := opr.OfflinePlanCell( relnew, Value :=  "班次开始时间" ); | 
|         shiftStartDateCell.OfflinePlanColumn( relset, typeOPC ); | 
|       } else if ( opr.Type() = "6" ) { | 
|         shiftEndDateCell := opr.OfflinePlanCell( relnew, Value :=  "班次结束时间" ); | 
|         shiftEndDateCell.OfflinePlanColumn( relset, typeOPC ); | 
|       } | 
|     } | 
|      | 
|     // 补全总量&班次&班次开始时间&班次结束时间 | 
|     totalOPRs          := selectset( opt, OfflinePlanRow, tempOPR, tempOPR.Type() = "3" ); | 
|     shiftOPRs          := selectset( opt, OfflinePlanRow, tempOPR, tempOPR.Type() = "4" ); | 
|     shiftStartDateOPRs := selectset( opt, OfflinePlanRow, tempOPR, tempOPR.Type() = "5" ); | 
|     shiftEndDateOPRs   := selectset( opt, OfflinePlanRow, tempOPR, tempOPR.Type() = "6" ); | 
|     traverse ( opt, OfflinePlanColumn, opc, opc.OfflinePlanCell( relsize ) > 0 and opc.ColumnDate() >= macroPlan.StartOfPlanning().Date() ) { | 
|       // 补全总量 | 
|       traverse ( totalOPRs, Elements, totalOPR ) { | 
|         total     := sum( opc, OfflinePlanCell, tempOPC, tempOPC.OfflinePlanRow().ProductionLine() = totalOPR.ProductionLine() and tempOPC.OfflinePlanRow().Type() = "1", [Real]tempOPC.Value() ); | 
|         totalCell := totalOPR.OfflinePlanCell( relnew, Value := [String]total ); | 
|         totalCell.OfflinePlanColumn( relset, opc ); | 
|       } | 
|        | 
|       // &班次 | 
|       traverse ( shiftOPRs, Elements, shiftOPR ) { | 
|         shift := select( opc, OfflinePlanCell, tempOPC, tempOPC.OfflinePlanRow().ProductionLine() = shiftOPR.ProductionLine() and tempOPC.OfflinePlanRow().Type() = "1" );  | 
|         if ( not isnull( shift ) ) { | 
|           shiftCell := shiftOPR.OfflinePlanCell( relnew, Value := shift.Shift() ); | 
|           shiftCell.OfflinePlanColumn( relset, opc ); | 
|         } | 
|       } | 
|        | 
|       // 班次开始时间 | 
|       traverse ( shiftStartDateOPRs, Elements, ssdOPR ) { | 
|         shift := select( opc, OfflinePlanCell, tempOPC, tempOPC.OfflinePlanRow().ProductionLine() = ssdOPR.ProductionLine() and tempOPC.OfflinePlanRow().Type() = "1" );  | 
|         if ( not isnull( shift ) ) { | 
|           startDate     := guard( minselect( macroPlan, ShiftPattern.ShiftDayTime, tempSDT, tempSDT.ShiftPattern().Name() = shift.Shift(), tempSDT.Sequence() ).StartDateTime().Format( "H:m" ), "" ); | 
|           startDateCell := ssdOPR.OfflinePlanCell( relnew, Value := startDate ); | 
|           startDateCell.OfflinePlanColumn( relset, opc ); | 
|         } | 
|       } | 
|        | 
|       // 班次结束时间 | 
|       traverse ( shiftEndDateOPRs, Elements, sedOPR ) { | 
|         shift := select( opc, OfflinePlanCell, tempOPC, tempOPC.OfflinePlanRow().ProductionLine() = sedOPR.ProductionLine() and tempOPC.OfflinePlanRow().Type() = "1" );  | 
|         if ( not isnull( shift ) ) { | 
|           endDate     := guard( maxselect( macroPlan, ShiftPattern.ShiftDayTime, tempSDT, tempSDT.ShiftPattern().Name() = shift.Shift(), tempSDT.Sequence() ).EndDateTIme().Format( "H:m" ), "" ); | 
|           endDateCell := sedOPR.OfflinePlanCell( relnew, Value := endDate ); | 
|           endDateCell.OfflinePlanColumn( relset, opc ); | 
|         } | 
|       } | 
|     } | 
|      | 
|     // 补全时间列 | 
|     indexDate := macroPlan.StartOfPlanning().Date(); | 
|     endDate   := Date::Construct( indexDate.Year(), 12, 31 ); | 
|     while ( indexDate <= endDate ) { | 
|       opc := select( opt, OfflinePlanColumn, tempOPC, tempOPC.ColumnDate() = indexDate ); | 
|       if ( isnull( opc ) ) { | 
|         opc := opt.OfflinePlanColumn( relnew, ColumnDate := indexDate ); | 
|       } | 
|       indexDate := indexDate + 1; | 
|     } | 
|      | 
|     // 补全空格子 | 
|     traverse ( opt, OfflinePlanRow, opr ) { | 
|       traverse ( opt, OfflinePlanColumn, opc ) { | 
|         cell := select( opr, OfflinePlanCell, tempOPC, tempOPC.OfflinePlanColumn() = opc ); | 
|         if ( isnull( cell ) ) { | 
|           cell := opr.OfflinePlanCell( relnew, Value := "" ); | 
|           cell.OfflinePlanColumn( relset, opc ); | 
|         } | 
|       } | 
|     } | 
|      | 
|     Transaction::Transaction().Propagate( attribute( OfflinePlanCell, Quantity ) ); | 
|     Transaction::Transaction().PropagateAll(); | 
|      | 
|     // 设置生产顺序 | 
|     productionLines := selectuniquevalues( opt, OfflinePlanRow, tempOPR, true, tempOPR.ProductionLine() ); | 
|     traverse ( productionLines, Elements, pl ) { | 
|       indexColumn    := select( opt, OfflinePlanColumn, tempOPC, tempOPC.ColumnDate() = macroPlan.StartOfPlanning().Date() ); | 
|       previousColumn := indexColumn.PreviousColumn(); | 
|       nextColumn     := indexColumn.NextColumn(); | 
|        | 
|       while ( not isnull( indexColumn.NextColumn() )  | 
|     //          and indexColumn.ColumnDate() <= Date::Construct( 2020, 4, 1 ) // 测试实际场景时可以过滤 | 
|              ) { | 
|         productionSerialNumber := 1; | 
|         opcs                   := selectsortedset( indexColumn, OfflinePlanCell, tempOPC, tempOPC.FindType( "2", pl ), tempOPC.InventoryWeight() ); | 
|         initialSize            := opcs.Size(); | 
|         info( "计划开始时间:", macroPlan.StartOfPlanning().Date().Format( "Y-M2-D2" ), "    索引时间:", indexDate.Format( "Y-M2-D2" ), "    个数:", opcs.Size() ); | 
|         if ( opcs.Size() > 0 ) {  | 
|           // 判定1 | 
|           previousOPC := maxselect( previousColumn, OfflinePlanCell, tempOPC, tempOPC.FindType( "2", pl ), tempOPC.ProductionSerialNumber() ); | 
|           if ( isnull( previousOPC ) or indexColumn.ColumnDate() = macroPlan.StartOfPlanning().Date() ) { | 
|             opc := opcs.Element( 0 ); | 
|             opc.Value( "#" + productionSerialNumber.Format( "N(LPad0(2))" ) ); | 
|             opc.ProductionSerialNumber( productionSerialNumber ); | 
|             productionSerialNumber++; | 
|             opcs.Remove( opc ); | 
|           } else { | 
|             opc := select( opcs, Elements, tempOPC, tempOPC.OfflinePlanRow().ProductID() = previousOPC.OfflinePlanRow().ProductID() ); | 
|             if ( not isnull( opc ) ) { | 
|               opc.Value( "#" + productionSerialNumber.Format( "N(LPad0(2))" ) ); | 
|               opc.ProductionSerialNumber( productionSerialNumber ); | 
|               productionSerialNumber++; | 
|               opcs.Remove( opc ); | 
|             } | 
|           } | 
|            | 
|           // 判定2 | 
|           nextOPCs := selectset( opcs, Elements, tempOPC, | 
|                                  exists( nextColumn, OfflinePlanCell, nextOPC, nextOPC.FindType( "2", pl ) and nextOPC.OfflinePlanRow().ProductID() = tempOPC.OfflinePlanRow().ProductID() ) ); | 
|           if ( nextOPCs.Size() = 1 ) { | 
|             opc := nextOPCs.Element( 0 ); | 
|             opc.Value( "#" + initialSize.Format( "N(LPad0(2))" ) ); | 
|             opc.ProductionSerialNumber( initialSize ); | 
|             opcs.Remove( opc ); | 
|           } | 
|            | 
|           // 判定3 | 
|           traverse ( opcs, Elements, opc ) { | 
|             opc.Value( "#" + productionSerialNumber.Format( "N(LPad0(2))" ) ); | 
|             opc.ProductionSerialNumber( productionSerialNumber ); | 
|             productionSerialNumber++; | 
|             opcs.Remove( opc ); | 
|           } | 
|         } | 
|          | 
|         indexColumn    := indexColumn.NextColumn(); | 
|         previousColumn := indexColumn.PreviousColumn(); | 
|         nextColumn     := indexColumn.NextColumn(); | 
|       } | 
|     } | 
|   *] | 
| } |