SupplierShipmentService.cs 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177
  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. private const int ShpcSerialWidth = 3; // yyMMdd + 3位流水:260508001
  19. private const int LabelXhStart = 10001;
  20. public SupplierShipmentService(
  21. ISqlSugarClient db,
  22. SqlSugarRepository<ScmShd> masterRep,
  23. SqlSugarRepository<ScmShdzb> detailRep,
  24. UserManager userManager)
  25. {
  26. _db = db;
  27. _masterRep = masterRep;
  28. _detailRep = detailRep;
  29. _userManager = userManager;
  30. }
  31. [DisplayName("供应商发货单列表")]
  32. [HttpGet("supplier-shipment/list")]
  33. public async Task<object> GetList([FromQuery] SupplierShipmentListInput input)
  34. {
  35. var pars = new List<SugarParameter>();
  36. var conditions = new List<string> { "IFNULL(m.state, 1) <> 0" };
  37. if (!string.IsNullOrWhiteSpace(input.JhshrqFrom))
  38. {
  39. conditions.Add("m.jhshrq >= @jhshrqFrom");
  40. pars.Add(new SugarParameter("@jhshrqFrom", input.JhshrqFrom.Trim()));
  41. }
  42. if (!string.IsNullOrWhiteSpace(input.Gysmc))
  43. {
  44. conditions.Add("m.sh_purchase_name LIKE @gysmc");
  45. pars.Add(new SugarParameter("@gysmc", $"%{input.Gysmc.Trim()}%"));
  46. }
  47. if (!string.IsNullOrWhiteSpace(input.Shddh))
  48. {
  49. conditions.Add("m.shddh LIKE @shddh");
  50. pars.Add(new SugarParameter("@shddh", $"%{input.Shddh.Trim()}%"));
  51. }
  52. if (!string.IsNullOrWhiteSpace(input.Wldh))
  53. {
  54. conditions.Add("m.wldh LIKE @wldh");
  55. pars.Add(new SugarParameter("@wldh", $"%{input.Wldh.Trim()}%"));
  56. }
  57. if (!string.IsNullOrWhiteSpace(input.PoBill))
  58. {
  59. conditions.Add("m.po_bill LIKE @poBill");
  60. pars.Add(new SugarParameter("@poBill", $"%{input.PoBill.Trim()}%"));
  61. }
  62. if (!string.IsNullOrWhiteSpace(input.ShMaterialCode))
  63. {
  64. conditions.Add("m.sh_material_code LIKE @shMaterialCode");
  65. pars.Add(new SugarParameter("@shMaterialCode", $"%{input.ShMaterialCode.Trim()}%"));
  66. }
  67. if (!string.IsNullOrWhiteSpace(input.Shzt))
  68. {
  69. conditions.Add("m.shzt = @shzt");
  70. pars.Add(new SugarParameter("@shzt", input.Shzt.Trim()));
  71. }
  72. if (!string.IsNullOrWhiteSpace(input.Shpc))
  73. {
  74. conditions.Add("m.shpc LIKE @shpc");
  75. pars.Add(new SugarParameter("@shpc", $"%{input.Shpc.Trim()}%"));
  76. }
  77. var where = $" WHERE {string.Join(" AND ", conditions)} ";
  78. var orderBy = BuildListOrderBy(input.SortField, input.SortOrder);
  79. var offset = (input.Page - 1) * input.PageSize;
  80. var wrapped = $"{BuildListSql()} {where}";
  81. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(*) FROM ({wrapped}) t", pars);
  82. var list = await _db.Ado.SqlQueryAsync<SupplierShipmentListRow>(
  83. $"SELECT * FROM ({wrapped}) t {orderBy} LIMIT {input.PageSize} OFFSET {offset}", pars);
  84. return new { total, page = input.Page, pageSize = input.PageSize, list };
  85. }
  86. [DisplayName("获取发货单详情")]
  87. [HttpGet("supplier-shipment/{id:long}")]
  88. public async Task<object> GetDetail(long id)
  89. {
  90. var master = await _masterRep.GetFirstAsync(x => x.Id == id) ?? throw Oops.Oh("发货单不存在");
  91. var details = await _detailRep.AsQueryable().Where(x => x.Glid == id.ToString()).OrderBy(x => x.Hh).ToListAsync();
  92. return new
  93. {
  94. id = master.Id,
  95. shddh = master.Shddh,
  96. jhshrq = master.Jhshrq,
  97. wlsc = master.Wlsc,
  98. yjdhrq = master.Yjdhrq,
  99. shPurchaseName = master.ShPurchaseName,
  100. shPurchaseNum = master.ShPurchaseNum,
  101. wldh = master.Wldh,
  102. sfpc = master.Sfpc ?? 0,
  103. chbg = master.Chbg,
  104. pcsm = master.Pcsm,
  105. state = master.State ?? 1,
  106. details = details.Select(d => new
  107. {
  108. id = d.Id,
  109. hh = d.Hh,
  110. poBill = d.PoBill,
  111. poBillLine = d.PoBillLine,
  112. orderType = d.OrderType,
  113. shMaterialCode = d.ShMaterialCode,
  114. shMaterialName = d.ShMaterialName,
  115. th = d.Th,
  116. shDeliveryQuantity = d.ShDeliveryQuantity,
  117. bzsl = d.Bzsl,
  118. bqsl = d.Bqsl,
  119. shMaterialDw = d.ShMaterialDw,
  120. scrq = d.Scrq,
  121. scph = d.Scph,
  122. remarks = d.Remarks,
  123. djsl = d.Djsl,
  124. jybb = d.Jybb,
  125. jhdbh = d.Jhdbh
  126. })
  127. };
  128. }
  129. [DisplayName("发货单新增草稿")]
  130. [HttpGet("supplier-shipment/create-draft")]
  131. public async Task<object> GetCreateDraft([FromQuery] string ids)
  132. {
  133. var pars = new List<SugarParameter> { new("@ids", ids) };
  134. var rows = await _db.Ado.SqlQueryAsync<SupplierShipmentDraftRow>(
  135. """
  136. SELECT
  137. CAST(IFNULL(ds.id, p.RecID) AS CHAR(50)) AS sourceId,
  138. ds.suppliercode AS gysdm,
  139. ds.supplier AS gysmc,
  140. p.PurOrd AS poBill,
  141. p.Line AS poBillLine,
  142. p.Potype AS orderType,
  143. p.ItemNum AS shMaterialCode,
  144. im.Descr AS shMaterialName,
  145. IFNULL(im.Drawing, p.Drawing) AS th,
  146. IFNULL(ds.SchedQty, p.QtyOrded - p.RctQty - p.ReceiptQty + p.QtyReturned) AS shDeliveryQuantity,
  147. p.StdPackQty AS bzsl,
  148. CASE
  149. WHEN IFNULL(p.StdPackQty, 0) > 0 THEN CEILING(IFNULL(ds.SchedQty, p.QtyOrded - p.RctQty - p.ReceiptQty + p.QtyReturned) / p.StdPackQty)
  150. ELSE 0
  151. END AS bqsl,
  152. p.UM AS shMaterialDw,
  153. (p.QtyOrded - p.RctQty - p.ReceiptQty + p.QtyReturned) AS djsl,
  154. ds.DSNum AS jhdbh,
  155. im.Descr1 AS shMaterialGgxh
  156. FROM PurOrdDetail p
  157. LEFT JOIN srm_polist_ds ds ON p.PurOrd = ds.ponumber AND p.Line = ds.poline
  158. LEFT JOIN ItemMaster im ON p.ItemNum = im.ItemNum
  159. WHERE FIND_IN_SET(CAST(IFNULL(ds.id, p.RecID) AS CHAR(50)), REPLACE(IFNULL(@ids, ''), ' ', '')) > 0
  160. ORDER BY p.PurOrd, p.Line
  161. """, pars);
  162. if (rows.Count == 0)
  163. throw Oops.Oh("未找到可生成的交货明细");
  164. return new
  165. {
  166. shddh = string.Empty,
  167. jhshrq = DateTime.Now.ToString("yyyy-MM-dd"),
  168. wlsc = string.Empty,
  169. yjdhrq = string.Empty,
  170. shPurchaseName = rows[0].Gysmc,
  171. shPurchaseNum = rows[0].Gysdm,
  172. wldh = string.Empty,
  173. sfpc = 0,
  174. chbg = string.Empty,
  175. pcsm = string.Empty,
  176. details = rows.Select((r, i) => new
  177. {
  178. id = (long?)null,
  179. hh = i + 1,
  180. poBill = r.PoBill,
  181. poBillLine = r.PoBillLine?.ToString(),
  182. orderType = r.OrderType,
  183. shMaterialCode = r.ShMaterialCode,
  184. shMaterialName = r.ShMaterialName,
  185. th = r.Th,
  186. shDeliveryQuantity = r.ShDeliveryQuantity,
  187. bzsl = r.Bzsl,
  188. bqsl = r.Bqsl ?? 0,
  189. shMaterialDw = r.ShMaterialDw,
  190. scrq = string.Empty,
  191. scph = string.Empty,
  192. remarks = string.Empty,
  193. djsl = r.Djsl,
  194. jybb = string.Empty,
  195. jhdbh = r.Jhdbh
  196. })
  197. };
  198. }
  199. [DisplayName("保存发货单")]
  200. [ApiDescriptionSettings(Name = "SaveSupplierShipment"), HttpPost("supplier-shipment/save")]
  201. public async Task<object> Save([FromBody] SupplierShipmentSaveInput input)
  202. {
  203. var now = DateTime.Now;
  204. var userId = _userManager.UserId.ToString();
  205. var userName = _userManager.Account ?? "system";
  206. var shipDate = string.IsNullOrWhiteSpace(input.Jhshrq) ? now.ToString("yyyy-MM-dd") : input.Jhshrq!.Trim();
  207. if (input.Id is null or 0)
  208. {
  209. var newId = YitIdHelper.NextId();
  210. var shddh = await AllocateShddhAsync(input.ShPurchaseNum ?? string.Empty);
  211. var entity = new ScmShd
  212. {
  213. Id = newId,
  214. Shddh = shddh,
  215. Jhshrq = shipDate,
  216. Wlsc = input.Wlsc,
  217. Yjdhrq = input.Yjdhrq,
  218. ShPurchaseName = input.ShPurchaseName,
  219. ShPurchaseNum = input.ShPurchaseNum,
  220. Wldh = input.Wldh,
  221. Sfpc = input.Sfpc ?? 0,
  222. Chbg = input.Chbg,
  223. Pcsm = input.Pcsm,
  224. State = 1,
  225. Shzt = "待收",
  226. Tjrid = userId,
  227. Tjrxm = userName,
  228. Tjrq = now.ToString("yyyy-MM-dd")
  229. };
  230. await _masterRep.InsertAsync(entity);
  231. await SaveDetailsAsync(newId, input.Details);
  232. return new { id = newId, message = "新增成功", shddh = entity.Shddh };
  233. }
  234. var master = await _masterRep.GetFirstAsync(x => x.Id == input.Id.Value) ?? throw Oops.Oh("发货单不存在");
  235. master.Jhshrq = shipDate;
  236. master.Wlsc = input.Wlsc;
  237. master.Yjdhrq = input.Yjdhrq;
  238. master.ShPurchaseName = input.ShPurchaseName;
  239. master.ShPurchaseNum = input.ShPurchaseNum;
  240. master.Wldh = input.Wldh;
  241. master.Sfpc = input.Sfpc ?? 0;
  242. master.Chbg = input.Chbg;
  243. master.Pcsm = input.Pcsm;
  244. await _masterRep.UpdateAsync(master);
  245. await SaveDetailsAsync(input.Id.Value, input.Details);
  246. return new { id = input.Id, message = "编辑成功", shddh = master.Shddh };
  247. }
  248. [DisplayName("删除发货单")]
  249. [ApiDescriptionSettings(Name = "DeleteSupplierShipment"), HttpPost("supplier-shipment/delete")]
  250. public async Task<object> Delete([FromBody] SupplierShipmentDeleteInput input)
  251. {
  252. var master = await _masterRep.GetFirstAsync(x => x.Id == input.Id) ?? throw Oops.Oh("发货单不存在");
  253. master.State = 0;
  254. await _masterRep.UpdateAsync(master);
  255. return new { message = "删除成功" };
  256. }
  257. [DisplayName("生成标签(预留)")]
  258. [HttpPost("supplier-shipment/generate-label")]
  259. public async Task<object> GenerateLabel([FromBody] SupplierShipmentOperationInput input)
  260. {
  261. if (input.Id <= 0)
  262. throw Oops.Oh("缺少发货单ID(id)");
  263. var userName = _userManager.Account ?? "system";
  264. var master = await _masterRep.GetFirstAsync(x => x.Id == input.Id) ?? throw Oops.Oh("发货单不存在");
  265. if (string.IsNullOrWhiteSpace(master.Shddh))
  266. throw Oops.Oh("发货单缺少发货单编号(shddh)");
  267. var shddh = master.Shddh.Trim();
  268. var gysmc = master.ShPurchaseName ?? string.Empty;
  269. var gysdm = master.ShPurchaseNum ?? string.Empty;
  270. // Domain(工厂编码)
  271. var domain = await _db.Ado.GetStringAsync(
  272. "SELECT Domain FROM GeneralizedCodeMaster WHERE FldName='SystemConfig' AND Val='CompanyCode' LIMIT 1");
  273. domain = string.IsNullOrWhiteSpace(domain) ? string.Empty : domain.Trim();
  274. string? lockKey = null;
  275. try
  276. {
  277. var tran = await _db.Ado.UseTranAsync(async () =>
  278. {
  279. // 1) 归档旧标签
  280. await _db.Ado.ExecuteCommandAsync(
  281. """
  282. INSERT INTO scm_shbqhis
  283. (glid, sh_material_code, sh_material_name, sh_material_ggxh, sh_delivery_quantity,
  284. sh_material_dw, remarks, bzsl, order_type, po_billno, shdh, shdhh, scrq, scph, xh,
  285. gysdm, gysmc, po_billline, bbh, th, yt, ccrq, shpc, jhdbh, jhdhh)
  286. SELECT
  287. glid, sh_material_code, sh_material_name, sh_material_ggxh, sh_delivery_quantity,
  288. sh_material_dw, remarks, bzsl, order_type, po_billno, shdh, shdhh, scrq, scph, xh,
  289. gysdm, gysmc, po_billline, bbh, th, yt, ccrq, shpc, jhdbh, jhdhh
  290. FROM scm_shbq WHERE shdh=@shdh;
  291. """,
  292. new List<SugarParameter> { new("@shdh", shddh) });
  293. await _db.Ado.ExecuteCommandAsync(
  294. "DELETE FROM scm_shbq WHERE shdh=@shdh",
  295. new List<SugarParameter> { new("@shdh", shddh) });
  296. // 2) 需要补 shpc 的明细(按物料+供应商批号去重)
  297. var needShpc = await _db.Ado.SqlQueryAsync<NeedShpcRow>(
  298. """
  299. SELECT DISTINCT
  300. b.sh_material_code AS wlbm,
  301. IFNULL(b.scph, '') AS scph
  302. FROM scm_shd a
  303. INNER JOIN scm_shdzb b ON a.id = b.glid
  304. WHERE a.shddh = @shddh
  305. AND IFNULL(b.sh_material_code, '') <> ''
  306. AND IFNULL(b.shpc, '') = '';
  307. """,
  308. new List<SugarParameter> { new("@shddh", shddh) });
  309. if (needShpc.Count > 0)
  310. {
  311. // 并发安全:按天加锁 + 取最大流水 + 批量生成
  312. var prefix = DateTime.Now.ToString("yyMMdd");
  313. lockKey = $"scm_shpc_seq_{prefix}";
  314. await AcquireMySqlLockAsync(lockKey, 10);
  315. var nextSerial = await GetNextDailySerialAsync(prefix, domain);
  316. var pairs = needShpc
  317. .OrderBy(x => x.Wlbm ?? string.Empty)
  318. .ThenBy(x => x.Scph ?? string.Empty)
  319. .Select((x, idx) => new
  320. {
  321. x.Wlbm,
  322. x.Scph,
  323. Shpc = $"{prefix}{(nextSerial + idx).ToString().PadLeft(ShpcSerialWidth, '0')}"
  324. })
  325. .ToList();
  326. // 更新 scm_shdzb.shpc(同一发货单下:物料+供应商批号相同的行统一批次号)
  327. foreach (var p in pairs)
  328. {
  329. await _db.Ado.ExecuteCommandAsync(
  330. """
  331. UPDATE scm_shdzb b
  332. INNER JOIN scm_shd a ON a.id = b.glid
  333. SET b.shpc = @shpc
  334. WHERE a.shddh = @shddh
  335. AND b.sh_material_code = @wlbm
  336. AND IFNULL(b.scph, '') = IFNULL(@scph, '')
  337. AND IFNULL(b.shpc, '') = '';
  338. """,
  339. new List<SugarParameter>
  340. {
  341. new("@shpc", p.Shpc),
  342. new("@shddh", shddh),
  343. new("@wlbm", p.Wlbm ?? string.Empty),
  344. new("@scph", p.Scph ?? string.Empty),
  345. });
  346. }
  347. // 兼容:如果线上存在 scm_shdshph,则同步写入(仓库脚本里未包含该表)
  348. var shdshphExists = await TableExistsAsync("scm_shdshph");
  349. if (shdshphExists)
  350. {
  351. foreach (var p in pairs)
  352. {
  353. await _db.Ado.ExecuteCommandAsync(
  354. """
  355. INSERT INTO scm_shdshph (shpc, wlbm, scph, shdh, xh, gysdm, create_time)
  356. SELECT @shpc, @wlbm, @scph, @shdh, @xh, @gysdm, NOW()
  357. FROM DUAL
  358. WHERE NOT EXISTS (
  359. SELECT 1 FROM scm_shdshph
  360. WHERE shdh=@shdh AND wlbm=@wlbm AND IFNULL(scph,'')=IFNULL(@scph,'')
  361. );
  362. """,
  363. new List<SugarParameter>
  364. {
  365. new("@shpc", p.Shpc),
  366. new("@wlbm", p.Wlbm ?? string.Empty),
  367. new("@scph", p.Scph ?? string.Empty),
  368. new("@shdh", shddh),
  369. new("@xh", p.Shpc),
  370. new("@gysdm", gysdm),
  371. });
  372. }
  373. }
  374. // 推进 NbrControl.NextValue,确保续号(仅当没有历史 shpc 且使用了 NbrControl 作为起点时也能正确续)
  375. // 这里直接推进到“本批次最后一个流水号”,下次取号将从 NextValue+1 开始。
  376. var lastSerial = nextSerial + pairs.Count - 1;
  377. await BumpNbrControlNextValueAsync(domain, lastSerial);
  378. }
  379. // 3) 补 jhdhh(同一发货单:为空的明细按 id 递增,起始 max(jhdhh) or 1000)
  380. var shdid = await _db.Ado.GetLongAsync(
  381. "SELECT id FROM scm_shd WHERE shddh=@shddh LIMIT 1",
  382. new List<SugarParameter> { new("@shddh", shddh) });
  383. var startNo = await _db.Ado.GetIntAsync(
  384. "SELECT IFNULL(MAX(CAST(IFNULL(jhdhh,'') AS SIGNED)), 1000) FROM scm_shdzb WHERE glid=@glid",
  385. new List<SugarParameter> { new("@glid", shdid.ToString()) });
  386. var jhdhhNeed = await _db.Ado.SqlQueryAsync<IdOnlyRow>(
  387. "SELECT id FROM scm_shdzb WHERE glid=@glid AND IFNULL(jhdhh,'')='' ORDER BY id",
  388. new List<SugarParameter> { new("@glid", shdid.ToString()) });
  389. var seq = startNo;
  390. foreach (var r in jhdhhNeed)
  391. {
  392. seq++;
  393. await _db.Ado.ExecuteCommandAsync(
  394. "UPDATE scm_shdzb SET jhdhh=@jhdhh WHERE id=@id",
  395. new List<SugarParameter>
  396. {
  397. new("@jhdhh", seq.ToString()),
  398. new("@id", r.Id)
  399. });
  400. }
  401. // 4) 生成 scm_shbq(按 bqsl 拆分;每箱 bzsl,最后一箱余数)
  402. var sourceRows = await _db.Ado.SqlQueryAsync<LabelSourceRow>(
  403. """
  404. SELECT
  405. a.shddh AS shddh,
  406. b.id AS detailId,
  407. b.glid AS glid,
  408. b.sh_material_code AS wlbm,
  409. b.sh_material_name AS wlmc,
  410. b.sh_material_ggxh AS ggxh,
  411. b.sh_delivery_quantity AS shsl,
  412. b.sh_material_dw AS dw,
  413. b.remarks AS bz,
  414. b.bzsl AS bzsl,
  415. b.bqsl AS bqsl,
  416. b.order_type AS ddlx,
  417. b.po_bill AS ddh,
  418. CAST(IFNULL(b.po_billline, '0') AS SIGNED) AS po_billline,
  419. b.hh AS hh,
  420. b.scrq AS scrq,
  421. b.scph AS scph,
  422. IFNULL(im.Drawing, pd.Drawing) AS th,
  423. IFNULL(im.Rev, pd.Rev) AS bbh,
  424. a.sh_purchase_name AS gysmc,
  425. a.sh_purchase_num AS gysdm,
  426. pm.Usage AS usage,
  427. b.ccrq AS ccrq,
  428. a.jhshrq AS jhshrq,
  429. b.jhdbh AS jhdbh,
  430. b.jhdhh AS jhdhh,
  431. b.shpc AS shpc
  432. FROM scm_shd a
  433. INNER JOIN scm_shdzb b ON a.id = b.glid
  434. LEFT JOIN PurOrdMaster pm ON pm.PurOrd = b.po_bill
  435. LEFT JOIN PurOrdDetail pd ON pd.PurOrd = b.po_bill AND pd.Line = b.po_billline
  436. LEFT JOIN ItemMaster im ON im.ItemNum = b.sh_material_code
  437. WHERE a.shddh = @shddh;
  438. """,
  439. new List<SugarParameter> { new("@shddh", shddh) });
  440. foreach (var row in sourceRows)
  441. {
  442. var totalQty = row.Shsl ?? 0m;
  443. var packQty = row.Bzsl ?? 0m;
  444. var labelCnt = (int)Math.Max(0, row.Bqsl ?? 0m);
  445. if (totalQty <= 0 || labelCnt <= 0)
  446. continue;
  447. var xh = LabelXhStart;
  448. var remainQty = totalQty;
  449. var remainLabels = labelCnt;
  450. // 多箱:前 N-1 箱按包装数量
  451. while (remainLabels > 1)
  452. {
  453. await InsertLabelAsync(shddh, row, packQty, xh, gysdm, gysmc);
  454. remainQty -= packQty;
  455. remainLabels--;
  456. xh++;
  457. }
  458. if (remainQty > 0)
  459. {
  460. await InsertLabelAsync(shddh, row, remainQty, xh, gysdm, gysmc);
  461. }
  462. }
  463. // 5) 更新发货单状态
  464. await _db.Ado.ExecuteCommandAsync(
  465. "UPDATE scm_shd SET shzt='待收', state=2, dycs=0 WHERE shddh=@shddh",
  466. new List<SugarParameter> { new("@shddh", shddh) });
  467. // 6) MissedPrint:作废旧未打印(U)
  468. await _db.Ado.ExecuteCommandAsync(
  469. """
  470. UPDATE MissedPrint SET
  471. PurOrd = CONCAT('作废_', IFNULL(PurOrd, '')),
  472. BarCode = CONCAT(CAST(RecID AS CHAR(20)), '_', IFNULL(BarCode, '')),
  473. Status = 'C',
  474. RelatedBarCode = '',
  475. UpdateTime = NOW(),
  476. UpdateUser = @u
  477. WHERE Domain = @domain
  478. AND ShipperNbr = @shddh
  479. AND Status = 'U';
  480. """,
  481. new List<SugarParameter>
  482. {
  483. new("@u", userName),
  484. new("@domain", domain),
  485. new("@shddh", shddh),
  486. });
  487. // 7) MissedPrint:插入待打印(U)
  488. await _db.Ado.ExecuteCommandAsync(
  489. """
  490. INSERT INTO MissedPrint
  491. (
  492. Domain, Site, PrintTime,
  493. ItemNum, Descr, Product, Carton, OrdNbr,
  494. PackingQty, Qty, Location, Status,
  495. Supply, LotSerial, CartonQty, BarCode,
  496. MoldNum, SuppLotSerial, ShipperNbr, ShipperLine,
  497. ProdDate, PurOrd, PurLine, PurQty, WorkOrd,
  498. LabelFormat, StandItem, EffSize, GP12CheckedQty, NetWeight,
  499. Remark, CreateTime, UpdateTime, CreateUser, UpdateUser,
  500. LevelChar, PurOrdDetBatchNbr, FirmString5, ExpireDate,
  501. Printer, Company, Checker, Position
  502. )
  503. SELECT
  504. @domain AS Domain,
  505. @domain AS Site,
  506. NULL AS PrintTime,
  507. s.sh_material_code AS ItemNum,
  508. s.sh_material_name AS Descr,
  509. s.sh_material_ggxh AS Product,
  510. RIGHT(s.xh, 4) AS Carton,
  511. s.po_billno AS OrdNbr,
  512. s.bzsl AS PackingQty,
  513. s.sh_delivery_quantity AS Qty,
  514. NULL AS Location,
  515. 'U' AS Status,
  516. s.gysdm AS Supply,
  517. s.shpc AS LotSerial,
  518. 1 AS CartonQty,
  519. s.xh AS BarCode,
  520. NULL AS MoldNum,
  521. s.scph AS SuppLotSerial,
  522. s.shdh AS ShipperNbr,
  523. s.shdhh AS ShipperLine,
  524. STR_TO_DATE(s.scrq, '%Y-%m-%d') AS ProdDate,
  525. s.po_billno AS PurOrd,
  526. s.po_billline AS PurLine,
  527. s.sh_delivery_quantity AS PurQty,
  528. NULL AS WorkOrd,
  529. 'cl01' AS LabelFormat,
  530. s.po_billno AS StandItem,
  531. s.po_billline AS EffSize,
  532. s.sh_delivery_quantity AS GP12CheckedQty,
  533. 0 AS NetWeight,
  534. s.remarks AS Remark,
  535. NOW() AS CreateTime,
  536. NOW() AS UpdateTime,
  537. @u AS CreateUser,
  538. @u AS UpdateUser,
  539. s.bbh AS LevelChar,
  540. s.jhdbh AS PurOrdDetBatchNbr,
  541. p.ActiveRlseID1 AS FirmString5,
  542. CASE
  543. WHEN i.SuppWarranty = 1 THEN
  544. CASE
  545. WHEN UPPER(i.WarrantyCode)='D' THEN DATE_ADD(STR_TO_DATE(s.scrq, '%Y-%m-%d'), INTERVAL IFNULL(i.DaysBetweenPM,0) DAY)
  546. WHEN UPPER(i.WarrantyCode)='Y' THEN DATE_ADD(STR_TO_DATE(s.scrq, '%Y-%m-%d'), INTERVAL IFNULL(i.DaysBetweenPM,0) YEAR)
  547. WHEN UPPER(i.WarrantyCode)='M' THEN DATE_ADD(STR_TO_DATE(s.scrq, '%Y-%m-%d'), INTERVAL IFNULL(i.DaysBetweenPM,0) MONTH)
  548. ELSE DATE_ADD(STR_TO_DATE(s.scrq, '%Y-%m-%d'), INTERVAL IFNULL(i.DaysBetweenPM,0) DAY)
  549. END
  550. ELSE NULL
  551. END AS ExpireDate,
  552. '' AS Printer,
  553. '' AS Company,
  554. '' AS Checker,
  555. '' AS Position
  556. FROM scm_shbq s
  557. LEFT JOIN PurOrdDetail p ON p.PurOrd = s.po_billno AND p.Line = s.po_billline
  558. LEFT JOIN ItemMaster i ON i.ItemNum = s.sh_material_code
  559. WHERE s.shdh = @shddh;
  560. """,
  561. new List<SugarParameter>
  562. {
  563. new("@domain", domain),
  564. new("@u", userName),
  565. new("@shddh", shddh),
  566. });
  567. });
  568. if (!tran.IsSuccess)
  569. {
  570. var msg = $"生成标签失败,{tran.ErrorMessage}";
  571. return new { success = false, msg, message = msg };
  572. }
  573. var ok = "生成标签成功!";
  574. return new { success = true, msg = ok, message = ok };
  575. }
  576. finally
  577. {
  578. if (!string.IsNullOrWhiteSpace(lockKey))
  579. {
  580. await ReleaseMySqlLockAsync(lockKey);
  581. }
  582. }
  583. }
  584. private async Task InsertLabelAsync(string shddh, LabelSourceRow row, decimal qty, int xh, string gysdm, string gysmc)
  585. {
  586. var jhdhh3 = Right(row.Jhdhh, 3);
  587. var xh4 = Right(xh.ToString(), 4);
  588. var bar = $"{shddh}{jhdhh3}{xh4}";
  589. await _db.Ado.ExecuteCommandAsync(
  590. """
  591. INSERT INTO scm_shbq
  592. (glid, sh_material_code, sh_material_name, sh_material_ggxh, sh_delivery_quantity,
  593. sh_material_dw, remarks, bzsl, order_type, po_billno, shdh, shdhh, scrq, scph, xh,
  594. gysdm, gysmc, po_billline, bbh, th, yt, ccrq, shpc, jhdbh, jhdhh)
  595. VALUES
  596. (@glid, @wlbm, @wlmc, @ggxh, @qty,
  597. @dw, @bz, @bzsl, @ddlx, @ddh, @shdh, @hh, @scrq, @scph, @xh,
  598. @gysdm, @gysmc, @pohh, @bbh, @th, @yt, @ccrq, @shpc, @jhdbh, @jhdhh);
  599. """,
  600. new List<SugarParameter>
  601. {
  602. new("@glid", row.DetailId),
  603. new("@wlbm", row.Wlbm ?? string.Empty),
  604. new("@wlmc", row.Wlmc ?? string.Empty),
  605. new("@ggxh", row.Ggxh ?? string.Empty),
  606. new("@qty", qty),
  607. new("@dw", row.Dw ?? string.Empty),
  608. new("@bz", row.Bz ?? string.Empty),
  609. new("@bzsl", row.Bzsl ?? 0m),
  610. new("@ddlx", row.Ddlx ?? string.Empty),
  611. new("@ddh", row.Ddh ?? string.Empty),
  612. new("@shdh", shddh),
  613. new("@hh", row.Hh ?? 0),
  614. new("@scrq", row.Scrq ?? string.Empty),
  615. new("@scph", row.Scph ?? string.Empty),
  616. new("@xh", bar),
  617. new("@gysdm", gysdm),
  618. new("@gysmc", gysmc),
  619. new("@pohh", row.PoBillLine ?? 0),
  620. new("@bbh", row.Bbh ?? string.Empty),
  621. new("@th", row.Th ?? string.Empty),
  622. new("@yt", row.Usage ?? string.Empty),
  623. new("@ccrq", row.Ccrq ?? string.Empty),
  624. new("@shpc", row.Shpc ?? string.Empty),
  625. new("@jhdbh", row.Jhdbh ?? string.Empty),
  626. new("@jhdhh", row.Jhdhh ?? string.Empty),
  627. });
  628. }
  629. private static string Right(string? s, int len)
  630. {
  631. if (string.IsNullOrEmpty(s)) return new string('0', len);
  632. var t = s.Trim();
  633. return t.Length <= len ? t.PadLeft(len, '0') : t[^len..];
  634. }
  635. private async Task AcquireMySqlLockAsync(string key, int timeoutSeconds)
  636. {
  637. var ok = await _db.Ado.GetIntAsync("SELECT GET_LOCK(@k, @t)", new List<SugarParameter>
  638. {
  639. new("@k", key),
  640. new("@t", timeoutSeconds),
  641. });
  642. if (ok != 1)
  643. throw Oops.Oh($"生成批次号锁等待超时({key})");
  644. }
  645. private Task ReleaseMySqlLockAsync(string key)
  646. => _db.Ado.ExecuteCommandAsync("SELECT RELEASE_LOCK(@k)", new List<SugarParameter> { new("@k", key) });
  647. /// <summary>
  648. /// 发货单号:供应商编码-yyyyMMdd-四位流水(同日同供应商递增,保存时分配)。
  649. /// </summary>
  650. private async Task<string> AllocateShddhAsync(string supplierCode)
  651. {
  652. var code = (supplierCode ?? string.Empty).Trim().Replace("-", string.Empty);
  653. if (string.IsNullOrEmpty(code))
  654. throw Oops.Oh("请先填写供应商编号,保存时将用于生成发货单号");
  655. var date = DateTime.Now.ToString("yyyyMMdd");
  656. var prefix = $"{code}-{date}-";
  657. if (prefix.Length > 240)
  658. throw Oops.Oh("供应商编码过长,无法生成发货单号");
  659. var lockKey = $"scm_shddh_{code}_{date}";
  660. if (lockKey.Length > 64)
  661. lockKey = lockKey[..64];
  662. await AcquireMySqlLockAsync(lockKey, 15);
  663. try
  664. {
  665. var maxSeq = await _db.Ado.GetIntAsync(
  666. """
  667. SELECT IFNULL(MAX(CAST(RIGHT(shddh, 4) AS UNSIGNED)), 0)
  668. FROM scm_shd
  669. WHERE shddh LIKE CONCAT(@pfx, '%')
  670. AND CHAR_LENGTH(shddh) = CHAR_LENGTH(@pfx) + 4
  671. AND RIGHT(shddh, 4) REGEXP '^[0-9]{4}$'
  672. """,
  673. new List<SugarParameter> { new("@pfx", prefix) });
  674. var next = maxSeq + 1;
  675. if (next > 9999)
  676. throw Oops.Oh("当日该供应商发货单流水号已超过 9999");
  677. return $"{prefix}{next:D4}";
  678. }
  679. finally
  680. {
  681. await ReleaseMySqlLockAsync(lockKey);
  682. }
  683. }
  684. private async Task<int> GetNextDailySerialAsync(string prefix, string domain)
  685. {
  686. // 从已存在的批次号中取当天最大流水:yyMMdd + 3位
  687. // 优先 scm_shdzb,其次 scm_shbq(兼容历史数据来源)
  688. var max1 = await _db.Ado.GetStringAsync(
  689. "SELECT MAX(shpc) FROM scm_shdzb WHERE shpc LIKE @pfx",
  690. new List<SugarParameter> { new("@pfx", $"{prefix}%") });
  691. var max2 = await _db.Ado.GetStringAsync(
  692. "SELECT MAX(shpc) FROM scm_shbq WHERE shpc LIKE @pfx",
  693. new List<SugarParameter> { new("@pfx", $"{prefix}%") });
  694. var max = string.CompareOrdinal(max1 ?? string.Empty, max2 ?? string.Empty) >= 0 ? max1 : max2;
  695. if (string.IsNullOrWhiteSpace(max) || max!.Length < prefix.Length + ShpcSerialWidth)
  696. {
  697. // 兜底:读取 NbrControl(NbrType='WoLot')的 NextValue + 1
  698. // 说明:有些环境可能会清理历史批次号表;此时用 NbrControl 保证连续。
  699. var nv = await _db.Ado.GetIntAsync(
  700. """
  701. SELECT IFNULL(NextValue, 0)
  702. FROM NbrControl
  703. WHERE NbrType='WoLot'
  704. AND (
  705. domain_code = @d
  706. OR Domain = @d
  707. OR IFNULL(@d,'') = ''
  708. )
  709. ORDER BY RecID
  710. LIMIT 1
  711. """,
  712. new List<SugarParameter> { new("@d", domain ?? string.Empty) });
  713. // 用户要求:NextValue+1 为下一流水号起点
  714. return Math.Max(1, nv + 1);
  715. }
  716. var suffix = max.Substring(prefix.Length);
  717. return int.TryParse(suffix, out var n) ? n + 1 : 1;
  718. }
  719. private async Task BumpNbrControlNextValueAsync(string domain, int lastSerial)
  720. {
  721. // 保守推进:仅当新值更大时更新,避免其它流程并发推进导致回退。
  722. await _db.Ado.ExecuteCommandAsync(
  723. """
  724. UPDATE NbrControl
  725. SET NextValue = CASE
  726. WHEN IFNULL(NextValue, 0) < @v THEN @v
  727. ELSE IFNULL(NextValue, 0)
  728. END,
  729. UpdateTime = NOW()
  730. WHERE NbrType = 'WoLot'
  731. AND (
  732. domain_code = @d
  733. OR Domain = @d
  734. OR IFNULL(@d,'') = ''
  735. );
  736. """,
  737. new List<SugarParameter>
  738. {
  739. new("@d", domain ?? string.Empty),
  740. new("@v", lastSerial),
  741. });
  742. }
  743. private async Task<bool> TableExistsAsync(string tableName)
  744. {
  745. var cnt = await _db.Ado.GetIntAsync(
  746. """
  747. SELECT COUNT(*)
  748. FROM information_schema.tables
  749. WHERE table_schema = DATABASE()
  750. AND table_name = @t
  751. """,
  752. new List<SugarParameter> { new("@t", tableName) });
  753. return cnt > 0;
  754. }
  755. private sealed class NeedShpcRow
  756. {
  757. public string? Wlbm { get; set; }
  758. public string? Scph { get; set; }
  759. }
  760. private sealed class IdOnlyRow
  761. {
  762. public long Id { get; set; }
  763. }
  764. private sealed class LabelSourceRow
  765. {
  766. public string? Shddh { get; set; }
  767. public long DetailId { get; set; }
  768. public string? Glid { get; set; }
  769. public string? Wlbm { get; set; }
  770. public string? Wlmc { get; set; }
  771. public string? Ggxh { get; set; }
  772. public decimal? Shsl { get; set; }
  773. public string? Dw { get; set; }
  774. public string? Bz { get; set; }
  775. public decimal? Bzsl { get; set; }
  776. public decimal? Bqsl { get; set; }
  777. public string? Ddlx { get; set; }
  778. public string? Ddh { get; set; }
  779. public int? PoBillLine { get; set; }
  780. public int? Hh { get; set; }
  781. public string? Scrq { get; set; }
  782. public string? Scph { get; set; }
  783. public string? Th { get; set; }
  784. public string? Bbh { get; set; }
  785. public string? Usage { get; set; }
  786. public string? Ccrq { get; set; }
  787. public string? Jhdbh { get; set; }
  788. public string? Jhdhh { get; set; }
  789. public string? Shpc { get; set; }
  790. }
  791. [DisplayName("打印送货单(预留)")]
  792. [HttpPost("supplier-shipment/print-shipping-note")]
  793. public Task<object> PrintShippingNote([FromBody] SupplierShipmentOperationInput input)
  794. => Task.FromResult<object>(new { message = $"发货单{input.Id}:功能预留,暂未启用" });
  795. [DisplayName("打印标签(预留)")]
  796. [HttpPost("supplier-shipment/print-label")]
  797. public Task<object> PrintLabel([FromBody] SupplierShipmentOperationInput input)
  798. => Task.FromResult<object>(new { message = $"发货单{input.Id}:功能预留,暂未启用" });
  799. [DisplayName("发货单标签数据(scm_shbq)")]
  800. [HttpGet("supplier-shipment/label-data")]
  801. public async Task<object> GetLabelData([FromQuery] string shddh)
  802. {
  803. if (string.IsNullOrWhiteSpace(shddh))
  804. throw Oops.Oh("缺少发货单编号(shddh)");
  805. var pars = new List<SugarParameter> { new("@shddh", shddh.Trim()) };
  806. const string sql = """
  807. SELECT DISTINCT
  808. glid,
  809. sh_material_code AS wlbm,
  810. sh_material_name AS wlmc,
  811. CONCAT(IFNULL(sh_material_name, ''), IFNULL(sh_material_ggxh, '')) AS ggxh,
  812. sh_delivery_quantity AS zxsl,
  813. sh_material_dw AS dw,
  814. remarks,
  815. bzsl AS bz,
  816. order_type AS ddlx,
  817. po_billno AS ddh,
  818. shdh AS shdh,
  819. shdhh AS shdhh,
  820. DATE_FORMAT(scrq, '%Y.%m.%d') AS scrq,
  821. scph,
  822. xh,
  823. gysmc,
  824. th,
  825. bbh,
  826. yt,
  827. ccrq,
  828. shpc
  829. FROM scm_shbq
  830. WHERE shdh = @shddh
  831. """;
  832. var list = await _db.Ado.SqlQueryAsync<SupplierShipmentLabelRow>(sql, pars);
  833. return new { list };
  834. }
  835. private async Task SaveDetailsAsync(long masterId, List<SupplierShipmentDetailInput> inputDetails)
  836. {
  837. var dbDetails = await _detailRep.AsQueryable().Where(x => x.Glid == masterId.ToString()).ToListAsync();
  838. var dbById = dbDetails.ToDictionary(x => x.Id);
  839. var inputIds = new HashSet<long>(inputDetails.Where(d => d.Id is > 0).Select(d => d.Id!.Value));
  840. for (var i = 0; i < inputDetails.Count; i++)
  841. {
  842. var d = inputDetails[i];
  843. if (d.Id is > 0 && dbById.TryGetValue(d.Id.Value, out var existing))
  844. {
  845. existing.Hh = d.Hh ?? (i + 1);
  846. existing.PoBill = d.PoBill;
  847. existing.PoBillLine = d.PoBillLine;
  848. existing.OrderType = d.OrderType;
  849. existing.ShMaterialCode = d.ShMaterialCode;
  850. existing.ShMaterialName = d.ShMaterialName;
  851. existing.Th = d.Th;
  852. existing.ShDeliveryQuantity = d.ShDeliveryQuantity;
  853. existing.Bzsl = d.Bzsl;
  854. existing.Bqsl = d.Bqsl;
  855. existing.ShMaterialDw = d.ShMaterialDw;
  856. existing.Scrq = d.Scrq;
  857. existing.Scph = d.Scph;
  858. existing.Remarks = d.Remarks;
  859. existing.Djsl = d.Djsl;
  860. existing.Jybb = d.Jybb;
  861. existing.Jhdbh = d.Jhdbh;
  862. await _detailRep.UpdateAsync(existing);
  863. }
  864. else
  865. {
  866. var detail = new ScmShdzb
  867. {
  868. Id = YitIdHelper.NextId(),
  869. Glid = masterId.ToString(),
  870. Hh = d.Hh ?? (i + 1),
  871. PoBill = d.PoBill,
  872. PoBillLine = d.PoBillLine,
  873. OrderType = d.OrderType,
  874. ShMaterialCode = d.ShMaterialCode,
  875. ShMaterialName = d.ShMaterialName,
  876. Th = d.Th,
  877. ShDeliveryQuantity = d.ShDeliveryQuantity,
  878. Bzsl = d.Bzsl,
  879. Bqsl = d.Bqsl,
  880. ShMaterialDw = d.ShMaterialDw,
  881. Scrq = d.Scrq,
  882. Scph = d.Scph,
  883. Remarks = d.Remarks,
  884. Djsl = d.Djsl,
  885. Jybb = d.Jybb,
  886. Jhdbh = d.Jhdbh
  887. };
  888. await _detailRep.InsertAsync(detail);
  889. }
  890. }
  891. foreach (var old in dbDetails.Where(x => !inputIds.Contains(x.Id)))
  892. {
  893. await _detailRep.DeleteAsync(x => x.Id == old.Id);
  894. }
  895. }
  896. private static string BuildListOrderBy(string? sortField, string? sortOrder)
  897. {
  898. var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  899. {
  900. ["shddh"] = "t.shddh",
  901. ["poBill"] = "t.po_bill",
  902. ["jhshrq"] = "t.jhshrq",
  903. ["shMaterialCode"] = "t.sh_material_code",
  904. ["shMaterialName"] = "t.sh_material_name",
  905. ["shDeliveryQuantity"] = "t.sh_delivery_quantity",
  906. ["sfpc"] = "t.sfpc",
  907. ["pcrksl"] = "t.pcrksl",
  908. ["shPurchaseName"] = "t.sh_purchase_name",
  909. ["shpc"] = "t.shpc",
  910. ["scph"] = "t.scph",
  911. ["wldh"] = "t.wldh",
  912. ["dycs"] = "t.dycs",
  913. ["shzt"] = "t.shzt"
  914. };
  915. var field = map.TryGetValue(sortField ?? string.Empty, out var sqlField) ? sqlField : "t.mid";
  916. var order = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  917. return $" ORDER BY {field} {order} ";
  918. }
  919. private static string BuildListSql() => """
  920. SELECT
  921. m.mid,
  922. m.id,
  923. m.sh_purchase_id,
  924. m.sh_purchase_name,
  925. m.sh_purchase_num,
  926. m.sh_purchase_address,
  927. m.sh_purchase_lxr,
  928. m.sh_purchase_phone,
  929. m.client,
  930. m.delivery_Address,
  931. m.expected_consignee,
  932. m.consignee_phone,
  933. m.estimated_delivery_date,
  934. m.po_billno,
  935. m.shddh,
  936. m.jhshrq,
  937. m.tjrid,
  938. m.tjrxm,
  939. m.tjrq,
  940. m.scbq,
  941. m.chbg,
  942. m.sfpc,
  943. m.pcsm,
  944. m.wlsc,
  945. m.yjdhrq,
  946. m.state,
  947. m.shzt,
  948. m.wldh,
  949. m.dycs,
  950. m.gys,
  951. m.sh_material_code,
  952. m.sh_material_name,
  953. m.sh_material_ggxh,
  954. m.hh,
  955. m.po_bill,
  956. m.shpc,
  957. m.scph,
  958. m.sh_delivery_quantity,
  959. m.th,
  960. n.rksl,
  961. n.bhgsl,
  962. n.thsl,
  963. n.zshl,
  964. n.Delivery,
  965. n.pc,
  966. l.pcrksl,
  967. po.potype,
  968. po.Usage
  969. FROM
  970. (
  971. SELECT
  972. a.id mid,
  973. b.id,
  974. a.sh_purchase_id,
  975. a.sh_purchase_name,
  976. a.sh_purchase_num,
  977. a.sh_purchase_address,
  978. a.sh_purchase_lxr,
  979. a.sh_purchase_phone,
  980. a.client,
  981. a.delivery_Address,
  982. a.expected_consignee,
  983. a.consignee_phone,
  984. a.estimated_delivery_date,
  985. a.po_billno,
  986. a.shddh,
  987. a.jhshrq,
  988. a.tjrid,
  989. a.tjrxm,
  990. a.tjrq,
  991. a.scbq,
  992. a.chbg,
  993. a.sfpc,
  994. a.pcsm,
  995. a.wlsc,
  996. a.yjdhrq,
  997. a.state,
  998. IFNULL(a.shzt, '待收') shzt,
  999. a.wldh,
  1000. a.dycs,
  1001. CONCAT(IFNULL(a.sh_purchase_num, ''), IFNULL(a.sh_purchase_name, '')) gys,
  1002. b.sh_material_code,
  1003. b.sh_material_name,
  1004. b.sh_material_ggxh,
  1005. b.hh,
  1006. b.po_bill,
  1007. c.shpc,
  1008. b.scph,
  1009. b.sh_delivery_quantity,
  1010. b.th
  1011. FROM scm_shd a
  1012. LEFT JOIN scm_shdzb b ON a.id = b.glid
  1013. LEFT JOIN (SELECT DISTINCT glid, shpc FROM scm_shbq) c ON b.id = c.glid
  1014. ) m
  1015. LEFT JOIN
  1016. (
  1017. SELECT
  1018. Delivery,
  1019. LotSerial pc,
  1020. SUM(ReceiptQty) zshl,
  1021. AVG(yssl) rksl,
  1022. SUM(QtyReturn) bhgsl,
  1023. SUM(QtyReturned) thsl
  1024. FROM vscm_cgshrk
  1025. GROUP BY Delivery, LotSerial
  1026. ) n ON m.shddh = n.Delivery AND m.shpc = n.pc
  1027. LEFT JOIN
  1028. (
  1029. SELECT LotSerial, SUM(QtyChange) pcrksl
  1030. FROM InvTransHist
  1031. WHERE QtyChange > 0 AND Reason LIKE '%收货'
  1032. GROUP BY LotSerial
  1033. ) l ON m.shpc = l.LotSerial
  1034. LEFT JOIN PurOrdMaster po ON m.po_bill = po.purord
  1035. """;
  1036. private sealed class SupplierShipmentListRow
  1037. {
  1038. public long Mid { get; set; }
  1039. public long Id { get; set; }
  1040. public string? ShPurchaseName { get; set; }
  1041. public string? ShPurchaseNum { get; set; }
  1042. public string? Shddh { get; set; }
  1043. public string? Jhshrq { get; set; }
  1044. public int? Sfpc { get; set; }
  1045. public string? Wldh { get; set; }
  1046. public int? Dycs { get; set; }
  1047. public string? Shzt { get; set; }
  1048. public string? ShMaterialCode { get; set; }
  1049. public string? ShMaterialName { get; set; }
  1050. public decimal? ShDeliveryQuantity { get; set; }
  1051. public decimal? Pcrksl { get; set; }
  1052. public string? PoBill { get; set; }
  1053. public string? Usage { get; set; }
  1054. public string? Shpc { get; set; }
  1055. public string? Scph { get; set; }
  1056. public string? Th { get; set; }
  1057. public int? State { get; set; }
  1058. }
  1059. private sealed class SupplierShipmentDraftRow
  1060. {
  1061. public string? SourceId { get; set; }
  1062. public string? Gysdm { get; set; }
  1063. public string? Gysmc { get; set; }
  1064. public string? PoBill { get; set; }
  1065. public int? PoBillLine { get; set; }
  1066. public string? OrderType { get; set; }
  1067. public string? ShMaterialCode { get; set; }
  1068. public string? ShMaterialName { get; set; }
  1069. public string? Th { get; set; }
  1070. public decimal? ShDeliveryQuantity { get; set; }
  1071. public decimal? Bzsl { get; set; }
  1072. public decimal? Bqsl { get; set; }
  1073. public string? ShMaterialDw { get; set; }
  1074. public decimal? Djsl { get; set; }
  1075. public string? Jhdbh { get; set; }
  1076. public string? ShMaterialGgxh { get; set; }
  1077. }
  1078. private sealed class SupplierShipmentLabelRow
  1079. {
  1080. public long? Glid { get; set; }
  1081. public string? Wlbm { get; set; }
  1082. public string? Wlmc { get; set; }
  1083. public string? Ggxh { get; set; }
  1084. public decimal? Zxsl { get; set; }
  1085. public string? Dw { get; set; }
  1086. public string? Remarks { get; set; }
  1087. public decimal? Bz { get; set; }
  1088. public string? Ddlx { get; set; }
  1089. public string? Ddh { get; set; }
  1090. public string? Shdh { get; set; }
  1091. public string? Shdhh { get; set; }
  1092. public string? Scrq { get; set; }
  1093. public string? Scph { get; set; }
  1094. public string? Xh { get; set; }
  1095. public string? Gysmc { get; set; }
  1096. public string? Th { get; set; }
  1097. public string? Bbh { get; set; }
  1098. public string? Yt { get; set; }
  1099. public string? Ccrq { get; set; }
  1100. public string? Shpc { get; set; }
  1101. }
  1102. }