From 3d8707012ed7b1fce076a9100e58e47f05d0f72b Mon Sep 17 00:00:00 2001
From: hongji.li <hongji.a.li@capgemini.com>
Date: 星期二, 17 十月 2023 10:51:12 +0800
Subject: [PATCH] 异步分发

---
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingProductData.qbl                                                                           |   94 +++
 _Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Response_pnlActions_btnCancel_OnClick.def                                 |   15 
 _Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Response_pnlActions_btnOk_OnClick.def                                     |   30 +
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingActualPISPIPData.qbl                                                                      |   55 +
 _Main/BL/Type_MacroPlan/Method_DoASyncCreateLog.qbl                                                                                    |    8 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingCustomerOrderData.qbl                                                                     |   58 ++
 _Main/BL/Type_Global_MappingOperation/StaticMethod_DoASyncFindMinSeq.qbl                                                               |   17 
 _Main/BL/Type_CustomerOrder/StaticMethod_DoASync.qbl                                                                                   |   18 
 _Main/BL/Type_InventoryValueAndCost/StaticMethod_DoASync.qbl                                                                           |   16 
 _Main/BL/Type_MacroPlan/StaticMethod_DoASync#253.qbl                                                                                   |  166 +++++
 _Main/BL/Type_MacroPlan/StaticMethod_DoASync.qbl                                                                                       |   25 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingSalesSegmentData.qbl                                                                      |   60 ++
 _Main/BL/Type_BaseConversionFactor/StaticMethod_DoASync.qbl                                                                            |   17 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationBOMDataRouting.qbl                                                               |  102 +++
 _Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Component_pnlActions.def                                                  |   40 +
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationData.qbl                                                                         |   98 +++
 _Main/BL/Type_MacroPlan/StaticMethod_DoASyncOnException.qbl                                                                            |   13 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingUnitData.qbl                                                                              |  106 +++
 _Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Component_ListScenario.def                                                   |   11 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingUnitOfMeasureData.qbl                                                                     |   25 
 _Main/BL/Type_MacroPlan/StaticMethod_DoASyncSuccess.qbl                                                                                |   12 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingExternalSupplyData.qbl                                                                    |   67 ++
 _Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/_ROOT_Component_DialogDataDistribution.def                                |   33 +
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingBaseConversionFactorData.qbl                                                              |   22 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationCostData.qbl                                                                     |   75 ++
 _Main/BL/Type_SalesSegment_MP/StaticMethod_DoASync.qbl                                                                                 |   17 
 _Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Response_TIANMA_JITUAN_ListScenario_MenuScenarioDataDistribution_OnClick.def |   21 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingForecastData.qbl                                                                          |   50 +
 _Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Component_pnlContent.def                                                  |   58 ++
 _Main/BL/Type_GlobalDTOTable/Method_OnAsyncExecuteFailure_GlobalOTDTable_ProductInLane.qbl                                             |   13 
 _Main/BL/Type_GlobalDTOTable/StaticMethod_GetBusnessStrings.qbl                                                                        |   31 +
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingInventoryValueAndCostData.qbl                                                             |   32 +
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingProductInLaneData.qbl                                                                     |   26 
 _Main/BL/Type_MacroPlan/StaticMethod_DoASync#896.qbl                                                                                   |   21 
 _Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationBOMData.qbl                                                                      |  160 +++++
 _Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Method_DataDistribution.def                                               |   16 
 _Main/BL/Type_Forecast/StaticMethod_DoASync.qbl                                                                                        |   18 
 _Main/BL/Type_UnitOfMeasure_MP/StaticMethod_DoASync.qbl                                                                                |   17 
 38 files changed, 1,651 insertions(+), 12 deletions(-)

diff --git a/_Main/BL/Type_BaseConversionFactor/StaticMethod_DoASync.qbl b/_Main/BL/Type_BaseConversionFactor/StaticMethod_DoASync.qbl
new file mode 100644
index 0000000..2e257e4
--- /dev/null
+++ b/_Main/BL/Type_BaseConversionFactor/StaticMethod_DoASync.qbl
@@ -0,0 +1,17 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  MacroPlan macroPlan,
+  const GlobalOTDTable globalOTDTable
+)
+{
+  Description: '鍗曟鍚屾'
+  TextBody:
+  [*
+    // yypsybs Aug-17-2023 (created)
+    //info( "Product Finished, Start BaseConversionFactor Data Broker" )
+    //macroPlan.Broker_OTD_BaseConversionFactor().Execute();
+    info( "BaseConversionFactor Data Broker Finished, Start BaseConversionFactor Mapping" )
+    macroPlan.DoASyncMappingBaseConversionFactorData(globalOTDTable);
+  *]
+}
diff --git a/_Main/BL/Type_CustomerOrder/StaticMethod_DoASync.qbl b/_Main/BL/Type_CustomerOrder/StaticMethod_DoASync.qbl
new file mode 100644
index 0000000..dd7d7c9
--- /dev/null
+++ b/_Main/BL/Type_CustomerOrder/StaticMethod_DoASync.qbl
@@ -0,0 +1,18 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  MacroPlan macroPlan,
+  Strings businessTypes,
+  const GlobalOTDTable globalOTDTable,
+  Strings organcodelist
+)
+{
+  TextBody:
+  [*
+    // yypsybs Aug-17-2023 (created)
+    //info( "Forecast Finished, Start CustomerOrder Data Broker" );
+    //macroPlan.Broker_OTD_CustomerOrder().Execute();
+    info( "CustomerOrder Data Broker Finished, Start CustomerOrder Mapping" );
+    macroPlan.DoASyncMappingCustomerOrderData( businessTypes,globalOTDTable, organcodelist );
+  *]
+}
diff --git a/_Main/BL/Type_Forecast/StaticMethod_DoASync.qbl b/_Main/BL/Type_Forecast/StaticMethod_DoASync.qbl
new file mode 100644
index 0000000..84406b6
--- /dev/null
+++ b/_Main/BL/Type_Forecast/StaticMethod_DoASync.qbl
@@ -0,0 +1,18 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  MacroPlan macroPlan,
+  Strings businessTypes,
+  const GlobalOTDTable globalOTDTable,
+  Strings organcodelist
+)
+{
+  TextBody:
+  [*
+    // yypsybs Aug-17-2023 (created)
+    //info( "OperationCost Finished, Start Forecast Data Broker" );
+    //macroPlan.Broker_OTD_Forecast().Execute();
+    info( "Forecast Data Broker Finished, Start Forecast Mapping" );
+    macroPlan.DoASyncMappingForecastData( businessTypes,globalOTDTable, organcodelist );
+  *]
+}
diff --git a/_Main/BL/Type_GlobalDTOTable/Method_OnAsyncExecuteFailure_GlobalOTDTable_ProductInLane.qbl b/_Main/BL/Type_GlobalDTOTable/Method_OnAsyncExecuteFailure_GlobalOTDTable_ProductInLane.qbl
index 5bf60a9..c137daf 100644
--- a/_Main/BL/Type_GlobalDTOTable/Method_OnAsyncExecuteFailure_GlobalOTDTable_ProductInLane.qbl
+++ b/_Main/BL/Type_GlobalDTOTable/Method_OnAsyncExecuteFailure_GlobalOTDTable_ProductInLane.qbl
@@ -5,16 +5,5 @@
   String errorMessage
 )
 {
-  TextBody:
-  [*
-    this.Global_BrokerExecuteLog( relnew,
-                                  BrokerName    := "GlobalOTDTable_ProductInLane",
-                                  ElementTotal  := 0,
-                                  ErrorMessage  := errorMessage,
-                                  IsSuccess     := false,
-                                  Name          := "浜у搧鍦ㄨ溅閬撴暟鎹�",
-                                  ErrorNo       := errorNo,
-                                  ErrorDateTime := DateTime::ActualTime().Format( "Y-M-D H2:m:s" )
-                                 );
-  *]
+  TextBody: 'this.SettingFailureDetails( errorNo, errorMessage, "GlobalOTDTable_ProductInLane", "浜у搧鍦ㄨ溅閬撴暟鎹�" );'
 }
diff --git a/_Main/BL/Type_GlobalDTOTable/StaticMethod_GetBusnessStrings.qbl b/_Main/BL/Type_GlobalDTOTable/StaticMethod_GetBusnessStrings.qbl
new file mode 100644
index 0000000..fe29518
--- /dev/null
+++ b/_Main/BL/Type_GlobalDTOTable/StaticMethod_GetBusnessStrings.qbl
@@ -0,0 +1,31 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod GetBusnessStrings (
+  const GlobalOTDTable globalOTDTable
+) as String
+{
+  TextBody:
+  [*
+    // NBoTk Sep-7-2023 (created)
+    // 鑾峰彇businessType 闆嗗悎
+    result := "";
+    scenarioNameList := construct( structured[String]);
+    
+    traverse( globalOTDTable,BusinessType,b)
+    {
+        scenarioNameList.Add( b.ScenarioName() ); 
+    }
+    
+    // 濡傛灉瀛樺湪businessType鏁版嵁 鍒欒繑鍥� : a,b,c
+    
+    if( not isnull( scenarioNameList ) )
+    {
+        result := scenarioNameList.Concatenate( ";" );
+    }
+    
+    // 娴嬭瘯鏁版嵁
+    //result := "闆嗗洟闈㈡澘;涓撲笟鏄剧ず浜嬩笟閮�;澶栧崠CELL;闈炴樉;杩愬姩鍋ュ悍;杩愬姩鍋ュ悍浜嬩笟閮�;鎵嬫満浜嬩笟閮�;鐗圭鏄剧ず;姹借溅鐢靛瓙浜嬩笟閮�;杞﹁浇鏄剧ず浜嬩笟閮�;IT浜嬩笟閮�";
+    
+    return result;
+  *]
+}
diff --git a/_Main/BL/Type_Global_MappingOperation/StaticMethod_DoASyncFindMinSeq.qbl b/_Main/BL/Type_Global_MappingOperation/StaticMethod_DoASyncFindMinSeq.qbl
new file mode 100644
index 0000000..554a652
--- /dev/null
+++ b/_Main/BL/Type_Global_MappingOperation/StaticMethod_DoASyncFindMinSeq.qbl
@@ -0,0 +1,17 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASyncFindMinSeq (
+  const GlobalOTDTable globalOTDTable,
+  String orgCode,
+  String productId,
+  String processSection
+) as Number
+{
+  TextBody:
+  [*
+    // renhao Sep-20-2023 (created)
+    return guard (min( globalOTDTable, Global_MappingOperation, item, 
+                item.OrganCode() = orgCode and item.ProductID() = productId and item.ProcessSection() = processSection,
+                item.SequenceNumber() ), Number::MinNumber() );
+  *]
+}
diff --git a/_Main/BL/Type_InventoryValueAndCost/StaticMethod_DoASync.qbl b/_Main/BL/Type_InventoryValueAndCost/StaticMethod_DoASync.qbl
new file mode 100644
index 0000000..076964d
--- /dev/null
+++ b/_Main/BL/Type_InventoryValueAndCost/StaticMethod_DoASync.qbl
@@ -0,0 +1,16 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  MacroPlan macroPlan,
+  const GlobalOTDTable globalOTDTable
+)
+{
+  TextBody:
+  [*
+    // yypsybs Aug-17-2023 (created)
+    //info( "ActualPISPIP Finished, Start InventoryCost Data Broker" );
+    //macroPlan.Broker_OTD_InventoryCost().Execute();
+    info( "InventoryCost Data Broker Finished, Start InventoryCost Mapping" );
+    macroPlan.DoASyncMappingInventoryValueAndCostData(globalOTDTable);
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncCreateLog.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncCreateLog.qbl
new file mode 100644
index 0000000..615b14d
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncCreateLog.qbl
@@ -0,0 +1,8 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncCreateLog (
+  GlobalOTDTable globalOTDTable
+)
+{
+  TextBody: 'Global_BrokerExecuteLog::CreateInOperation( globalOTDTable, "DataDistribution", "" );'
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingActualPISPIPData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingActualPISPIPData.qbl
new file mode 100644
index 0000000..f9c40e7
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingActualPISPIPData.qbl
@@ -0,0 +1,55 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingActualPISPIPData (
+  Strings businessTypes,
+  const GlobalOTDTable globalOTDTable,
+  Boolean nuclear,
+  Strings organcodelist
+)
+{
+  TextBody:
+  [*
+    // renhao Aug-14-2023 (created)
+    listtodeal := selectset( globalOTDTable,
+                             Global_MappingActualProductInStockingPointInPeriod,
+                             actual,
+                             ( actual.ActualInventoryLevelEnd() > 0 ) and 
+                             ( organcodelist.Find( actual.StockingPointID().SubString( 0, 3 ) ) >= 0 ) );
+    totalcount := listtodeal.Size();
+    info( "ActualPISPIP has " + totalcount.AsQUILL() + " rows in total" );
+    
+    count := 0;
+    traverse( listtodeal,Elements,actual){
+      count := count + 1;
+      if( count - [Number](count/1000) * 1000 = 0 or count = totalcount ){
+        info( "Now is dealing with the " + count.AsQUILL() + "ActualPISPIP " + "( " + count.AsQUILL() + "/" + totalcount.AsQUILL() + " ) " + (count/totalcount*100).Round( 1 ).AsQUILL() + "%" );
+        }
+      product := select( globalOTDTable,Global_MappingProduct_MP,product,product.ID() = actual.ProductID() and product.KeyProduct() = nuclear,true);
+      if( not isnull( product)){
+      if( not isnull(businessTypes)){
+      
+        for( i :=0 ;i < businessTypes.Size();i++ ){
+          businessType := businessTypes.Element( i );
+          if( product.BusinessType() = businessType and not product.IsCommon()){
+            ActualProductInStockingPointInPeriod::CreateOrUpdate( this,
+                                                                  actual.ProductID(),
+                                                                  actual.StockingPointID(),
+                                                                  actual.Description(),
+                                                                  actual.ActualInventoryLevelEnd(),
+                                                                  actual.ManufacturedDate());
+          }
+        }
+        
+      }else{
+        ActualProductInStockingPointInPeriod::CreateOrUpdate( this,
+                                                              actual.ProductID(),
+                                                              actual.StockingPointID(),
+                                                              actual.Description(),
+                                                              actual.ActualInventoryLevelEnd(),
+                                                              actual.ManufacturedDate());
+      }
+        
+      }
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingBaseConversionFactorData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingBaseConversionFactorData.qbl
new file mode 100644
index 0000000..1a82f50
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingBaseConversionFactorData.qbl
@@ -0,0 +1,22 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingBaseConversionFactorData (
+  const GlobalOTDTable globalOTDTable
+)
+{
+  Description: 'ETL鏁版嵁杞ā鍨嬫暟鎹�'
+  TextBody:
+  [*
+    // yypsybs Aug-15-2023 (created)
+    traverse( globalOTDTable, Global_MappingConversionFactor, item ) {
+      //濡傛灉ProductID涓嶄负绌猴紝涓斿尮閰嶄笉鍒癙roduct锛屽垯涓嶅垱寤猴紝杩斿洖涓虹┖
+    //    info( item.ProductId().AsQUILL() );
+        BaseConversionFactor::CreateOrUpdate( this, 
+                                              item.SourceUnitOfMeasureName(), 
+                                              item.TargetUnitOfMeasureName(), 
+                                              item.IsEnabled(), 
+                                              item.ProductID(), 
+                                              item.Factor() );
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingCustomerOrderData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingCustomerOrderData.qbl
new file mode 100644
index 0000000..e56a4f7
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingCustomerOrderData.qbl
@@ -0,0 +1,58 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingCustomerOrderData (
+  Strings businessTypes,
+  const GlobalOTDTable globalOTDTable,
+  Strings organcodelist
+)
+{
+  Description: 'ETL璁㈠崟棰勬祴'
+  TextBody:
+  [*
+    // yypsybs Aug-15-2023 (created)
+    // 寰呭鐞嗘暟鎹�
+    listToDeal := construct( Global_MappingCustomOrders, constcontent );
+    if( isnull( businessTypes ) or businessTypes.Size() = 0 ) {
+        listToDeal := selectset( globalOTDTable, Global_MappingCustomOrder, item, true );
+    } else {
+        listToDeal := selectset( globalOTDTable, 
+                                 Global_MappingCustomOrder, 
+                                 item, 
+                                 ( businessTypes.Find( item.BusinessType() ) <> -1 ) and 
+                                 ( organcodelist.Find( item.StockingPointID().SubString( 0, 3 ) ) >= 0 ) );
+    }
+    queryStartDate := guard( min( this, Period_MP, item, true, item.StartDate() ) - Duration::Days( 30 ), DateTime::MinDateTime() ).Date();
+    queryEndDate := guard( max( this, Period_MP, item, true, item.EndDate() ), Date::MaxDate() );
+    listToDeal := selectset( listToDeal, Elements, item, item.OrderDate() >= queryStartDate /*and item.OrderDate() <= queryEndDate*/, not isnull( Product_MP::FindById( this, item.ProductID() ) ) and not isnull( StockingPoint_MP::FindById( this, item.StockingPointID() ) ) and not isnull( SalesSegment_MP::FindByName( this, item.SalesSegmentName() ) ) );
+    
+    totalcount := listToDeal.Size();
+    info( "CustomerOrder has " + totalcount.AsQUILL() + " rows in total" );
+    
+    count := 0;
+    priorityName :="Normal";
+    // 澶勭悊
+    traverse( listToDeal, Elements, item ) {
+      count := count + 1;
+      if( count - [Number](count/100) * 100 = 0 or count = totalcount ){
+        info( "Now is dealing with the " + count.AsQUILL() + "CustomerOrder " + "( " + count.AsQUILL() + "/" + totalcount.AsQUILL() + " ) " + (count/totalcount*100).Round( 1 ).AsQUILL() + "%" );
+        }
+    //  if( not isnull( Product_MP::FindById( this, item.ProductID() ) ) ){
+    //    info( item.ProductID().AsQUILL() );
+      customOrder := CustomerOrder::CreateOrUpdate( this, item.CurrencyID(), item.Customer(), item.CustomerID(), item.ID(), 
+                                                    item.OrderDate(), item.OrderID(), item.OrderLineID(), item.Price(), 
+                                                    priorityName, item.ProductID(), 
+                                                    item.StockingPointID(), 
+                                                    item.SalesSegmentName(), 
+                                                    item.Quantity(), item.UnitOfMeasureName(), item.OrderType(), item.IsAvailable());
+      customOrder.BusinessType( item.BusinessType() );
+      customOrder.OrderType( item.OrderType() );
+      customOrder.OrderTime( item.OrderTime() );
+      customOrder.ProductGrade( item.ProductGrade() );
+      customOrder.SegmentPriority( item.SegmentPriority() );
+      customOrder.SheetProfitability( item.SheetProfitability() );
+    //  }else{
+    //    info( "invaild product: " + item.ProductID().AsQUILL() );
+    //    }
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingExternalSupplyData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingExternalSupplyData.qbl
new file mode 100644
index 0000000..e8950bc
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingExternalSupplyData.qbl
@@ -0,0 +1,67 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingExternalSupplyData (
+  Strings businessTypes,
+  Boolean nuclear,
+  const GlobalOTDTable globalOTDTable,
+  Strings organcodelist
+)
+{
+  TextBody:
+  [*
+    // renhao Aug-14-2023 (created)
+    queryStartDate := guard( min( this, Period_MP, item, true, item.StartDate() ) - Duration::Days( 30 ), DateTime::MinDateTime() ).Date();
+    queryEndDate := guard( max( this, Period_MP, item, true, item.EndDate() ), Date::MaxDate() );
+    listtodeal := selectset( globalOTDTable,
+                             Global_MappingInventorySupply,
+                             externalSupply, 
+                             ( externalSupply.UserQuantity()>0 ) and 
+                             ( externalSupply.Date() >= queryStartDate ) and 
+                             ( organcodelist.Find( externalSupply.StockingPointID().SubString( 0, 3 ) ) >= 0 )/*and externalSupply.Date() <= queryEndDate*/);
+    totalcount := listtodeal.Size();
+    description := "鍦ㄩ�斿湪鍒�";
+    info( "ExternalSupply has " + totalcount.AsQUILL() + " rows in total" );
+    
+    count := 0;
+    traverse( listtodeal,Elements,externalSupply){
+      count := count + 1;
+      if( count - [Number](count/1000) * 1000 = 0 or count = totalcount ){
+        info( "Now is dealing with the " + count.AsQUILL() + "ExternalSupply " + "( " + count.AsQUILL() + "/" + totalcount.AsQUILL() + " ) " + (count/totalcount*100).Round( 1 ).AsQUILL() + "%" );
+       }
+      product := select( globalOTDTable,Global_MappingProduct_MP,product,product.ID() = externalSupply.ProductID(),true);
+      
+      
+      if( not isnull( product)){
+        productMP :=  Product_MP :: FindProductTypeIndex( externalSupply.ProductID());
+        stockingpoint := select( this,StockingPoint_MP,st,st.ID() = externalSupply.StockingPointID(),true);
+        if( not isnull( stockingpoint) and not isnull( productMP)){
+        if( not isnull(businessTypes)){
+        
+          for( i :=0 ;i < businessTypes.Size();i++ ){
+            businessType := businessTypes.Element( i );
+            if( product.BusinessType() = businessType and not product.IsCommon()){
+              InventorySupply::CreateOrUpdate( externalSupply.ID(),
+                                               productMP,
+                                               stockingpoint,
+                                               externalSupply.Date(),
+                                               externalSupply.ManufacturedDate(),
+                                               externalSupply.UserQuantity(),description);
+            }
+          }
+          
+        }else{
+          
+            InventorySupply::CreateOrUpdate( externalSupply.ID(),
+                                           productMP,
+                                           stockingpoint,
+                                           externalSupply.Date(),
+                                           externalSupply.ManufacturedDate(),
+                                           externalSupply.UserQuantity(),description);
+          }
+      
+        }
+        
+      }
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingForecastData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingForecastData.qbl
new file mode 100644
index 0000000..e49c4b4
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingForecastData.qbl
@@ -0,0 +1,50 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingForecastData (
+  Strings businessTypes,
+  const GlobalOTDTable globalOTDTable,
+  Strings organcodelist
+)
+{
+  Description: 'ETL璁㈠崟棰勬祴'
+  TextBody:
+  [*
+    // yypsybs Aug-15-2023 (created)
+    // 寰呭鐞嗘暟鎹�
+    listToDeal := construct( Global_MappingForecasts, constcontent );
+    if( isnull( businessTypes ) or businessTypes.Size() = 0 ) {
+        listToDeal := selectset( globalOTDTable, Global_MappingForecast, item, item.Quantity() > 0 );
+    } else {
+        listToDeal := selectset( globalOTDTable, 
+                                 Global_MappingForecast, 
+                                 item, 
+                                 ( businessTypes.Find( item.BusinessType() ) <> -1 ) and 
+                                 ( item.Quantity()>0 ) and 
+                                 ( organcodelist.Find( item.StockingPointID().SubString( 0, 3 ) ) >= 0 ) );
+    }
+    queryStartDate := guard( min( this, Period_MP, item, true, item.StartDate() ) - Duration::Days( 30 ), DateTime::MinDateTime() ).Date();
+    queryEndDate := guard( max( this, Period_MP, item, true, item.EndDate() ), Date::MaxDate() );
+    listToDeal := selectset( listToDeal, Elements, item, item.StartDate() >= queryStartDate and item.EndDate() <= queryEndDate, not isnull( Product_MP::FindById( this, item.ProductID() ) ) and not isnull( StockingPoint_MP::FindById( this, item.StockingPointID() ) ) and not isnull( SalesSegment_MP::FindByName( this, item.SalesSegmentName() ) ) );
+    
+    totalcount := listToDeal.Size();
+    info( "Forecast has " + totalcount.AsQUILL() + " rows in total" );
+    
+    count := 0;
+    priorityName := "Normal";
+    // 澶勭悊
+    traverse( listToDeal, Elements, item ) {
+      count := count + 1;
+      if( count - [Number](count/100) * 100 = 0 or count = totalcount ){
+        info( "Now is dealing with the " + count.AsQUILL() + "Forecast " + "( " + count.AsQUILL() + "/" + totalcount.AsQUILL() + " ) " + (count/totalcount*100).Round( 1 ).AsQUILL() + "%" );
+        }
+    //  if( not isnull( Product_MP::FindById( this, item.ProductID() ) ) ){
+      Forecast::CreateOrUpdate( this, 
+                                item.ProductID(), item.SalesSegmentName(), item.StockingPointID(), priorityName,
+                                item.CurrencyID(), item.UnitOfMeasureName(),
+                                item.ID(), item.StartDate(), item.EndDate(), item.Quantity(), item.Price());
+    //  }else{
+    //    info( "invaild product" + item.ProductID().AsQUILL() );
+    //    }
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingInventoryValueAndCostData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingInventoryValueAndCostData.qbl
new file mode 100644
index 0000000..b0e0349
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingInventoryValueAndCostData.qbl
@@ -0,0 +1,32 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingInventoryValueAndCostData (
+  const GlobalOTDTable globalOTDTable
+)
+{
+  TextBody:
+  [*
+    // yypsybs Aug-15-2023 (created)
+    listtodeal := selectset( globalOTDTable,Global_MappingStockingPointCost,item,true );
+    totalcount := listtodeal.Size();
+    
+    info( "InventoryCost has " + totalcount.AsQUILL() + " rows in total" );
+    
+    count := 0;
+    traverse( listtodeal, Elements, item ) {
+        count := count + 1;
+        if( count - [Number](count/1000) * 1000 = 0 or count = totalcount ){
+          info( "Now is dealing with the " + count.AsQUILL() + "InventoryCost " + "( " + count.AsQUILL() + "/" + totalcount.AsQUILL() + " ) " + (count/totalcount*100).Round( 1 ).AsQUILL() + "%" );
+        }
+        
+        InventoryValueAndCost::CreateOrUpdate( this, item.ID(), item.ProductID(), 
+                                                 item.StockingPointID(), 
+                                                 item.AccountName(), item.CostDriver(), item.Start(), item.Cost());
+    //  if( not isnull( Product_MP::FindProductTypeIndex( item.ProductID() ) )  and not isnull( StockingPoint_MP :: FindStockingPointTypeIndex( item.StockingPointID()))){
+    //      
+    //  }else{
+    //    info( "invaild product: " + item.ProductID().AsQUILL() );
+    //    }
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationBOMData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationBOMData.qbl
new file mode 100644
index 0000000..8c1a8e4
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationBOMData.qbl
@@ -0,0 +1,160 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingOperationBOMData (
+  Strings businessTypes,
+  Boolean isKeyProduct,
+  Boolean createPurchaseSupplyMaterial,
+  const GlobalOTDTable globalOTDTable,
+  Strings organcodelist
+)
+{
+  TextBody:
+  [*
+    // yypsybs Aug-21-2023 (created)
+    keyProductList := construct( Strings );
+    if( isKeyProduct ) {
+        keyProductList := selectuniquevalues( globalOTDTable, Global_MappingProduct_MP, item, item.ProductMajorType()="鎴愬搧" or item.ProductMajorType()="鍗婃垚鍝�", item.ID() );
+    }
+    bomList := selectsortedset(  globalOTDTable, Global_MappingOperationBOM, item,
+                                 ifexpr( isnull( businessTypes ) or businessTypes.Size() = 0, 
+                                         true, 
+    //                                     businessTypes.Difference( businessTypes.Difference( item.BusinessType().Tokenize( ", " ) ) ).Size() > 0 )
+                                         ( businessTypes.Find( item.BusinessType() ) >= 0 ) and 
+                                         ( organcodelist.Find( item.OrganCode() ) >= 0 ) )
+    //                             and ifexpr( isKeyProduct, 
+    //                                         keyProductList.Size() > 0 and keyProductList.Find( item.ComponentCode() ) >= 0,
+    //                                         true )
+                                 ,
+                                 item.OrganCode() + "_" + item.ProductCode() + "_" + item.ProcessSection() );
+    // 鎸塺outing鍙妑outingStep鍒嗙粍
+    routingIds := selectuniquevalues( bomList, Elements, item, true, item.OrganCode() + "_" + item.ProductCode() );
+    traverse( routingIds, Elements, routingId ) {
+        routingRows := selectset( bomList, Elements, item, true, routingId = item.OrganCode() + "_" + item.ProductCode() );
+        if( routingRows.Size() > 0 ) {
+            firstRow := routingRows.Element( 0 );
+    //        stockingPointId := firstRow.OrganCode()  + "_" + firstRow.ProductType() + "_Stock";
+    //        inputStockingPointId := firstRow.OrganCode()  + "_" + firstRow.ComponentType() + "_Stock";
+            stockingPointId := firstRow.OrganCode()  + "_Stock";
+    
+            // ========妫�鏌�========
+            product := Product_MP::FindProductTypeIndex( firstRow.ProductCode() );
+            if( not isnull( product ) ) {
+    //              error( "product : " + firstRow.ProductCode() + " not found" );
+              stockingPoint := StockingPoint_MP::FindStockingPointTypeIndex( stockingPointId );
+    
+    //          info( stockingPointId.AsQUILL() );
+      //        if( isnull( stockingPoint ) ) {
+      //            error( "stockingPoint : " + stockingPointId + " not found" );
+      //        }
+              routing := Routing::FindRoutingTypeIndex(  routingId );
+              if( not isnull( routing ) ) {
+    //              error( "routing : " + routingId + " not found" );
+                // ========澶勭悊杈撳嚭========
+                if( not isnull( stockingPoint ) ) {
+    //              info( 1 );
+                  operationsInLastSteps := Operation::FindFinalOperationsByRoutingId( this, routingId );
+                  traverse( operationsInLastSteps, Elements, operationsInLastStep ) {
+                      operationsInLastStep.CreateOperationBOM( product, stockingPoint, false, true );
+                      operationsInLastStep.GetOperationBOM( product.ID(), stockingPoint.ID(), false ).Quantity( 1 );
+                  }
+                  // ========鍒嗙粍澶勭悊杈撳叆========
+                  
+                }
+                
+                if( not isnull( stockingPoint)){
+                  if( isKeyProduct){
+                      keyRows := selectset( routingRows,Elements,routingrow,keyProductList.Find( routingrow.ComponentCode())>=0);
+                      this.DoASyncMappingOperationBOMDataRouting( routing,globalOTDTable,keyRows);
+                    }else{
+                      this.DoASyncMappingOperationBOMDataRouting( routing,globalOTDTable,routingRows);
+                    }
+                    
+                }
+              }
+           }
+        }
+    }
+    if( createPurchaseSupplyMaterial ) {
+        toCreateBomList := selectuniquevalues( bomList, Elements, item, 
+                                               item.ComponentType() = "P" and keyProductList.Find( item.ComponentCode())>=0, item.OrganCode() + item.ComponentCode());
+        traverse( toCreateBomList, Elements, key ) {
+            boms := selectset( bomList, Elements, item, item.ComponentType() = "P" and item.OrganCode() + item.ComponentCode() = key );
+            bom := boms.First();
+            this.MappingOperationBOMDataSupplyPurchase( bom.OrganCode(), bom.ComponentCode(), bom.ComponentType());
+        }
+    }
+    
+    
+    //
+    //keyProductList := construct( Strings );
+    //if( isKeyProduct ) {
+    //    keyProductList := selectuniquevalues( globalOTDTable, Global_MappingProduct_MP, item, item.ProductMajorType()="鎴愬搧" or item.ProductMajorType()="鍗婃垚鍝�", item.ID() );
+    //}
+    //bomList := selectsortedset(  globalOTDTable, Global_MappingOperationBOM, item,
+    //                             ifexpr( isnull( businessTypes ) or businessTypes.Size() = 0, 
+    //                                     true, 
+    ////                                     businessTypes.Difference( businessTypes.Difference( item.BusinessType().Tokenize( ", " ) ) ).Size() > 0 )
+    //                                     businessTypes.Find( item.BusinessType() ) >= 0 )
+    ////                             and ifexpr( isKeyProduct, 
+    ////                                         keyProductList.Size() > 0 and keyProductList.Find( item.ComponentCode() ) >= 0,
+    ////                                         true )
+    //                             ,
+    //                             item.OrganCode() + "_" + item.ProductCode() + "_" + item.ProcessSection() );
+    //// 鎸塺outing鍙妑outingStep鍒嗙粍
+    //routingIds := selectuniquevalues( bomList, Elements, item, true, item.OrganCode() + "_" + item.ProductCode() );
+    //traverse( routingIds, Elements, routingId ) {
+    //    routingRows := selectset( bomList, Elements, item, true, routingId = item.OrganCode() + "_" + item.ProductCode() );
+    //    if( routingRows.Size() > 0 ) {
+    //        firstRow := routingRows.Element( 0 );
+    ////        stockingPointId := firstRow.OrganCode()  + "_" + firstRow.ProductType() + "_Stock";
+    ////        inputStockingPointId := firstRow.OrganCode()  + "_" + firstRow.ComponentType() + "_Stock";
+    //        stockingPointId := firstRow.OrganCode()  + "_Stock";
+    //
+    //        // ========妫�鏌�========
+    //        product := Product_MP::FindProductTypeIndex( firstRow.ProductCode() );
+    //        if( not isnull( product ) ) {
+    ////              error( "product : " + firstRow.ProductCode() + " not found" );
+    //          stockingPoint := StockingPoint_MP::FindStockingPointTypeIndex( stockingPointId );
+    //
+    ////          info( stockingPointId.AsQUILL() );
+    //  //        if( isnull( stockingPoint ) ) {
+    //  //            error( "stockingPoint : " + stockingPointId + " not found" );
+    //  //        }
+    //          routing := Routing::FindRoutingTypeIndex(  routingId );
+    //          if( not isnull( routing ) ) {
+    ////              error( "routing : " + routingId + " not found" );
+    //            // ========澶勭悊杈撳嚭========
+    //            if( not isnull( stockingPoint ) ) {
+    ////              info( 1 );
+    //              operationsInLastSteps := Operation::FindFinalOperationsByRoutingId( this, routingId );
+    //              traverse( operationsInLastSteps, Elements, operationsInLastStep ) {
+    //                  operationsInLastStep.CreateOperationBOM( product, stockingPoint, false, true );
+    //                  operationsInLastStep.GetOperationBOM( product.ID(), stockingPoint.ID(), false ).Quantity( 1 );
+    //              }
+    //              // ========鍒嗙粍澶勭悊杈撳叆========
+    //              
+    //            }
+    //            if( not isnull( stockingPoint)){
+    //              if( isKeyProduct){
+    //                  keyRows := selectset( routingRows,Elements,routingrow,keyProductList.Find( routingrow.ComponentCode())>=0);
+    //                  this.MappingOperationBOMDataRouting( routing,globalOTDTable,keyRows);
+    //                }else{
+    //                  this.MappingOperationBOMDataRouting( routing,globalOTDTable,routingRows);
+    //                }
+    //                
+    //            }
+    //          }
+    //       }
+    //    }
+    //}
+    //if( createPurchaseSupplyMaterial ) {
+    //    toCreateBomList := selectuniquevalues( bomList, Elements, item, 
+    //                                           item.ComponentType() = "P", item.OrganCode() + item.ComponentCode());
+    //    traverse( toCreateBomList, Elements, key ) {
+    //        boms := selectset( bomList, Elements, item, item.ComponentType() = "P" and item.OrganCode() + item.ComponentCode() = key );
+    //        bom := boms.First();
+    //        this.MappingOperationBOMDataSupplyPurchase( bom.OrganCode(), bom.ComponentCode(), bom.ComponentType());
+    //    }
+    //}
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationBOMDataRouting.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationBOMDataRouting.qbl
new file mode 100644
index 0000000..43f35a0
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationBOMDataRouting.qbl
@@ -0,0 +1,102 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingOperationBOMDataRouting (
+  Routing routing,
+  const GlobalOTDTable globalOTDTable,
+  constcontent Global_MappingOperationBOMs routingRows
+)
+{
+  TextBody:
+  [*
+    // yypsybs Aug-21-2023 (created)
+    
+    // 鏍规嵁宸ヨ壓娈垫眹鎬诲鐞�
+    processSections := selectuniquevalues( routingRows, Elements, row, true, row.ProcessSection() );
+    traverse( processSections, Elements, processSection ) {
+        rows := selectset( routingRows, Elements, row, row.ProcessSection() = processSection );
+        firstRow := rows.Element( 0 );
+        // 鎵緊rgCode/productId/processSection鍖归厤涓攕equence鏈�灏忕殑涓�缁勬暟鎹�
+        minSeq := Global_MappingOperation::DoASyncFindMinSeq( globalOTDTable, firstRow.OrganCode(), firstRow.ProductCode(), processSection );
+        routingStepId := processSection + "_" + [String]minSeq;
+        // 鎵惧搴攔outingStep
+        routingStep := RoutingStep::FindByName( routing, routingStepId );
+        if( not isnull( routingStep ) ) {
+    //        error( "routing step : " + routing.ID() + "|" + routingStepId + " not found" );  
+        // 浠呬富鏂欙紝缁熶竴澶勭悊
+          noAlterRows := selectset( rows, Elements, noAlterRow, noAlterRow.AlternativeMaterialCode() = "" );
+          traverse( noAlterRows, Elements, noAlterRow ) {
+              traverse( routingStep, Operation, toLink ) {
+                  // 妫�鏌ヤ富鏂�
+                  component := Product_MP::FindById( this, noAlterRow.ComponentCode() );
+                  if( not isnull( component ) ) {
+    //                  error( "component : " + noAlterRow.ComponentCode() + " not found" );
+                  // 杩炴帴浜у搧涓巓peration
+    //              inputStockingPointId := noAlterRow.OrganCode()  + "_" + noAlterRow.ComponentType() + "_Stock";
+                  inputStockingPointId := noAlterRow.OrganCode() + "_Stock";
+                  stockingPoint := StockingPoint_MP::FindStockingPointTypeIndex( inputStockingPointId);
+                  pisp := ProductInStockingPoint_MP::CreateIfNotFound( component, stockingPoint );
+                  trash := construct( OperationBOMs );
+                  operationBOM := toLink.LinkProduct( component, true, BaseOperationLink::GetGroupID( toLink, true, false ), pisp, 
+                                                      true, trash );
+                  operationBOM.MinQuantityInGroup( 0 );
+                  operationBOM.Quantity( noAlterRow.UnitUsageOfComponents() / noAlterRow.ComponentOutputRate() );
+                  operationBOM.MaxQuantityInGroup( noAlterRow.UnitUsageOfComponents() / noAlterRow.ComponentOutputRate() );
+                  }
+              }
+          }
+          // 鏇挎崲鏂欙紝鎸変富鏂欏垎缁勫鐞�
+          alterComponentIds := selectuniquevalues( rows, Elements, row, row.AlternativeMaterialCode() <> "", row.ComponentCode() );
+          traverse( alterComponentIds, Elements, alterComponentId ) {
+              alterRows := selectset( rows, Elements, row, row.ComponentCode() = alterComponentId );
+              firstAlterRow := alterRows.Element( 0 );
+              // 妫�鏌ヤ富鏂�
+              component := Product_MP::FindById( this, firstAlterRow.ComponentCode() );
+              if( not isnull( component ) ) {
+    //              error( "component : " + firstAlterRow.ComponentCode() + " not found" );
+    //          }
+                traverse( routingStep, Operation, toLink ) {
+                    // 杩炴帴浜у搧涓巓peration
+    //                inputStockingPointId := firstAlterRow.OrganCode()  + "_" + firstAlterRow.ComponentType() + "_Stock";
+                    inputStockingPointId := firstAlterRow.OrganCode() + "_Stock";
+                    stockingPoint := StockingPoint_MP::FindStockingPointTypeIndex( inputStockingPointId);
+                    pisp := ProductInStockingPoint_MP::CreateIfNotFound( component, stockingPoint );
+                    trash := construct( OperationBOMs );
+                    mainBOM := toLink.LinkProduct( component, true, BaseOperationLink::GetGroupID( toLink, true, false ), pisp, 
+                                                        true, trash );
+                    mainBOM.Quantity( firstAlterRow.UnitUsageOfComponents() / firstAlterRow.ComponentOutputRate() );
+                    mainBOM.MaxQuantityInGroup( firstAlterRow.UnitUsageOfComponents() / firstAlterRow.ComponentOutputRate() );
+                    mainQty := mainBOM.Quantity();
+                    Transaction::Transaction().Propagate();
+                    // 娣诲姞杈呮枡
+                    traverse( alterRows, Elements, alterRow ) {
+                        alterProd := Product_MP::FindById( this, alterRow.AlternativeMaterialCode() );
+                        if( not isnull( alterProd ) ) {
+        //                    error( "alterProd : " + alterRow.AlternativeMaterialCode() + " not found" );
+    //                      stockingPointIdAlter := alterRow.OrganCode()  + "_" + alterRow.ComponentType() + "_Stock";
+                          stockingPointIdAlter := alterRow.OrganCode() + "_Stock";
+                          stockingPointAlter := StockingPoint_MP::FindById( this, stockingPointIdAlter );
+                          if( isnull( stockingPointAlter ) ) {
+                              error( "stockingPoint : " + stockingPointIdAlter + " not found" );
+                          }
+                          pispAlter := ProductInStockingPoint_MP::CreateIfNotFound( alterProd, stockingPointAlter );
+                          //mainInput := toLink.LastOperationInput(); 
+                          mainInput := select( toLink,OperationInput,operationInut,operationInut.ProductID()=alterRow.ComponentCode());
+                          trash := construct( OperationBOMs );
+                          if( not isnull( mainInput ) ) {
+                              alterBom := mainInput.Operation().LinkPISP( pispAlter, true, mainInput.OperationLinkGroupID(), trash );
+                              alterBom.Quantity( mainBOM.MaxQuantityInGroup() * alterRow.AlternativeRate() );
+                              alterBom.MaxQuantityInGroup( mainBOM.MaxQuantityInGroup() );
+    //                          info( "Quantity" + [String]alterBom.Quantity() )
+    //                          info( "MaxQuantityInGroup" + [String]alterBom.MaxQuantityInGroup() )
+                              mainQty := mainQty - alterBom.Quantity();
+                          }
+                      }
+                      mainBOM.Quantity( mainQty );
+                   }
+                }
+              }
+          }
+       }
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationCostData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationCostData.qbl
new file mode 100644
index 0000000..af739ac
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationCostData.qbl
@@ -0,0 +1,75 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingOperationCostData (
+  const GlobalOTDTable globalOTDTable,
+  Strings businesstypes,
+  Strings organcodelist
+)
+{
+  Description: 'Get operation cost data from operation mapping'
+  TextBody:
+  [*
+    // Administrator Aug-21-2023 (created)
+    // list to deal
+    listtodeal := construct( structured[Global_MappingOperation], constcontent );
+    
+    if( isnull( businesstypes ) or businesstypes.Size() = 0 ) {
+        listtodeal := selectset( globalOTDTable, Global_MappingOperation, item, true );
+    } else {
+        listtodeal := selectset( globalOTDTable, Global_MappingOperation, 
+                                 item, 
+                                 ( businesstypes.Find( item.BusinessType() ) <> -1 ) and 
+                                 ( organcodelist.Find( item.OrganCode() ) >= 0 ) );
+    }
+    
+    // Get the list to deal with max sequence number
+    listtodealwithmaxsn := construct( structured[Global_MappingOperation], constcontent );
+    traverse( listtodeal, Elements, item ){
+      maxsn := maxselect( globalOTDTable, Global_MappingOperation, 
+                          moperation, 
+                          moperation.OrganCode() = item.OrganCode(), 
+                          moperation.ProductID() = item.ProductID(), 
+                          moperation.SequenceNumber() ).SequenceNumber()
+      if( item.SequenceNumber() = maxsn ){
+          listtodealwithmaxsn.Add( item );
+        }
+      }
+    
+    totalcount := listtodealwithmaxsn.Size();
+    info( "OperationCost has " + totalcount.AsQUILL() + " rows in total" );
+    
+    count := 0;
+    // Get the operation cost data
+    traverse( listtodealwithmaxsn, Elements, item ){
+      count := count + 1;
+      if( count - [Number](count/1000) * 1000 = 0 or count = totalcount ){
+        info( "Now is dealing with the " + count.AsQUILL() + "OperationCost " + "( " + count.AsQUILL() + "/" + totalcount.AsQUILL() + " ) " + (count/totalcount*100).Round( 1 ).AsQUILL() + "%" );
+        }
+      id := item.OrganCode() + "_" + item.ProductID() + "_" + item.ProcessSection()+"_" + [String]item.SequenceNumber();
+      if( guard( item.Line(), "" ).Length() > 0 ) {
+          id := id + "_" + item.Line();
+        }
+      operation := Operation::FindOperationTypeIndex( id );
+      if(not isnull(operation)){
+            account := Account_MP::FindByName( this, "Operating cost" );
+            isfromdb := false;
+            existoperationcost := OperationCost::FindOperationCostTypeIndex( id );
+            if( isnull( existoperationcost ) ){
+              connecteditem := select( globalOTDTable,
+                                       Global_MappingOperationCost,
+                                       moperationcost,
+                                       moperationcost.OrgCode() = item.OrganCode(),
+                                       moperationcost.ProductID() = item.ProductID() );
+              if( not isnull( connecteditem)){
+                  cost := connecteditem.Cost();
+                  lengthoftime := connecteditem.LengthOfTime();
+                  start := connecteditem.Start();
+                  timeunit := connecteditem.TimeUnit();
+                  OperationCost::Create( id, operation, account, "Volume", start, timeunit, lengthoftime, cost, isfromdb );
+                }
+    
+              }
+      }
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationData.qbl
new file mode 100644
index 0000000..31953be
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingOperationData.qbl
@@ -0,0 +1,98 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingOperationData (
+  Strings businessTypes,
+  const GlobalOTDTable globalOTDTable,
+  Strings organcodelist
+)
+{
+  TextBody:
+  [*
+    // yypsybs Aug-18-2023 (created)
+    // 鑾峰彇鏈夊簭鐨勫緟澶勭悊璁板綍
+    toDealList := construct( Global_MappingOperations, constcontent ) ;
+    if( not isnull( businessTypes ) and businessTypes.Size() > 0 ) {
+        toDealList := selectsortedset( globalOTDTable, Global_MappingOperation, item, 
+                                       ( businessTypes.Find( item.BusinessType() ) >= 0 ) and 
+                                       ( organcodelist.Find( item.OrganCode() ) >= 0 ), 
+    //                                   businessTypes.Difference( businessTypes.Difference( item.BusinessType().Tokenize( ", " ) ) ).Size() > 0, 
+                                       item.SequenceNumber() );
+    } else {
+        toDealList := selectsortedset( globalOTDTable, Global_MappingOperation, item, 
+                                       true, 
+                                       item.SequenceNumber() );
+    }
+    // 閫愭潯澶勭悊锛岄娆″鐞嗘椂鍒犻櫎steps
+    dealtRoutingIds := construct( Strings );
+    routingList := construct( Routings );
+    traverse( toDealList, Elements, item ) {
+        routingId := item.OrganCode() + "_" + item.ProductID();
+        unitId := item.OrganCode() + "_" + item.PlantName() + "_" + item.ProcessSection();
+        routingStepName := item.ProcessSection() + "_" + [String]item.SequenceNumber();
+        operationId := item.OrganCode() + "_" + item.ProductID() + "_" + item.ProcessSection()+"_" + [String]item.SequenceNumber();
+    //    info( "========" )
+    //    info( "routingId:" + routingId );
+    //    info( "unitId:" + unitId );
+    //    info( "routingStepName:" + routingStepName );
+    //    info( "operationId:" + operationId );
+        if( guard( item.Line(), "" ).Length() > 0 ) {
+            unitId := unitId + "_" + item.Line();
+            operationId := operationId + "_" + item.Line()
+        }
+        routing := Routing::CreateOrUpdate( this, routingId );
+        if( routingList.Find( routing ) < 0 ) {
+            routingList.Add( routing );  
+        }
+        // 鍒犻櫎steps(units鍏崇郴銆乷perations涔熶細鍒犻櫎)
+        if( dealtRoutingIds.Find( routingId ) < 0 ) {
+            toDeleteSteps := selectset( routing, RoutingStep, routingStep, true );
+            RoutingStep::Delete( toDeleteSteps );
+            dealtRoutingIds.Add( routingId );
+        }
+        // RoutingStep
+        routingStep := RoutingStep::FindByName( routing, routingStepName );
+        if( isnull( routingStep ) ) {
+            routingStep := RoutingStep::Create( routing, routingStepName, "", true );
+        }
+        
+        // Unit
+        unit := Unit::FindById( this, unitId );
+        if( isnull( unit ) ) {
+          // UnitOfMeasure
+        unitOfMeasure := UnitOfMeasure_MP::FindByName( this, item.UnitOfMeasureName() );
+          if( isnull( unitOfMeasure ) ) {
+              info( "unit of measure [" + item.UnitOfMeasureName() + "] not found for routing [" + routingId + "]" );
+          }
+           unit := this.Unit( relnew, 
+                              ID := unitId, Name := unitId, CapacityType := "Infinite",
+                              DefaultGridX := 0, DefaultGridY := 0,
+                              IsManuallyConfigured := false,
+                              Currency_MP := this.BaseCurrency(), UnitOfMeasure_MP := unitOfMeasure );
+        }
+        // Operation
+        haveMaxQty := item.MaximumQuantity() <> 0.0;
+        op := Operation::FindOperationTypeIndex( operationId );
+        if( isnull( op)){
+          op := Operation::Create( operationId, unit, operationId, routingStep, 
+                                        Duration::Days( item.UserLeadTime() ), Duration::Zero(), item.ActualCapacity(), false, 
+                                        [Real]item.MinimumQuantity(), haveMaxQty, guard( [Real]item.MaximumQuantity(), Real::MaxReal() ), 
+                                        0.0, 0.0, false, true );
+        }
+        
+    //    ManufactureLTProcessSection::CreateOrUpdate( op );
+    }
+    info( "========" )
+    // 閬嶅巻routing锛岃繘琛宭ink
+    lastStep := null( RoutingStep );
+    traverse( routingList, Elements.RoutingStep, step ) {
+        // 鍚屽伐鑹鸿矾绾挎椂杩炴帴
+        if( not isnull( lastStep ) and lastStep.Routing() = step.Routing() ) {
+            toLink := construct( RoutingSteps );
+            toLink.Add( lastStep );
+            // todo 瀛樼枒
+            step.LinkOperations( toLink );
+        }
+        lastStep := step;
+    }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingProductData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingProductData.qbl
new file mode 100644
index 0000000..55562f4
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingProductData.qbl
@@ -0,0 +1,94 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingProductData (
+  Strings businesstypes,
+  const GlobalOTDTable globalOTDTable,
+  Boolean iskeyproduct
+)
+{
+  Description: 'Method to get needed data from mapping product data'
+  TextBody:
+  [*
+    // Administrator Aug-16-2023 (created)
+    // list to deal
+    info( "Get list to deal of product" );
+    listToDeal := construct( Global_MappingProduct_MPs, constcontent );
+    
+    if( isnull( businesstypes ) or businesstypes.Size() = 0 ) {
+        if( iskeyproduct = true ){
+          listToDeal := selectset( globalOTDTable, Global_MappingProduct_MP, item, item.KeyProduct() = true );
+          }
+        else{
+          listToDeal := selectset( globalOTDTable, Global_MappingProduct_MP, item, true );
+          }
+    } else {
+        if( iskeyproduct = true ){
+          listToDeal := selectset( globalOTDTable, Global_MappingProduct_MP, item, item.KeyProduct() = true and businesstypes.Find( item.BusinessType() ) >= 0 );
+         }
+        else{
+          listToDeal := selectset( globalOTDTable, Global_MappingProduct_MP, item, businesstypes.Find( item.BusinessType() ) >= 0 );
+        }
+    }
+    totalcount := listToDeal.Size();
+    info( "Product has " + totalcount.AsQUILL() + " rows in total" );
+    
+    // Get the root data
+    Product_MP::CreateOrUpdate( this, 
+                                "鍏ㄩ儴鐗╂枡浜у搧", 
+                                "", 
+                                "鍏ㄩ儴鐗╂枡浜у搧", 
+                                "PCS", 
+                                "鍏ㄩ儴鐗╂枡浜у搧",
+                                0.0,false,false
+                                );
+    count := 0;
+    // Get the ProductMajorType list & ProductSubclassType list
+    traverse( listToDeal, Elements, item ){
+      count := count + 1;
+      if( count - [Number](count/100) * 100 = 0 or count = totalcount ){
+        info( "Now is dealing with the " + count.AsQUILL() + "Product " + "( " + count.AsQUILL() + "/" + totalcount.AsQUILL() + " ) " + (count/totalcount*100).Round( 1 ).AsQUILL() + "%" );
+        }
+      if( not item.ProductMajorType() = "" ){
+        Product_MP::CreateOrUpdate( this, 
+                                    item.ProductMajorType(), 
+                                    "鍏ㄩ儴鐗╂枡浜у搧", 
+                                    item.ProductMajorType(), 
+                                    "PCS", 
+                                    item.ProductMajorType(),
+                                    0.0,false,false
+                                    );
+      }
+      productmajortype := item.ProductMajorType();
+      if( productmajortype="" ){
+        productmajortype := "鍏ㄩ儴鐗╂枡浜у搧";
+        }
+       
+    //  if( not item.ProductSubclassType() = "" ){
+    //    Product_MP::CreateOrUpdate( this, 
+    //                                item.ProductSubclassType(), 
+    //                                productmajortype, 
+    //                                item.ProductSubclassType(), 
+    //                                "PCS", 
+    //                                item.ProductSubclassType(),
+    //                                0.0,false,false
+    //                                );
+    //    }
+    //  productsubclasstype := item.ProductSubclassType();
+    //  if( productsubclasstype="" ){
+    //      productsubclasstype := productmajortype;
+    //    }
+      
+      if( not item.ID() = "" ){
+          Product_MP::CreateOrUpdate( this, 
+                                      item.ID(), 
+                                      productmajortype, 
+                                      item.ID(), 
+                                      item.UnitOfMeasureName(), 
+                                      item.Name(),
+                                      item.ShelfLife(),
+                                      item.KeyProduct(),item.IsCommon()
+                                      );
+        }
+      }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingProductInLaneData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingProductInLaneData.qbl
new file mode 100644
index 0000000..a0521a8
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingProductInLaneData.qbl
@@ -0,0 +1,26 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingProductInLaneData (
+  const GlobalOTDTable globalOTDTable
+)
+{
+  Description: 'Get Product In Line data'
+  TextBody:
+  [*
+    // Administrator Aug-17-2023 (created)
+    // list to deal
+    listtodeal := selectset( globalOTDTable, Global_MappingProductInLane, item ,true );
+    totalcount := listtodeal.Size();
+    info( "ProductInLane has " + totalcount.AsQUILL() + " rows in total" );
+    
+    count := 0;
+    // Create ProductInLane
+    traverse( listtodeal, Elements, item ){
+      count := count + 1;
+      if( count - [Number](count/1000) * 1000 = 0 or count = totalcount ){
+        info( "Now is dealing with the " + count.AsQUILL() + "ProductInLane " + "( " + count.AsQUILL() + "/" + totalcount.AsQUILL() + " ) " + (count/totalcount*100).Round( 1 ).AsQUILL() + "%" );
+        }
+      ProductInLane::CreateOrUpdate( this, item.ProductID(), item.LineID() );
+      }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingSalesSegmentData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingSalesSegmentData.qbl
new file mode 100644
index 0000000..8ba6f6f
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingSalesSegmentData.qbl
@@ -0,0 +1,60 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingSalesSegmentData (
+  Strings businessTypes,
+  const GlobalOTDTable globalOTDTable
+)
+{
+  Description: 'ETL閿�鍞儴闂�'
+  TextBody:
+  [*
+    // yypsybs Aug-15-2023 (created)
+    // 寰呭鐞嗘暟鎹�
+    listToDeal := construct( Global_MappingSalesSegment_MPs, constcontent );
+    if( isnull( businessTypes ) or businessTypes.Size() = 0 ) {
+        listToDeal := selectset( globalOTDTable, Global_MappingSalesSegment_MP, item, true );
+    } else {
+        listToDeal := selectset( globalOTDTable, 
+                                 Global_MappingSalesSegment_MP, 
+                                 item, 
+                                 businessTypes.Find( item.BusinessType() ) <> -1 );
+    }
+    //nameList := construct( structured[String] );
+    //nameList := selectvalues( listToDeal, Elements, item, true, item.Name() );
+    //// 妫�鏌arent
+    //traverse( listToDeal, Elements, item, item.ParentName() <> "" ) {
+    //    if( nameList.Find( item.ParentName() ) = -1 ) {
+    //        error( "sales segment parent not found: " + item.Name().AsQUILL() );
+    //    }
+    //}
+    //// 閫愬眰閬嶅巻锛岀洿鑷冲鐞嗗畬鎴�
+    //dealtNameList := construct( structured[String] );
+    //while( listToDeal.Size() <> dealtNameList.Size() ) {
+    //    traverse( listToDeal, Elements, item ) {
+    //        // 鏈鐞嗚繃
+    //        if( dealtNameList.Find( item.Name() ) < 0 ){
+    //            // 鏃犵埗閮ㄩ棬锛岀洿鎺ュ鐞�
+    //            if( item.ParentName() = "" ) {
+    //                SalesSegment_MP::CreateOrUpdate( this, item.Name(), item.DisplayIndex(), item.ParentName() );
+    //                dealtNameList.Add( item.Name() );
+    //            } 
+    //            // 鏈夌埗閮ㄩ棬涓旂埗閮ㄩ棬宸插鐞�
+    //            else if( item.ParentName() <> "" and dealtNameList.Find(item.ParentName()) >= 0 ) {
+    //                SalesSegment_MP::CreateOrUpdate( this, item.Name(), item.DisplayIndex(), item.ParentName() );
+    //                dealtNameList.Add( item.Name() );
+    //            }
+    //            // 鏈夌埗閮ㄩ棬涓旂埗閮ㄩ棬鏈鐞嗭紝涓嬩竴杞鐞�
+    //        }
+    //    }
+    //}
+    traverse( listToDeal,Elements,item ){
+      if( item.ParentName() <> "" and isnull( SalesSegment_MP::FindByName( this, item.ParentName() ) ) ){
+        SalesSegment_MP::CreateOrUpdate( this, item.ParentName(), item.DisplayIndex(), "" );
+        }
+      }
+    
+    traverse( listToDeal,Elements,item ){
+        SalesSegment_MP::CreateOrUpdate( this, item.Name(), item.DisplayIndex(), item.ParentName() );
+      }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingUnitData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingUnitData.qbl
new file mode 100644
index 0000000..171448e
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingUnitData.qbl
@@ -0,0 +1,106 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingUnitData (
+  Strings businesstypes,
+  const GlobalOTDTable globalOTDTable,
+  Strings organcodelist
+)
+{
+  Description: 'Get unit data from operation mapping'
+  TextBody:
+  [*
+    // Administrator Aug-21-2023 (created)
+    // list to deal
+    listtodeal := construct( Global_MappingOperations, constcontent );
+    
+    if( isnull( businesstypes ) or businesstypes.Size() = 0 ) {
+        listtodeal := selectset( globalOTDTable, Global_MappingOperation, item, true );
+    } else {
+        listtodeal := selectset( globalOTDTable, Global_MappingOperation, item, 
+    //                             businesstypes.Difference( businesstypes.Difference( item.BusinessType().Tokenize( ", " ) ) ).Size() > 0
+                                 ( businesstypes.Find( item.BusinessType() ) >= 0 ) and 
+                                 ( organcodelist.Find( item.OrganCode() ) >= 0 )
+                                );
+    }
+    
+    //Set the Default values
+    unitofmeasurename := "PCS";
+    infinite := "Infinite";
+    capacitytype := "Transport quantity";
+    
+    // Get the root data
+    Unit::CreateOrUpdate( this, 
+                          "澶╅┈闆嗗洟", 
+                          "澶╅┈闆嗗洟", 
+                          "", 
+                          infinite, 
+                          unitofmeasurename );
+    
+    // Get the sub root data
+    Unit::CreateOrUpdate( this, 
+                          "鐢熶骇", 
+                          "鐢熶骇", 
+                          "澶╅┈闆嗗洟", 
+                          infinite, 
+                          unitofmeasurename );
+    
+    supplyunit := Unit::CreateOrUpdate( this, 
+                          "渚涘簲鍟�", 
+                          "渚涘簲鍟�", 
+                          "澶╅┈闆嗗洟", 
+                          infinite, 
+                          unitofmeasurename );
+    supplyunit.IsSupplier(true);
+    
+    Unit::CreateOrUpdate( this, 
+                          "鏁磋溅杩愯緭", 
+                          "鏁磋溅杩愯緭", 
+                          "澶╅┈闆嗗洟", 
+                          capacitytype, 
+                          unitofmeasurename );
+    
+    // Get the unit list
+    traverse( listtodeal, Elements, item){
+      OrgName := item.OrganName();
+      if( OrgName = ''){
+        OrgName := item.OrganCode();
+      }
+      //Get the first level unit
+      Unit::CreateOrUpdate( this, 
+                            item.OrganCode(), 
+                            OrgName, 
+                            "鐢熶骇", 
+                            infinite, 
+                            unitofmeasurename );
+                           
+      //Get the second level unit
+      secondlevelid := item.OrganCode() + "_" + item.PlantName();
+      Unit::CreateOrUpdate( this, 
+                            secondlevelid, 
+                            secondlevelid, 
+                            item.OrganCode(), 
+                            infinite, 
+                            unitofmeasurename );
+        
+      //Get the third level unit
+      thirdlevelid := secondlevelid + "_" + item.ProcessSection();
+      Unit::CreateOrUpdate( this, 
+                            thirdlevelid, 
+                            thirdlevelid, 
+                            secondlevelid, 
+                            "Time", 
+                            item.UnitOfMeasureName() );
+                   
+      //Get the last level unit
+      if( item.Line()<>"" ){
+        lastlevelid := thirdlevelid + "_" + item.Line();
+        Unit::CreateOrUpdate( this, 
+                              lastlevelid, 
+                              lastlevelid, 
+                              thirdlevelid, 
+                              "Time", 
+                              item.UnitOfMeasureName() );
+      }
+      }
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/Method_DoASyncMappingUnitOfMeasureData.qbl b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingUnitOfMeasureData.qbl
new file mode 100644
index 0000000..bc88799
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/Method_DoASyncMappingUnitOfMeasureData.qbl
@@ -0,0 +1,25 @@
+Quintiq file version 2.0
+#parent: #root
+Method DoASyncMappingUnitOfMeasureData (
+  const GlobalOTDTable globalotdtable
+)
+{
+  Description: 'ETL鏁版嵁杞ā鍨嬫暟鎹�'
+  TextBody:
+  [*
+    // yypsybs Aug-15-2023 (created)
+    
+    defaultOld := UnitOfMeasure_MP::FindDefault( this );
+    defaultNew := select( globalotdtable, Global_MappingUnitOfMeasure_MP, item, true, item.IsDefault() );
+    
+    if( not isnull( defaultOld ) and not isnull( defaultNew )
+        and defaultOld.Name() <> defaultNew.Name() ) {
+        defaultOld.IsDefault(false);
+        //error( "multi default unit of measure" )
+    }
+    
+    traverse( globalotdtable, Global_MappingUnitOfMeasure_MP, item ) {
+        UnitOfMeasure_MP::CreateOrUpdate( this, item.Name(), item.IsDefault() );
+    }
+  *]
+}
diff --git "a/_Main/BL/Type_MacroPlan/StaticMethod_DoASync\043253.qbl" "b/_Main/BL/Type_MacroPlan/StaticMethod_DoASync\043253.qbl"
new file mode 100644
index 0000000..8e5e156
--- /dev/null
+++ "b/_Main/BL/Type_MacroPlan/StaticMethod_DoASync\043253.qbl"
@@ -0,0 +1,166 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  MacroPlan macroPlan,
+  Strings businessTypes,
+  Boolean isKeyProduct,
+  Boolean createPurchaseSupplyMaterial,
+  const GlobalOTDTable globalOTDTable
+)
+{
+  Description: '鍗曟鍚屾'
+  TextBody:
+  [*
+    // yypsybs Aug-17-2023 (created)
+    // 甯佺鍜屽竵绉嶆眹鐜�
+    //Currency_MP::CreateCurrencyFromJson( macroPlan, jsonDataRow );
+    info( "Prepare to do sync" )
+    info( "Get organ code list" )
+    organcodelist := selectvalues( globalOTDTable, Global_MappingOperationBOM, bom,true, bom.OrganCode() );
+    if( not isnull( businessTypes ) and businessTypes.Size() > 0 ) {
+        traverse( businessTypes, Elements, item ) {
+            info( "Business type : " + item )  
+        }
+        organcodelist := selectvalues( globalOTDTable, BusinessType.OrganCode, organ, businessTypes.Find( organ.BusinessType().BusinessTypeName() ) <> -1, organ.OrganCodeName() );
+    }
+    else{
+      if( businessTypes.Size() = 0 ){
+        if( not isnull( selectset( globalOTDTable, BusinessType, b, b.BusinessTypeName() = "" ) ) ){
+          organcodelist := selectvalues( globalOTDTable, BusinessType.OrganCode, organ, organ.BusinessType().BusinessTypeName() = "", organ.OrganCodeName() );
+          }
+        }
+      }
+    
+    if( organcodelist.Size() = 0 ){
+      organcodelist := selectvalues( globalOTDTable, Global_MappingOperationBOM, bom,true, bom.OrganCode() );
+      }
+    
+    info( "KeyProduct : " + [String]isKeyProduct )
+    info( "Create purchase supply material : " + [String]createPurchaseSupplyMaterial )
+    
+    // 鍒濆鎹�-0
+    info( "Start Initial" );
+    macroPlan.InitialUnitAndStockingPoint();
+    
+    // 鍗曚綅-1
+    UnitOfMeasure_MP::DoASync( globalOTDTable, macroPlan );
+    
+    // 閿�鍞儴闂�-2
+    SalesSegment_MP::DoASync( macroPlan, businessTypes,globalOTDTable );
+    
+    // Unit-9 
+    // 涓嶰peration涓�鍚屽鐞�
+    //info( "BaseConversionFactor Finished, Start Operation Data Broker" );
+    //macroPlan.Broker_OTD_Operation().Execute();
+    info( "Operation Data Broker Finished, Start Unit Mapping" );
+    macroPlan.DoASyncMappingUnitData( businessTypes ,globalOTDTable, organcodelist);
+    
+    // 搴撳瓨鐐�-3
+    info( "Sales Segment Finished, Start Get StockingPoint From Api" )
+    bodynumber := "2";
+    postrequestbody := macroPlan.ApiBuildPostRequestBody( bodynumber );
+    address := "api-uat-sgc.tianma.cn";
+    url := "/otdService/https/GetStockingPointsInfo";
+    port := 443;
+    data := macroPlan.ApiResponesCheck( address, url, port, postrequestbody );
+    macroPlan.ApiStockingPointData( data );
+    
+    // 璐у竵淇℃伅-4
+    info( "Get StockingPoint From Api Finished, Start Get CurrencyInfo From Api" )
+    bodynumber := "1";
+    postrequestbody := macroPlan.ApiBuildPostRequestBody( bodynumber );
+    address := "api-uat-sgc.tianma.cn";
+    url := "/otdService/https/GetCurrenciesInfo";
+    port := 443;
+    data := macroPlan.ApiResponesCheck( address, url, port, postrequestbody );
+    macroPlan.ApiCurenciesData( data );
+    
+    //璐у竵姹囩巼淇℃伅-5
+    info( "Get CurrencyInfo From Api Finished, Start Get CurrencyRates From Api" )
+    bodynumber := "5";
+    postrequestbody := macroPlan.ApiBuildPostRequestBody( bodynumber );
+    address := "api-uat-sgc.tianma.cn";
+    url := "/otdService/https/GetCurrencyRatesInfo";
+    port := 443;
+    data := macroPlan.ApiResponesCheck( address, url, port, postrequestbody );
+    macroPlan.ApiCurencyRatesData( data );
+    
+    // 浜у搧鐗╂枡-6
+    //info( "Get CurrencyRates From Api Finished, Start Product Data Broker" )
+    //macroPlan.Broker_OTD_Product().Execute();
+    info( "Product Data Broker Finished, Start Product Mapping" );
+    //testproduct := construct( Strings );
+    macroPlan.DoASyncMappingProductData( businessTypes, globalOTDTable,isKeyProduct );
+    
+    //鍗曚綅杞崲-7
+    BaseConversionFactor::DoASync( macroPlan ,globalOTDTable);
+    
+    // 宸ヨ壓璺嚎 + BOM-9
+    info( "Unit Finished, Start Operation Mapping" );
+    macroPlan.DoASyncMappingOperationData( businessTypes ,globalOTDTable, organcodelist );  
+    //info( "Operation Finished, Start BOM Data Broker" );
+    //macroPlan.Broker_OTD_BOM().Execute();
+    info( "BOM Data Broker Finished, Start BOM Mapping" );
+    macroPlan.DoASyncMappingOperationBOMData( businessTypes, isKeyProduct, createPurchaseSupplyMaterial,globalOTDTable, organcodelist );
+    
+    //杞﹂亾淇℃伅-10
+    info( "BOM Finished, Start Get Lanes From Api" );
+    bodynumber := "3";
+    postrequestbody := macroPlan.ApiBuildPostRequestBody( bodynumber );
+    address := "api-uat-sgc.tianma.cn";
+    url := "/otdService/https/GetLanesInfo";
+    port := 443;
+    data := macroPlan.ApiResponesCheck( address, url, port, postrequestbody );
+    macroPlan.ApiLanesData( data );
+    
+    //杞﹂亾杩愯緭娈�-11
+    info( "Get Lanes From Api Finished, Start Get LaneLegs From Api" );
+    bodynumber := "4";
+    postrequestbody := macroPlan.ApiBuildPostRequestBody( bodynumber );
+    address := "api-uat-sgc.tianma.cn";
+    url := "/otdService/https/GetLaneLegsInfo";
+    port := 443;
+    data := macroPlan.ApiResponesCheck( address, url, port, postrequestbody );
+    macroPlan.ApiLaneLegsData( data );
+    
+    // 渚涘簲缃戠粶锛堣溅閬擄級-12
+    //info( "Get Lanelegs From Api Finished, Start ProductInLane Data Broker" );
+    //macroPlan.Broker_OTD_ProductInLane().Execute();
+    info( "ProductInLane Data Broker Finished, Start ProductInLane Mapping" );
+    macroPlan.DoASyncMappingProductInLaneData(globalOTDTable);
+    
+    // 鍦ㄥ簱搴撳瓨鏁版嵁-13
+    //info( "ProductInLane Finished, Start ActualPISPIP Data Broker" );
+    //macroPlan.Broker_OTD_ActualPISPIP().Execute();
+    info( "ActualPISPIP Data Broker Finished, Start ActualPISPIP Mapping" );
+    macroPlan.DoASyncMappingActualPISPIPData( businessTypes, globalOTDTable,isKeyProduct, organcodelist );
+    
+    // 鍦ㄩ�斿簱瀛�-14
+    //info( "ActualPISPIP Finished, Start ExternalSupply Data Broker" );
+    //macroPlan.Broker_OTD_ExternalSupply().Execute();
+    info( "ExternalSupply Data Broker Finished, Start ExternalSupply Mapping" );
+    macroPlan.DoASyncMappingExternalSupplyData( businessTypes, isKeyProduct ,globalOTDTable, organcodelist );
+    
+    // 搴撳瓨鎴愭湰-15
+    InventoryValueAndCost::DoASync( macroPlan,globalOTDTable );
+    
+    // todo 鍒堕�犳垚鏈�-16
+    info( "InventoryCost Finished, Start OperationCost Mapping" );
+    macroPlan.DoASyncMappingOperationCostData( globalOTDTable, businessTypes, organcodelist );
+    
+    // 璁㈠崟棰勬祴-17
+    Forecast::DoASync( macroPlan, businessTypes, globalOTDTable, organcodelist );
+    
+    // 璁㈠崟闇�姹�-18
+    CustomerOrder::DoASync( macroPlan, businessTypes, globalOTDTable, organcodelist );
+    
+    //鍒犻櫎澶氫綑鎶ラ敊鏁版嵁-19
+    macroPlan.DeleteSnaityCheck();
+    
+    //// todo 渚涘簲鍟嗚兘鍔�
+    //info( "ProviderCapacity::DoSync" )
+    
+    //// todo 渚涘簲鍟嗗洖澶�
+    //info( "ProviderReply::DoSync" )
+  *]
+}
diff --git "a/_Main/BL/Type_MacroPlan/StaticMethod_DoASync\043896.qbl" "b/_Main/BL/Type_MacroPlan/StaticMethod_DoASync\043896.qbl"
new file mode 100644
index 0000000..b543dcf
--- /dev/null
+++ "b/_Main/BL/Type_MacroPlan/StaticMethod_DoASync\043896.qbl"
@@ -0,0 +1,21 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  MacroPlan macroPlan,
+  String businessTypeStr,
+  Boolean isKeyProduct,
+  Boolean createPurchaseSupplyMaterial,
+  const GlobalOTDTable globalOTDTable
+)
+{
+  Description: '鍗曟鍚屾'
+  TextBody:
+  [*
+    // yypsybs Aug-17-2023 (created)
+    businessTypes := construct( Strings );
+    if( businessTypeStr.Length() > 0 ) {
+        businessTypes := businessTypeStr.Tokenize( ',' );
+    }
+    MacroPlan::DoASync( macroPlan, businessTypes, isKeyProduct, createPurchaseSupplyMaterial ,globalOTDTable);
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/StaticMethod_DoASync.qbl b/_Main/BL/Type_MacroPlan/StaticMethod_DoASync.qbl
new file mode 100644
index 0000000..f0f1769
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/StaticMethod_DoASync.qbl
@@ -0,0 +1,25 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  Scenario scenario,
+  String businessTypeStr,
+  Boolean isKeyProduct,
+  Boolean createPurchaseSupplyMaterial,
+  const GlobalOTDTable globalOTDTable
+)
+{
+  TextBody:
+  [*
+    info( scenario.DatasetMDSID(), "    浜嬩笟閮細", businessTypeStr, "    isKeyProduct锛�", isKeyProduct, "    createPurchaseSupplyMaterial锛�", createPurchaseSupplyMaterial );
+    
+    MDSMacroPlan::Root( scenario.DatasetMDSID() )
+    //-> DoASyncCreateLog( globalOTDTable )
+    -> MacroPlan::DoASync( businessTypeStr,
+                           isKeyProduct,
+                           createPurchaseSupplyMaterial,
+                           globalOTDTable
+                          )
+    -> Exception()
+    -> MacroPlan::DoASyncOnException( globalOTDTable );
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/StaticMethod_DoASyncOnException.qbl b/_Main/BL/Type_MacroPlan/StaticMethod_DoASyncOnException.qbl
new file mode 100644
index 0000000..92fef0b
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/StaticMethod_DoASyncOnException.qbl
@@ -0,0 +1,13 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASyncOnException (
+  Exception exception,
+  GlobalOTDTable globalOTDTable
+)
+{
+  TextBody:
+  [*
+    Global_BrokerExecuteLog::CreateInOperation( globalOTDTable, "DataDistribution", "" );
+    globalOTDTable.SettingFailureDetails( exception.ErrorNumber(), exception.Message(), "DataDistribution", "鏁版嵁鍒嗗彂" );
+  *]
+}
diff --git a/_Main/BL/Type_MacroPlan/StaticMethod_DoASyncSuccess.qbl b/_Main/BL/Type_MacroPlan/StaticMethod_DoASyncSuccess.qbl
new file mode 100644
index 0000000..dfc34f1
--- /dev/null
+++ b/_Main/BL/Type_MacroPlan/StaticMethod_DoASyncSuccess.qbl
@@ -0,0 +1,12 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASyncSuccess (
+  Void void,
+  GlobalOTDTable globalOTDTable
+)
+{
+  TextBody:
+  [*
+    // hongjli Oct-15-2023 (created)
+  *]
+}
diff --git a/_Main/BL/Type_SalesSegment_MP/StaticMethod_DoASync.qbl b/_Main/BL/Type_SalesSegment_MP/StaticMethod_DoASync.qbl
new file mode 100644
index 0000000..47fef26
--- /dev/null
+++ b/_Main/BL/Type_SalesSegment_MP/StaticMethod_DoASync.qbl
@@ -0,0 +1,17 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  MacroPlan macroPlan,
+  Strings businessTypes,
+  const GlobalOTDTable globalOTDTable
+)
+{
+  TextBody:
+  [*
+    // yypsybs Aug-17-2023 (created)
+    //info( "UnitOfMeasure_MP Finished, Start Sales Segment Data Broker" )
+    //macroPlan.Broker_OTD_SalesSegment().Execute();
+    info( "Sales Segment Data Broker Finished, Start Sales Segment Mapping" )
+    macroPlan.DoASyncMappingSalesSegmentData( businessTypes,globalOTDTable );
+  *]
+}
diff --git a/_Main/BL/Type_UnitOfMeasure_MP/StaticMethod_DoASync.qbl b/_Main/BL/Type_UnitOfMeasure_MP/StaticMethod_DoASync.qbl
new file mode 100644
index 0000000..f5f1797
--- /dev/null
+++ b/_Main/BL/Type_UnitOfMeasure_MP/StaticMethod_DoASync.qbl
@@ -0,0 +1,17 @@
+Quintiq file version 2.0
+#parent: #root
+StaticMethod DoASync (
+  const GlobalOTDTable globalotdtable,
+  MacroPlan macroplan
+)
+{
+  Description: '鍗曟鍚屾'
+  TextBody:
+  [*
+    // yypsybs Aug-17-2023 (created)
+    //info( "Inital Finished, Start UnitOfMeasure_MP Data Broker" )
+    //macroPlan.Broker_OTD_UnitOfMeasure().Execute();
+    info( "UnitOfMeasure_MP Data Broker Finished, Start UnitOfMeasure_MP Mapping" )
+    macroplan.DoASyncMappingUnitOfMeasureData(globalotdtable);
+  *]
+}
diff --git a/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Component_pnlActions.def b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Component_pnlActions.def
new file mode 100644
index 0000000..1675d55
--- /dev/null
+++ b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Component_pnlActions.def
@@ -0,0 +1,40 @@
+Quintiq file version 2.0
+Component pnlActions
+{
+  #keys: '[414702.1.84941726]'
+  BaseType: 'WebPanel'
+  Children:
+  [
+    Component btnOk
+    {
+      #keys: '[414702.1.84941730]'
+      BaseType: 'WebButton'
+      Properties:
+      [
+        Label: 'OK'
+        Taborder: 0
+      ]
+    }
+    Component btnCancel
+    {
+      #keys: '[414702.1.84941732]'
+      BaseType: 'WebButton'
+      Properties:
+      [
+        Label: 'Cancel'
+        Taborder: 1
+      ]
+    }
+  ]
+  Properties:
+  [
+    Alignment: 'trailing'
+    Border: true
+    ExcludeFromActiveComponent: true
+    FixedSize: true
+    Orientation: 'horizontal'
+    Padding: 'true'
+    Style: 'footer'
+    Taborder: 1
+  ]
+}
diff --git a/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Component_pnlContent.def b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Component_pnlContent.def
new file mode 100644
index 0000000..5c05df7
--- /dev/null
+++ b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Component_pnlContent.def
@@ -0,0 +1,58 @@
+Quintiq file version 2.0
+Component pnlContent
+{
+  #keys: '[414702.1.84941724]'
+  BaseType: 'WebPanel'
+  Children:
+  [
+    Component dropDownStringListGeneral id:dropDownStringListGeneral_549
+    {
+      #keys: '[414702.1.87540205]'
+      BaseType: 'WebDropDownStringList'
+      Properties:
+      [
+        AllowEmpty: true
+        DataBinding: 'DataHolderScenario.Data.ScenarioName'
+        Label: 'Scenario Name'
+        Taborder: 0
+      ]
+    }
+    Component checkboxIsKeyProduct id:checkboxIsKeyProduct_593
+    {
+      #keys: '[414702.1.87540263]'
+      BaseType: 'WebCheckbox'
+      Properties:
+      [
+        DataBinding: 'DataHolderScenario.Data.IsKeyProduct'
+        Label: 'S&OP'
+        Taborder: 1
+      ]
+    }
+    Component checkboxCreatePurchaseSupplyProduct id:checkboxCreatePurchaseSupplyProduct_102
+    {
+      #keys: '[414702.1.84942148]'
+      BaseType: 'WebCheckbox'
+      Properties:
+      [
+        DataBinding: 'DataHolderScenario.Data.CreatePurchaseSupplyMaterial'
+        Label: 'CreatePurchase'
+        Taborder: 2
+      ]
+    }
+    Component CheckboxIsCreateNewVersion id:CheckboxIsCreateNewVersion_653
+    {
+      #keys: '[414702.1.84942205]'
+      BaseType: 'WebCheckbox'
+      Properties:
+      [
+        Label: 'Create a new version'
+        Taborder: 3
+      ]
+    }
+  ]
+  Properties:
+  [
+    Padding: 'true'
+    Taborder: 0
+  ]
+}
diff --git a/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Method_DataDistribution.def b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Method_DataDistribution.def
new file mode 100644
index 0000000..c72dc11
--- /dev/null
+++ b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Method_DataDistribution.def
@@ -0,0 +1,16 @@
+Quintiq file version 2.0
+#parent: #root
+Method DataDistribution (
+  Scenario scenario
+) id:Method_DialogDataDistribution_DataDistribution
+{
+  #keys: '[414702.1.87530593]'
+  Body:
+  [*
+    DataHolderScenario.Data( scenario );
+    
+    dropDownStringListGeneral.Strings( GlobalOTDTable::GetBusnessStrings( GlobalOTDTable ) );
+    
+    ApplicationMacroPlanner.ShowFormModal( this );
+  *]
+}
diff --git a/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Response_pnlActions_btnCancel_OnClick.def b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Response_pnlActions_btnCancel_OnClick.def
new file mode 100644
index 0000000..c9c2e6a
--- /dev/null
+++ b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Response_pnlActions_btnCancel_OnClick.def
@@ -0,0 +1,15 @@
+Quintiq file version 2.0
+#parent: pnlActions/btnCancel
+Response OnClick () id:Response_pnlActions_btnCancel_OnClick
+{
+  #keys: '[414702.1.84941736]'
+  DefinitionID: 'Responsedef_WebButton_OnClick'
+  GroupServerCalls: true
+  QuillAction
+  {
+    Body:
+    [*
+      Form.Close();
+    *]
+  }
+}
diff --git a/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Response_pnlActions_btnOk_OnClick.def b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Response_pnlActions_btnOk_OnClick.def
new file mode 100644
index 0000000..f2e440f
--- /dev/null
+++ b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/Response_pnlActions_btnOk_OnClick.def
@@ -0,0 +1,30 @@
+Quintiq file version 2.0
+#parent: pnlActions/btnOk
+Response OnClick () id:Response_pnlActions_btnOk_OnClick
+{
+  #keys: '[414702.1.84941735]'
+  DefinitionID: 'Responsedef_WebButton_OnClick'
+  QuillAction
+  {
+    Body:
+    [*
+      Form.ApplyChanges();
+      
+      businessType := select( GlobalOTDTable, 
+                              BusinessType, 
+                              b, 
+                              b.ScenarioName() = dropDownStringListGeneral.Text()
+                             );
+      
+      //Global_BrokerExecuteLog::CreateInOperation( GlobalOTDTable, "DataDistribution", ApplicationMacroPlanner.GetUserName() );
+      MacroPlan::DoASync( DataHolderScenario.Data(), 
+                          businessType.BusinessTypeName(),
+                          checkboxIsKeyProduct.Checked(), 
+                          checkboxCreatePurchaseSupplyProduct.Checked(),
+                          GlobalOTDTable );
+      
+      Form.Close();
+    *]
+    GroupServerCalls: false
+  }
+}
diff --git a/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/_ROOT_Component_DialogDataDistribution.def b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/_ROOT_Component_DialogDataDistribution.def
new file mode 100644
index 0000000..db6e245
--- /dev/null
+++ b/_Main/UI/MacroPlannerWebApp/Component_DialogDataDistribution/_ROOT_Component_DialogDataDistribution.def
@@ -0,0 +1,33 @@
+Quintiq file version 2.0
+#root
+#parent: MacroPlannerWebApp
+OrphanComponent DialogDataDistribution
+{
+  #keys: '[414702.1.84941722]'
+  BaseType: 'WebForm'
+  Children:
+  [
+    #child: pnlContent
+    #child: pnlActions
+    Component DataHolderScenario
+    {
+      #keys: '[414702.1.89308159]'
+      BaseType: 'WebDataHolder'
+      Databinding: 'Scenario'
+      Properties:
+      [
+        Taborder: 2
+      ]
+    }
+  ]
+  Properties:
+  [
+    Alignment: 'trailing'
+    EnterButton: 'btnOk'
+    EscapeButton: 'btnCancel'
+    ExcludeFromActiveComponent: true
+    Image: 'CAKE2'
+    Padding: 'false'
+    Title: '鏁版嵁鍒嗗彂'
+  ]
+}
diff --git a/_Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Component_ListScenario.def b/_Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Component_ListScenario.def
index 015e693..71c782e 100644
--- a/_Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Component_ListScenario.def
+++ b/_Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Component_ListScenario.def
@@ -110,6 +110,17 @@
                 Taborder: 21
               ]
             }
+            Component MenuScenarioDataDistribution
+            {
+              #keys: '[414702.1.89337672]'
+              BaseType: 'WebMenu'
+              Properties:
+              [
+                Image: 'CABINET_FLASH'
+                Taborder: 22
+                Title: '鏁版嵁鍒嗗彂'
+              ]
+            }
           ]
         }
       ]
diff --git a/_Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Response_TIANMA_JITUAN_ListScenario_MenuScenarioDataDistribution_OnClick.def b/_Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Response_TIANMA_JITUAN_ListScenario_MenuScenarioDataDistribution_OnClick.def
new file mode 100644
index 0000000..4528faf
--- /dev/null
+++ b/_Main/UI/MacroPlannerWebApp/Component_FormScenarioManager/Response_TIANMA_JITUAN_ListScenario_MenuScenarioDataDistribution_OnClick.def
@@ -0,0 +1,21 @@
+Quintiq file version 2.0
+#parent: ListScenario
+Response OnClick (
+  Scenario selection
+) id:Response_ListScenario_MenuScenarioDataDistribution_OnClick
+{
+  #keys: '[414702.1.89410379]'
+  CanBindMultiple: false
+  DefinitionID => /ListScenario/Responsedef_ListScenario_WebMenu_OnClick
+  Initiator: 'MenuScenarioDataDistribution'
+  QuillAction
+  {
+    Body:
+    [*
+      dlg := construct( DialogDataDistribution );
+      
+      dlg.DataDistribution( selection );
+    *]
+    GroupServerCalls: false
+  }
+}

--
Gitblit v1.9.3