SupplierShipmentService.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. using Admin.NET.Plugin.AiDOP.ProcurementExecution.Dto;
  2. using Admin.NET.Plugin.AiDOP.ProcurementExecution.Entity;
  3. using Yitter.IdGenerator;
  4. namespace Admin.NET.Plugin.AiDOP.ProcurementExecution;
  5. /// <summary>
  6. /// S4 供应商发货单(列表 + 表单)
  7. /// </summary>
  8. [ApiDescriptionSettings(Order = 321, Description = "S4供应商发货单")]
  9. [Route("api/ProcurementExecution")]
  10. [AllowAnonymous]
  11. [NonUnify]
  12. public class SupplierShipmentService : IDynamicApiController, ITransient
  13. {
  14. private readonly ISqlSugarClient _db;
  15. private readonly SqlSugarRepository<ScmShd> _masterRep;
  16. private readonly SqlSugarRepository<ScmShdzb> _detailRep;
  17. private readonly UserManager _userManager;
  18. public SupplierShipmentService(
  19. ISqlSugarClient db,
  20. SqlSugarRepository<ScmShd> masterRep,
  21. SqlSugarRepository<ScmShdzb> detailRep,
  22. UserManager userManager)
  23. {
  24. _db = db;
  25. _masterRep = masterRep;
  26. _detailRep = detailRep;
  27. _userManager = userManager;
  28. }
  29. [DisplayName("供应商发货单列表")]
  30. [HttpGet("supplier-shipment/list")]
  31. public async Task<object> GetList([FromQuery] SupplierShipmentListInput input)
  32. {
  33. var pars = new List<SugarParameter>();
  34. var conditions = new List<string> { "IFNULL(m.state, 1) <> 0" };
  35. if (!string.IsNullOrWhiteSpace(input.JhshrqFrom))
  36. {
  37. conditions.Add("m.jhshrq >= @jhshrqFrom");
  38. pars.Add(new SugarParameter("@jhshrqFrom", input.JhshrqFrom.Trim()));
  39. }
  40. if (!string.IsNullOrWhiteSpace(input.Gysmc))
  41. {
  42. conditions.Add("m.sh_purchase_name LIKE @gysmc");
  43. pars.Add(new SugarParameter("@gysmc", $"%{input.Gysmc.Trim()}%"));
  44. }
  45. if (!string.IsNullOrWhiteSpace(input.Shddh))
  46. {
  47. conditions.Add("m.shddh LIKE @shddh");
  48. pars.Add(new SugarParameter("@shddh", $"%{input.Shddh.Trim()}%"));
  49. }
  50. if (!string.IsNullOrWhiteSpace(input.Wldh))
  51. {
  52. conditions.Add("m.wldh LIKE @wldh");
  53. pars.Add(new SugarParameter("@wldh", $"%{input.Wldh.Trim()}%"));
  54. }
  55. if (!string.IsNullOrWhiteSpace(input.PoBill))
  56. {
  57. conditions.Add("m.po_bill LIKE @poBill");
  58. pars.Add(new SugarParameter("@poBill", $"%{input.PoBill.Trim()}%"));
  59. }
  60. if (!string.IsNullOrWhiteSpace(input.ShMaterialCode))
  61. {
  62. conditions.Add("m.sh_material_code LIKE @shMaterialCode");
  63. pars.Add(new SugarParameter("@shMaterialCode", $"%{input.ShMaterialCode.Trim()}%"));
  64. }
  65. if (!string.IsNullOrWhiteSpace(input.Shzt))
  66. {
  67. conditions.Add("m.shzt = @shzt");
  68. pars.Add(new SugarParameter("@shzt", input.Shzt.Trim()));
  69. }
  70. if (!string.IsNullOrWhiteSpace(input.Shpc))
  71. {
  72. conditions.Add("m.shpc LIKE @shpc");
  73. pars.Add(new SugarParameter("@shpc", $"%{input.Shpc.Trim()}%"));
  74. }
  75. var where = $" WHERE {string.Join(" AND ", conditions)} ";
  76. var orderBy = BuildListOrderBy(input.SortField, input.SortOrder);
  77. var offset = (input.Page - 1) * input.PageSize;
  78. var wrapped = $"{BuildListSql()} {where}";
  79. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(*) FROM ({wrapped}) t", pars);
  80. var list = await _db.Ado.SqlQueryAsync<SupplierShipmentListRow>(
  81. $"SELECT * FROM ({wrapped}) t {orderBy} LIMIT {input.PageSize} OFFSET {offset}", pars);
  82. return new { total, page = input.Page, pageSize = input.PageSize, list };
  83. }
  84. [DisplayName("获取发货单详情")]
  85. [HttpGet("supplier-shipment/{id:long}")]
  86. public async Task<object> GetDetail(long id)
  87. {
  88. var master = await _masterRep.GetFirstAsync(x => x.Id == id) ?? throw Oops.Oh("发货单不存在");
  89. var details = await _detailRep.AsQueryable().Where(x => x.Glid == id.ToString()).OrderBy(x => x.Hh).ToListAsync();
  90. return new
  91. {
  92. id = master.Id,
  93. shddh = master.Shddh,
  94. jhshrq = master.Jhshrq,
  95. wlsc = master.Wlsc,
  96. yjdhrq = master.Yjdhrq,
  97. shPurchaseName = master.ShPurchaseName,
  98. shPurchaseNum = master.ShPurchaseNum,
  99. wldh = master.Wldh,
  100. sfpc = master.Sfpc ?? 0,
  101. chbg = master.Chbg,
  102. pcsm = master.Pcsm,
  103. state = master.State ?? 1,
  104. details = details.Select(d => new
  105. {
  106. id = d.Id,
  107. hh = d.Hh,
  108. poBill = d.PoBill,
  109. poBillLine = d.PoBillLine,
  110. orderType = d.OrderType,
  111. shMaterialCode = d.ShMaterialCode,
  112. shMaterialName = d.ShMaterialName,
  113. th = d.Th,
  114. shDeliveryQuantity = d.ShDeliveryQuantity,
  115. bzsl = d.Bzsl,
  116. bqsl = d.Bqsl,
  117. shMaterialDw = d.ShMaterialDw,
  118. scrq = d.Scrq,
  119. scph = d.Scph,
  120. remarks = d.Remarks,
  121. djsl = d.Djsl,
  122. jybb = d.Jybb,
  123. jhdbh = d.Jhdbh
  124. })
  125. };
  126. }
  127. [DisplayName("发货单新增草稿")]
  128. [HttpGet("supplier-shipment/create-draft")]
  129. public async Task<object> GetCreateDraft([FromQuery] string ids)
  130. {
  131. var pars = new List<SugarParameter> { new("@ids", ids) };
  132. var rows = await _db.Ado.SqlQueryAsync<SupplierShipmentDraftRow>(
  133. """
  134. SELECT
  135. CAST(IFNULL(ds.id, p.RecID) AS CHAR(50)) AS sourceId,
  136. ds.suppliercode AS gysdm,
  137. ds.supplier AS gysmc,
  138. p.PurOrd AS poBill,
  139. p.Line AS poBillLine,
  140. p.Potype AS orderType,
  141. p.ItemNum AS shMaterialCode,
  142. im.Descr AS shMaterialName,
  143. IFNULL(im.Drawing, p.Drawing) AS th,
  144. IFNULL(ds.SchedQty, p.QtyOrded - p.RctQty - p.ReceiptQty + p.QtyReturned) AS shDeliveryQuantity,
  145. p.StdPackQty AS bzsl,
  146. p.UM AS shMaterialDw,
  147. (p.QtyOrded - p.RctQty - p.ReceiptQty + p.QtyReturned) AS djsl,
  148. ds.DSNum AS jhdbh,
  149. im.Descr1 AS shMaterialGgxh
  150. FROM PurOrdDetail p
  151. LEFT JOIN srm_polist_ds ds ON p.PurOrd = ds.ponumber AND p.Line = ds.poline
  152. LEFT JOIN ItemMaster im ON p.ItemNum = im.ItemNum
  153. WHERE FIND_IN_SET(CAST(IFNULL(ds.id, p.RecID) AS CHAR(50)), REPLACE(IFNULL(@ids, ''), ' ', '')) > 0
  154. ORDER BY p.PurOrd, p.Line
  155. """, pars);
  156. if (rows.Count == 0)
  157. throw Oops.Oh("未找到可生成的交货明细");
  158. return new
  159. {
  160. shddh = string.Empty,
  161. jhshrq = DateTime.Now.ToString("yyyy-MM-dd"),
  162. wlsc = string.Empty,
  163. yjdhrq = string.Empty,
  164. shPurchaseName = rows[0].Gysmc,
  165. shPurchaseNum = rows[0].Gysdm,
  166. wldh = string.Empty,
  167. sfpc = 0,
  168. chbg = string.Empty,
  169. pcsm = string.Empty,
  170. details = rows.Select((r, i) => new
  171. {
  172. id = (long?)null,
  173. hh = i + 1,
  174. poBill = r.PoBill,
  175. poBillLine = r.PoBillLine?.ToString(),
  176. orderType = r.OrderType,
  177. shMaterialCode = r.ShMaterialCode,
  178. shMaterialName = r.ShMaterialName,
  179. th = r.Th,
  180. shDeliveryQuantity = r.ShDeliveryQuantity,
  181. bzsl = r.Bzsl,
  182. bqsl = 0,
  183. shMaterialDw = r.ShMaterialDw,
  184. scrq = string.Empty,
  185. scph = string.Empty,
  186. remarks = string.Empty,
  187. djsl = r.Djsl,
  188. jybb = string.Empty,
  189. jhdbh = r.Jhdbh
  190. })
  191. };
  192. }
  193. [DisplayName("保存发货单")]
  194. [ApiDescriptionSettings(Name = "SaveSupplierShipment"), HttpPost("supplier-shipment/save")]
  195. public async Task<object> Save([FromBody] SupplierShipmentSaveInput input)
  196. {
  197. var now = DateTime.Now;
  198. var userId = _userManager.UserId.ToString();
  199. var userName = _userManager.Account ?? "system";
  200. var shipDate = string.IsNullOrWhiteSpace(input.Jhshrq) ? now.ToString("yyyy-MM-dd") : input.Jhshrq!.Trim();
  201. if (input.Id is null or 0)
  202. {
  203. var newId = YitIdHelper.NextId();
  204. var entity = new ScmShd
  205. {
  206. Id = newId,
  207. Shddh = string.IsNullOrWhiteSpace(input.Shddh) ? $"SH{DateTime.Now:yyyyMMddHHmmss}" : input.Shddh!.Trim(),
  208. Jhshrq = shipDate,
  209. Wlsc = input.Wlsc,
  210. Yjdhrq = input.Yjdhrq,
  211. ShPurchaseName = input.ShPurchaseName,
  212. ShPurchaseNum = input.ShPurchaseNum,
  213. Wldh = input.Wldh,
  214. Sfpc = input.Sfpc ?? 0,
  215. Chbg = input.Chbg,
  216. Pcsm = input.Pcsm,
  217. State = 1,
  218. Shzt = "待收",
  219. Tjrid = userId,
  220. Tjrxm = userName,
  221. Tjrq = now.ToString("yyyy-MM-dd")
  222. };
  223. await _masterRep.InsertAsync(entity);
  224. await SaveDetailsAsync(newId, input.Details);
  225. return new { id = newId, message = "新增成功" };
  226. }
  227. var master = await _masterRep.GetFirstAsync(x => x.Id == input.Id.Value) ?? throw Oops.Oh("发货单不存在");
  228. master.Shddh = input.Shddh;
  229. master.Jhshrq = shipDate;
  230. master.Wlsc = input.Wlsc;
  231. master.Yjdhrq = input.Yjdhrq;
  232. master.ShPurchaseName = input.ShPurchaseName;
  233. master.ShPurchaseNum = input.ShPurchaseNum;
  234. master.Wldh = input.Wldh;
  235. master.Sfpc = input.Sfpc ?? 0;
  236. master.Chbg = input.Chbg;
  237. master.Pcsm = input.Pcsm;
  238. await _masterRep.UpdateAsync(master);
  239. await SaveDetailsAsync(input.Id.Value, input.Details);
  240. return new { id = input.Id, message = "编辑成功" };
  241. }
  242. [DisplayName("删除发货单")]
  243. [ApiDescriptionSettings(Name = "DeleteSupplierShipment"), HttpPost("supplier-shipment/delete")]
  244. public async Task<object> Delete([FromBody] SupplierShipmentDeleteInput input)
  245. {
  246. var master = await _masterRep.GetFirstAsync(x => x.Id == input.Id) ?? throw Oops.Oh("发货单不存在");
  247. master.State = 0;
  248. await _masterRep.UpdateAsync(master);
  249. return new { message = "删除成功" };
  250. }
  251. [DisplayName("生成标签(预留)")]
  252. [HttpPost("supplier-shipment/generate-label")]
  253. public Task<object> GenerateLabel([FromBody] SupplierShipmentOperationInput input)
  254. => Task.FromResult<object>(new { message = $"发货单{input.Id}:功能预留,暂未启用" });
  255. [DisplayName("打印送货单(预留)")]
  256. [HttpPost("supplier-shipment/print-shipping-note")]
  257. public Task<object> PrintShippingNote([FromBody] SupplierShipmentOperationInput input)
  258. => Task.FromResult<object>(new { message = $"发货单{input.Id}:功能预留,暂未启用" });
  259. [DisplayName("打印标签(预留)")]
  260. [HttpPost("supplier-shipment/print-label")]
  261. public Task<object> PrintLabel([FromBody] SupplierShipmentOperationInput input)
  262. => Task.FromResult<object>(new { message = $"发货单{input.Id}:功能预留,暂未启用" });
  263. private async Task SaveDetailsAsync(long masterId, List<SupplierShipmentDetailInput> inputDetails)
  264. {
  265. var dbDetails = await _detailRep.AsQueryable().Where(x => x.Glid == masterId.ToString()).ToListAsync();
  266. var dbById = dbDetails.ToDictionary(x => x.Id);
  267. var inputIds = new HashSet<long>(inputDetails.Where(d => d.Id is > 0).Select(d => d.Id!.Value));
  268. for (var i = 0; i < inputDetails.Count; i++)
  269. {
  270. var d = inputDetails[i];
  271. if (d.Id is > 0 && dbById.TryGetValue(d.Id.Value, out var existing))
  272. {
  273. existing.Hh = d.Hh ?? (i + 1);
  274. existing.PoBill = d.PoBill;
  275. existing.PoBillLine = d.PoBillLine;
  276. existing.OrderType = d.OrderType;
  277. existing.ShMaterialCode = d.ShMaterialCode;
  278. existing.ShMaterialName = d.ShMaterialName;
  279. existing.Th = d.Th;
  280. existing.ShDeliveryQuantity = d.ShDeliveryQuantity;
  281. existing.Bzsl = d.Bzsl;
  282. existing.Bqsl = d.Bqsl;
  283. existing.ShMaterialDw = d.ShMaterialDw;
  284. existing.Scrq = d.Scrq;
  285. existing.Scph = d.Scph;
  286. existing.Remarks = d.Remarks;
  287. existing.Djsl = d.Djsl;
  288. existing.Jybb = d.Jybb;
  289. existing.Jhdbh = d.Jhdbh;
  290. await _detailRep.UpdateAsync(existing);
  291. }
  292. else
  293. {
  294. var detail = new ScmShdzb
  295. {
  296. Id = YitIdHelper.NextId(),
  297. Glid = masterId.ToString(),
  298. Hh = d.Hh ?? (i + 1),
  299. PoBill = d.PoBill,
  300. PoBillLine = d.PoBillLine,
  301. OrderType = d.OrderType,
  302. ShMaterialCode = d.ShMaterialCode,
  303. ShMaterialName = d.ShMaterialName,
  304. Th = d.Th,
  305. ShDeliveryQuantity = d.ShDeliveryQuantity,
  306. Bzsl = d.Bzsl,
  307. Bqsl = d.Bqsl,
  308. ShMaterialDw = d.ShMaterialDw,
  309. Scrq = d.Scrq,
  310. Scph = d.Scph,
  311. Remarks = d.Remarks,
  312. Djsl = d.Djsl,
  313. Jybb = d.Jybb,
  314. Jhdbh = d.Jhdbh
  315. };
  316. await _detailRep.InsertAsync(detail);
  317. }
  318. }
  319. foreach (var old in dbDetails.Where(x => !inputIds.Contains(x.Id)))
  320. {
  321. await _detailRep.DeleteAsync(x => x.Id == old.Id);
  322. }
  323. }
  324. private static string BuildListOrderBy(string? sortField, string? sortOrder)
  325. {
  326. var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  327. {
  328. ["shddh"] = "t.shddh",
  329. ["poBill"] = "t.po_bill",
  330. ["jhshrq"] = "t.jhshrq",
  331. ["shMaterialCode"] = "t.sh_material_code",
  332. ["shMaterialName"] = "t.sh_material_name",
  333. ["shDeliveryQuantity"] = "t.sh_delivery_quantity",
  334. ["sfpc"] = "t.sfpc",
  335. ["pcrksl"] = "t.pcrksl",
  336. ["shPurchaseName"] = "t.sh_purchase_name",
  337. ["shpc"] = "t.shpc",
  338. ["scph"] = "t.scph",
  339. ["wldh"] = "t.wldh",
  340. ["dycs"] = "t.dycs",
  341. ["shzt"] = "t.shzt"
  342. };
  343. var field = map.TryGetValue(sortField ?? string.Empty, out var sqlField) ? sqlField : "t.mid";
  344. var order = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  345. return $" ORDER BY {field} {order} ";
  346. }
  347. private static string BuildListSql() => """
  348. SELECT
  349. m.mid,
  350. m.id,
  351. m.sh_purchase_id,
  352. m.sh_purchase_name,
  353. m.sh_purchase_num,
  354. m.sh_purchase_address,
  355. m.sh_purchase_lxr,
  356. m.sh_purchase_phone,
  357. m.client,
  358. m.delivery_Address,
  359. m.expected_consignee,
  360. m.consignee_phone,
  361. m.estimated_delivery_date,
  362. m.po_billno,
  363. m.shddh,
  364. m.jhshrq,
  365. m.tjrid,
  366. m.tjrxm,
  367. m.tjrq,
  368. m.scbq,
  369. m.chbg,
  370. m.sfpc,
  371. m.pcsm,
  372. m.wlsc,
  373. m.yjdhrq,
  374. m.state,
  375. m.shzt,
  376. m.wldh,
  377. m.dycs,
  378. m.gys,
  379. m.sh_material_code,
  380. m.sh_material_name,
  381. m.sh_material_ggxh,
  382. m.hh,
  383. m.po_bill,
  384. m.shpc,
  385. m.scph,
  386. m.sh_delivery_quantity,
  387. m.th,
  388. n.rksl,
  389. n.bhgsl,
  390. n.thsl,
  391. n.zshl,
  392. n.Delivery,
  393. n.pc,
  394. l.pcrksl,
  395. po.potype,
  396. po.Usage
  397. FROM
  398. (
  399. SELECT
  400. a.id mid,
  401. b.id,
  402. a.sh_purchase_id,
  403. a.sh_purchase_name,
  404. a.sh_purchase_num,
  405. a.sh_purchase_address,
  406. a.sh_purchase_lxr,
  407. a.sh_purchase_phone,
  408. a.client,
  409. a.delivery_Address,
  410. a.expected_consignee,
  411. a.consignee_phone,
  412. a.estimated_delivery_date,
  413. a.po_billno,
  414. a.shddh,
  415. a.jhshrq,
  416. a.tjrid,
  417. a.tjrxm,
  418. a.tjrq,
  419. a.scbq,
  420. a.chbg,
  421. a.sfpc,
  422. a.pcsm,
  423. a.wlsc,
  424. a.yjdhrq,
  425. a.state,
  426. IFNULL(a.shzt, '待收') shzt,
  427. a.wldh,
  428. a.dycs,
  429. CONCAT(IFNULL(a.sh_purchase_num, ''), IFNULL(a.sh_purchase_name, '')) gys,
  430. b.sh_material_code,
  431. b.sh_material_name,
  432. b.sh_material_ggxh,
  433. b.hh,
  434. b.po_bill,
  435. c.shpc,
  436. b.scph,
  437. b.sh_delivery_quantity,
  438. b.th
  439. FROM scm_shd a
  440. LEFT JOIN scm_shdzb b ON a.id = b.glid
  441. LEFT JOIN (SELECT DISTINCT glid, shpc FROM scm_shbq) c ON b.id = c.glid
  442. ) m
  443. LEFT JOIN
  444. (
  445. SELECT
  446. Delivery,
  447. LotSerial pc,
  448. SUM(ReceiptQty) zshl,
  449. AVG(yssl) rksl,
  450. SUM(QtyReturn) bhgsl,
  451. SUM(QtyReturned) thsl
  452. FROM vscm_cgshrk
  453. GROUP BY Delivery, LotSerial
  454. ) n ON m.shddh = n.Delivery AND m.shpc = n.pc
  455. LEFT JOIN
  456. (
  457. SELECT LotSerial, SUM(QtyChange) pcrksl
  458. FROM InvTransHist
  459. WHERE QtyChange > 0 AND Reason LIKE '%收货'
  460. GROUP BY LotSerial
  461. ) l ON m.shpc = l.LotSerial
  462. LEFT JOIN PurOrdMaster po ON m.po_bill = po.purord
  463. """;
  464. private sealed class SupplierShipmentListRow
  465. {
  466. public long Mid { get; set; }
  467. public long Id { get; set; }
  468. public string? ShPurchaseName { get; set; }
  469. public string? ShPurchaseNum { get; set; }
  470. public string? Shddh { get; set; }
  471. public string? Jhshrq { get; set; }
  472. public int? Sfpc { get; set; }
  473. public string? Wldh { get; set; }
  474. public int? Dycs { get; set; }
  475. public string? Shzt { get; set; }
  476. public string? ShMaterialCode { get; set; }
  477. public string? ShMaterialName { get; set; }
  478. public decimal? ShDeliveryQuantity { get; set; }
  479. public decimal? Pcrksl { get; set; }
  480. public string? PoBill { get; set; }
  481. public string? Usage { get; set; }
  482. public string? Shpc { get; set; }
  483. public string? Scph { get; set; }
  484. public string? Th { get; set; }
  485. public int? State { get; set; }
  486. }
  487. private sealed class SupplierShipmentDraftRow
  488. {
  489. public string? SourceId { get; set; }
  490. public string? Gysdm { get; set; }
  491. public string? Gysmc { get; set; }
  492. public string? PoBill { get; set; }
  493. public int? PoBillLine { get; set; }
  494. public string? OrderType { get; set; }
  495. public string? ShMaterialCode { get; set; }
  496. public string? ShMaterialName { get; set; }
  497. public string? Th { get; set; }
  498. public decimal? ShDeliveryQuantity { get; set; }
  499. public decimal? Bzsl { get; set; }
  500. public string? ShMaterialDw { get; set; }
  501. public decimal? Djsl { get; set; }
  502. public string? Jhdbh { get; set; }
  503. public string? ShMaterialGgxh { get; set; }
  504. }
  505. }