Просмотр исходного кода

fix: 采购/齐套/外协多模块修复 | server 1.0.410

- PurchaseOrderService: DWD 路径 0 条时自动回退 legacy 路径

- DemandOrderService: PurOrdMaster INSERT 补全 NOT NULL 字段(CreditTermsInt 等)

- UniversalWorkOrderService: 工序外协工单选择列表改为供应商粒度 EXISTS 过滤

- WorkOrderSchedulingService: 采购订单 tab 增加 srm_pr_main 中间表关联 DO/PO

- WorkOrderMaterialReadinessService: shortage_qty 用 GREATEST 公式兜底重算
Pengxy 23 часов назад
Родитель
Сommit
cf88e20b54

+ 3 - 3
server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

@@ -11,9 +11,9 @@
     <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
     <Copyright>Admin.NET</Copyright>
     <Description>Admin.NET 通用权限开发平台</Description>
-    <AssemblyVersion>1.0.409</AssemblyVersion>
-    <FileVersion>1.0.409</FileVersion>
-    <Version>1.0.409</Version>
+    <AssemblyVersion>1.0.410</AssemblyVersion>
+    <FileVersion>1.0.410</FileVersion>
+    <Version>1.0.410</Version>
   </PropertyGroup>
 
   <ItemGroup>

+ 46 - 27
server/Plugins/Admin.NET.Plugin.AiDOP/Production/WorkOrderSchedulingService.cs

@@ -489,39 +489,58 @@ public class WorkOrderSchedulingService : IDynamicApiController, ITransient
 
     private async Task<List<TracePoRow>> QueryTracePoAsync(string workOrd, long tenantId)
     {
+        // 双路合并:
+        //   ① PW(委外加工):PurOrdMaster.WorkOrd 直连
+        //   ② DO/PO(要货令/采购订单):srm_pr_main.pr_billno = PurOrdDetail.Req
         const string sql = """
             SELECT
-                ROW_NUMBER() OVER (ORDER BY t.po_billno) AS Sno,
-                t.po_billno AS PoBillno,
-                t.supplier_no AS SupplierNo,
-                t.supplier_name AS SupplierName,
-                CAST(t.po_total AS CHAR) AS PoTotal,
-                IFNULL(t.po_purchaser, '') AS PoPurchaser,
-                t.state AS State,
-                IFNULL(DATE_FORMAT(t.po_ssend_date, '%Y-%m-%d'), '') AS PoSsendDate,
-                IFNULL(DATE_FORMAT(t.create_time, '%Y-%m-%d %H:%i:%s'), '') AS CreateTime
+                ROW_NUMBER() OVER (ORDER BY PoBillno) AS Sno,
+                PoBillno, SupplierNo, SupplierName, PoTotal, PoPurchaser, State, PoSsendDate, CreateTime
             FROM (
                 SELECT DISTINCT
-                    pm.po_billno,
-                    pm.supplier_no,
-                    pm.supplier_name,
-                    pm.po_total,
-                    pm.po_purchaser,
-                    pm.create_time,
-                    pm.po_ssend_date,
-                    CASE pm.state
-                        WHEN 0 THEN '新增'
-                        WHEN 1 THEN '审核中'
-                        WHEN 2 THEN '同意'
-                        WHEN 3 THEN '关闭'
-                        ELSE CAST(pm.state AS CHAR)
-                    END AS state
-                FROM srm_po_main pm
-                INNER JOIN srm_po_list pl ON pl.po_id = pm.id AND pl.po_mono = @WorkOrd
+                    pm.PurOrd AS PoBillno,
+                    IFNULL(TRIM(pm.Supp), '') AS SupplierNo,
+                    IFNULL(TRIM(s.SortName), '') AS SupplierName,
+                    CAST(IFNULL(det.PoTotal, 0) AS CHAR) AS PoTotal,
+                    IFNULL(TRIM(pm.Buyer), '') AS PoPurchaser,
+                    CASE
+                        WHEN IFNULL(LENGTH(pm.Status), 0) = 0 THEN '新增'
+                        WHEN pm.Status = 'R' THEN '新增'
+                        WHEN pm.Status = 'S' THEN '已下达'
+                        WHEN pm.Status = 'C' THEN '关闭'
+                        ELSE pm.Status
+                    END AS State,
+                    IFNULL(DATE_FORMAT(pm.OrdDate, '%Y-%m-%d'), '') AS PoSsendDate,
+                    IFNULL(DATE_FORMAT(pm.CreateTime, '%Y-%m-%d %H:%i:%s'), '') AS CreateTime
+                FROM PurOrdMaster pm
+                LEFT JOIN SuppMaster s ON pm.Supp = s.Supp AND s.tenant_id = pm.tenant_id
+                LEFT JOIN (
+                    SELECT PurOrdRecID,
+                           SUM(IFNULL(QtyOrded, 0) * IFNULL(PurCost, 0)) AS PoTotal
+                    FROM PurOrdDetail
+                    GROUP BY PurOrdRecID
+                ) det ON det.PurOrdRecID = pm.RecID
                 WHERE pm.tenant_id = @TenantId
-                  AND pm.IsDeleted = 0
+                  AND IFNULL(pm.IsActive, 0) <= 1
+                  AND (
+                      -- ① PW:PurOrdMaster.WorkOrd 直连
+                      pm.WorkOrd = @WorkOrd
+                      OR
+                      -- ② DO/PO:srm_pr_main.pr_billno → PurOrdDetail.Req
+                      pm.RecID IN (
+                          SELECT pd.PurOrdRecID
+                          FROM PurOrdDetail pd
+                          INNER JOIN srm_pr_main pr
+                              ON (pr.pr_billno = pd.Req OR pr.SAP_pr_billno = pd.Req)
+                              AND pr.tenant_id = pd.tenant_id
+                          WHERE pr.pr_mono = @WorkOrd
+                            AND pr.tenant_id = @TenantId
+                            AND IFNULL(pr.state, 0) <> 0
+                            AND IFNULL(pr.IsDeleted, 0) = 0
+                      )
+                  )
             ) AS t
-            ORDER BY t.po_billno
+            ORDER BY PoBillno
             """;
         return await _db.Ado.SqlQueryAsync<TracePoRow>(sql, new { WorkOrd = workOrd, TenantId = tenantId });
     }

+ 20 - 7
server/Plugins/Admin.NET.Plugin.AiDOP/Supply/DemandOrderService.cs

@@ -177,22 +177,35 @@ public class DemandOrderService : IDynamicApiController, ITransient
                 await _db.Ado.ExecuteCommandAsync(
                     """
                     INSERT INTO PurOrdMaster
-                    (PurOrd,OrdDate,Supp,ReqBy,Buyer,Department,Curr,`Usage`,Remark,Potype,FSTID,Status,IsActive,Confirming,
-                     CreateUser,CreateTime,UpdateUser,UpdateTime,Domain,tenant_id)
+                    (
+                        Confirming, CreditTermsInt, Disc, ExchRate, EstVal, ExchRate1, ExchRate2,
+                        FixedPrice, FixedRate, Frt, PartialOK, AmtPrepaid, PrintPO, PST, Recurr,
+                        `Release`, Revision, Scheduled, ServiceCharge, SpecialCharge, Taxable,
+                        Tax1, Tax2, Tax3, TransportDays, IsActive, IsConfirm, Potype, IsChanged,
+                        TaxIn, Amt, IsPriceChanged,
+                        PurOrd, OrdDate, ReqBy, Status, Buyer, Supp, Department, `Usage`, FSTID, Remark,
+                        CreateUser, CreateTime, UpdateUser, UpdateTime, Domain, tenant_id
+                    )
                     VALUES
-                    (@PurOrd,@OrdDate,@Supp,@ReqBy,@Buyer,@Department,@Curr,@Usage,@Remark,'PO',@FSTID,'R',1,0,
-                     @CreateUser,@CreateTime,@UpdateUser,@UpdateTime,@Domain,@TenantId)
+                    (
+                        0, 0, 0, 1, 0, 1, 1,
+                        0, 0, 0, 1, 0, 0, 0, 0,
+                        0, 0, 0, 0, 0, 1,
+                        0, 0, 0, 0, 1, 1, 'PO', 0,
+                        1, 0, 0,
+                        @PurOrd, @OrdDate, @ReqBy, 'R', @Buyer, @Supp, @Department, @Usage, @FSTID, @Remark,
+                        @CreateUser, @CreateTime, @UpdateUser, @UpdateTime, @Domain, @TenantId
+                    )
                     """,
                     new SugarParameter("@PurOrd", purOrd),
                     new SugarParameter("@OrdDate", ordDate),
-                    new SugarParameter("@Supp", input.Supp.Trim()),
                     new SugarParameter("@ReqBy", reqBy),
                     new SugarParameter("@Buyer", buyer),
+                    new SugarParameter("@Supp", input.Supp.Trim()),
                     new SugarParameter("@Department", input.Department.Trim()),
-                    new SugarParameter("@Curr", input.Curr?.Trim()),
                     new SugarParameter("@Usage", usage),
-                    new SugarParameter("@Remark", input.Remark?.Trim()),
                     new SugarParameter("@FSTID", fstid),
+                    new SugarParameter("@Remark", input.Remark?.Trim()),
                     new SugarParameter("@CreateUser", _userManager.Account),
                     new SugarParameter("@CreateTime", now),
                     new SugarParameter("@UpdateUser", _userManager.Account),

+ 26 - 3
server/Plugins/Admin.NET.Plugin.AiDOP/Supply/PurchaseOrderService.cs

@@ -28,9 +28,23 @@ public class PurchaseOrderService : IDynamicApiController, ITransient
 
         if (await ShouldUseDwdAsync())
         {
-            return await GetDwdListAsync(input, page, pageSize, offset);
+            var dwdResult = (LegacyResult)(await GetDwdListAsync(input, page, pageSize, offset));
+            // DWD 路径有数据但当前查询未命中 → 回退 legacy 路径(覆盖 MDP 同步延迟场景)
+            if (dwdResult.total == 0)
+            {
+                var legacyResult = (LegacyResult)(await GetLegacyListAsync(input, page, pageSize, offset));
+                if (legacyResult.total > 0)
+                    return legacyResult;
+            }
+            return dwdResult;
         }
 
+        return await GetLegacyListAsync(input, page, pageSize, offset);
+    }
+
+    /// <summary>Legacy 路径:查 vscm_jhjh + PurOrdMaster</summary>
+    private async Task<object> GetLegacyListAsync(PurchaseOrderListInput input, int page, int pageSize, int offset)
+    {
         var where = new List<string> { "1=1", "a.cgdd IN (SELECT PurOrd FROM PurOrdMaster WHERE Potype='PO')" };
         var pars = new List<SugarParameter>();
 
@@ -126,7 +140,7 @@ public class PurchaseOrderService : IDynamicApiController, ITransient
             """,
             pars);
 
-        return new { total, page, pageSize, list };
+        return new LegacyResult { total = total, page = page, pageSize = pageSize, list = list, source = "vscm_jhjh" };
     }
 
     private async Task<bool> ShouldUseDwdAsync()
@@ -190,7 +204,7 @@ public class PurchaseOrderService : IDynamicApiController, ITransient
             """,
             pars);
 
-        return new { total, page, pageSize, list, source = "dwd_supplier_delivery" };
+        return new LegacyResult { total = total, page = page, pageSize = pageSize, list = list, source = "dwd_supplier_delivery" };
     }
 
     private static string BuildDwdOrderBy(string? sortField, string? sortOrder)
@@ -236,6 +250,15 @@ public class PurchaseOrderService : IDynamicApiController, ITransient
         };
     }
 
+    private sealed class LegacyResult
+    {
+        public int total { get; set; }
+        public int page { get; set; }
+        public int pageSize { get; set; }
+        public object? list { get; set; }
+        public string source { get; set; } = "";
+    }
+
     private sealed class PurchaseOrderListRow
     {
         public string? Gysdm { get; set; }

+ 12 - 6
server/Plugins/Admin.NET.Plugin.AiDOP/Supply/WorkOrderMaterialReadinessService.cs

@@ -184,8 +184,11 @@ public class WorkOrderMaterialReadinessService : IDynamicApiController, ITransie
                 IFNULL(ztsl, 0),
                 IFNULL(PurOrdQty, 0),
                 IFNULL(available, 0),
-                IFNULL(shortage, 0),
-                CASE WHEN IFNULL(shortage, 0) > 0 THEN 'SHORTAGE' ELSE 'READY' END,
+                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),
@@ -232,21 +235,24 @@ public class WorkOrderMaterialReadinessService : IDynamicApiController, ITransie
                 IFNULL(workord, ''),
                 CAST(IFNULL(Op, 0) AS CHAR),
                 COALESCE(NULLIF(ItemNum, ''), NULLIF(PMBOM, ''), CONCAT('WORKORDTEMP-', IFNULL(RowNum, 0))),
-                IFNULL(shortage, 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 IFNULL(shortage, 0) > IFNULL(QtyRequired, 0) THEN 'HIGH'
-                    WHEN IFNULL(shortage, 0) > 0 THEN 'MEDIUM'
+                    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 IFNULL(shortage, 0) > 0
+              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),

+ 13 - 1
server/Plugins/Admin.NET.Plugin.AiDOP/Universal/UniversalWorkOrderService.cs

@@ -31,7 +31,19 @@ public class UniversalWorkOrderService : IDynamicApiController, ITransient
         {
             "a.tenant_id = @TenantId",
             "a.Status!='c'",
-            "a.Workord in (select Workord from WorkOrdRouting where ProcessOut=1 and WorkOrd not in (SELECT WorkOrd from PurOrdMaster where Potype='PW' and WorkOrd is not null))"
+            // 只展示:至少存在一个“外协工序+供应商”尚未生成 PW 采购单的工单
+            @"EXISTS (
+                SELECT 1
+                FROM WorkOrdRouting r
+                INNER JOIN RoutingOpDetail d ON r.Op = d.Op AND r.ItemNum = d.RoutingCode
+                WHERE r.WorkOrd = a.WorkOrd
+                  AND r.ProcessOut = 1
+                  AND IFNULL(TRIM(d.SupplierCode),'') <> ''
+                  AND NOT EXISTS (
+                      SELECT 1 FROM PurOrdMaster p
+                      WHERE p.WorkOrd = a.WorkOrd AND p.Supp = d.SupplierCode AND p.Potype = 'PW'
+                  )
+            )"
         };
         var pars = new List<SugarParameter> { new SugarParameter("@TenantId", tenantId) };
         if (!string.IsNullOrWhiteSpace(input.WorkOrd))