|
|
@@ -17,6 +17,8 @@ public class SupplierShipmentService : IDynamicApiController, ITransient
|
|
|
private readonly SqlSugarRepository<ScmShd> _masterRep;
|
|
|
private readonly SqlSugarRepository<ScmShdzb> _detailRep;
|
|
|
private readonly UserManager _userManager;
|
|
|
+ private const int ShpcSerialWidth = 3; // yyMMdd + 3位流水:260508001
|
|
|
+ private const int LabelXhStart = 10001;
|
|
|
|
|
|
public SupplierShipmentService(
|
|
|
ISqlSugarClient db,
|
|
|
@@ -266,8 +268,540 @@ public class SupplierShipmentService : IDynamicApiController, ITransient
|
|
|
|
|
|
[DisplayName("生成标签(预留)")]
|
|
|
[HttpPost("supplier-shipment/generate-label")]
|
|
|
- public Task<object> GenerateLabel([FromBody] SupplierShipmentOperationInput input)
|
|
|
- => Task.FromResult<object>(new { message = $"发货单{input.Id}:功能预留,暂未启用" });
|
|
|
+ public async Task<object> GenerateLabel([FromBody] SupplierShipmentOperationInput input)
|
|
|
+ {
|
|
|
+ if (input.Id <= 0)
|
|
|
+ throw Oops.Oh("缺少发货单ID(id)");
|
|
|
+
|
|
|
+ var userName = _userManager.Account ?? "system";
|
|
|
+
|
|
|
+ var master = await _masterRep.GetFirstAsync(x => x.Id == input.Id) ?? throw Oops.Oh("发货单不存在");
|
|
|
+ if (string.IsNullOrWhiteSpace(master.Shddh))
|
|
|
+ throw Oops.Oh("发货单缺少发货单编号(shddh)");
|
|
|
+
|
|
|
+ var shddh = master.Shddh.Trim();
|
|
|
+ var gysmc = master.ShPurchaseName ?? string.Empty;
|
|
|
+ var gysdm = master.ShPurchaseNum ?? string.Empty;
|
|
|
+
|
|
|
+ // Domain(工厂编码)
|
|
|
+ var domain = await _db.Ado.GetStringAsync(
|
|
|
+ "SELECT Domain FROM GeneralizedCodeMaster WHERE FldName='SystemConfig' AND Val='CompanyCode' LIMIT 1");
|
|
|
+ domain = string.IsNullOrWhiteSpace(domain) ? string.Empty : domain.Trim();
|
|
|
+
|
|
|
+ string? lockKey = null;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var tran = await _db.Ado.UseTranAsync(async () =>
|
|
|
+ {
|
|
|
+ // 1) 归档旧标签
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ INSERT INTO scm_shbqhis
|
|
|
+ (glid, sh_material_code, sh_material_name, sh_material_ggxh, sh_delivery_quantity,
|
|
|
+ sh_material_dw, remarks, bzsl, order_type, po_billno, shdh, shdhh, scrq, scph, xh,
|
|
|
+ gysdm, gysmc, po_billline, bbh, th, yt, ccrq, shpc, jhdbh, jhdhh)
|
|
|
+ SELECT
|
|
|
+ glid, sh_material_code, sh_material_name, sh_material_ggxh, sh_delivery_quantity,
|
|
|
+ sh_material_dw, remarks, bzsl, order_type, po_billno, shdh, shdhh, scrq, scph, xh,
|
|
|
+ gysdm, gysmc, po_billline, bbh, th, yt, ccrq, shpc, jhdbh, jhdhh
|
|
|
+ FROM scm_shbq WHERE shdh=@shdh;
|
|
|
+ """,
|
|
|
+ new List<SugarParameter> { new("@shdh", shddh) });
|
|
|
+
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "DELETE FROM scm_shbq WHERE shdh=@shdh",
|
|
|
+ new List<SugarParameter> { new("@shdh", shddh) });
|
|
|
+
|
|
|
+ // 2) 需要补 shpc 的明细(按物料+供应商批号去重)
|
|
|
+ var needShpc = await _db.Ado.SqlQueryAsync<NeedShpcRow>(
|
|
|
+ """
|
|
|
+ SELECT DISTINCT
|
|
|
+ b.sh_material_code AS wlbm,
|
|
|
+ IFNULL(b.scph, '') AS scph
|
|
|
+ FROM scm_shd a
|
|
|
+ INNER JOIN scm_shdzb b ON a.id = b.glid
|
|
|
+ WHERE a.shddh = @shddh
|
|
|
+ AND IFNULL(b.sh_material_code, '') <> ''
|
|
|
+ AND IFNULL(b.shpc, '') = '';
|
|
|
+ """,
|
|
|
+ new List<SugarParameter> { new("@shddh", shddh) });
|
|
|
+
|
|
|
+ if (needShpc.Count > 0)
|
|
|
+ {
|
|
|
+ // 并发安全:按天加锁 + 取最大流水 + 批量生成
|
|
|
+ var prefix = DateTime.Now.ToString("yyMMdd");
|
|
|
+ lockKey = $"scm_shpc_seq_{prefix}";
|
|
|
+ await AcquireMySqlLockAsync(lockKey, 10);
|
|
|
+
|
|
|
+ var nextSerial = await GetNextDailySerialAsync(prefix, domain);
|
|
|
+ var pairs = needShpc
|
|
|
+ .OrderBy(x => x.Wlbm ?? string.Empty)
|
|
|
+ .ThenBy(x => x.Scph ?? string.Empty)
|
|
|
+ .Select((x, idx) => new
|
|
|
+ {
|
|
|
+ x.Wlbm,
|
|
|
+ x.Scph,
|
|
|
+ Shpc = $"{prefix}{(nextSerial + idx).ToString().PadLeft(ShpcSerialWidth, '0')}"
|
|
|
+ })
|
|
|
+ .ToList();
|
|
|
+
|
|
|
+ // 更新 scm_shdzb.shpc(同一发货单下:物料+供应商批号相同的行统一批次号)
|
|
|
+ foreach (var p in pairs)
|
|
|
+ {
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ UPDATE scm_shdzb b
|
|
|
+ INNER JOIN scm_shd a ON a.id = b.glid
|
|
|
+ SET b.shpc = @shpc
|
|
|
+ WHERE a.shddh = @shddh
|
|
|
+ AND b.sh_material_code = @wlbm
|
|
|
+ AND IFNULL(b.scph, '') = IFNULL(@scph, '')
|
|
|
+ AND IFNULL(b.shpc, '') = '';
|
|
|
+ """,
|
|
|
+ new List<SugarParameter>
|
|
|
+ {
|
|
|
+ new("@shpc", p.Shpc),
|
|
|
+ new("@shddh", shddh),
|
|
|
+ new("@wlbm", p.Wlbm ?? string.Empty),
|
|
|
+ new("@scph", p.Scph ?? string.Empty),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 兼容:如果线上存在 scm_shdshph,则同步写入(仓库脚本里未包含该表)
|
|
|
+ var shdshphExists = await TableExistsAsync("scm_shdshph");
|
|
|
+ if (shdshphExists)
|
|
|
+ {
|
|
|
+ foreach (var p in pairs)
|
|
|
+ {
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ INSERT INTO scm_shdshph (shpc, wlbm, scph, shdh, xh, gysdm, create_time)
|
|
|
+ SELECT @shpc, @wlbm, @scph, @shdh, @xh, @gysdm, NOW()
|
|
|
+ FROM DUAL
|
|
|
+ WHERE NOT EXISTS (
|
|
|
+ SELECT 1 FROM scm_shdshph
|
|
|
+ WHERE shdh=@shdh AND wlbm=@wlbm AND IFNULL(scph,'')=IFNULL(@scph,'')
|
|
|
+ );
|
|
|
+ """,
|
|
|
+ new List<SugarParameter>
|
|
|
+ {
|
|
|
+ new("@shpc", p.Shpc),
|
|
|
+ new("@wlbm", p.Wlbm ?? string.Empty),
|
|
|
+ new("@scph", p.Scph ?? string.Empty),
|
|
|
+ new("@shdh", shddh),
|
|
|
+ new("@xh", p.Shpc),
|
|
|
+ new("@gysdm", gysdm),
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 推进 NbrControl.NextValue,确保续号(仅当没有历史 shpc 且使用了 NbrControl 作为起点时也能正确续)
|
|
|
+ // 这里直接推进到“本批次最后一个流水号”,下次取号将从 NextValue+1 开始。
|
|
|
+ var lastSerial = nextSerial + pairs.Count - 1;
|
|
|
+ await BumpNbrControlNextValueAsync(domain, lastSerial);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3) 补 jhdhh(同一发货单:为空的明细按 id 递增,起始 max(jhdhh) or 1000)
|
|
|
+ var shdid = await _db.Ado.GetLongAsync(
|
|
|
+ "SELECT id FROM scm_shd WHERE shddh=@shddh LIMIT 1",
|
|
|
+ new List<SugarParameter> { new("@shddh", shddh) });
|
|
|
+
|
|
|
+ var startNo = await _db.Ado.GetIntAsync(
|
|
|
+ "SELECT IFNULL(MAX(CAST(IFNULL(jhdhh,'') AS SIGNED)), 1000) FROM scm_shdzb WHERE glid=@glid",
|
|
|
+ new List<SugarParameter> { new("@glid", shdid.ToString()) });
|
|
|
+
|
|
|
+ var jhdhhNeed = await _db.Ado.SqlQueryAsync<IdOnlyRow>(
|
|
|
+ "SELECT id FROM scm_shdzb WHERE glid=@glid AND IFNULL(jhdhh,'')='' ORDER BY id",
|
|
|
+ new List<SugarParameter> { new("@glid", shdid.ToString()) });
|
|
|
+
|
|
|
+ var seq = startNo;
|
|
|
+ foreach (var r in jhdhhNeed)
|
|
|
+ {
|
|
|
+ seq++;
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "UPDATE scm_shdzb SET jhdhh=@jhdhh WHERE id=@id",
|
|
|
+ new List<SugarParameter>
|
|
|
+ {
|
|
|
+ new("@jhdhh", seq.ToString()),
|
|
|
+ new("@id", r.Id)
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4) 生成 scm_shbq(按 bqsl 拆分;每箱 bzsl,最后一箱余数)
|
|
|
+ var sourceRows = await _db.Ado.SqlQueryAsync<LabelSourceRow>(
|
|
|
+ """
|
|
|
+ SELECT
|
|
|
+ a.shddh AS shddh,
|
|
|
+ b.id AS detailId,
|
|
|
+ b.glid AS glid,
|
|
|
+ b.sh_material_code AS wlbm,
|
|
|
+ b.sh_material_name AS wlmc,
|
|
|
+ b.sh_material_ggxh AS ggxh,
|
|
|
+ b.sh_delivery_quantity AS shsl,
|
|
|
+ b.sh_material_dw AS dw,
|
|
|
+ b.remarks AS bz,
|
|
|
+ b.bzsl AS bzsl,
|
|
|
+ b.bqsl AS bqsl,
|
|
|
+ b.order_type AS ddlx,
|
|
|
+ b.po_bill AS ddh,
|
|
|
+ CAST(IFNULL(b.po_billline, '0') AS SIGNED) AS po_billline,
|
|
|
+ b.hh AS hh,
|
|
|
+ b.scrq AS scrq,
|
|
|
+ b.scph AS scph,
|
|
|
+ IFNULL(im.Drawing, pd.Drawing) AS th,
|
|
|
+ IFNULL(im.Rev, pd.Rev) AS bbh,
|
|
|
+ a.sh_purchase_name AS gysmc,
|
|
|
+ a.sh_purchase_num AS gysdm,
|
|
|
+ pm.Usage AS usage,
|
|
|
+ b.ccrq AS ccrq,
|
|
|
+ a.jhshrq AS jhshrq,
|
|
|
+ b.jhdbh AS jhdbh,
|
|
|
+ b.jhdhh AS jhdhh,
|
|
|
+ b.shpc AS shpc
|
|
|
+ FROM scm_shd a
|
|
|
+ INNER JOIN scm_shdzb b ON a.id = b.glid
|
|
|
+ LEFT JOIN PurOrdMaster pm ON pm.PurOrd = b.po_bill
|
|
|
+ LEFT JOIN PurOrdDetail pd ON pd.PurOrd = b.po_bill AND pd.Line = b.po_billline
|
|
|
+ LEFT JOIN ItemMaster im ON im.ItemNum = b.sh_material_code
|
|
|
+ WHERE a.shddh = @shddh;
|
|
|
+ """,
|
|
|
+ new List<SugarParameter> { new("@shddh", shddh) });
|
|
|
+
|
|
|
+ foreach (var row in sourceRows)
|
|
|
+ {
|
|
|
+ var totalQty = row.Shsl ?? 0m;
|
|
|
+ var packQty = row.Bzsl ?? 0m;
|
|
|
+ var labelCnt = (int)Math.Max(0, row.Bqsl ?? 0m);
|
|
|
+ if (totalQty <= 0 || labelCnt <= 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ var xh = LabelXhStart;
|
|
|
+ var remainQty = totalQty;
|
|
|
+ var remainLabels = labelCnt;
|
|
|
+
|
|
|
+ // 多箱:前 N-1 箱按包装数量
|
|
|
+ while (remainLabels > 1)
|
|
|
+ {
|
|
|
+ await InsertLabelAsync(shddh, row, packQty, xh, gysdm, gysmc);
|
|
|
+ remainQty -= packQty;
|
|
|
+ remainLabels--;
|
|
|
+ xh++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (remainQty > 0)
|
|
|
+ {
|
|
|
+ await InsertLabelAsync(shddh, row, remainQty, xh, gysdm, gysmc);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5) 更新发货单状态
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "UPDATE scm_shd SET shzt='待收', state=2, dycs=0 WHERE shddh=@shddh",
|
|
|
+ new List<SugarParameter> { new("@shddh", shddh) });
|
|
|
+
|
|
|
+ // 6) MissedPrint:作废旧未打印(U)
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ UPDATE MissedPrint SET
|
|
|
+ PurOrd = CONCAT('作废_', IFNULL(PurOrd, '')),
|
|
|
+ BarCode = CONCAT(CAST(RecID AS CHAR(20)), '_', IFNULL(BarCode, '')),
|
|
|
+ Status = 'C',
|
|
|
+ RelatedBarCode = '',
|
|
|
+ UpdateTime = NOW(),
|
|
|
+ UpdateUser = @u
|
|
|
+ WHERE Domain = @domain
|
|
|
+ AND ShipperNbr = @shddh
|
|
|
+ AND Status = 'U';
|
|
|
+ """,
|
|
|
+ new List<SugarParameter>
|
|
|
+ {
|
|
|
+ new("@u", userName),
|
|
|
+ new("@domain", domain),
|
|
|
+ new("@shddh", shddh),
|
|
|
+ });
|
|
|
+
|
|
|
+ // 7) MissedPrint:插入待打印(U)
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ INSERT INTO MissedPrint
|
|
|
+ (
|
|
|
+ Domain, Site, PrintTime,
|
|
|
+ ItemNum, Descr, Product, Carton, OrdNbr,
|
|
|
+ PackingQty, Qty, Location, Status,
|
|
|
+ Supply, LotSerial, CartonQty, BarCode,
|
|
|
+ MoldNum, SuppLotSerial, ShipperNbr, ShipperLine,
|
|
|
+ ProdDate, PurOrd, PurLine, PurQty, WorkOrd,
|
|
|
+ LabelFormat, StandItem, EffSize, GP12CheckedQty, NetWeight,
|
|
|
+ Remark, CreateTime, UpdateTime, CreateUser, UpdateUser,
|
|
|
+ LevelChar, PurOrdDetBatchNbr, FirmString5, ExpireDate,
|
|
|
+ Printer, Company, Checker, Position
|
|
|
+ )
|
|
|
+ SELECT
|
|
|
+ @domain AS Domain,
|
|
|
+ @domain AS Site,
|
|
|
+ NULL AS PrintTime,
|
|
|
+ s.sh_material_code AS ItemNum,
|
|
|
+ s.sh_material_name AS Descr,
|
|
|
+ s.sh_material_ggxh AS Product,
|
|
|
+ RIGHT(s.xh, 4) AS Carton,
|
|
|
+ s.po_billno AS OrdNbr,
|
|
|
+ s.bzsl AS PackingQty,
|
|
|
+ s.sh_delivery_quantity AS Qty,
|
|
|
+ NULL AS Location,
|
|
|
+ 'U' AS Status,
|
|
|
+ s.gysdm AS Supply,
|
|
|
+ s.shpc AS LotSerial,
|
|
|
+ 1 AS CartonQty,
|
|
|
+ s.xh AS BarCode,
|
|
|
+ NULL AS MoldNum,
|
|
|
+ s.scph AS SuppLotSerial,
|
|
|
+ s.shdh AS ShipperNbr,
|
|
|
+ s.shdhh AS ShipperLine,
|
|
|
+ STR_TO_DATE(s.scrq, '%Y-%m-%d') AS ProdDate,
|
|
|
+ s.po_billno AS PurOrd,
|
|
|
+ s.po_billline AS PurLine,
|
|
|
+ s.sh_delivery_quantity AS PurQty,
|
|
|
+ NULL AS WorkOrd,
|
|
|
+ 'cl01' AS LabelFormat,
|
|
|
+ s.po_billno AS StandItem,
|
|
|
+ s.po_billline AS EffSize,
|
|
|
+ s.sh_delivery_quantity AS GP12CheckedQty,
|
|
|
+ 0 AS NetWeight,
|
|
|
+ s.remarks AS Remark,
|
|
|
+ NOW() AS CreateTime,
|
|
|
+ NOW() AS UpdateTime,
|
|
|
+ @u AS CreateUser,
|
|
|
+ @u AS UpdateUser,
|
|
|
+ s.bbh AS LevelChar,
|
|
|
+ s.jhdbh AS PurOrdDetBatchNbr,
|
|
|
+ p.ActiveRlseID1 AS FirmString5,
|
|
|
+ CASE
|
|
|
+ WHEN i.SuppWarranty = 1 THEN
|
|
|
+ CASE
|
|
|
+ WHEN UPPER(i.WarrantyCode)='D' THEN DATE_ADD(STR_TO_DATE(s.scrq, '%Y-%m-%d'), INTERVAL IFNULL(i.DaysBetweenPM,0) DAY)
|
|
|
+ WHEN UPPER(i.WarrantyCode)='Y' THEN DATE_ADD(STR_TO_DATE(s.scrq, '%Y-%m-%d'), INTERVAL IFNULL(i.DaysBetweenPM,0) YEAR)
|
|
|
+ WHEN UPPER(i.WarrantyCode)='M' THEN DATE_ADD(STR_TO_DATE(s.scrq, '%Y-%m-%d'), INTERVAL IFNULL(i.DaysBetweenPM,0) MONTH)
|
|
|
+ ELSE DATE_ADD(STR_TO_DATE(s.scrq, '%Y-%m-%d'), INTERVAL IFNULL(i.DaysBetweenPM,0) DAY)
|
|
|
+ END
|
|
|
+ ELSE NULL
|
|
|
+ END AS ExpireDate,
|
|
|
+ '' AS Printer,
|
|
|
+ '' AS Company,
|
|
|
+ '' AS Checker,
|
|
|
+ '' AS Position
|
|
|
+ FROM scm_shbq s
|
|
|
+ LEFT JOIN PurOrdDetail p ON p.PurOrd = s.po_billno AND p.Line = s.po_billline
|
|
|
+ LEFT JOIN ItemMaster i ON i.ItemNum = s.sh_material_code
|
|
|
+ WHERE s.shdh = @shddh;
|
|
|
+ """,
|
|
|
+ new List<SugarParameter>
|
|
|
+ {
|
|
|
+ new("@domain", domain),
|
|
|
+ new("@u", userName),
|
|
|
+ new("@shddh", shddh),
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!tran.IsSuccess)
|
|
|
+ {
|
|
|
+ var msg = $"生成标签失败,{tran.ErrorMessage}";
|
|
|
+ return new { success = false, msg, message = msg };
|
|
|
+ }
|
|
|
+
|
|
|
+ var ok = "生成标签成功!";
|
|
|
+ return new { success = true, msg = ok, message = ok };
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ if (!string.IsNullOrWhiteSpace(lockKey))
|
|
|
+ {
|
|
|
+ await ReleaseMySqlLockAsync(lockKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task InsertLabelAsync(string shddh, LabelSourceRow row, decimal qty, int xh, string gysdm, string gysmc)
|
|
|
+ {
|
|
|
+ var jhdhh3 = Right(row.Jhdhh, 3);
|
|
|
+ var xh4 = Right(xh.ToString(), 4);
|
|
|
+ var bar = $"{shddh}{jhdhh3}{xh4}";
|
|
|
+
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ INSERT INTO scm_shbq
|
|
|
+ (glid, sh_material_code, sh_material_name, sh_material_ggxh, sh_delivery_quantity,
|
|
|
+ sh_material_dw, remarks, bzsl, order_type, po_billno, shdh, shdhh, scrq, scph, xh,
|
|
|
+ gysdm, gysmc, po_billline, bbh, th, yt, ccrq, shpc, jhdbh, jhdhh)
|
|
|
+ VALUES
|
|
|
+ (@glid, @wlbm, @wlmc, @ggxh, @qty,
|
|
|
+ @dw, @bz, @bzsl, @ddlx, @ddh, @shdh, @hh, @scrq, @scph, @xh,
|
|
|
+ @gysdm, @gysmc, @pohh, @bbh, @th, @yt, @ccrq, @shpc, @jhdbh, @jhdhh);
|
|
|
+ """,
|
|
|
+ new List<SugarParameter>
|
|
|
+ {
|
|
|
+ new("@glid", row.DetailId),
|
|
|
+ new("@wlbm", row.Wlbm ?? string.Empty),
|
|
|
+ new("@wlmc", row.Wlmc ?? string.Empty),
|
|
|
+ new("@ggxh", row.Ggxh ?? string.Empty),
|
|
|
+ new("@qty", qty),
|
|
|
+ new("@dw", row.Dw ?? string.Empty),
|
|
|
+ new("@bz", row.Bz ?? string.Empty),
|
|
|
+ new("@bzsl", row.Bzsl ?? 0m),
|
|
|
+ new("@ddlx", row.Ddlx ?? string.Empty),
|
|
|
+ new("@ddh", row.Ddh ?? string.Empty),
|
|
|
+ new("@shdh", shddh),
|
|
|
+ new("@hh", row.Hh ?? 0),
|
|
|
+ new("@scrq", row.Scrq ?? string.Empty),
|
|
|
+ new("@scph", row.Scph ?? string.Empty),
|
|
|
+ new("@xh", bar),
|
|
|
+ new("@gysdm", gysdm),
|
|
|
+ new("@gysmc", gysmc),
|
|
|
+ new("@pohh", row.PoBillLine ?? 0),
|
|
|
+ new("@bbh", row.Bbh ?? string.Empty),
|
|
|
+ new("@th", row.Th ?? string.Empty),
|
|
|
+ new("@yt", row.Usage ?? string.Empty),
|
|
|
+ new("@ccrq", row.Ccrq ?? string.Empty),
|
|
|
+ new("@shpc", row.Shpc ?? string.Empty),
|
|
|
+ new("@jhdbh", row.Jhdbh ?? string.Empty),
|
|
|
+ new("@jhdhh", row.Jhdhh ?? string.Empty),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private static string Right(string? s, int len)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(s)) return new string('0', len);
|
|
|
+ var t = s.Trim();
|
|
|
+ return t.Length <= len ? t.PadLeft(len, '0') : t[^len..];
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task AcquireMySqlLockAsync(string key, int timeoutSeconds)
|
|
|
+ {
|
|
|
+ var ok = await _db.Ado.GetIntAsync("SELECT GET_LOCK(@k, @t)", new List<SugarParameter>
|
|
|
+ {
|
|
|
+ new("@k", key),
|
|
|
+ new("@t", timeoutSeconds),
|
|
|
+ });
|
|
|
+ if (ok != 1)
|
|
|
+ throw Oops.Oh($"生成批次号锁等待超时({key})");
|
|
|
+ }
|
|
|
+
|
|
|
+ private Task ReleaseMySqlLockAsync(string key)
|
|
|
+ => _db.Ado.ExecuteCommandAsync("SELECT RELEASE_LOCK(@k)", new List<SugarParameter> { new("@k", key) });
|
|
|
+
|
|
|
+ private async Task<int> GetNextDailySerialAsync(string prefix, string domain)
|
|
|
+ {
|
|
|
+ // 从已存在的批次号中取当天最大流水:yyMMdd + 3位
|
|
|
+ // 优先 scm_shdzb,其次 scm_shbq(兼容历史数据来源)
|
|
|
+ var max1 = await _db.Ado.GetStringAsync(
|
|
|
+ "SELECT MAX(shpc) FROM scm_shdzb WHERE shpc LIKE @pfx",
|
|
|
+ new List<SugarParameter> { new("@pfx", $"{prefix}%") });
|
|
|
+ var max2 = await _db.Ado.GetStringAsync(
|
|
|
+ "SELECT MAX(shpc) FROM scm_shbq WHERE shpc LIKE @pfx",
|
|
|
+ new List<SugarParameter> { new("@pfx", $"{prefix}%") });
|
|
|
+
|
|
|
+ var max = string.CompareOrdinal(max1 ?? string.Empty, max2 ?? string.Empty) >= 0 ? max1 : max2;
|
|
|
+ if (string.IsNullOrWhiteSpace(max) || max!.Length < prefix.Length + ShpcSerialWidth)
|
|
|
+ {
|
|
|
+ // 兜底:读取 NbrControl(NbrType='WoLot')的 NextValue + 1
|
|
|
+ // 说明:有些环境可能会清理历史批次号表;此时用 NbrControl 保证连续。
|
|
|
+ var nv = await _db.Ado.GetIntAsync(
|
|
|
+ """
|
|
|
+ SELECT IFNULL(NextValue, 0)
|
|
|
+ FROM NbrControl
|
|
|
+ WHERE NbrType='WoLot'
|
|
|
+ AND (
|
|
|
+ domain_code = @d
|
|
|
+ OR Domain = @d
|
|
|
+ OR IFNULL(@d,'') = ''
|
|
|
+ )
|
|
|
+ ORDER BY RecID
|
|
|
+ LIMIT 1
|
|
|
+ """,
|
|
|
+ new List<SugarParameter> { new("@d", domain ?? string.Empty) });
|
|
|
+ // 用户要求:NextValue+1 为下一流水号起点
|
|
|
+ return Math.Max(1, nv + 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ var suffix = max.Substring(prefix.Length);
|
|
|
+ return int.TryParse(suffix, out var n) ? n + 1 : 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task BumpNbrControlNextValueAsync(string domain, int lastSerial)
|
|
|
+ {
|
|
|
+ // 保守推进:仅当新值更大时更新,避免其它流程并发推进导致回退。
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ UPDATE NbrControl
|
|
|
+ SET NextValue = CASE
|
|
|
+ WHEN IFNULL(NextValue, 0) < @v THEN @v
|
|
|
+ ELSE IFNULL(NextValue, 0)
|
|
|
+ END,
|
|
|
+ UpdateTime = NOW()
|
|
|
+ WHERE NbrType = 'WoLot'
|
|
|
+ AND (
|
|
|
+ domain_code = @d
|
|
|
+ OR Domain = @d
|
|
|
+ OR IFNULL(@d,'') = ''
|
|
|
+ );
|
|
|
+ """,
|
|
|
+ new List<SugarParameter>
|
|
|
+ {
|
|
|
+ new("@d", domain ?? string.Empty),
|
|
|
+ new("@v", lastSerial),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<bool> TableExistsAsync(string tableName)
|
|
|
+ {
|
|
|
+ var cnt = await _db.Ado.GetIntAsync(
|
|
|
+ """
|
|
|
+ SELECT COUNT(*)
|
|
|
+ FROM information_schema.tables
|
|
|
+ WHERE table_schema = DATABASE()
|
|
|
+ AND table_name = @t
|
|
|
+ """,
|
|
|
+ new List<SugarParameter> { new("@t", tableName) });
|
|
|
+ return cnt > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class NeedShpcRow
|
|
|
+ {
|
|
|
+ public string? Wlbm { get; set; }
|
|
|
+ public string? Scph { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class IdOnlyRow
|
|
|
+ {
|
|
|
+ public long Id { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class LabelSourceRow
|
|
|
+ {
|
|
|
+ public string? Shddh { get; set; }
|
|
|
+ public long DetailId { get; set; }
|
|
|
+ public string? Glid { get; set; }
|
|
|
+ public string? Wlbm { get; set; }
|
|
|
+ public string? Wlmc { get; set; }
|
|
|
+ public string? Ggxh { get; set; }
|
|
|
+ public decimal? Shsl { get; set; }
|
|
|
+ public string? Dw { get; set; }
|
|
|
+ public string? Bz { get; set; }
|
|
|
+ public decimal? Bzsl { get; set; }
|
|
|
+ public decimal? Bqsl { get; set; }
|
|
|
+ public string? Ddlx { get; set; }
|
|
|
+ public string? Ddh { get; set; }
|
|
|
+ public int? PoBillLine { get; set; }
|
|
|
+ public int? Hh { get; set; }
|
|
|
+ public string? Scrq { get; set; }
|
|
|
+ public string? Scph { get; set; }
|
|
|
+ public string? Th { get; set; }
|
|
|
+ public string? Bbh { get; set; }
|
|
|
+ public string? Usage { get; set; }
|
|
|
+ public string? Ccrq { get; set; }
|
|
|
+ public string? Jhdbh { get; set; }
|
|
|
+ public string? Jhdhh { get; set; }
|
|
|
+ public string? Shpc { get; set; }
|
|
|
+ }
|
|
|
|
|
|
[DisplayName("打印送货单(预留)")]
|
|
|
[HttpPost("supplier-shipment/print-shipping-note")]
|
|
|
@@ -279,6 +813,45 @@ public class SupplierShipmentService : IDynamicApiController, ITransient
|
|
|
public Task<object> PrintLabel([FromBody] SupplierShipmentOperationInput input)
|
|
|
=> Task.FromResult<object>(new { message = $"发货单{input.Id}:功能预留,暂未启用" });
|
|
|
|
|
|
+ [DisplayName("发货单标签数据(scm_shbq)")]
|
|
|
+ [HttpGet("supplier-shipment/label-data")]
|
|
|
+ public async Task<object> GetLabelData([FromQuery] string shddh)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(shddh))
|
|
|
+ throw Oops.Oh("缺少发货单编号(shddh)");
|
|
|
+
|
|
|
+ var pars = new List<SugarParameter> { new("@shddh", shddh.Trim()) };
|
|
|
+ const string sql = """
|
|
|
+ SELECT DISTINCT
|
|
|
+ glid,
|
|
|
+ sh_material_code AS wlbm,
|
|
|
+ sh_material_name AS wlmc,
|
|
|
+ CONCAT(IFNULL(sh_material_name, ''), IFNULL(sh_material_ggxh, '')) AS ggxh,
|
|
|
+ sh_delivery_quantity AS zxsl,
|
|
|
+ sh_material_dw AS dw,
|
|
|
+ remarks,
|
|
|
+ bzsl AS bz,
|
|
|
+ order_type AS ddlx,
|
|
|
+ po_billno AS ddh,
|
|
|
+ shdh AS shdh,
|
|
|
+ shdhh AS shdhh,
|
|
|
+ DATE_FORMAT(scrq, '%Y.%m.%d') AS scrq,
|
|
|
+ scph,
|
|
|
+ xh,
|
|
|
+ gysmc,
|
|
|
+ th,
|
|
|
+ bbh,
|
|
|
+ yt,
|
|
|
+ ccrq,
|
|
|
+ shpc
|
|
|
+ FROM scm_shbq
|
|
|
+ WHERE shdh = @shddh
|
|
|
+ """;
|
|
|
+
|
|
|
+ var list = await _db.Ado.SqlQueryAsync<SupplierShipmentLabelRow>(sql, pars);
|
|
|
+ return new { list };
|
|
|
+ }
|
|
|
+
|
|
|
private async Task SaveDetailsAsync(long masterId, List<SupplierShipmentDetailInput> inputDetails)
|
|
|
{
|
|
|
var dbDetails = await _detailRep.AsQueryable().Where(x => x.Glid == masterId.ToString()).ToListAsync();
|
|
|
@@ -527,5 +1100,30 @@ public class SupplierShipmentService : IDynamicApiController, ITransient
|
|
|
public string? Jhdbh { get; set; }
|
|
|
public string? ShMaterialGgxh { get; set; }
|
|
|
}
|
|
|
+
|
|
|
+ private sealed class SupplierShipmentLabelRow
|
|
|
+ {
|
|
|
+ public long? Glid { get; set; }
|
|
|
+ public string? Wlbm { get; set; }
|
|
|
+ public string? Wlmc { get; set; }
|
|
|
+ public string? Ggxh { get; set; }
|
|
|
+ public decimal? Zxsl { get; set; }
|
|
|
+ public string? Dw { get; set; }
|
|
|
+ public string? Remarks { get; set; }
|
|
|
+ public decimal? Bz { get; set; }
|
|
|
+ public string? Ddlx { get; set; }
|
|
|
+ public string? Ddh { get; set; }
|
|
|
+ public string? Shdh { get; set; }
|
|
|
+ public string? Shdhh { get; set; }
|
|
|
+ public string? Scrq { get; set; }
|
|
|
+ public string? Scph { get; set; }
|
|
|
+ public string? Xh { get; set; }
|
|
|
+ public string? Gysmc { get; set; }
|
|
|
+ public string? Th { get; set; }
|
|
|
+ public string? Bbh { get; set; }
|
|
|
+ public string? Yt { get; set; }
|
|
|
+ public string? Ccrq { get; set; }
|
|
|
+ public string? Shpc { get; set; }
|
|
|
+ }
|
|
|
}
|
|
|
|