WorkOrderMaterialDetailSyncService.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. namespace Admin.NET.Plugin.AiDOP.WorkOrder;
  2. /// <summary>从资源检查结果同步工单物料明细(WorkOrdDetail)。</summary>
  3. public class WorkOrderMaterialDetailSyncService : ITransient
  4. {
  5. private readonly ISqlSugarClient _db;
  6. public WorkOrderMaterialDetailSyncService(ISqlSugarClient db)
  7. {
  8. _db = db;
  9. }
  10. public async Task<int> EnsureFromResourceCheckAsync(long tenantId, string workOrd, string account)
  11. {
  12. var wo = await LoadWorkOrderAsync(tenantId, workOrd);
  13. if (wo is null)
  14. return 0;
  15. var components = await LoadResourceCheckComponentsAsync(tenantId, workOrd);
  16. if (components.Count == 0)
  17. return 0;
  18. var now = DateTime.Now;
  19. // 加载已有明细,按 (ItemNum) 匹配进行 upsert
  20. var existingDetails = await _db.Ado.SqlQueryAsync<ExistingDetailRow>(
  21. """
  22. SELECT RecID, ItemNum, QtyRequired
  23. FROM WorkOrdDetail
  24. WHERE tenant_id = @TenantId AND WorkOrd = @WorkOrd AND IFNULL(IsActive, 0) = 1
  25. """,
  26. new SugarParameter("@TenantId", tenantId),
  27. new SugarParameter("@WorkOrd", workOrd));
  28. var existingMap = existingDetails.ToDictionary(x => x.ItemNum, x => x);
  29. var inserted = 0;
  30. var line = existingDetails.Count + 1;
  31. foreach (var c in components)
  32. {
  33. if (existingMap.TryGetValue(c.ItemNumber, out var ex))
  34. {
  35. // 已有明细:更新 QtyRequired
  36. if (ex.QtyRequired != c.QtyRequired)
  37. {
  38. await _db.Ado.ExecuteCommandAsync(
  39. """
  40. UPDATE WorkOrdDetail
  41. SET QtyRequired = @QtyRequired, UpdateUser = @User, UpdateTime = @Now
  42. WHERE RecID = @RecID
  43. """,
  44. new SugarParameter("@QtyRequired", c.QtyRequired),
  45. new SugarParameter("@User", account),
  46. new SugarParameter("@Now", now),
  47. new SugarParameter("@RecID", ex.RecID));
  48. }
  49. existingMap.Remove(c.ItemNumber);
  50. }
  51. else
  52. {
  53. // 新增明细
  54. await _db.Ado.ExecuteCommandAsync(
  55. """
  56. INSERT INTO WorkOrdDetail (
  57. WorkOrdMasterRecID, `Domain`, WorkOrd, LineNum, ItemNum, Op,
  58. Location, QtyRequired, QtyPosted, UM, IsActive, IsConfirm,
  59. CreateUser, CreateTime, UpdateUser, UpdateTime, tenant_id
  60. ) VALUES (
  61. @MasterRecId, @Domain, @WorkOrd, @LineNum, @ItemNum, @Op,
  62. @Location, @QtyRequired, 0, @UM, 1, 0,
  63. @User, @Now, @User, @Now, @TenantId
  64. )
  65. """,
  66. new SugarParameter("@MasterRecId", wo.RecId),
  67. new SugarParameter("@Domain", wo.Domain ?? tenantId.ToString()),
  68. new SugarParameter("@WorkOrd", workOrd),
  69. new SugarParameter("@LineNum", line),
  70. new SugarParameter("@ItemNum", c.ItemNumber),
  71. new SugarParameter("@Op", c.Op),
  72. new SugarParameter("@Location", c.Location ?? (object)DBNull.Value),
  73. new SugarParameter("@QtyRequired", c.QtyRequired),
  74. new SugarParameter("@UM", c.Unit ?? (object)DBNull.Value),
  75. new SugarParameter("@User", account),
  76. new SugarParameter("@Now", now),
  77. new SugarParameter("@TenantId", tenantId));
  78. inserted++;
  79. line++;
  80. }
  81. }
  82. // 删除资源检查中没有的子物料
  83. foreach (var stale in existingMap.Values)
  84. {
  85. await _db.Ado.ExecuteCommandAsync(
  86. "DELETE FROM WorkOrdDetail WHERE RecID = @RecID",
  87. new SugarParameter("@RecID", stale.RecID));
  88. }
  89. return components.Count;
  90. }
  91. private async Task<WorkOrderRow?> LoadWorkOrderAsync(long tenantId, string workOrd)
  92. {
  93. var rows = await _db.Ado.SqlQueryAsync<WorkOrderRow>(
  94. """
  95. SELECT RecID AS RecId, `Domain`, ItemNum
  96. FROM WorkOrdMaster
  97. WHERE tenant_id = @TenantId AND WorkOrd = @WorkOrd
  98. LIMIT 1
  99. """,
  100. new SugarParameter("@TenantId", tenantId),
  101. new SugarParameter("@WorkOrd", workOrd));
  102. return rows.FirstOrDefault();
  103. }
  104. private async Task<List<ComponentRow>> LoadResourceCheckComponentsAsync(long tenantId, string workOrd)
  105. {
  106. return await _db.Ado.SqlQueryAsync<ComponentRow>(
  107. """
  108. SELECT
  109. bce.item_number AS ItemNumber,
  110. SUM(IFNULL(bce.needCount, 0)) AS QtyRequired,
  111. IFNULL(MAX(im.Um), '') AS Unit,
  112. IFNULL(MAX(im.Location), '') AS Location,
  113. IFNULL(MAX(bce.Op), 0) AS Op
  114. FROM b_examine_result ber
  115. INNER JOIN b_bom_child_examine bce ON ber.Id = bce.examine_id AND bce.is_use = 1
  116. LEFT JOIN ItemMaster im ON bce.item_number = im.ItemNum
  117. WHERE ber.tenant_id = @TenantId
  118. AND ber.IsDeleted = 0
  119. AND ber.morder_no = @WorkOrd
  120. AND ber.Id = (
  121. SELECT br.Id FROM b_examine_result br
  122. WHERE br.tenant_id = @TenantId AND br.morder_no = @WorkOrd AND br.IsDeleted = 0
  123. ORDER BY br.create_time DESC LIMIT 1
  124. )
  125. AND IFNULL(bce.num, '') <> '1'
  126. AND IFNULL(bce.backflush, 0) = 0
  127. AND IFNULL(bce.erp_cls, 3) <> 4
  128. GROUP BY bce.item_number
  129. HAVING SUM(IFNULL(bce.needCount, 0)) > 0
  130. ORDER BY MIN(bce.id)
  131. """,
  132. new SugarParameter("@TenantId", tenantId),
  133. new SugarParameter("@WorkOrd", workOrd));
  134. }
  135. private sealed class WorkOrderRow
  136. {
  137. public long RecId { get; set; }
  138. public string? Domain { get; set; }
  139. public string? ItemNum { get; set; }
  140. }
  141. private sealed class ComponentRow
  142. {
  143. public string ItemNumber { get; set; } = string.Empty;
  144. public decimal QtyRequired { get; set; }
  145. public string? Unit { get; set; }
  146. public string? Location { get; set; }
  147. public int Op { get; set; }
  148. }
  149. private sealed class ExistingDetailRow
  150. {
  151. public long RecID { get; set; }
  152. public string ItemNum { get; set; } = string.Empty;
  153. public decimal QtyRequired { get; set; }
  154. }
  155. }