WorkOrderExecutionProgressSql.cs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. namespace Admin.NET.Plugin.AiDOP.Production;
  2. /// <summary>
  3. /// 工单工序执行进度口径(唯一真相):工单执行进度看板与工序进度明细共用,避免两处各写一套算法。
  4. /// <para>
  5. /// 流程件语义:产品需逐序加工,全部工序走完一遍才算完成,因此「完成件数」取末道工序完成量,
  6. /// 而非各工序 QtyComplete 累加(165 报工会把当前工序之前的一段工序一并推进,累加值无业务意义)。
  7. /// </para>
  8. /// </summary>
  9. internal static class WorkOrderExecutionProgressSql
  10. {
  11. /// <summary>当前工序:优先正在生产(Status='W')的一序;否则取已有完成量的最靠后一序。</summary>
  12. public static string CurrentOp(string routingMatch) => $"""
  13. SELECT r.OP AS Op, r.Descr AS Descr, IFNULL(r.QtyComplete, 0) AS QtyComplete
  14. FROM WorkOrdRouting r
  15. WHERE {routingMatch}
  16. AND IFNULL(r.IsActive, 1) = 1
  17. AND (UPPER(IFNULL(r.Status, '')) = 'W' OR IFNULL(r.QtyComplete, 0) > 0)
  18. ORDER BY (UPPER(IFNULL(r.Status, '')) = 'W') DESC, (r.OP + 0) DESC
  19. LIMIT 1
  20. """;
  21. /// <summary>末道工序:工艺路线最大 OP,作为「整单工艺走完」的判定序。</summary>
  22. public static string LastOp(string routingMatch) => $"""
  23. SELECT r.OP AS Op, r.Descr AS Descr, IFNULL(r.QtyComplete, 0) AS QtyComplete
  24. FROM WorkOrdRouting r
  25. WHERE {routingMatch}
  26. AND IFNULL(r.IsActive, 1) = 1
  27. ORDER BY (r.OP + 0) DESC
  28. LIMIT 1
  29. """;
  30. /// <summary>全工序完成量求和:仅作技术对账,不代表流程件进度。</summary>
  31. public static string OpSum(string routingMatch) => $"""
  32. SELECT SUM(IFNULL(r.QtyComplete, 0)) AS QtyOpComplete
  33. FROM WorkOrdRouting r
  34. WHERE {routingMatch}
  35. AND IFNULL(r.IsActive, 1) = 1
  36. """;
  37. /// <summary>统一的进度列表达式,供看板行与明细汇总共用。</summary>
  38. public static string Columns(string master, string cur, string last, string sum) => $"""
  39. {cur}.Op AS CurrentOp,
  40. {cur}.Descr AS CurrentOpName,
  41. IFNULL({cur}.QtyComplete, 0) AS CurrentOpQty,
  42. {last}.Op AS LastOp,
  43. {last}.Descr AS LastOpName,
  44. IFNULL({last}.QtyComplete, 0) AS LastOpQty,
  45. GREATEST(IFNULL({cur}.QtyComplete, 0) - IFNULL({last}.QtyComplete, 0), 0) AS QtyWip,
  46. IFNULL({sum}.QtyOpComplete, 0) AS QtyOpComplete,
  47. CASE
  48. WHEN IFNULL({master}.QtyOrded, 0) = 0 THEN 0
  49. ELSE ROUND(100.0 * IFNULL({last}.QtyComplete, 0) / {master}.QtyOrded, 4)
  50. END AS ProgressExec
  51. """;
  52. /// <summary>三个 LATERAL 关联,别名固定为 curOp / lastOp / opAgg。</summary>
  53. public static string Laterals(string routingMatch) => $"""
  54. LEFT JOIN LATERAL (
  55. {CurrentOp(routingMatch)}
  56. ) AS curOp ON TRUE
  57. LEFT JOIN LATERAL (
  58. {LastOp(routingMatch)}
  59. ) AS lastOp ON TRUE
  60. LEFT JOIN LATERAL (
  61. {OpSum(routingMatch)}
  62. ) AS opAgg ON TRUE
  63. """;
  64. }