WorkOrderMaterialReadinessService.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. namespace Admin.NET.Plugin.AiDOP.Supply;
  2. /// <summary>
  3. /// 工单物料齐套上线看板服务
  4. /// </summary>
  5. [ApiDescriptionSettings(Order = 312, Description = "工单物料齐套上线看板")]
  6. [Route("api/Supply")]
  7. [AllowAnonymous]
  8. [NonUnify]
  9. public class WorkOrderMaterialReadinessService : IDynamicApiController, ITransient
  10. {
  11. private readonly ISqlSugarClient _db;
  12. private readonly S3MdpSyncTransformService _mdpService;
  13. public WorkOrderMaterialReadinessService(ISqlSugarClient db, S3MdpSyncTransformService mdpService)
  14. {
  15. _db = db;
  16. _mdpService = mdpService;
  17. }
  18. [DisplayName("工单物料齐套上线看板列表")]
  19. [HttpGet("work-order-material-readiness/list")]
  20. public async Task<object> GetList([FromQuery] WorkOrderMaterialReadinessListInput input)
  21. {
  22. var page = input.Page <= 0 ? 1 : input.Page;
  23. var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
  24. var offset = (page - 1) * pageSize;
  25. var where = new List<string> { "1=1" };
  26. var pars = new List<SugarParameter>();
  27. if (!string.IsNullOrWhiteSpace(input.SeOrder))
  28. {
  29. where.Add("a.SeOrder LIKE @SeOrder");
  30. pars.Add(new SugarParameter("@SeOrder", $"%{input.SeOrder.Trim()}%"));
  31. }
  32. if (!string.IsNullOrWhiteSpace(input.Workord))
  33. {
  34. where.Add("a.workord LIKE @Workord");
  35. pars.Add(new SugarParameter("@Workord", $"%{input.Workord.Trim()}%"));
  36. }
  37. if (!string.IsNullOrWhiteSpace(input.BomNumber))
  38. {
  39. where.Add("a.bom_number = @BomNumber");
  40. pars.Add(new SugarParameter("@BomNumber", input.BomNumber.Trim()));
  41. }
  42. if (!string.IsNullOrWhiteSpace(input.ItemNum))
  43. {
  44. where.Add("a.ItemNum = @ItemNum");
  45. pars.Add(new SugarParameter("@ItemNum", input.ItemNum.Trim()));
  46. }
  47. if (!string.IsNullOrWhiteSpace(input.SupplierName))
  48. {
  49. where.Add("a.supplier_name LIKE @SupplierName");
  50. pars.Add(new SugarParameter("@SupplierName", $"%{input.SupplierName.Trim()}%"));
  51. }
  52. if (await ShouldUseDwdAsync(input))
  53. {
  54. return await GetDwdListAsync(input, page, pageSize, offset);
  55. }
  56. var fromSql = $"FROM WorkOrdTemp a WHERE {string.Join(" AND ", where)}";
  57. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(1) {fromSql}", pars);
  58. var orderBy = BuildOrderBy(input.SortField, input.SortOrder);
  59. var list = await _db.Ado.SqlQueryAsync<WorkOrderMaterialReadinessRow>(
  60. $"""
  61. SELECT
  62. a.RowNum AS RowNum,
  63. a.SeOrder AS SeOrder,
  64. a.SePriority AS SePriority,
  65. a.workord AS Workord,
  66. a.Priority AS Priority,
  67. a.morder_type AS MorderType,
  68. a.LotSerial AS LotSerial,
  69. a.Op AS Op,
  70. a.OpOrdDate AS OpOrdDate,
  71. a.bom_number AS BomNumber,
  72. a.Descr AS Descr,
  73. a.Descr1 AS Descr1,
  74. a.production_number AS ProductionNumber,
  75. a.checktime AS Checktime,
  76. a.PMBOM AS PMBOM,
  77. a.ItemType AS ItemType,
  78. a.OwnerApplication AS OwnerApplication,
  79. a.ItemNum AS ItemNum,
  80. a.ComponentItemDescr AS ComponentItemDescr,
  81. a.ComponentItemDrawing AS ComponentItemDrawing,
  82. a.QtyRequired AS QtyRequired,
  83. a.ComponentItemUM AS ComponentItemUM,
  84. a.ComponentItemKittingTime AS ComponentItemKittingTime,
  85. a.QtyRec AS QtyRec,
  86. a.RecDate AS RecDate,
  87. a.shortage AS Shortage,
  88. a.QtyFrom AS QtyFrom,
  89. a.FromDate AS FromDate,
  90. a.zjsl AS Zjsl,
  91. a.jykssj AS Jykssj,
  92. a.ztsl AS Ztsl,
  93. a.jhshrq AS Jhshrq,
  94. a.jqsl AS Jqsl,
  95. a.hfjq AS Hfjq,
  96. a.PurOrdQty AS PurOrdQty,
  97. a.PurOrdorddate AS PurOrdorddate,
  98. a.PurOrds AS PurOrds,
  99. a.ContractDate AS ContractDate,
  100. a.ContractNO AS ContractNO,
  101. a.supplier_number AS SupplierNumber,
  102. a.supplier_name AS SupplierName,
  103. a.Buyer AS Buyer,
  104. a.BuyerUser AS BuyerUser
  105. {fromSql}
  106. ORDER BY {orderBy}
  107. LIMIT {pageSize} OFFSET {offset}
  108. """,
  109. pars);
  110. return new { total, page, pageSize, list };
  111. }
  112. [DisplayName("刷新工单物料齐套上线看板")]
  113. [HttpPost("work-order-material-readiness/refresh")]
  114. public async Task<object> Refresh()
  115. {
  116. var result = await _mdpService.RefreshMaterialReadinessAsync();
  117. return new
  118. {
  119. enabled = true,
  120. batchId = result.BatchId,
  121. stdCount = result.StdCount,
  122. dwdCount = result.ReadinessCount,
  123. shortageCount = result.ShortageCount,
  124. message = "齐套/缺料刷新完成,已通过 S3 MDP 转换服务写入标准层和 DWD。"
  125. };
  126. }
  127. private async Task<int> CountMaterialReadinessStdAsync()
  128. {
  129. return await _db.Ado.GetIntAsync("SELECT COUNT(1) FROM mdp_std_material_readiness WHERE IFNULL(work_order, '') <> ''");
  130. }
  131. private async Task<int> UpsertMaterialReadinessDwdAsync(string batchId, DateTime now)
  132. {
  133. return await _db.Ado.ExecuteCommandAsync(
  134. """
  135. INSERT INTO dwd_material_readiness
  136. (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)
  137. SELECT
  138. s.tenant_id,
  139. @StatDate,
  140. s.work_order,
  141. s.op_code,
  142. s.item_code,
  143. s.component_item_code,
  144. IFNULL(i.Descr, ''),
  145. IFNULL(s.required_qty, 0),
  146. IFNULL(s.required_qty, 0),
  147. IFNULL(s.available_qty, 0),
  148. 0,
  149. IFNULL(s.in_transit_qty, 0),
  150. IFNULL(s.incoming_qty, 0),
  151. IFNULL(s.available_qty, 0),
  152. IFNULL(s.shortage_qty, 0),
  153. CASE WHEN IFNULL(s.shortage_qty, 0) > 0 THEN 'SHORTAGE' ELSE 'READY' END,
  154. s.supplier_code,
  155. IFNULL(sp.SortName, ''),
  156. s.need_date,
  157. @BatchId,
  158. @Now
  159. FROM mdp_std_material_readiness s
  160. LEFT JOIN ItemMaster i ON s.component_item_code = i.ItemNum
  161. LEFT JOIN SuppMaster sp ON s.supplier_code = sp.Supp
  162. WHERE IFNULL(s.work_order, '') <> ''
  163. ON DUPLICATE KEY UPDATE
  164. component_item_name=VALUES(component_item_name),
  165. required_qty=VALUES(required_qty),
  166. cumulative_required_qty=VALUES(cumulative_required_qty),
  167. stock_available_qty=VALUES(stock_available_qty),
  168. qc_pending_qty=VALUES(qc_pending_qty),
  169. in_transit_qty=VALUES(in_transit_qty),
  170. delivery_reply_qty=VALUES(delivery_reply_qty),
  171. available_qty=VALUES(available_qty),
  172. shortage_qty=VALUES(shortage_qty),
  173. ready_status=VALUES(ready_status),
  174. supplier_code=VALUES(supplier_code),
  175. supplier_name=VALUES(supplier_name),
  176. need_date=VALUES(need_date),
  177. calc_batch_id=VALUES(calc_batch_id),
  178. calc_time=VALUES(calc_time),
  179. update_time=CURRENT_TIMESTAMP
  180. """,
  181. new SugarParameter("@StatDate", now.Date),
  182. new SugarParameter("@BatchId", batchId),
  183. new SugarParameter("@Now", now));
  184. }
  185. private async Task<int> DeleteCurrentShortageAsync(DateTime statDate)
  186. {
  187. return await _db.Ado.ExecuteCommandAsync(
  188. """
  189. DELETE s
  190. FROM dwd_material_shortage s
  191. JOIN (
  192. SELECT DISTINCT work_order
  193. FROM mdp_std_material_readiness
  194. WHERE IFNULL(work_order, '') <> ''
  195. ) w ON s.work_order = w.work_order
  196. WHERE s.stat_date = @StatDate
  197. """,
  198. new SugarParameter("@StatDate", statDate));
  199. }
  200. private async Task<int> InsertMaterialShortageAsync(string batchId, DateTime now)
  201. {
  202. return await _db.Ado.ExecuteCommandAsync(
  203. """
  204. INSERT INTO dwd_material_shortage
  205. (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)
  206. SELECT
  207. s.tenant_id,
  208. @StatDate,
  209. s.work_order,
  210. s.op_code,
  211. s.component_item_code,
  212. IFNULL(s.shortage_qty, 0),
  213. '标准层缺料数量大于 0',
  214. s.need_date,
  215. s.supplier_code,
  216. NULL,
  217. CASE
  218. WHEN IFNULL(s.shortage_qty, 0) > IFNULL(s.required_qty, 0) THEN 'HIGH'
  219. WHEN IFNULL(s.shortage_qty, 0) > 0 THEN 'MEDIUM'
  220. ELSE 'LOW'
  221. END,
  222. @BatchId,
  223. @Now
  224. FROM mdp_std_material_readiness s
  225. WHERE IFNULL(s.work_order, '') <> ''
  226. AND IFNULL(s.shortage_qty, 0) > 0
  227. """,
  228. new SugarParameter("@StatDate", now.Date),
  229. new SugarParameter("@BatchId", batchId),
  230. new SugarParameter("@Now", now));
  231. }
  232. private async Task<bool> ShouldUseDwdAsync(WorkOrderMaterialReadinessListInput input)
  233. {
  234. if (!string.IsNullOrWhiteSpace(input.SeOrder))
  235. return false;
  236. return await _db.Ado.GetIntAsync("SELECT COUNT(1) FROM dwd_material_readiness LIMIT 1") > 0;
  237. }
  238. private async Task<object> GetDwdListAsync(WorkOrderMaterialReadinessListInput input, int page, int pageSize, int offset)
  239. {
  240. var where = new List<string> { "1=1" };
  241. var pars = new List<SugarParameter>();
  242. if (!string.IsNullOrWhiteSpace(input.Workord))
  243. {
  244. where.Add("a.work_order LIKE @Workord");
  245. pars.Add(new SugarParameter("@Workord", $"%{input.Workord.Trim()}%"));
  246. }
  247. if (!string.IsNullOrWhiteSpace(input.BomNumber))
  248. {
  249. where.Add("a.parent_item_code = @BomNumber");
  250. pars.Add(new SugarParameter("@BomNumber", input.BomNumber.Trim()));
  251. }
  252. if (!string.IsNullOrWhiteSpace(input.ItemNum))
  253. {
  254. where.Add("a.component_item_code = @ItemNum");
  255. pars.Add(new SugarParameter("@ItemNum", input.ItemNum.Trim()));
  256. }
  257. if (!string.IsNullOrWhiteSpace(input.SupplierName))
  258. {
  259. where.Add("a.supplier_name LIKE @SupplierName");
  260. pars.Add(new SugarParameter("@SupplierName", $"%{input.SupplierName.Trim()}%"));
  261. }
  262. var fromSql = $"FROM dwd_material_readiness a WHERE {string.Join(" AND ", where)}";
  263. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(1) {fromSql}", pars);
  264. var orderBy = BuildDwdOrderBy(input.SortField, input.SortOrder);
  265. var list = await _db.Ado.SqlQueryAsync<WorkOrderMaterialReadinessRow>(
  266. $"""
  267. SELECT
  268. CAST(a.id AS CHAR) AS RowNum,
  269. NULL AS SeOrder,
  270. NULL AS SePriority,
  271. a.work_order AS Workord,
  272. NULL AS Priority,
  273. NULL AS MorderType,
  274. NULL AS LotSerial,
  275. a.op_code AS Op,
  276. a.need_date AS OpOrdDate,
  277. a.parent_item_code AS BomNumber,
  278. a.parent_item_code AS Descr,
  279. NULL AS Descr1,
  280. NULL AS ProductionNumber,
  281. a.calc_time AS Checktime,
  282. a.parent_item_code AS PMBOM,
  283. NULL AS ItemType,
  284. 'DWD' AS OwnerApplication,
  285. a.component_item_code AS ItemNum,
  286. a.component_item_name AS ComponentItemDescr,
  287. NULL AS ComponentItemDrawing,
  288. a.required_qty AS QtyRequired,
  289. NULL AS ComponentItemUM,
  290. NULL AS ComponentItemKittingTime,
  291. NULL AS QtyRec,
  292. NULL AS RecDate,
  293. a.shortage_qty AS Shortage,
  294. NULL AS QtyFrom,
  295. NULL AS FromDate,
  296. a.available_qty AS Zjsl,
  297. NULL AS Jykssj,
  298. a.in_transit_qty AS Ztsl,
  299. a.need_date AS Jhshrq,
  300. a.qc_pending_qty AS Jqsl,
  301. a.need_date AS Hfjq,
  302. a.delivery_reply_qty AS PurOrdQty,
  303. NULL AS PurOrdorddate,
  304. NULL AS PurOrds,
  305. NULL AS ContractDate,
  306. NULL AS ContractNO,
  307. a.supplier_code AS SupplierNumber,
  308. a.supplier_name AS SupplierName,
  309. NULL AS Buyer,
  310. NULL AS BuyerUser
  311. {fromSql}
  312. ORDER BY {orderBy}
  313. LIMIT {pageSize} OFFSET {offset}
  314. """,
  315. pars);
  316. return new { total, page, pageSize, list, source = "dwd_material_readiness" };
  317. }
  318. private static string BuildDwdOrderBy(string? sortField, string? sortOrder)
  319. {
  320. var dir = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  321. return sortField?.ToLowerInvariant() switch
  322. {
  323. "rownum" => $"a.id {dir}",
  324. "workord" => $"a.work_order {dir}",
  325. "op" => $"a.op_code {dir}",
  326. "bomnumber" => $"a.parent_item_code {dir}",
  327. "itemnum" => $"a.component_item_code {dir}",
  328. "componentitemdescr" => $"a.component_item_name {dir}",
  329. "qtyrequired" => $"a.required_qty {dir}",
  330. "shortage" => $"a.shortage_qty {dir}",
  331. "zjsl" => $"a.available_qty {dir}",
  332. "ztsl" => $"a.in_transit_qty {dir}",
  333. "jhshrq" => $"a.need_date {dir}",
  334. "jqsl" => $"a.qc_pending_qty {dir}",
  335. "hfjq" => $"a.need_date {dir}",
  336. "purordqty" => $"a.delivery_reply_qty {dir}",
  337. "suppliernumber" => $"a.supplier_code {dir}",
  338. "suppliername" => $"a.supplier_name {dir}",
  339. _ => "a.stat_date DESC, a.work_order ASC, a.component_item_code ASC"
  340. };
  341. }
  342. private static string BuildOrderBy(string? sortField, string? sortOrder)
  343. {
  344. var dir = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  345. return sortField?.ToLowerInvariant() switch
  346. {
  347. "rownum" => $"a.RowNum {dir}",
  348. "seorder" => $"a.SeOrder {dir}",
  349. "sepriority" => $"a.SePriority {dir}",
  350. "workord" => $"a.workord {dir}",
  351. "priority" => $"a.Priority {dir}",
  352. "mordertype" => $"a.morder_type {dir}",
  353. "lotserial" => $"a.LotSerial {dir}",
  354. "op" => $"a.Op {dir}",
  355. "oporddate" => $"a.OpOrdDate {dir}",
  356. "bomnumber" => $"a.bom_number {dir}",
  357. "descr" => $"a.Descr {dir}",
  358. "descr1" => $"a.Descr1 {dir}",
  359. "productionnumber" => $"a.production_number {dir}",
  360. "checktime" => $"a.checktime {dir}",
  361. "pmbom" => $"a.PMBOM {dir}",
  362. "itemtype" => $"a.ItemType {dir}",
  363. "ownerapplication" => $"a.OwnerApplication {dir}",
  364. "itemnum" => $"a.ItemNum {dir}",
  365. "componentitemdescr" => $"a.ComponentItemDescr {dir}",
  366. "componentitemdrawing" => $"a.ComponentItemDrawing {dir}",
  367. "qtyrequired" => $"a.QtyRequired {dir}",
  368. "componentitemum" => $"a.ComponentItemUM {dir}",
  369. "componentitemkittingtime" => $"a.ComponentItemKittingTime {dir}",
  370. "qtyrec" => $"a.QtyRec {dir}",
  371. "recdate" => $"a.RecDate {dir}",
  372. "shortage" => $"a.shortage {dir}",
  373. "qtyfrom" => $"a.QtyFrom {dir}",
  374. "fromdate" => $"a.FromDate {dir}",
  375. "zjsl" => $"a.zjsl {dir}",
  376. "jykssj" => $"a.jykssj {dir}",
  377. "ztsl" => $"a.ztsl {dir}",
  378. "jhshrq" => $"a.jhshrq {dir}",
  379. "jqsl" => $"a.jqsl {dir}",
  380. "hfjq" => $"a.hfjq {dir}",
  381. "purordqty" => $"a.PurOrdQty {dir}",
  382. "purordorddate" => $"a.PurOrdorddate {dir}",
  383. "purords" => $"a.PurOrds {dir}",
  384. "contractdate" => $"a.ContractDate {dir}",
  385. "contractno" => $"a.ContractNO {dir}",
  386. "suppliernumber" => $"a.supplier_number {dir}",
  387. "suppliername" => $"a.supplier_name {dir}",
  388. "buyer" => $"a.Buyer {dir}",
  389. "buyeruser" => $"a.BuyerUser {dir}",
  390. _ => "a.Priority ASC, a.workord ASC"
  391. };
  392. }
  393. }