|
|
@@ -352,59 +352,13 @@ public class ProcessOutsourceOrderService : IDynamicApiController, ITransient
|
|
|
private async Task UpsertProcessOutsourceMdpAsync(string purOrd, DateTime now)
|
|
|
{
|
|
|
var batchId = $"S3_OUTSOURCE_{now:yyyyMMddHHmmss}";
|
|
|
- await _db.Ado.ExecuteCommandAsync(
|
|
|
- """
|
|
|
- INSERT INTO mdp_std_purchase_order
|
|
|
- (tenant_id, source_system, po_no, po_line, po_type, supplier_code, item_code, item_name, order_qty, received_qty, returned_qty, due_date, need_date, order_date, status, buyer, work_order, source_biz_key, sync_batch_id, sync_time)
|
|
|
- SELECT
|
|
|
- NULLIF(m.tenant_id, 0),
|
|
|
- 'AIDOP',
|
|
|
- IFNULL(m.PurOrd, ''),
|
|
|
- CAST(IFNULL(d.Line, 0) AS CHAR),
|
|
|
- IFNULL(m.Potype, ''),
|
|
|
- IFNULL(m.Supp, ''),
|
|
|
- IFNULL(d.ItemNum, ''),
|
|
|
- IFNULL(i.Descr, ''),
|
|
|
- IFNULL(d.QtyOrded, 0),
|
|
|
- IFNULL(d.QtyReceived, IFNULL(d.RctQty, 0)),
|
|
|
- IFNULL(d.QtyReturned, 0),
|
|
|
- d.DueDate,
|
|
|
- d.NeedDate,
|
|
|
- m.OrdDate,
|
|
|
- CASE WHEN IFNULL(LENGTH(m.Status), 0) = 0 THEN 'R' ELSE m.Status END,
|
|
|
- m.Buyer,
|
|
|
- m.WorkOrd,
|
|
|
- CONCAT(IFNULL(m.PurOrd,''), '|', IFNULL(d.Line, 0)),
|
|
|
- @BatchId,
|
|
|
- @Now
|
|
|
- FROM PurOrdMaster m
|
|
|
- JOIN PurOrdDetail d ON m.RecID = d.PurOrdRecID
|
|
|
- LEFT JOIN ItemMaster i ON d.ItemNum = i.ItemNum
|
|
|
- WHERE m.PurOrd = @PurOrd
|
|
|
- AND IFNULL(d.ItemNum, '') <> ''
|
|
|
- AND NULLIF(m.tenant_id, 0) IS NOT NULL
|
|
|
- ON DUPLICATE KEY UPDATE
|
|
|
- po_type=VALUES(po_type),
|
|
|
- supplier_code=VALUES(supplier_code),
|
|
|
- item_code=VALUES(item_code),
|
|
|
- item_name=VALUES(item_name),
|
|
|
- order_qty=VALUES(order_qty),
|
|
|
- received_qty=VALUES(received_qty),
|
|
|
- returned_qty=VALUES(returned_qty),
|
|
|
- due_date=VALUES(due_date),
|
|
|
- need_date=VALUES(need_date),
|
|
|
- order_date=VALUES(order_date),
|
|
|
- status=VALUES(status),
|
|
|
- buyer=VALUES(buyer),
|
|
|
- work_order=VALUES(work_order),
|
|
|
- sync_batch_id=VALUES(sync_batch_id),
|
|
|
- sync_time=VALUES(sync_time),
|
|
|
- update_time=CURRENT_TIMESTAMP
|
|
|
- """,
|
|
|
- new SugarParameter("@PurOrd", purOrd),
|
|
|
- new SugarParameter("@BatchId", batchId),
|
|
|
- new SugarParameter("@Now", now));
|
|
|
-
|
|
|
+ // S8-RULE01-AUTHORITY-SINGLE-WRITER:已移除对 mdp_std_purchase_order 的直写。
|
|
|
+ //
|
|
|
+ // 标准层现在是「本轮批次的镜像」:S3 全量转换按 @BatchId 收窄并淘汰不属于本批次的行。
|
|
|
+ // 本旁路写入的行带的是 S3_OUTSOURCE_* 批次号,下一轮全量必然把它淘汰掉,
|
|
|
+ // 留着它只会让标准层短暂出现一批镜像不认识的行,且与「单一 Authority 写入方」相矛盾。
|
|
|
+ //
|
|
|
+ // 采购订单行的标准层由 S3 从源表统一派生;本服务只负责写源单据。
|
|
|
await _db.Ado.ExecuteCommandAsync(
|
|
|
"""
|
|
|
INSERT INTO mdp_std_process_outsource_order
|
|
|
@@ -448,61 +402,20 @@ public class ProcessOutsourceOrderService : IDynamicApiController, ITransient
|
|
|
new SugarParameter("@BatchId", batchId),
|
|
|
new SugarParameter("@Now", now));
|
|
|
|
|
|
- await _db.Ado.ExecuteCommandAsync(
|
|
|
- """
|
|
|
- INSERT INTO dwd_supplier_delivery
|
|
|
- (tenant_id, factory_id, stat_date, po_no, po_line, po_type, supplier_code, supplier_name, item_code, item_name, order_qty, schedule_qty, delivery_qty, receipt_qty, return_qty, remaining_qty, due_date, need_date, delivery_status, source_system, sync_batch_id, calc_time)
|
|
|
- SELECT
|
|
|
- NULLIF(m.tenant_id, 0),
|
|
|
- 1,
|
|
|
- @StatDate,
|
|
|
- IFNULL(m.PurOrd, ''),
|
|
|
- CAST(IFNULL(d.Line, 0) AS CHAR),
|
|
|
- IFNULL(m.Potype, ''),
|
|
|
- IFNULL(m.Supp, ''),
|
|
|
- IFNULL(s.SortName, ''),
|
|
|
- IFNULL(d.ItemNum, ''),
|
|
|
- IFNULL(i.Descr, ''),
|
|
|
- IFNULL(d.QtyOrded, 0),
|
|
|
- 0,
|
|
|
- 0,
|
|
|
- IFNULL(d.QtyReceived, IFNULL(d.RctQty, 0)),
|
|
|
- IFNULL(d.QtyReturned, 0),
|
|
|
- GREATEST(IFNULL(d.QtyOrded, 0) - IFNULL(d.QtyReceived, IFNULL(d.RctQty, 0)) - IFNULL(d.QtyReturned, 0), 0),
|
|
|
- d.DueDate,
|
|
|
- d.NeedDate,
|
|
|
- 'OPEN',
|
|
|
- 'AIDOP',
|
|
|
- @BatchId,
|
|
|
- @Now
|
|
|
- FROM PurOrdMaster m
|
|
|
- JOIN PurOrdDetail d ON m.RecID = d.PurOrdRecID
|
|
|
- LEFT JOIN ItemMaster i ON d.ItemNum = i.ItemNum
|
|
|
- LEFT JOIN SuppMaster s ON m.Domain = s.Domain AND m.Supp = s.Supp
|
|
|
- WHERE m.PurOrd = @PurOrd
|
|
|
- AND IFNULL(d.ItemNum, '') <> ''
|
|
|
- AND NULLIF(m.tenant_id, 0) IS NOT NULL
|
|
|
- ON DUPLICATE KEY UPDATE
|
|
|
- po_type=VALUES(po_type),
|
|
|
- supplier_code=VALUES(supplier_code),
|
|
|
- supplier_name=VALUES(supplier_name),
|
|
|
- item_code=VALUES(item_code),
|
|
|
- item_name=VALUES(item_name),
|
|
|
- order_qty=VALUES(order_qty),
|
|
|
- receipt_qty=VALUES(receipt_qty),
|
|
|
- return_qty=VALUES(return_qty),
|
|
|
- remaining_qty=VALUES(remaining_qty),
|
|
|
- due_date=VALUES(due_date),
|
|
|
- need_date=VALUES(need_date),
|
|
|
- delivery_status=VALUES(delivery_status),
|
|
|
- sync_batch_id=VALUES(sync_batch_id),
|
|
|
- calc_time=VALUES(calc_time),
|
|
|
- update_time=CURRENT_TIMESTAMP
|
|
|
- """,
|
|
|
- new SugarParameter("@PurOrd", purOrd),
|
|
|
- new SugarParameter("@StatDate", now.Date),
|
|
|
- new SugarParameter("@BatchId", batchId),
|
|
|
- new SugarParameter("@Now", now));
|
|
|
+ // S8-RULE01-AUTHORITY-SINGLE-WRITER:已移除对 dwd_supplier_delivery 的直写。
|
|
|
+ //
|
|
|
+ // 该表是 Rule01 的 Authority,必须只有一个运行期写入方(S3 的 MDP 转换)。
|
|
|
+ // 这里原本直读 PurOrdMaster/PurOrdDetail/ItemMaster/SuppMaster 后直接 upsert DWD,
|
|
|
+ // 且把 delivery_status 硬编码为 'OPEN' 并放进 ON DUPLICATE KEY UPDATE ——
|
|
|
+ // 于是一次建单调用就能把 MDP 刚判定的 COMPLETED / DELAYED 无条件翻回 OPEN。
|
|
|
+ //
|
|
|
+ // 更严重的是它能凭空造出一个「最新快照」:Rule01 取 MAX(stat_date) 那一天的全部行,
|
|
|
+ // 实测租户 797403760988229 的 2026-05-16 快照只有 2 行、且全部出自本旁路 ——
|
|
|
+ // MDP 停摆时,一次业务 API 调用就让 Rule01 在 2 行残缺数据上评估,而快照看起来还很新鲜。
|
|
|
+ //
|
|
|
+ // 业务侧只负责写源单据;DWD 由 S3 全量转换派生,最长 60 分钟可见延迟。
|
|
|
+ // 若将来需要更快可见,用 ModuleRebuildService.EnqueueAsync("S3", ...) 在**事务提交之后**
|
|
|
+ // 触发一次正式重建,而不是重新引入第二个写入方。
|
|
|
}
|
|
|
|
|
|
[DisplayName("保存工序外协订单")]
|