OrderDeliveryService.cs 31 KB

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