OrderDeliveryService.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. namespace Admin.NET.Plugin.AiDOP.Order;
  2. /// <summary>
  3. /// 订单交付服务 📦
  4. /// 路由前缀:/api/Order/delivery/...
  5. /// </summary>
  6. [ApiDescriptionSettings(Order = 270, Description = "订单交付")]
  7. [Route("api/Order")]
  8. [AllowAnonymous]
  9. [NonUnify]
  10. public class OrderDeliveryService : IDynamicApiController, ITransient
  11. {
  12. private readonly ISqlSugarClient _db;
  13. private readonly UserManager _userManager;
  14. public OrderDeliveryService(ISqlSugarClient db, UserManager userManager)
  15. {
  16. _db = db;
  17. _userManager = userManager;
  18. }
  19. // ══════════════════════════════════════════════════════════════
  20. // 列表 GET /api/Order/delivery/list
  21. // ══════════════════════════════════════════════════════════════
  22. /// <summary>获取订单交付分页列表 📦</summary>
  23. [DisplayName("获取订单交付列表")]
  24. [HttpGet("delivery/list")]
  25. public async Task<object> GetDeliveryList([FromQuery] OrderDeliveryListInput input)
  26. {
  27. var tenantId = _userManager.TenantId;
  28. var pars = new List<SugarParameter> { new("@TenantId", tenantId) };
  29. var innerConditions = new List<string>
  30. {
  31. "d.tenant_id = @TenantId",
  32. "d.order_no IS NOT NULL",
  33. """
  34. d.calc_batch_id = (
  35. SELECT r.batch_id
  36. FROM mdp_transform_run_log r
  37. WHERE r.job_code = 'S1_MDP_SYNC_TRANSFORM'
  38. AND r.status = 'SUCCESS'
  39. ORDER BY r.start_time DESC, r.id DESC
  40. LIMIT 1
  41. )
  42. """
  43. };
  44. if (!string.IsNullOrWhiteSpace(input.BillNo))
  45. {
  46. innerConditions.Add("d.order_no LIKE @BillNo");
  47. pars.Add(new SugarParameter("@BillNo", $"%{input.BillNo.Trim()}%"));
  48. }
  49. if (!string.IsNullOrWhiteSpace(input.CustomNo))
  50. {
  51. innerConditions.Add("d.customer_no LIKE @CustomNo");
  52. pars.Add(new SugarParameter("@CustomNo", $"%{input.CustomNo.Trim()}%"));
  53. }
  54. if (!string.IsNullOrWhiteSpace(input.ItemNumber))
  55. {
  56. innerConditions.Add("d.item_code LIKE @ItemNumber");
  57. pars.Add(new SugarParameter("@ItemNumber", $"%{input.ItemNumber.Trim()}%"));
  58. }
  59. if (!string.IsNullOrWhiteSpace(input.PlanDateFrom))
  60. {
  61. innerConditions.Add("d.plan_delivery_date >= @PlanDateFrom");
  62. pars.Add(new SugarParameter("@PlanDateFrom", input.PlanDateFrom.Trim()));
  63. }
  64. var innerWhere = string.Join(" AND ", innerConditions);
  65. var baseSql = BuildListBaseSql(innerWhere);
  66. // progress 是计算列,需包一层过滤
  67. var outerWhere = "";
  68. if (!string.IsNullOrWhiteSpace(input.Progress))
  69. {
  70. outerWhere = "WHERE t.progress = @Progress";
  71. pars.Add(new SugarParameter("@Progress", input.Progress.Trim()));
  72. }
  73. var offset = (input.Page - 1) * input.PageSize;
  74. var total = await _db.Ado.GetIntAsync(
  75. $"SELECT COUNT(*) FROM ({baseSql}) AS t {outerWhere}", pars);
  76. var list = await _db.Ado.SqlQueryAsync<OrderDeliveryListRow>(
  77. $"SELECT * FROM ({baseSql}) AS t {outerWhere} ORDER BY t.RDate DESC, t.BillNo ASC, IFNULL(t.EntrySeq, 0) ASC LIMIT {input.PageSize} OFFSET {offset}",
  78. pars);
  79. return new { total, page = input.Page, pageSize = input.PageSize, list };
  80. }
  81. private static string BuildListBaseSql(string innerWhere) => $"""
  82. SELECT
  83. COALESCE(d.order_entry_id, d.id) AS Id,
  84. d.order_id AS SeOrderId,
  85. d.order_no AS BillNo,
  86. NULL AS Rstate,
  87. CAST(NULLIF(d.order_line, '') AS SIGNED) AS EntrySeq,
  88. IFNULL(NULLIF(d.item_code, 'null'), '') AS ItemNumber,
  89. IFNULL(NULLIF(d.item_name, 'null'), '') AS ItemName,
  90. IFNULL(NULLIF(d.item_spec, 'null'), '') AS Specification,
  91. NULL AS Unit,
  92. IFNULL(NULLIF(d.customer_no, 'null'), '') AS CustomNo,
  93. NULL AS CustomOrderBillNo,
  94. NULL AS CustomOrderItemNo,
  95. NULL AS CustomLevel,
  96. COALESCE(se.qty, d.order_qty) AS Qty,
  97. d.shipped_qty AS DeliverCount,
  98. NULL AS BomNumber,
  99. NULL AS EmpName,
  100. NULL AS PlannerName,
  101. d.plan_delivery_date AS PlanDate,
  102. d.promised_delivery_date AS SysCapacityDate,
  103. d.promised_delivery_date AS Date,
  104. d.order_date AS RDate,
  105. d.create_time AS CreateTime,
  106. NULL AS CreateByName,
  107. NULL AS Auditor,
  108. CASE
  109. WHEN d.delivery_status = 'COMPLETED' THEN '7'
  110. WHEN d.planned_ship_qty > 0 THEN '7'
  111. WHEN d.order_status = 'CLOSED' THEN '7'
  112. WHEN d.review_status IS NOT NULL THEN '1'
  113. ELSE '1'
  114. END AS Progress,
  115. NULL AS ProdLine,
  116. NULL AS ProdRange,
  117. d.actual_ship_date AS ShipDate,
  118. NULL AS Recid,
  119. IF(IFNULL(d.planned_ship_qty, 0) > 0, '是', '否') AS Spstatus,
  120. IFNULL(NULLIF(me.mono_list, 'null'), '') AS MoentryMono
  121. FROM dwd_ship_trans d
  122. LEFT JOIN crm_seorderentry se
  123. ON se.id = d.order_entry_id
  124. AND se.tenant_id = d.tenant_id
  125. AND se.IsDeleted = 0
  126. LEFT JOIN (
  127. SELECT soentry_id, tenant_id,
  128. GROUP_CONCAT(moentry_mono SEPARATOR ', ') AS mono_list
  129. FROM mes_moentry
  130. WHERE IsDeleted = 0
  131. GROUP BY soentry_id, tenant_id
  132. ) me ON me.soentry_id = d.order_entry_id
  133. AND me.tenant_id = d.tenant_id
  134. WHERE {innerWhere}
  135. """;
  136. // ══════════════════════════════════════════════════════════════
  137. // 价值流 GET /api/Order/delivery/flow/{entryId}
  138. // ══════════════════════════════════════════════════════════════
  139. /// <summary>获取订单价值流全量数据(8个Tab)📦</summary>
  140. [DisplayName("获取订单价值流")]
  141. [HttpGet("delivery/flow/{entryId}")]
  142. public async Task<object> GetDeliveryFlow(long entryId)
  143. {
  144. // 同一 ISqlSugarClient 底层共用一条连接,不能对 _db.Ado 并发执行多查询(MySqlConnector 会报 Cannot Open when State is Connecting)
  145. var basic = await QueryBasic(entryId);
  146. var orderInfo = await QueryOrderInfo(entryId);
  147. var review = await QueryReview(entryId);
  148. var changes = await QueryChanges(entryId);
  149. var schedule = await QuerySchedule(entryId);
  150. var material = await QueryMaterials(entryId);
  151. var po = await QueryPo(entryId);
  152. var pr = await QueryPr(entryId);
  153. var deliverySchedule = await QueryDeliverySchedule(entryId);
  154. var nbr = await QueryNbr(entryId);
  155. var prodReport = await QueryProdReport(entryId);
  156. var iqc = await QueryIqc(entryId);
  157. var fqc = await QueryFqc(entryId);
  158. var receipt = await QueryReceipt(entryId);
  159. var shipment = await QueryShipment(entryId);
  160. return new
  161. {
  162. basic,
  163. tab1 = orderInfo,
  164. tab2 = new { review, changes },
  165. tab3 = schedule,
  166. tab4 = material,
  167. tab5 = new { po, pr, deliverySchedule },
  168. tab6 = nbr,
  169. tab7 = new { prodReport, iqc },
  170. tab8 = new { fqc, receipt, shipment },
  171. };
  172. }
  173. // ── 基础信息 ──
  174. private async Task<FlowBasicRow?> QueryBasic(long entryId)
  175. {
  176. var tenantId = _userManager.TenantId;
  177. const string sql = """
  178. SELECT
  179. IFNULL(NULLIF(d.item_code, 'null'), '') AS ItemNumber,
  180. IFNULL(NULLIF(d.item_name, 'null'), '') AS ItemName,
  181. IFNULL(NULLIF(d.item_spec, 'null'), '') AS Specification,
  182. d.order_qty AS Qty,
  183. COALESCE(so.customer_order_no, '') AS BillFrom,
  184. d.plan_delivery_date AS PlanDate,
  185. CASE
  186. WHEN d.delivery_status = 'COMPLETED' THEN '7'
  187. WHEN d.planned_ship_qty > 0 THEN '7'
  188. WHEN d.order_status = 'CLOSED' THEN '7'
  189. WHEN d.review_status IS NOT NULL THEN '1'
  190. ELSE '1'
  191. END AS Progress,
  192. d.update_time AS Mrptime
  193. FROM dwd_ship_trans d
  194. LEFT JOIN mdp_std_so so
  195. ON so.tenant_id = d.tenant_id
  196. AND so.order_entry_id = d.order_entry_id
  197. WHERE (d.order_entry_id = @Id OR d.id = @Id)
  198. AND d.tenant_id = @TenantId
  199. AND d.order_no IS NOT NULL
  200. ORDER BY d.calc_time DESC, d.id DESC
  201. LIMIT 1
  202. """;
  203. return (await _db.Ado.SqlQueryAsync<FlowBasicRow>(sql, new { Id = entryId, TenantId = tenantId })).FirstOrDefault();
  204. }
  205. // ── Tab1 订单信息 ──
  206. private async Task<FlowOrderInfoRow?> QueryOrderInfo(long entryId)
  207. {
  208. var tenantId = _userManager.TenantId;
  209. const string sql = """
  210. SELECT
  211. so.order_no AS BillNo,
  212. so.customer_name AS CustomName,
  213. NULL AS CustomLevel,
  214. COALESCE(so.flow_state, '') AS State,
  215. so.order_date AS Date,
  216. '' AS CreateByName,
  217. so.order_type AS OrderType,
  218. so.update_time AS AuditDate,
  219. '' AS Auditor,
  220. so.customer_order_no AS BillFrom,
  221. COALESCE(so.planner_name, '') AS EmpName,
  222. COALESCE(so.planner_no, '') AS EmpNo
  223. FROM mdp_std_so so
  224. WHERE so.order_entry_id = @Id
  225. AND so.tenant_id = @TenantId
  226. ORDER BY so.sync_time DESC, so.id DESC
  227. LIMIT 1
  228. """;
  229. return (await _db.Ado.SqlQueryAsync<FlowOrderInfoRow>(sql, new { Id = entryId, TenantId = tenantId })).FirstOrDefault();
  230. }
  231. // ── Tab2 订单评审 ──
  232. /// <remarks>
  233. /// 查询最新评审记录(num=1 的首行物料)的预计交期评估信息。
  234. /// </remarks>
  235. private async Task<FlowReviewRow?> QueryReview(long entryId)
  236. {
  237. var tenantId = _userManager.TenantId;
  238. const string sql = """
  239. SELECT
  240. d.satisfy_time AS SysCapacityDate,
  241. d.morder_no AS MorderNo,
  242. d.create_time AS CreateTime,
  243. d.make_qty AS MakeQty,
  244. d.use_qty AS UseQty,
  245. d.mo_qty AS MoQty
  246. FROM dwd_requirement_examine_detail d
  247. WHERE d.order_entry_id = @EntryId
  248. AND d.tenant_id = @TenantId
  249. AND d.num = '1'
  250. AND d.calc_batch_id = (
  251. SELECT r.batch_id
  252. FROM mdp_transform_run_log r
  253. WHERE r.job_code = 'S1_MDP_SYNC_TRANSFORM'
  254. AND r.status = 'SUCCESS'
  255. ORDER BY r.start_time DESC, r.id DESC
  256. LIMIT 1
  257. )
  258. ORDER BY d.create_time DESC, d.row_id
  259. LIMIT 1
  260. """;
  261. return (await _db.Ado.SqlQueryAsync<FlowReviewRow>(sql, new { EntryId = entryId, TenantId = tenantId })).FirstOrDefault();
  262. }
  263. // ── Tab2 变更记录(MySQL:按订单行关联变更单)──
  264. private async Task<List<FlowChangeRow>> QueryChanges(long entryId)
  265. {
  266. var tenantId = _userManager.TenantId;
  267. const string sql = """
  268. SELECT
  269. ROW_NUMBER() OVER (ORDER BY a.id) AS Sno,
  270. JSON_UNQUOTE(JSON_EXTRACT(a.raw_data, '$.bill_no')) AS BillNo,
  271. JSON_UNQUOTE(JSON_EXTRACT(a.raw_data, '$.change_Type')) AS ChangeType,
  272. JSON_UNQUOTE(JSON_EXTRACT(a.raw_data, '$.change_Reason')) AS ChangeReason,
  273. JSON_UNQUOTE(JSON_EXTRACT(a.raw_data, '$.change_content')) AS ChangeContent,
  274. a.update_time AS UpdateTime
  275. FROM mdp_stg_so a
  276. INNER JOIN mdp_std_so so
  277. ON so.order_entry_id = @Id
  278. AND so.order_no = JSON_UNQUOTE(JSON_EXTRACT(a.raw_data, '$.bill_no'))
  279. AND so.tenant_id = a.tenant_id
  280. WHERE a.source_table = 'crm_seorder_change'
  281. AND a.tenant_id = @TenantId
  282. ORDER BY a.id
  283. """;
  284. return await _db.Ado.SqlQueryAsync<FlowChangeRow>(sql, new { Id = entryId, TenantId = tenantId });
  285. }
  286. // ── Tab3 计划排程1(MySQL:按分录 Id 关联工单排程 + 物料主档)──
  287. private async Task<List<FlowScheduleRow>> QuerySchedule(long entryId)
  288. {
  289. const string sql = """
  290. SELECT
  291. ROW_NUMBER() OVER (ORDER BY sch.PlanDate) AS Sno,
  292. sch.WorkOrds,
  293. DATE(sch.PlanDate) AS WorkDate,
  294. sch.Line,
  295. sch.Op,
  296. CAST(sch.OrdQty AS DECIMAL(18, 2)) AS WorkQty,
  297. sch.ItemNum,
  298. im.Descr,
  299. im.Descr1,
  300. sch.CreateTime
  301. FROM PeriodSequenceDet sch
  302. LEFT JOIN ItemMaster im ON sch.ItemNum = im.ItemNum LEFT JOIN mes_morder mo ON mo.morder_no = sch.WorkOrds INNER JOIN mes_moentry me ON mo.Id = me.moentry_moid
  303. LEFT JOIN crm_seorderentry se ON se.Id = me.soentry_id
  304. WHERE se.id = @EntryId AND mo.IsDeleted = 0
  305. ORDER BY sch.PlanDate
  306. """;
  307. return await _db.Ado.SqlQueryAsync<FlowScheduleRow>(sql, new { EntryId = entryId });
  308. }
  309. // ── Tab4 物料需求(MySQL:最新评审结果 + BOM 子件明细,同需求明细核验口径)──
  310. private async Task<List<FlowMaterialRow>> QueryMaterials(long entryId)
  311. {
  312. var tenantId = _userManager.TenantId;
  313. const string sql = """
  314. SELECT
  315. ROW_NUMBER() OVER (ORDER BY d.row_id) AS Sno,
  316. d.num AS Num,
  317. IFNULL(NULLIF(d.item_number, 'null'), '') AS ItemNumber,
  318. IFNULL(NULLIF(d.item_name, 'null'), '') AS ItemName,
  319. d.bom_number AS BomNumber,
  320. d.model AS Model,
  321. DATE_FORMAT(d.kitting_time, '%Y-%m-%d') AS KittingTime,
  322. d.item_type AS Type,
  323. NULL AS Unit,
  324. d.erp_cls_name AS ErpClsName,
  325. d.qty AS Qty,
  326. d.need_count AS NeedCount,
  327. d.sqty AS Sqty,
  328. d.use_qty AS UseQty,
  329. d.self_lack_qty AS SelfLackQty,
  330. d.lack_qty AS LackQty,
  331. d.mo_qty AS MoQty,
  332. d.make_qty AS MakeQty,
  333. d.purchase_qty AS PurchaseQty,
  334. d.purchase_occupy_qty AS PurchaseOccupyQty,
  335. NULL AS SubcontractingQty,
  336. NULL AS Backflush,
  337. DATE_FORMAT(d.satisfy_time, '%Y-%m-%d') AS SatisfyTime,
  338. d.have_ic_subs AS HaveIcSubs,
  339. d.substitute_code AS SubstituteCode
  340. FROM dwd_requirement_examine_detail d
  341. WHERE d.order_entry_id = @EntryId
  342. AND d.tenant_id = @TenantId
  343. AND d.calc_batch_id = (
  344. SELECT r.batch_id
  345. FROM mdp_transform_run_log r
  346. WHERE r.job_code = 'S1_MDP_SYNC_TRANSFORM'
  347. AND r.status = 'SUCCESS'
  348. ORDER BY r.start_time DESC, r.id DESC
  349. LIMIT 1
  350. )
  351. ORDER BY d.row_id ASC
  352. """;
  353. return await _db.Ado.SqlQueryAsync<FlowMaterialRow>(sql, new { EntryId = entryId, TenantId = tenantId });
  354. }
  355. // ── Tab5 采购订单(MySQL:经占用/工单关联到订单分录)──
  356. private async Task<List<FlowPoRow>> QueryPo(long entryId)
  357. {
  358. const string sql = """
  359. SELECT
  360. ROW_NUMBER() OVER (ORDER BY t.PoBillNo) AS Sno,
  361. t.PoBillNo,
  362. t.SupplierNo,
  363. t.SupplierName,
  364. t.PoTotal,
  365. t.PoPurchaser,
  366. t.State,
  367. t.PoSsendDate,
  368. t.CreateTime
  369. FROM (
  370. SELECT DISTINCT
  371. pm.po_billno AS PoBillNo,
  372. pm.supplier_no AS SupplierNo,
  373. pm.supplier_name AS SupplierName,
  374. CAST(pm.po_total AS DECIMAL(18, 2)) AS PoTotal,
  375. pm.po_purchaser AS PoPurchaser,
  376. pm.create_time AS CreateTime,
  377. pm.po_ssend_date AS PoSsendDate,
  378. CASE pm.state
  379. WHEN 0 THEN '新增'
  380. WHEN 1 THEN '审核中'
  381. WHEN 2 THEN '同意'
  382. WHEN 3 THEN '关闭'
  383. END AS State
  384. FROM srm_po_main pm
  385. LEFT JOIN srm_po_list pl ON pm.id = pl.po_id
  386. LEFT JOIN srm_po_occupy occ ON pl.Id = occ.polist_id AND CAST(pm.factory_id AS CHAR(64)) = CAST(occ.factory_id AS CHAR(64)) LEFT JOIN mes_morder mo ON mo.id = occ.morder_id
  387. LEFT JOIN mes_moentry me ON mo.id = me.moentry_moid
  388. LEFT JOIN crm_seorderentry se ON me.soentry_id = se.id
  389. WHERE se.id = @EntryId AND pm.IsDeleted = 0
  390. ) AS t
  391. ORDER BY t.PoBillNo
  392. """;
  393. return await _db.Ado.SqlQueryAsync<FlowPoRow>(sql, new { EntryId = entryId });
  394. }
  395. // ── Tab5 采购申请(MySQL)──
  396. private async Task<List<FlowPrRow>> QueryPr(long entryId)
  397. {
  398. const string sql = """
  399. SELECT
  400. ROW_NUMBER() OVER (ORDER BY pm.pr_billno) AS Sno,
  401. pm.pr_billno AS PrBillNo,
  402. ic.number AS Number,
  403. pm.icitem_name AS IcItemName,
  404. ic.model AS Model,
  405. pm.pr_purchasenumber AS PrPurchaseNumber,
  406. pm.pr_purchasename AS PrPurchaseName,
  407. CAST(pm.pr_aqty AS DECIMAL(18, 2)) AS PrAQty,
  408. pm.pr_ssend_date AS PrSsendDate,
  409. pm.pr_sarrive_date AS PrSarriveDate,
  410. pm.pr_unit AS PrUnit,
  411. pm.pr_purchaser AS PrPurchaser
  412. FROM srm_pr_main pm
  413. LEFT JOIN srm_po_occupy occ ON pm.Id = occ.polist_id AND CAST(pm.factory_id AS CHAR(64)) = CAST(occ.factory_id AS CHAR(64)) LEFT JOIN ic_item ic ON pm.icitem_id = ic.Id
  414. LEFT JOIN mes_morder mo ON mo.id = occ.morder_id
  415. LEFT JOIN mes_moentry me ON mo.id = me.moentry_moid
  416. LEFT JOIN crm_seorderentry se ON me.soentry_id = se.id
  417. WHERE se.id = @EntryId AND pm.state <> 0 AND pm.IsDeleted = 0
  418. ORDER BY pm.pr_billno
  419. """;
  420. return await _db.Ado.SqlQueryAsync<FlowPrRow>(sql, new { EntryId = entryId });
  421. }
  422. // ── Tab5 物料交货单(MySQL:srm_polist_ds)──
  423. private async Task<List<FlowDeliveryScheduleRow>> QueryDeliverySchedule(long entryId)
  424. {
  425. const string sql = """
  426. SELECT
  427. ROW_NUMBER() OVER (ORDER BY t.DsNum) AS Sno,
  428. t.PoNumber,
  429. t.DsNum,
  430. t.ItemNum,
  431. t.Descr,
  432. t.SupplierCode,
  433. t.Supplier,
  434. t.RequestDate,
  435. t.NeedDate,
  436. t.SchedQty,
  437. t.SentQty,
  438. t.RestQty
  439. FROM (
  440. SELECT
  441. ds.dsnum AS DsNum,
  442. ds.ponumber AS PoNumber,
  443. ds.itemnum AS ItemNum,
  444. im.Descr AS Descr,
  445. ds.suppliercode AS SupplierCode,
  446. ds.supplier AS Supplier,
  447. ds.requestdate AS RequestDate,
  448. ds.needdate AS NeedDate,
  449. ds.schedqty AS SchedQty,
  450. ds.sentqty AS SentQty,
  451. ds.restqty AS RestQty
  452. FROM srm_po_main pm
  453. LEFT JOIN srm_po_list pl ON pm.id = pl.po_id
  454. LEFT JOIN srm_po_occupy occ ON pl.Id = occ.polist_id AND CAST(pm.factory_id AS CHAR(64)) = CAST(occ.factory_id AS CHAR(64)) LEFT JOIN mes_morder mo ON mo.id = occ.morder_id
  455. LEFT JOIN mes_moentry me ON mo.id = me.moentry_moid
  456. LEFT JOIN crm_seorderentry se ON me.soentry_id = se.id
  457. LEFT JOIN srm_polist_ds ds ON pl.po_billno = ds.ponumber AND pl.ItemNum = ds.itemnum LEFT JOIN ItemMaster im ON ds.itemnum = im.ItemNum WHERE se.id = @EntryId AND pm.IsDeleted = 0
  458. ) AS t
  459. ORDER BY t.DsNum
  460. """;
  461. return await _db.Ado.SqlQueryAsync<FlowDeliveryScheduleRow>(sql, new { EntryId = entryId });
  462. }
  463. // ── Tab6 生产备料(MySQL:备料明细经工单关联分录)──
  464. private async Task<List<FlowNbrRow>> QueryNbr(long entryId)
  465. {
  466. const string sql = """
  467. SELECT
  468. CAST(nd.Line AS CHAR) AS Line,
  469. nm.Nbr AS Nbr,
  470. nd.ItemNum,
  471. im.Descr AS Descr,
  472. im.Descr1 AS Descr1,
  473. CAST(nd.QtyFrom AS DECIMAL(18, 2)) AS QtyFrom,
  474. CAST(nd.QtyOrd AS DECIMAL(18, 2)) AS QtyOrd,
  475. CAST(nd.CurrQtyOpened AS DECIMAL(18, 2)) AS CurrQtyOpened,
  476. CAST(nd.QtyRec AS DECIMAL(18, 2)) AS QtyRec,
  477. nd.UM AS Unit,
  478. nd.LocationFrom,
  479. nd.LocationTo,
  480. nd.UpdateTime AS UpdateTime,
  481. nd.WorkOrd AS WorkOrd
  482. FROM NbrDetail nd
  483. LEFT JOIN NbrMaster nm ON nd.nbr = nm.nbr LEFT JOIN mes_morder mo ON mo.morder_no = nm.WorkOrd INNER JOIN mes_moentry me ON mo.Id = me.moentry_moid
  484. LEFT JOIN crm_seorderentry se ON se.Id = me.soentry_id AND mo.product_code = se.item_number LEFT JOIN ItemMaster im ON nd.ItemNum = im.ItemNum WHERE se.id = @EntryId AND mo.IsDeleted = 0 AND nm.Type = 'SM'
  485. ORDER BY nm.Nbr
  486. """;
  487. return await _db.Ado.SqlQueryAsync<FlowNbrRow>(sql, new { EntryId = entryId });
  488. }
  489. // ── Tab7 生产报工(OpTransEmployee + 销售订单行)──
  490. private async Task<List<FlowProdReportRow>> QueryProdReport(long entryId)
  491. {
  492. const string sql = """
  493. SELECT
  494. ROW_NUMBER() OVER (ORDER BY ote.ProdDate DESC) AS Sno,
  495. ote.WorkOrd,
  496. ote.Name AS Name,
  497. ote.Op,
  498. ote.ProdDate,
  499. im.ItemNum,
  500. im.Descr,
  501. im.Descr1,
  502. im.Um,
  503. CAST(ote.QtyCompleted AS DECIMAL(18,2)) AS QtyCompleted,
  504. CAST(ote.QtyReject AS DECIMAL(18,2)) AS QtyReject,
  505. CAST(ote.QtyScrapped AS DECIMAL(18,2)) AS QtyScrapped
  506. FROM OpTransEmployee ote
  507. LEFT JOIN mes_morder mo ON mo.morder_no = ote.WorkOrd INNER JOIN mes_moentry me ON mo.Id = me.moentry_moid
  508. LEFT JOIN crm_seorderentry se ON se.Id = me.soentry_id
  509. LEFT JOIN ItemMaster im ON ote.ItemNum = im.ItemNum WHERE se.id = @EntryId AND mo.IsDeleted = 0
  510. ORDER BY ote.ProdDate DESC
  511. """;
  512. return await _db.Ado.SqlQueryAsync<FlowProdReportRow>(sql, new { EntryId = entryId });
  513. }
  514. // ── Tab7 IQC信息(请购/采购/检验链路)──
  515. private async Task<List<FlowIqcRow>> QueryIqc(long entryId)
  516. {
  517. const string sql = """
  518. SELECT
  519. ROW_NUMBER() OVER (ORDER BY iqcjy.FAPPLYTIME DESC) AS Line,
  520. iqcjy.po_bill AS PoBill,
  521. iqcjy.po_billline AS PoBillLine,
  522. iqcjy.FBILLNO AS FBillNo,
  523. iqcjy.FMATERIALCFG AS FMaterialCfg,
  524. iqcjy.wlmc AS Wlmc,
  525. iqcjy.ggxh AS Ggxh,
  526. CAST(iqcjy.FRINSQTY AS DECIMAL(18,2)) AS FRinsQty,
  527. iqcjy.FAPPLYTIME AS FApplyTime,
  528. iqcjy.FINSPESTARTDATE AS FInspeStartDate,
  529. iqcjy.FINSPECTSTATUS AS FInspecStatus
  530. FROM srm_pr_main pr
  531. LEFT JOIN PurOrdDetail pod ON pr.pr_billno = pod.Req LEFT JOIN vscm_cgshrk sh ON pod.PurOrd = sh.OrdNbr AND CAST(pod.Line AS CHAR(32)) = CAST(sh.OrdLine AS CHAR(32)) LEFT JOIN scm_jhjh_jq jq ON pod.PurOrd = jq.cgdd AND CAST(pod.Line AS CHAR(32)) = CAST(jq.ddhh AS CHAR(32)) LEFT JOIN (
  532. SELECT
  533. f.po_bill,
  534. f.po_billline,
  535. a.FINSPESTARTDATE,
  536. b.FAPPLYTIME,
  537. b.FBILLNO,
  538. a.FMATERIALCFG,
  539. a.wlmc,
  540. a.ggxh,
  541. a.FRINSQTY,
  542. c.FINSPECTSTATUS
  543. FROM qms_qcp_inspbill a
  544. LEFT JOIN qms_qcp_inspecapplyn b ON a.lydjbh = b.FBILLNO LEFT JOIN qms_qcp_insappnentry c ON b.id = c.glid
  545. LEFT JOIN scm_shd cd ON c.shdh = cd.shddh LEFT JOIN scm_shdzb f ON f.shpc = c.FLOTNUMBER AND f.sh_material_code = c.FMATERIALCFG AND f.glid = cd.id
  546. ) iqcjy ON pod.PurOrd = iqcjy.po_bill AND CAST(pod.Line AS CHAR(32)) = CAST(iqcjy.po_billline AS CHAR(32)) WHERE jq.flag = '1'
  547. AND pr.sentry_id IS NOT NULL
  548. AND pr.sentry_id = @EntryId
  549. ORDER BY iqcjy.FAPPLYTIME DESC
  550. """;
  551. return await _db.Ado.SqlQueryAsync<FlowIqcRow>(sql, new { EntryId = entryId });
  552. }
  553. // ── Tab8 FQC信息 ──
  554. private async Task<List<FlowFqcRow>> QueryFqc(long entryId)
  555. {
  556. const string sql = """
  557. SELECT
  558. ROW_NUMBER() OVER (ORDER BY a.FBILLNO ASC) AS Sno,
  559. b.WorkOrd,
  560. a.sczld,
  561. a.FBILLNO,
  562. a.FMATERIALCFG AS FMaterialCfg,
  563. a.wlmc,
  564. a.ggxh,
  565. a.scph,
  566. CAST(a.FRINSQTY AS DECIMAL(18,2)) AS FRinsQty,
  567. a.FINSPEENDDATE AS FInspeEndDate
  568. FROM qms_qcpp_inspbill a
  569. LEFT JOIN WorkOrdMaster b ON a.sczld = b.Batch LEFT JOIN mes_moentry c ON b.WorkOrd = c.moentry_mono WHERE c.soentry_id = @EntryId
  570. ORDER BY a.FBILLNO
  571. """;
  572. return await _db.Ado.SqlQueryAsync<FlowFqcRow>(sql, new { EntryId = entryId });
  573. }
  574. // ── Tab8 生产入库 ──
  575. private async Task<List<FlowReceiptRow>> QueryReceipt(long entryId)
  576. {
  577. const string sql = """
  578. SELECT
  579. ROW_NUMBER() OVER (ORDER BY mo.morder_no) AS Sno,
  580. CASE
  581. WHEN c.Status = 'r' THEN '下达'
  582. WHEN c.Status = 'c' THEN '关闭'
  583. WHEN c.Status = 'w' THEN '投产'
  584. ELSE '初始'
  585. END AS Status,
  586. c.WorkOrd,
  587. im.Um,
  588. c.QtyOrded,
  589. IFNULL(compp.CompQty, 0) AS CompQty,
  590. NULL AS InspectionNumber,
  591. NULL AS QualifiedNumber,
  592. CASE WHEN a.QtyChange > 0 THEN a.QtyChange ELSE 0 END AS QtyChangeAdvance,
  593. mo.moentry_prdname,
  594. mo.moentry_wrkcname,
  595. c.OrdDate,
  596. c.DueDate,
  597. a.CreateTime,
  598. c.itemnum AS ItemNum,
  599. im.descr AS Descr
  600. FROM WorkOrdMaster c
  601. LEFT JOIN InvTransHist a ON a.WorkOrd = c.WorkOrd AND a.TransType = 'rct-wo' AND a.QtyChange > 0
  602. LEFT JOIN GeneralizedCodeMaster b ON a.TransType = b.Val AND b.FldName = 'TransType' AND b.`Domain` = c.`Domain` LEFT JOIN ItemMaster im ON c.ItemNum = im.ItemNum AND im.`Domain` = c.`Domain` LEFT JOIN PeriodSequenceDet det ON det.ItemNum = im.ItemNum AND det.WorkOrds = c.WorkOrd AND det.`Domain` = c.`Domain` LEFT JOIN (
  603. SELECT WorkOrds, SUM(IFNULL(CompQty, 0)) AS CompQty
  604. FROM PeriodSequenceDet
  605. GROUP BY WorkOrds
  606. ) compp ON c.WorkOrd = compp.WorkOrds LEFT JOIN mes_morder mo ON mo.morder_no = c.WorkOrd AND mo.IsDeleted = 0
  607. LEFT JOIN mes_moentry motry ON motry.moentry_moid = mo.Id AND motry.IsDeleted = 0
  608. WHERE motry.soentry_id = @EntryId
  609. ORDER BY mo.morder_no
  610. """;
  611. return await _db.Ado.SqlQueryAsync<FlowReceiptRow>(sql, new { EntryId = entryId });
  612. }
  613. // ── Tab8 成品发运(与订单行 OrdNbr + 行物料一致)──
  614. private async Task<List<FlowShipmentRow>> QueryShipment(long entryId)
  615. {
  616. var tenantId = _userManager.TenantId;
  617. const string sql = """
  618. SELECT
  619. ROW_NUMBER() OVER (ORDER BY st.actual_ship_date DESC, st.source_row_id DESC) AS Sno,
  620. COALESCE(st.shipper_no, CAST(st.source_row_id AS CHAR)) AS Id,
  621. st.site AS Location,
  622. st.actual_ship_date AS ShipDate,
  623. CAST(st.qty_to_ship AS DECIMAL(18,2)) AS QtyToShip,
  624. st.site AS LocationCode,
  625. st.real_qty AS RealQty
  626. FROM mdp_std_ship_trans st
  627. INNER JOIN mdp_std_so so
  628. ON so.order_entry_id = @EntryId
  629. AND so.tenant_id = st.tenant_id
  630. AND so.order_no = st.order_no
  631. AND IFNULL(so.order_line, '') = IFNULL(st.order_line, '')
  632. AND IFNULL(so.item_code, '') = IFNULL(st.item_code, '')
  633. WHERE st.trans_type = 'ASN_SHIPPER'
  634. AND st.tenant_id = @TenantId
  635. ORDER BY st.actual_ship_date DESC, st.source_row_id DESC
  636. """;
  637. return await _db.Ado.SqlQueryAsync<FlowShipmentRow>(sql, new { EntryId = entryId, TenantId = tenantId });
  638. }
  639. }