|
|
@@ -1,3 +1,5 @@
|
|
|
+using Admin.NET.Plugin.AiDOP.DataPlatform;
|
|
|
+
|
|
|
namespace Admin.NET.Plugin.AiDOP.Supply;
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -9,13 +11,23 @@ namespace Admin.NET.Plugin.AiDOP.Supply;
|
|
|
[NonUnify]
|
|
|
public class WorkOrderMaterialReadinessService : IDynamicApiController, ITransient
|
|
|
{
|
|
|
+ private const string SqlServerSourceCode = "DOPDEMORQ_SQLSERVER";
|
|
|
+
|
|
|
private readonly ISqlSugarClient _db;
|
|
|
private readonly UserManager _userManager;
|
|
|
+ private readonly MdpSourceScopeFactory _scopeFactory;
|
|
|
+ private readonly SourceDomainTenantResolver _domainTenant;
|
|
|
|
|
|
- public WorkOrderMaterialReadinessService(ISqlSugarClient db, UserManager userManager)
|
|
|
+ public WorkOrderMaterialReadinessService(
|
|
|
+ ISqlSugarClient db,
|
|
|
+ UserManager userManager,
|
|
|
+ MdpSourceScopeFactory scopeFactory,
|
|
|
+ SourceDomainTenantResolver domainTenant)
|
|
|
{
|
|
|
_db = db;
|
|
|
_userManager = userManager;
|
|
|
+ _scopeFactory = scopeFactory;
|
|
|
+ _domainTenant = domainTenant;
|
|
|
}
|
|
|
|
|
|
[DisplayName("工单物料齐套上线看板列表")]
|
|
|
@@ -29,6 +41,11 @@ public class WorkOrderMaterialReadinessService : IDynamicApiController, ITransie
|
|
|
var where = new List<string> { "1=1" };
|
|
|
var pars = new List<SugarParameter>();
|
|
|
|
|
|
+ // 租户隔离:只查当前租户的 WorkOrdTemp 数据
|
|
|
+ var tenantId = Infrastructure.AidopTenantScope.ResolveOrThrow(_userManager);
|
|
|
+ where.Add("a.tenant_id = @TenantId");
|
|
|
+ pars.Add(new SugarParameter("@TenantId", tenantId));
|
|
|
+
|
|
|
if (!string.IsNullOrWhiteSpace(input.SeOrder))
|
|
|
{
|
|
|
where.Add("a.SeOrder LIKE @SeOrder");
|
|
|
@@ -55,13 +72,15 @@ public class WorkOrderMaterialReadinessService : IDynamicApiController, ITransie
|
|
|
pars.Add(new SugarParameter("@SupplierName", $"%{input.SupplierName.Trim()}%"));
|
|
|
}
|
|
|
|
|
|
- if (await ShouldUseDwdAsync(input))
|
|
|
- {
|
|
|
- return await GetDwdListAsync(input, page, pageSize, offset);
|
|
|
- }
|
|
|
+ // 始终使用 WorkOrdTemp 路径(字段完整);dwd_material_readiness 缺少 SeOrder/Priority/MorderType 等列,
|
|
|
+ // 走 DWD 路径会导致订单号、工单优先级、工单类型等列全部为 NULL。
|
|
|
+ // if (await ShouldUseDwdAsync(input))
|
|
|
+ // {
|
|
|
+ // return await GetDwdListAsync(input, page, pageSize, offset);
|
|
|
+ // }
|
|
|
|
|
|
var fromSql = $"FROM WorkOrdTemp a WHERE {string.Join(" AND ", where)}";
|
|
|
- var total = await _db.Ado.GetIntAsync($"SELECT COUNT(1) FROM (SELECT DISTINCT a.RowNum, a.SeOrder, a.workord, a.ItemNum, a.Op, a.bom_number, a.supplier_number {fromSql}) t", pars);
|
|
|
+ var total = await _db.Ado.GetIntAsync($"SELECT COUNT(1) FROM (SELECT DISTINCT a.RowNum, a.SeOrder, a.workord, a.ItemNum, a.bom_number, a.supplier_number {fromSql}) t", pars);
|
|
|
var orderBy = BuildOrderBy(input.SortField, input.SortOrder);
|
|
|
|
|
|
var list = await _db.Ado.SqlQueryAsync<WorkOrderMaterialReadinessRow>(
|
|
|
@@ -74,8 +93,6 @@ public class WorkOrderMaterialReadinessService : IDynamicApiController, ITransie
|
|
|
a.Priority AS Priority,
|
|
|
a.morder_type AS MorderType,
|
|
|
a.LotSerial AS LotSerial,
|
|
|
- a.Op AS Op,
|
|
|
- a.OpOrdDate AS OpOrdDate,
|
|
|
a.bom_number AS BomNumber,
|
|
|
a.Descr AS Descr,
|
|
|
a.Descr1 AS Descr1,
|
|
|
@@ -123,160 +140,436 @@ public class WorkOrderMaterialReadinessService : IDynamicApiController, ITransie
|
|
|
[HttpPost("work-order-material-readiness/refresh")]
|
|
|
public async Task<object> Refresh()
|
|
|
{
|
|
|
- var tid = Infrastructure.AidopTenantScope.ResolveOrThrow(_userManager);
|
|
|
- var now = DateTime.Now;
|
|
|
- var batchId = $"workordtemp-{now:yyyyMMdd-HHmmss}";
|
|
|
- var tenantPar = new SugarParameter("@TenantId", tid);
|
|
|
-
|
|
|
- // Step 1: 执行存储过程填充 WorkOrdTemp。
|
|
|
- // 不在此处删除 dwd_material_readiness:Step 2 删过期工单 + Step 3 upsert 已等价于完整重建,
|
|
|
- // 且前置全表删除会在存储过程失败时把看板清空且无法重建(2026-08-11 实测丢失 20872 行)。
|
|
|
- // 该存储过程实测需 30 秒以上(2026-08-11),单独放宽超时;仅覆盖本次调用,随后还原。
|
|
|
- var prevTimeout = _db.Ado.CommandTimeOut;
|
|
|
+ var sw = System.Diagnostics.Stopwatch.StartNew();
|
|
|
try
|
|
|
{
|
|
|
- _db.Ado.CommandTimeOut = 300;
|
|
|
- await _db.Ado.ExecuteCommandAsync("CALL pr_MES_Report_DayWorkOrdDetailShortage_New('')");
|
|
|
+ var tenantId = Infrastructure.AidopTenantScope.ResolveOrThrow(_userManager);
|
|
|
+ var now = DateTime.Now;
|
|
|
+ var batchId = $"workordtemp-{now:yyyyMMdd-HHmmss}";
|
|
|
+ var tenantPar = new SugarParameter("@TenantId", tenantId);
|
|
|
+ var domain = await _domainTenant.ResolveDomainAsync(SqlServerSourceCode, tenantId);
|
|
|
+
|
|
|
+ // Step 0: 从 SQL Server 读取 InvMaster,写入 MySQL 辅助表
|
|
|
+ await PrepareInvMasterTempAsync(domain, tenantId);
|
|
|
+
|
|
|
+ // Step 1: 清空本租户 WorkOrdTemp
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "DELETE FROM WorkOrdTemp WHERE tenant_id = @TenantId", tenantPar);
|
|
|
+
|
|
|
+ // Step 2: INSERT(原存储过程核心查询,所有源表加租户过滤)
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ INSERT INTO WorkOrdTemp(
|
|
|
+ RowNum, Status, workord, Priority, OrdDate, morder_type, bom_number,
|
|
|
+ Descr, Descr1, production_number, QtyRequired, supplier_name, ItemNum,
|
|
|
+ supplier_number, ljsl, SeOrder, SePriority, LotSerial,
|
|
|
+ checktime, PMBOM, ItemType, OwnerApplication, ComponentItemDescr,
|
|
|
+ ComponentItemDrawing, ComponentItemUM, ComponentItemKittingTime,
|
|
|
+ QtyRec, RecDate, QtyFrom, FromDate, jhshrq, PurOrdQty, PurOrdorddate,
|
|
|
+ Buyer, BuyerUser, tenant_id
|
|
|
+ )
|
|
|
+ SELECT
|
|
|
+ viewql.RowNum, viewql.Status, viewql.workord, viewql.Priority, viewql.OrdDate,
|
|
|
+ viewql.morder_type, viewql.bom_number, viewql.Descr, viewql.Descr1,
|
|
|
+ viewql.morder_production_number, viewql.QtyRequired, viewql.supplier_name,
|
|
|
+ viewql.ItemNum, viewql.supplier_number,
|
|
|
+ SUM(viewql.QtyRequired) OVER (
|
|
|
+ PARTITION BY viewql.ItemNum
|
|
|
+ ORDER BY viewql.Priority, viewql.workord
|
|
|
+ ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
|
+ ) AS ljsl,
|
|
|
+ viewql.fbill_no, viewql.Priority AS SePriority,
|
|
|
+ viewql.LotSerial, viewql.checktime,
|
|
|
+ viewql.PMBOM, viewql.ItemType, viewql.OwnerApplication,
|
|
|
+ viewql.ComponentItemDescr, viewql.ComponentItemDrawing,
|
|
|
+ viewql.ComponentItemUM, viewql.ComponentItemKittingTime,
|
|
|
+ viewql.QtyRec, viewql.RecDate, viewql.QtyFrom, viewql.FromDate,
|
|
|
+ viewql.jhshrq, viewql.PurOrdQty, viewql.PurOrdorddate,
|
|
|
+ viewql.Buyer, '' AS BuyerUser,
|
|
|
+ @TenantId AS tenant_id
|
|
|
+ FROM (
|
|
|
+ SELECT
|
|
|
+ a.RowNum, a.Status, a.workord, a.Priority, a.OrdDate, a.LotSerial,
|
|
|
+ b.morder_type, c.ItemNum AS bom_number, c.Descr, c.Descr1,
|
|
|
+ b.morder_production_number, d.QtyRequired, e.supplier_name,
|
|
|
+ d.ItemNum, e.supplier_number, f.fbill_no,
|
|
|
+ c.PMBOM,
|
|
|
+ Sonitem.ItemType, Sonitem.OwnerApplication,
|
|
|
+ Sonitem.Descr AS ComponentItemDescr,
|
|
|
+ Sonitem.Drawing AS ComponentItemDrawing,
|
|
|
+ Sonitem.UM AS ComponentItemUM,
|
|
|
+ exm.checktime,
|
|
|
+ exd.kitting_time AS ComponentItemKittingTime,
|
|
|
+ j.QtyRec, j.updatetime AS RecDate,
|
|
|
+ j.QtyFrom, j.updatetime AS FromDate,
|
|
|
+ sh.jhshrq,
|
|
|
+ pm.QtyOrded AS PurOrdQty, pm.orddate AS PurOrdorddate,
|
|
|
+ pm.Buyer
|
|
|
+ FROM (
|
|
|
+ SELECT RowNum, Status, workord, Priority, OrdDate, LotSerial
|
|
|
+ FROM (
|
|
|
+ SELECT CAST(ROW_NUMBER() OVER (PARTITION BY Status ORDER BY Priority ASC) AS CHAR(50)) AS RowNum,
|
|
|
+ Status, workord, Priority, OrdDate, LotSerial
|
|
|
+ FROM WorkOrdMaster
|
|
|
+ WHERE Status IN ('W','S','r','p') AND tenant_id = @TenantId
|
|
|
+ ) AS workview
|
|
|
+ WHERE workord IN (
|
|
|
+ SELECT WorkOrd FROM NbrMaster
|
|
|
+ WHERE Type = 'SM' AND Status = ''
|
|
|
+ GROUP BY WorkOrd
|
|
|
+ ) OR Status = 'p'
|
|
|
+ ) a
|
|
|
+ LEFT JOIN mes_morder b ON a.workord = b.morder_no AND b.tenant_id = @TenantId
|
|
|
+ LEFT JOIN ItemMaster c ON b.product_code = c.ItemNum AND c.tenant_id = @TenantId
|
|
|
+ LEFT JOIN WorkOrdDetail d ON a.WorkOrd = d.WorkOrd AND d.tenant_id = @TenantId
|
|
|
+ LEFT JOIN ItemMaster Sonitem ON d.itemnum = Sonitem.itemnum AND Sonitem.tenant_id = @TenantId
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT moentry_mono, GROUP_CONCAT(DISTINCT fbill_no SEPARATOR ',') AS fbill_no
|
|
|
+ FROM mes_moentry WHERE tenant_id = @TenantId
|
|
|
+ GROUP BY moentry_mono
|
|
|
+ ) f ON b.morder_no = f.moentry_mono
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT workord, itemnum,
|
|
|
+ SUM(QtyRec) AS QtyRec, MAX(updatetime) AS updatetime,
|
|
|
+ SUM(QtyFrom) AS QtyFrom
|
|
|
+ FROM NbrDetail
|
|
|
+ WHERE tenant_id = @TenantId
|
|
|
+ GROUP BY workord, itemnum
|
|
|
+ ) j ON a.workord = j.workord AND d.itemnum = j.itemnum
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT a0.number,
|
|
|
+ GROUP_CONCAT(a0.supplier_name SEPARATOR ',') AS supplier_name,
|
|
|
+ GROUP_CONCAT(a0.supplier_number SEPARATOR ',') AS supplier_number
|
|
|
+ FROM srm_purchase a0
|
|
|
+ WHERE a0.quota_rate > 0 AND a0.tenant_id = @TenantId
|
|
|
+ GROUP BY a0.number
|
|
|
+ ) e ON d.ItemNum = e.number
|
|
|
+ LEFT JOIN LATERAL (
|
|
|
+ SELECT create_time AS checktime, id
|
|
|
+ FROM b_examine_result
|
|
|
+ WHERE morder_no = a.WorkOrd AND tenant_id = @TenantId
|
|
|
+ ORDER BY create_time DESC LIMIT 1
|
|
|
+ ) exm ON TRUE
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT examine_id, item_number, MIN(kitting_time) AS kitting_time
|
|
|
+ FROM b_bom_child_examine
|
|
|
+ GROUP BY examine_id, item_number
|
|
|
+ ) exd ON exm.id = exd.examine_id AND exd.item_number != b.product_code and exd.item_number=d.ItemNum
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT MIN(shd.jhshrq) AS jhshrq,
|
|
|
+ SUM(shzb.sh_delivery_quantity) AS sh_delivery_quantity,
|
|
|
+ shzb.sh_material_code
|
|
|
+ FROM scm_shd shd
|
|
|
+ LEFT JOIN scm_shdzb shzb ON shd.id = shzb.glid AND shzb.tenant_id = shd.tenant_id
|
|
|
+ WHERE shd.shzt != '完成' AND shd.tenant_id = @TenantId
|
|
|
+ GROUP BY shzb.sh_material_code
|
|
|
+ ) sh ON d.itemnum = sh.sh_material_code
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT SUM(pd.QtyOrded) AS QtyOrded, pd.itemnum,
|
|
|
+ MIN(pm.orddate) AS orddate,
|
|
|
+ GROUP_CONCAT(pm.Buyer SEPARATOR ',') AS Buyer
|
|
|
+ FROM PurOrdMaster pm
|
|
|
+ LEFT JOIN PurOrdDetail pd ON pm.purord = pd.purord AND pd.tenant_id = pm.tenant_id
|
|
|
+ WHERE pm.status != 'C' AND pd.status != 'C' AND pm.tenant_id = @TenantId
|
|
|
+ GROUP BY pd.itemnum
|
|
|
+ ) pm ON d.itemnum = pm.itemnum
|
|
|
+ ) viewql
|
|
|
+ """, new List<SugarParameter> { tenantPar });
|
|
|
+
|
|
|
+ // 兜底:确保所有行都有 tenant_id(防止参数绑定异常导致写入 NULL)
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "UPDATE WorkOrdTemp SET tenant_id = @TenantId WHERE tenant_id IS NULL",
|
|
|
+ new List<SugarParameter> { tenantPar });
|
|
|
+
|
|
|
+ var workOrdTempCount = await _db.Ado.GetIntAsync(
|
|
|
+ "SELECT COUNT(1) FROM WorkOrdTemp WHERE IFNULL(workord,'') <> '' AND tenant_id = @TenantId",
|
|
|
+ tenantPar);
|
|
|
+
|
|
|
+ // Step 3: UPDATE locStock(从 _tmp_inv_master 辅助表 JOIN)
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ UPDATE WorkOrdTemp a
|
|
|
+ LEFT JOIN _tmp_inv_master t ON a.ItemNum = t.ItemNum AND t.tenant_id = @TenantId
|
|
|
+ SET a.locStock = IFNULL(t.locStock, 0)
|
|
|
+ WHERE a.tenant_id = @TenantId
|
|
|
+ """, tenantPar);
|
|
|
+
|
|
|
+ // Step 4: UPDATE ztsl / zjsl / jykssj
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ UPDATE WorkOrdTemp a
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT ItemNum, SUM(ztsl) AS ztsl, SUM(zsl1) AS zjsl, MIN(jykssj) AS jykssj
|
|
|
+ FROM (
|
|
|
+ SELECT p.ItemNum,
|
|
|
+ CASE WHEN IFNULL(p.zfhl, 0) <= IFNULL(p.zshl, 0) THEN 0
|
|
|
+ ELSE IFNULL(p.zfhl, 0) - IFNULL(p.zshl, 0) END AS ztsl,
|
|
|
+ IFNULL(p.zsl, 0) AS zsl1, p.jykssj
|
|
|
+ FROM (
|
|
|
+ SELECT p0.PurOrd, p0.Line, p0.ItemNum,
|
|
|
+ IFNULL(s0.fhsl, 0) - IFNULL(Do0.thsl, 0) AS zfhl,
|
|
|
+ p0.ReceiptQty + p0.RctQty AS zshl,
|
|
|
+ IFNULL(i0.zjsl, 0) AS zsl, i0.jykssj
|
|
|
+ FROM PurOrdDetail p0
|
|
|
+ LEFT JOIN LATERAL (
|
|
|
+ SELECT SUM(sh_delivery_quantity) AS fhsl
|
|
|
+ FROM scm_shdzb WHERE po_bill = p0.PurOrd AND po_billline = p0.Line
|
|
|
+ AND tenant_id = p0.tenant_id
|
|
|
+ ) s0 ON TRUE
|
|
|
+ LEFT JOIN LATERAL (
|
|
|
+ SELECT SUM(m.Qty) AS zjsl, MIN(qms.FAPPLYTIME) AS jykssj
|
|
|
+ FROM MissedPrint m
|
|
|
+ LEFT JOIN qms_qcp_inspecapplyn qms ON m.checkno = qms.FBILLNO
|
|
|
+ WHERE m.Status = 'I' AND m.PurOrd = p0.PurOrd AND m.PurLine = p0.Line
|
|
|
+ AND m.tenant_id = p0.tenant_id
|
|
|
+ ) i0 ON TRUE
|
|
|
+ LEFT JOIN LATERAL (
|
|
|
+ SELECT SUM(t.QtyReturn) AS thsl
|
|
|
+ FROM PurOrdRctDetail t
|
|
|
+ WHERE t.OrdNbr = p0.PurOrd AND t.OrdLine = p0.Line
|
|
|
+ AND t.rcttype IN ('pt','temp') AND t.tenant_id = p0.tenant_id
|
|
|
+ ) Do0 ON TRUE
|
|
|
+ WHERE p0.Status <> 'C' AND p0.tenant_id = @TenantId
|
|
|
+ ) p
|
|
|
+ ) viewpur
|
|
|
+ GROUP BY ItemNum
|
|
|
+ ) b ON a.ItemNum = b.ItemNum
|
|
|
+ SET a.ztsl = IFNULL(b.ztsl, 0),
|
|
|
+ a.zjsl = IFNULL(b.zjsl, 0),
|
|
|
+ a.jykssj = b.jykssj
|
|
|
+ WHERE a.tenant_id = @TenantId
|
|
|
+ """, tenantPar);
|
|
|
+
|
|
|
+ // Step 5: UPDATE jqsl / hfjq
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ UPDATE WorkOrdTemp a
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT IF(jqsl > schedqty, schedqty, jqsl) AS jqsl, ItemNum, jqhf
|
|
|
+ FROM (
|
|
|
+ SELECT SUM(CAST(IFNULL(jq.jhdsl, 0) AS DECIMAL(18,6))) AS jqsl,
|
|
|
+ MAX(ds.schedqty) AS schedqty,
|
|
|
+ ds.ItemNum, DATE(jq.jqhf) AS jqhf
|
|
|
+ FROM scm_jhjh_jq jq
|
|
|
+ INNER JOIN srm_polist_ds ds ON jq.glid = ds.id AND ds.tenant_id = jq.tenant_id
|
|
|
+ LEFT JOIN (
|
|
|
+ SELECT SUM(sh_delivery_quantity) AS sh_delivery_quantity, jhdbh
|
|
|
+ FROM scm_shdzb WHERE tenant_id = @TenantId
|
|
|
+ GROUP BY jhdbh
|
|
|
+ ) c ON ds.dsnum = c.jhdbh
|
|
|
+ WHERE jq.flag = 0 AND jq.tenant_id = @TenantId
|
|
|
+ AND IFNULL(c.sh_delivery_quantity, 0) < ds.schedqty
|
|
|
+ AND ds.status = 'P' AND ds.schedqty - ds.sentqty > 0
|
|
|
+ AND DATE(jq.jqhf) > CURDATE()
|
|
|
+ GROUP BY ds.ItemNum, DATE(jq.jqhf)
|
|
|
+ ) jqview
|
|
|
+ ) b ON a.ItemNum = b.ItemNum AND DATE(a.OrdDate) = b.jqhf
|
|
|
+ SET a.jqsl = CAST(IFNULL(b.jqsl, 0) AS DECIMAL(18,6)),
|
|
|
+ a.hfjq = b.jqhf
|
|
|
+ WHERE a.tenant_id = @TenantId
|
|
|
+ """, tenantPar);
|
|
|
+
|
|
|
+ // Step 6: UPDATE available / shortage(按日期分段累加)
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ UPDATE WorkOrdTemp
|
|
|
+ SET available = IFNULL(locStock, 0),
|
|
|
+ shortage = IFNULL(locStock, 0) - IFNULL(ljsl, 0)
|
|
|
+ WHERE tenant_id = @TenantId AND DATE(OrdDate) <= CURDATE() + INTERVAL 1 DAY
|
|
|
+ """, tenantPar);
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ UPDATE WorkOrdTemp
|
|
|
+ SET available = IFNULL(locStock, 0) + IFNULL(zjsl, 0),
|
|
|
+ shortage = IFNULL(locStock, 0) + IFNULL(zjsl, 0) - IFNULL(ljsl, 0)
|
|
|
+ WHERE tenant_id = @TenantId AND DATE(OrdDate) = CURDATE() + INTERVAL 2 DAY
|
|
|
+ """, tenantPar);
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ UPDATE WorkOrdTemp
|
|
|
+ SET available = IFNULL(locStock, 0) + IFNULL(zjsl, 0) + IFNULL(ztsl, 0),
|
|
|
+ shortage = IFNULL(locStock, 0) + IFNULL(zjsl, 0) + IFNULL(ztsl, 0) - IFNULL(ljsl, 0)
|
|
|
+ WHERE tenant_id = @TenantId AND DATE(OrdDate) = CURDATE() + INTERVAL 3 DAY
|
|
|
+ """, tenantPar);
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ UPDATE WorkOrdTemp
|
|
|
+ SET available = IFNULL(locStock, 0) + IFNULL(zjsl, 0) + IFNULL(ztsl, 0) + IFNULL(jqsl, 0),
|
|
|
+ shortage = IFNULL(locStock, 0) + IFNULL(zjsl, 0) + IFNULL(ztsl, 0) + IFNULL(jqsl, 0) - IFNULL(ljsl, 0)
|
|
|
+ WHERE tenant_id = @TenantId
|
|
|
+ AND DATE(OrdDate) >= CURDATE() + INTERVAL 4 DAY
|
|
|
+ AND DATE(OrdDate) <= CURDATE() + INTERVAL 14 DAY
|
|
|
+ """, tenantPar);
|
|
|
+
|
|
|
+ // 清理辅助表(仅本租户)
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "DELETE FROM _tmp_inv_master WHERE tenant_id = @TenantId", tenantPar);
|
|
|
+
|
|
|
+ // ── DWD 写入 ──
|
|
|
+ await _db.Ado.BeginTranAsync();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ DELETE FROM dwd_material_readiness
|
|
|
+ WHERE tenant_id = @TenantId
|
|
|
+ AND work_order NOT IN (
|
|
|
+ SELECT DISTINCT workord FROM WorkOrdTemp WHERE IFNULL(workord, '') <> '' AND tenant_id = @TenantId
|
|
|
+ )
|
|
|
+ """, tenantPar);
|
|
|
+
|
|
|
+ var dwdCount = await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ INSERT INTO dwd_material_readiness
|
|
|
+ (tenant_id, stat_date, work_order, op_code, parent_item_code, component_item_code, component_item_name, required_qty, cumulative_required_qty, stock_available_qty, qc_pending_qty, in_transit_qty, delivery_reply_qty, available_qty, shortage_qty, ready_status, supplier_code, supplier_name, need_date, calc_batch_id, calc_time)
|
|
|
+ SELECT
|
|
|
+ @TenantId, @StatDate,
|
|
|
+ IFNULL(workord, ''), CAST(IFNULL(Op, 0) AS CHAR), IFNULL(bom_number, ''),
|
|
|
+ COALESCE(NULLIF(ItemNum, ''), NULLIF(PMBOM, ''), CONCAT('WORKORDTEMP-', IFNULL(RowNum, 0))),
|
|
|
+ IFNULL(ComponentItemDescr, ''),
|
|
|
+ IFNULL(QtyRequired, 0), IFNULL(ljsl, 0), IFNULL(locStock, 0),
|
|
|
+ IFNULL(jqsl, 0), IFNULL(ztsl, 0), IFNULL(PurOrdQty, 0), IFNULL(available, 0),
|
|
|
+ GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)),
|
|
|
+ CASE WHEN GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)) > 0 THEN 'SHORTAGE' ELSE 'READY' END,
|
|
|
+ supplier_number, supplier_name, COALESCE(jhshrq, hfjq, RecDate),
|
|
|
+ @BatchId, @SyncTime
|
|
|
+ FROM WorkOrdTemp
|
|
|
+ WHERE IFNULL(workord, '') <> '' AND tenant_id = @TenantId
|
|
|
+ ON DUPLICATE KEY UPDATE
|
|
|
+ component_item_name = VALUES(component_item_name),
|
|
|
+ required_qty = VALUES(required_qty),
|
|
|
+ cumulative_required_qty = VALUES(cumulative_required_qty),
|
|
|
+ stock_available_qty = VALUES(stock_available_qty),
|
|
|
+ qc_pending_qty = VALUES(qc_pending_qty),
|
|
|
+ in_transit_qty = VALUES(in_transit_qty),
|
|
|
+ delivery_reply_qty = VALUES(delivery_reply_qty),
|
|
|
+ available_qty = VALUES(available_qty),
|
|
|
+ shortage_qty = VALUES(shortage_qty),
|
|
|
+ ready_status = VALUES(ready_status),
|
|
|
+ supplier_code = VALUES(supplier_code),
|
|
|
+ supplier_name = VALUES(supplier_name),
|
|
|
+ need_date = VALUES(need_date),
|
|
|
+ calc_time = VALUES(calc_time),
|
|
|
+ update_time = CURRENT_TIMESTAMP
|
|
|
+ """,
|
|
|
+ new SugarParameter("@StatDate", now.Date),
|
|
|
+ new SugarParameter("@BatchId", batchId),
|
|
|
+ new SugarParameter("@SyncTime", now),
|
|
|
+ tenantPar);
|
|
|
+
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "DELETE FROM dwd_material_shortage WHERE stat_date = @StatDate AND tenant_id = @TenantId",
|
|
|
+ new SugarParameter("@StatDate", now.Date), tenantPar);
|
|
|
+
|
|
|
+ var shortageCount = await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ INSERT INTO dwd_material_shortage
|
|
|
+ (tenant_id, stat_date, work_order, op_code, component_item_code, shortage_qty, shortage_reason, expected_supply_date, supplier_code, related_po_no, risk_level, calc_batch_id, calc_time)
|
|
|
+ SELECT
|
|
|
+ @TenantId, @StatDate,
|
|
|
+ IFNULL(workord, ''), CAST(IFNULL(Op, 0) AS CHAR),
|
|
|
+ COALESCE(NULLIF(ItemNum, ''), NULLIF(PMBOM, ''), CONCAT('WORKORDTEMP-', IFNULL(RowNum, 0))),
|
|
|
+ GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)),
|
|
|
+ '当前可用量不足', COALESCE(jhshrq, hfjq, RecDate),
|
|
|
+ supplier_number, PurOrds,
|
|
|
+ CASE
|
|
|
+ WHEN GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)) > IFNULL(QtyRequired, 0) THEN 'HIGH'
|
|
|
+ WHEN GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)) > 0 THEN 'MEDIUM'
|
|
|
+ ELSE 'LOW'
|
|
|
+ END,
|
|
|
+ @BatchId, @SyncTime
|
|
|
+ FROM WorkOrdTemp
|
|
|
+ WHERE IFNULL(workord, '') <> '' AND tenant_id = @TenantId
|
|
|
+ AND GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)) > 0
|
|
|
+ """,
|
|
|
+ new SugarParameter("@StatDate", now.Date),
|
|
|
+ new SugarParameter("@BatchId", batchId),
|
|
|
+ new SugarParameter("@SyncTime", now),
|
|
|
+ tenantPar);
|
|
|
+
|
|
|
+ await _db.Ado.CommitTranAsync();
|
|
|
+ return new
|
|
|
+ {
|
|
|
+ enabled = true, batchId, workOrdTempCount, dwdCount, shortageCount,
|
|
|
+ elapsedMs = sw.ElapsedMilliseconds,
|
|
|
+ message = $"齐套/缺料刷新完成:获取 {workOrdTempCount} 条 WorkOrdTemp,写入 DWD 齐套 {dwdCount} 条、缺料 {shortageCount} 条。"
|
|
|
+ };
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+ await _db.Ado.RollbackTranAsync();
|
|
|
+ throw;
|
|
|
+ }
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
- return new { enabled = false, message = $"执行存储过程 pr_MES_Report_DayWorkOrdDetailShortage_New 失败: {ex.Message}" };
|
|
|
+ return new { enabled = false, elapsedMs = sw.ElapsedMilliseconds, message = ex.Message };
|
|
|
}
|
|
|
- finally
|
|
|
- {
|
|
|
- _db.Ado.CommandTimeOut = prevTimeout;
|
|
|
- }
|
|
|
-
|
|
|
- var workOrdTempCount = await _db.Ado.GetIntAsync(
|
|
|
- "SELECT COUNT(1) FROM WorkOrdTemp WHERE IFNULL(workord, '') <> ''");
|
|
|
+ }
|
|
|
|
|
|
- await _db.Ado.BeginTranAsync();
|
|
|
- try
|
|
|
- {
|
|
|
- // Step 2: 清理 dwd_material_readiness 旧数据(保留当前 WorkOrdTemp 中存在的工单)
|
|
|
- await _db.Ado.ExecuteCommandAsync(
|
|
|
- """
|
|
|
- DELETE FROM dwd_material_readiness
|
|
|
- WHERE tenant_id = @TenantId
|
|
|
- AND work_order NOT IN (
|
|
|
- SELECT DISTINCT workord FROM WorkOrdTemp WHERE IFNULL(workord, '') <> ''
|
|
|
+ /// <summary>
|
|
|
+ /// 从 SQL Server 读取 InvMaster 在库库存,写入 MySQL 辅助表 _tmp_inv_master。
|
|
|
+ /// </summary>
|
|
|
+ private async Task PrepareInvMasterTempAsync(string domain, long tenantId)
|
|
|
+ {
|
|
|
+ await _db.Ado.ExecuteCommandAsync("""
|
|
|
+ CREATE TABLE IF NOT EXISTS _tmp_inv_master (
|
|
|
+ tenant_id BIGINT NOT NULL,
|
|
|
+ ItemNum VARCHAR(50) NOT NULL,
|
|
|
+ locStock DECIMAL(18,6),
|
|
|
+ PRIMARY KEY (tenant_id, ItemNum)
|
|
|
)
|
|
|
- """,
|
|
|
- tenantPar);
|
|
|
+ """);
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "DELETE FROM _tmp_inv_master WHERE tenant_id = @TenantId",
|
|
|
+ new List<SugarParameter> { new("@TenantId", tenantId) });
|
|
|
|
|
|
- // Step 3: 从 WorkOrdTemp 写入 dwd_material_readiness
|
|
|
- var dwdCount = await _db.Ado.ExecuteCommandAsync(
|
|
|
- """
|
|
|
- INSERT INTO dwd_material_readiness
|
|
|
- (tenant_id, stat_date, work_order, op_code, parent_item_code, component_item_code, component_item_name, required_qty, cumulative_required_qty, stock_available_qty, qc_pending_qty, in_transit_qty, delivery_reply_qty, available_qty, shortage_qty, ready_status, supplier_code, supplier_name, need_date, calc_batch_id, calc_time)
|
|
|
- SELECT
|
|
|
- @TenantId,
|
|
|
- @StatDate,
|
|
|
- IFNULL(workord, ''),
|
|
|
- CAST(IFNULL(Op, 0) AS CHAR),
|
|
|
- IFNULL(bom_number, ''),
|
|
|
- COALESCE(NULLIF(ItemNum, ''), NULLIF(PMBOM, ''), CONCAT('WORKORDTEMP-', IFNULL(RowNum, 0))),
|
|
|
- IFNULL(ComponentItemDescr, ''),
|
|
|
- IFNULL(QtyRequired, 0),
|
|
|
- IFNULL(ljsl, 0),
|
|
|
- IFNULL(locStock, 0),
|
|
|
- IFNULL(jqsl, 0),
|
|
|
- IFNULL(ztsl, 0),
|
|
|
- IFNULL(PurOrdQty, 0),
|
|
|
- IFNULL(available, 0),
|
|
|
- GREATEST(
|
|
|
- IFNULL(shortage, 0),
|
|
|
- IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)
|
|
|
- ),
|
|
|
- CASE WHEN GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)) > 0 THEN 'SHORTAGE' ELSE 'READY' END,
|
|
|
- supplier_number,
|
|
|
- supplier_name,
|
|
|
- COALESCE(jhshrq, hfjq, RecDate),
|
|
|
- @BatchId,
|
|
|
- @SyncTime
|
|
|
- FROM WorkOrdTemp
|
|
|
- WHERE IFNULL(workord, '') <> ''
|
|
|
- ON DUPLICATE KEY UPDATE
|
|
|
- component_item_name = VALUES(component_item_name),
|
|
|
- required_qty = VALUES(required_qty),
|
|
|
- cumulative_required_qty = VALUES(cumulative_required_qty),
|
|
|
- stock_available_qty = VALUES(stock_available_qty),
|
|
|
- qc_pending_qty = VALUES(qc_pending_qty),
|
|
|
- in_transit_qty = VALUES(in_transit_qty),
|
|
|
- delivery_reply_qty = VALUES(delivery_reply_qty),
|
|
|
- available_qty = VALUES(available_qty),
|
|
|
- shortage_qty = VALUES(shortage_qty),
|
|
|
- ready_status = VALUES(ready_status),
|
|
|
- supplier_code = VALUES(supplier_code),
|
|
|
- supplier_name = VALUES(supplier_name),
|
|
|
- need_date = VALUES(need_date),
|
|
|
- calc_time = VALUES(calc_time),
|
|
|
- update_time = CURRENT_TIMESTAMP
|
|
|
- """,
|
|
|
- new SugarParameter("@StatDate", now.Date),
|
|
|
- new SugarParameter("@BatchId", batchId),
|
|
|
- new SugarParameter("@SyncTime", now),
|
|
|
- tenantPar);
|
|
|
+ var locRange = await _db.Ado.GetStringAsync(
|
|
|
+ "SELECT Val FROM ConfigurationItem WHERE FldName='MRPLocationRange' AND tenant_id = @TenantId LIMIT 1",
|
|
|
+ new List<SugarParameter> { new("@TenantId", tenantId) });
|
|
|
+ if (string.IsNullOrWhiteSpace(locRange)) return;
|
|
|
|
|
|
- // Step 4: 从 WorkOrdTemp 写入 dwd_material_shortage(先清理当天旧数据)
|
|
|
- // WorkOrdTemp 为存储过程产出的无租户中间表,租户来自调用方 JWT。
|
|
|
- await _db.Ado.ExecuteCommandAsync(
|
|
|
- "DELETE FROM dwd_material_shortage WHERE stat_date = @StatDate AND tenant_id = @TenantId",
|
|
|
- new SugarParameter("@StatDate", now.Date),
|
|
|
- tenantPar);
|
|
|
-
|
|
|
- var shortageCount = await _db.Ado.ExecuteCommandAsync(
|
|
|
- """
|
|
|
- INSERT INTO dwd_material_shortage
|
|
|
- (tenant_id, stat_date, work_order, op_code, component_item_code, shortage_qty, shortage_reason, expected_supply_date, supplier_code, related_po_no, risk_level, calc_batch_id, calc_time)
|
|
|
- SELECT
|
|
|
- @TenantId,
|
|
|
- @StatDate,
|
|
|
- IFNULL(workord, ''),
|
|
|
- CAST(IFNULL(Op, 0) AS CHAR),
|
|
|
- COALESCE(NULLIF(ItemNum, ''), NULLIF(PMBOM, ''), CONCAT('WORKORDTEMP-', IFNULL(RowNum, 0))),
|
|
|
- GREATEST(
|
|
|
- IFNULL(shortage, 0),
|
|
|
- IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)
|
|
|
- ),
|
|
|
- '当前可用量不足',
|
|
|
- COALESCE(jhshrq, hfjq, RecDate),
|
|
|
- supplier_number,
|
|
|
- PurOrds,
|
|
|
- CASE
|
|
|
- WHEN GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)) > IFNULL(QtyRequired, 0) THEN 'HIGH'
|
|
|
- WHEN GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)) > 0 THEN 'MEDIUM'
|
|
|
- ELSE 'LOW'
|
|
|
- END,
|
|
|
- @BatchId,
|
|
|
- @SyncTime
|
|
|
- FROM WorkOrdTemp
|
|
|
- WHERE IFNULL(workord, '') <> ''
|
|
|
- AND GREATEST(IFNULL(shortage, 0), IFNULL(QtyRequired, 0) - IFNULL(locStock, 0) - IFNULL(jqsl, 0) - IFNULL(ztsl, 0) - IFNULL(PurOrdQty, 0)) > 0
|
|
|
- """,
|
|
|
- new SugarParameter("@StatDate", now.Date),
|
|
|
- new SugarParameter("@BatchId", batchId),
|
|
|
- new SugarParameter("@SyncTime", now),
|
|
|
- tenantPar);
|
|
|
+ var locations = locRange.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
|
+ if (locations.Length == 0) return;
|
|
|
|
|
|
- await _db.Ado.CommitTranAsync();
|
|
|
- return new
|
|
|
- {
|
|
|
- enabled = true,
|
|
|
- batchId,
|
|
|
- workOrdTempCount,
|
|
|
- dwdCount,
|
|
|
- shortageCount,
|
|
|
- message = $"齐套/缺料刷新完成:执行存储过程获取 {workOrdTempCount} 条 WorkOrdTemp 数据,写入 DWD 齐套 {dwdCount} 条、缺料 {shortageCount} 条。"
|
|
|
- };
|
|
|
+ var remote = await _scopeFactory.GetScopeAsync(SqlServerSourceCode);
|
|
|
+ var locParams = new List<string>();
|
|
|
+ var pars = new List<SugarParameter> { new("@Domain", domain) };
|
|
|
+ for (var i = 0; i < locations.Length; i++)
|
|
|
+ {
|
|
|
+ locParams.Add($"@loc{i}");
|
|
|
+ pars.Add(new SugarParameter($"@loc{i}", locations[i]));
|
|
|
}
|
|
|
- catch
|
|
|
+
|
|
|
+ var invSql = $"""
|
|
|
+ SELECT ItemNum, SUM(ISNULL(AvailStatusQty, 0) + ISNULL(Assay, 0)) AS locStock
|
|
|
+ FROM InvMaster
|
|
|
+ WHERE Domain = @Domain AND Location IN ({string.Join(",", locParams)})
|
|
|
+ GROUP BY ItemNum
|
|
|
+ """;
|
|
|
+ var rows = await remote.Ado.SqlQueryAsync<LocStockRow>(invSql, pars);
|
|
|
+ if (rows.Count == 0) return;
|
|
|
+
|
|
|
+ const int batchSize = 500;
|
|
|
+ for (var offset = 0; offset < rows.Count; offset += batchSize)
|
|
|
{
|
|
|
- await _db.Ado.RollbackTranAsync();
|
|
|
- throw;
|
|
|
+ var batch = rows.Skip(offset).Take(batchSize).ToList();
|
|
|
+ var batchPars = new List<SugarParameter> { new("@TenantId", tenantId) };
|
|
|
+ var values = new List<string>();
|
|
|
+ for (var i = 0; i < batch.Count; i++)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(batch[i].ItemNum)) continue;
|
|
|
+ values.Add($"(@TenantId, @item{i}, @stock{i})");
|
|
|
+ batchPars.Add(new SugarParameter($"@item{i}", batch[i].ItemNum));
|
|
|
+ batchPars.Add(new SugarParameter($"@stock{i}", batch[i].LocStock));
|
|
|
+ }
|
|
|
+ if (values.Count > 0)
|
|
|
+ {
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ $"INSERT INTO _tmp_inv_master (tenant_id, ItemNum, locStock) VALUES {string.Join(",", values)}",
|
|
|
+ batchPars);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private sealed class LocStockRow
|
|
|
+ {
|
|
|
+ public string? ItemNum { get; set; }
|
|
|
+ public decimal LocStock { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
private async Task<bool> ShouldUseDwdAsync(WorkOrderMaterialReadinessListInput input)
|
|
|
{
|
|
|
if (!string.IsNullOrWhiteSpace(input.SeOrder))
|