|
|
@@ -0,0 +1,1080 @@
|
|
|
+using System.Text.Json;
|
|
|
+using Admin.NET.Plugin.AiDOP.DataPlatform.Executors;
|
|
|
+using Admin.NET.Plugin.AiDOP.Entity.DataPlatform;
|
|
|
+using Microsoft.AspNetCore.Http;
|
|
|
+using Microsoft.Extensions.Logging;
|
|
|
+using SqlSugar;
|
|
|
+
|
|
|
+namespace Admin.NET.Plugin.AiDOP.DataPlatform.Wms;
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// WP4 · 采购链入 165:自建采购单 / 交货计划 / 送货单 → Outbox UPSERT,供 WMS 扫码收货读取。
|
|
|
+/// 旧 DOP 与 WMS 同库,建单即可见;新架构拆库后必须补这段回写。
|
|
|
+/// 范围:Potype='po' 且 ReqBy in ('PO','DO') 的自建单,SAP 下发单不回推。
|
|
|
+/// </summary>
|
|
|
+[ApiDescriptionSettings(Order = 327, Description = "采购链推送 WMS")]
|
|
|
+[Route("api/aidop/wms-purord")]
|
|
|
+[AllowAnonymous]
|
|
|
+[NonUnify]
|
|
|
+public class PurOrdWmsPushService : IDynamicApiController, ITransient
|
|
|
+{
|
|
|
+ public const string TargetSource = "DOPDEMORQ_SQLSERVER";
|
|
|
+ public const string ActionPom = "PO_WMS_POM";
|
|
|
+ public const string ActionPod = "PO_WMS_POD";
|
|
|
+ public const string ActionDs = "PO_WMS_DS";
|
|
|
+ public const string ActionShd = "PO_WMS_SHD";
|
|
|
+ public const string ActionShdzb = "PO_WMS_SHDZB";
|
|
|
+ public const string ActionShph = "PO_WMS_SHPH";
|
|
|
+ public const string ActionMp = "PO_WMS_MP";
|
|
|
+
|
|
|
+ private readonly ISqlSugarClient _db;
|
|
|
+ private readonly MdpOutboxEnqueueService _enqueue;
|
|
|
+ private readonly MdpOutboxWakeSignal _wake;
|
|
|
+ private readonly UserManager _userManager;
|
|
|
+ private readonly IHttpContextAccessor _httpContextAccessor;
|
|
|
+ private readonly ILogger<PurOrdWmsPushService> _logger;
|
|
|
+
|
|
|
+ public PurOrdWmsPushService(
|
|
|
+ ISqlSugarClient db,
|
|
|
+ MdpOutboxEnqueueService enqueue,
|
|
|
+ MdpOutboxWakeSignal wake,
|
|
|
+ UserManager userManager,
|
|
|
+ IHttpContextAccessor httpContextAccessor,
|
|
|
+ ILogger<PurOrdWmsPushService> logger)
|
|
|
+ {
|
|
|
+ _db = db;
|
|
|
+ _enqueue = enqueue;
|
|
|
+ _wake = wake;
|
|
|
+ _userManager = userManager;
|
|
|
+ _httpContextAccessor = httpContextAccessor;
|
|
|
+ _logger = logger;
|
|
|
+ }
|
|
|
+
|
|
|
+ public sealed class PushInput
|
|
|
+ {
|
|
|
+ /// <summary>采购单号。与 Shddh 二选一。</summary>
|
|
|
+ public string? PurOrd { get; set; }
|
|
|
+
|
|
|
+ /// <summary>送货单号。给定时按「PO + 交货计划 + 送货单」整链补推。</summary>
|
|
|
+ public string? Shddh { get; set; }
|
|
|
+
|
|
|
+ public long TenantId { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ public sealed class PushResult
|
|
|
+ {
|
|
|
+ public bool Ok { get; set; }
|
|
|
+ public int Enqueued { get; set; }
|
|
|
+ public int PurOrdCount { get; set; }
|
|
|
+ public int DetailCount { get; set; }
|
|
|
+ public int ScheduleCount { get; set; }
|
|
|
+ public int ShipmentCount { get; set; }
|
|
|
+ public int LabelCount { get; set; }
|
|
|
+ public int BarcodeCount { get; set; }
|
|
|
+ public List<string> Skipped { get; set; } = new();
|
|
|
+ public string? Message { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>补偿 / 联调:按本库快照把采购链推 165。</summary>
|
|
|
+ [DisplayName("推送采购链到 WMS")]
|
|
|
+ [HttpPost("push")]
|
|
|
+ public async Task<PushResult> Push([FromBody] PushInput input, CancellationToken ct = default)
|
|
|
+ {
|
|
|
+ if (input == null) throw Oops.Oh("请求体不能为空");
|
|
|
+ if (string.IsNullOrWhiteSpace(input.PurOrd) && string.IsNullOrWhiteSpace(input.Shddh))
|
|
|
+ throw Oops.Oh("采购单号与送货单号至少给一个");
|
|
|
+ if (input.TenantId <= 0) throw Oops.Oh("租户号无效,请指定有效的 TenantId");
|
|
|
+
|
|
|
+ var jwtTenantId = _userManager.TenantId;
|
|
|
+ if (jwtTenantId > 0 && input.TenantId != jwtTenantId)
|
|
|
+ throw Oops.Oh("请求租户与当前登录租户不一致");
|
|
|
+ if (jwtTenantId <= 0)
|
|
|
+ {
|
|
|
+ var clientIp = _httpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? "unknown";
|
|
|
+ _logger.LogInformation(
|
|
|
+ "[PurOrdWmsPush] 无登录态推送 audit ip={ClientIp} purOrd={PurOrd} shddh={Shddh} tenantId={TenantId}",
|
|
|
+ clientIp, input.PurOrd, input.Shddh, input.TenantId);
|
|
|
+ }
|
|
|
+
|
|
|
+ var result = new PushResult { Ok = true };
|
|
|
+ if (!string.IsNullOrWhiteSpace(input.Shddh))
|
|
|
+ await EnqueueShipmentChainAsync(input.TenantId, input.Shddh.Trim(), result, ct);
|
|
|
+ else
|
|
|
+ await EnqueuePurchaseOrderAsync(input.TenantId, input.PurOrd!.Trim(), result, ct);
|
|
|
+
|
|
|
+ _wake.Pulse();
|
|
|
+ result.Message = $"已入队 {result.Enqueued} 条(采购单 {result.PurOrdCount}/明细 {result.DetailCount}/"
|
|
|
+ + $"交货计划 {result.ScheduleCount}/送货单 {result.ShipmentCount}/装箱标签 {result.LabelCount}/"
|
|
|
+ + $"箱码 {result.BarcodeCount})";
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>业务挂接点用:失败只记日志与 warning,不拖垮本库事务。</summary>
|
|
|
+ public async Task TryEnqueuePurchaseOrderSafeAsync(long tenantId, string purOrd, List<string>? warnings = null)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(purOrd)) return;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var r = new PushResult();
|
|
|
+ await EnqueuePurchaseOrderAsync(tenantId, purOrd.Trim(), r, CancellationToken.None);
|
|
|
+ _wake.Pulse();
|
|
|
+ if (r.Enqueued > 0)
|
|
|
+ warnings?.Add($"采购单 {purOrd} 已入队推送 WMS({r.Enqueued} 条)");
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogWarning(ex, "[PurOrdWmsPush] purOrd enqueue failed tenant={Tenant} po={PurOrd}", tenantId, purOrd);
|
|
|
+ warnings?.Add($"采购单 {purOrd} 推送 WMS 入队失败:{ex.Message}(可调用 /api/aidop/wms-purord/push 补偿)");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>业务挂接点用:交货计划发布/取消后同步 165。</summary>
|
|
|
+ public async Task TryEnqueueSchedulesSafeAsync(long tenantId, List<long> ids, List<string>? warnings = null)
|
|
|
+ {
|
|
|
+ if (ids == null || ids.Count == 0) return;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var r = new PushResult();
|
|
|
+ await EnqueueSchedulesByIdsAsync(tenantId, ids, r, CancellationToken.None);
|
|
|
+ _wake.Pulse();
|
|
|
+ if (r.Enqueued > 0)
|
|
|
+ warnings?.Add($"交货计划已入队推送 WMS({r.Enqueued} 条)");
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogWarning(ex, "[PurOrdWmsPush] ds enqueue failed tenant={Tenant} ids={Count}", tenantId, ids.Count);
|
|
|
+ warnings?.Add($"交货计划推送 WMS 入队失败:{ex.Message}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>业务挂接点用:发货单生成标签后推整链(PO → 交货计划 → 送货单 → 装箱标签)。</summary>
|
|
|
+ public async Task TryEnqueueShipmentChainSafeAsync(long tenantId, string shddh, List<string>? warnings = null)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(shddh)) return;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var r = new PushResult();
|
|
|
+ await EnqueueShipmentChainAsync(tenantId, shddh.Trim(), r, CancellationToken.None);
|
|
|
+ _wake.Pulse();
|
|
|
+ if (r.Enqueued > 0)
|
|
|
+ warnings?.Add($"送货单 {shddh} 及采购链已入队推送 WMS({r.Enqueued} 条)");
|
|
|
+ foreach (var s in r.Skipped) warnings?.Add(s);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogWarning(ex, "[PurOrdWmsPush] shipment enqueue failed tenant={Tenant} shddh={Shddh}", tenantId, shddh);
|
|
|
+ warnings?.Add($"送货单 {shddh} 推送 WMS 入队失败:{ex.Message}(可调用 /api/aidop/wms-purord/push 补偿)");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ── 采购单主表 + 明细 ──────────────────────────────────────────────
|
|
|
+
|
|
|
+ private async Task EnqueuePurchaseOrderAsync(
|
|
|
+ long tenantId, string purOrd, PushResult result, CancellationToken ct)
|
|
|
+ {
|
|
|
+ var masters = await _db.Ado.SqlQueryAsync<PurOrdMasterRow>(
|
|
|
+ """
|
|
|
+ SELECT
|
|
|
+ TRIM(IFNULL(`Domain`, '')) AS Domain,
|
|
|
+ TRIM(IFNULL(PurOrd, '')) AS PurOrd,
|
|
|
+ TRIM(IFNULL(Potype, '')) AS Potype,
|
|
|
+ TRIM(IFNULL(Typed, '')) AS Typed,
|
|
|
+ TRIM(IFNULL(Supp, '')) AS Supp,
|
|
|
+ TRIM(IFNULL(Buyer, '')) AS Buyer,
|
|
|
+ TRIM(IFNULL(ReqBy, '')) AS ReqBy,
|
|
|
+ TRIM(IFNULL(`Usage`, '')) AS UsageText,
|
|
|
+ TRIM(IFNULL(FSTID, '')) AS FSTID,
|
|
|
+ TRIM(IFNULL(Remark, '')) AS Remark,
|
|
|
+ OrdDate, DueDate,
|
|
|
+ IFNULL(IsActive, 1) AS IsActive,
|
|
|
+ IFNULL(IsConfirm, 0) AS IsConfirm,
|
|
|
+ CreateUser, CreateTime, UpdateUser, UpdateTime
|
|
|
+ FROM PurOrdMaster
|
|
|
+ WHERE tenant_id = @TenantId AND PurOrd = @PurOrd
|
|
|
+ LIMIT 1
|
|
|
+ """,
|
|
|
+ new SugarParameter("@TenantId", tenantId),
|
|
|
+ new SugarParameter("@PurOrd", purOrd));
|
|
|
+ if (masters.Count == 0)
|
|
|
+ throw Oops.Oh($"本库未找到采购单 {purOrd}");
|
|
|
+ var m = masters[0];
|
|
|
+
|
|
|
+ if (!IsSelfBuilt(m.Potype, m.ReqBy))
|
|
|
+ {
|
|
|
+ result.Skipped.Add($"采购单 {purOrd} 非自建单(Potype={m.Potype} / ReqBy={m.ReqBy}),按约定不回推 165");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var domain = string.IsNullOrWhiteSpace(m.Domain) ? "8010" : m.Domain;
|
|
|
+ var potype = string.IsNullOrWhiteSpace(m.Potype) ? "po" : m.Potype;
|
|
|
+ var now = DateTime.Now;
|
|
|
+
|
|
|
+ var keys = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["Domain"] = domain,
|
|
|
+ ["PurOrd"] = purOrd,
|
|
|
+ ["Potype"] = potype
|
|
|
+ };
|
|
|
+ var insert = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["Domain"] = domain,
|
|
|
+ ["PurOrd"] = Trunc(purOrd, 48),
|
|
|
+ ["Potype"] = Trunc(potype, 8),
|
|
|
+ ["Typed"] = Trunc(m.Typed, 18),
|
|
|
+ ["Supp"] = Trunc(m.Supp, 20),
|
|
|
+ ["Buyer"] = Trunc(m.Buyer, 30),
|
|
|
+ ["ReqBy"] = Trunc(m.ReqBy, 8),
|
|
|
+ // Status 只建行写空串;收货态由 WMS 维护
|
|
|
+ ["Status"] = "",
|
|
|
+ ["Usage"] = Trunc(m.UsageText, 30),
|
|
|
+ ["FSTID"] = Trunc(m.FSTID, 24),
|
|
|
+ ["Remark"] = Trunc(m.Remark, 200),
|
|
|
+ ["IsActive"] = m.IsActive != 0,
|
|
|
+ ["IsConfirm"] = m.IsConfirm != 0,
|
|
|
+ ["CreateUser"] = Trunc(m.CreateUser ?? "aidop", 24),
|
|
|
+ ["UpdateUser"] = Trunc(m.UpdateUser ?? m.CreateUser ?? "aidop", 24),
|
|
|
+ ["CreateTime"] = Fmt(m.CreateTime ?? now),
|
|
|
+ ["UpdateTime"] = Fmt(m.UpdateTime ?? now)
|
|
|
+ };
|
|
|
+ if (m.OrdDate != null) insert["OrdDate"] = Fmt(m.OrdDate);
|
|
|
+ if (m.DueDate != null) insert["DueDate"] = Fmt(m.DueDate);
|
|
|
+
|
|
|
+ var update = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["Typed"] = Trunc(m.Typed, 18),
|
|
|
+ ["Supp"] = Trunc(m.Supp, 20),
|
|
|
+ ["Buyer"] = Trunc(m.Buyer, 30),
|
|
|
+ ["ReqBy"] = Trunc(m.ReqBy, 8),
|
|
|
+ ["Usage"] = Trunc(m.UsageText, 30),
|
|
|
+ ["FSTID"] = Trunc(m.FSTID, 24),
|
|
|
+ ["Remark"] = Trunc(m.Remark, 200),
|
|
|
+ ["IsActive"] = m.IsActive != 0,
|
|
|
+ ["UpdateUser"] = Trunc(m.UpdateUser ?? "aidop", 24),
|
|
|
+ ["UpdateTime"] = Fmt(now)
|
|
|
+ };
|
|
|
+ if (m.DueDate != null) update["DueDate"] = Fmt(m.DueDate);
|
|
|
+
|
|
|
+ if (await EnqueueRowAsync(tenantId, $"po|{domain}|{potype}|{purOrd}|pom",
|
|
|
+ ActionPom, "PurOrdMaster", keys, insert, update, null, ct))
|
|
|
+ result.Enqueued++;
|
|
|
+ result.PurOrdCount++;
|
|
|
+
|
|
|
+ var details = await _db.Ado.SqlQueryAsync<PurOrdDetailRow>(
|
|
|
+ """
|
|
|
+ SELECT
|
|
|
+ TRIM(IFNULL(`Domain`, '')) AS Domain,
|
|
|
+ IFNULL(Line, 0) AS Line,
|
|
|
+ TRIM(IFNULL(ItemNum, '')) AS ItemNum,
|
|
|
+ TRIM(IFNULL(Descr, '')) AS Descr,
|
|
|
+ TRIM(IFNULL(UM, '')) AS UM,
|
|
|
+ TRIM(IFNULL(Rev, '')) AS Rev,
|
|
|
+ TRIM(IFNULL(Drawing, '')) AS Drawing,
|
|
|
+ TRIM(IFNULL(Location, '')) AS Location,
|
|
|
+ TRIM(IFNULL(LotSerial, '')) AS LotSerial,
|
|
|
+ TRIM(IFNULL(Req, '')) AS Req,
|
|
|
+ IFNULL(QtyOrded, 0) AS QtyOrded,
|
|
|
+ DueDate, NeedDate,
|
|
|
+ IFNULL(IsActive, 1) AS IsActive,
|
|
|
+ IFNULL(IsConfirm, 0) AS IsConfirm,
|
|
|
+ CreateUser, CreateTime, UpdateUser, UpdateTime
|
|
|
+ FROM PurOrdDetail
|
|
|
+ WHERE tenant_id = @TenantId AND PurOrd = @PurOrd
|
|
|
+ ORDER BY Line
|
|
|
+ """,
|
|
|
+ new SugarParameter("@TenantId", tenantId),
|
|
|
+ new SugarParameter("@PurOrd", purOrd));
|
|
|
+
|
|
|
+ foreach (var d in details)
|
|
|
+ {
|
|
|
+ var dKeys = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["Domain"] = domain,
|
|
|
+ ["PurOrd"] = purOrd,
|
|
|
+ ["Potype"] = potype,
|
|
|
+ ["Line"] = d.Line,
|
|
|
+ ["BlanketLine"] = 0
|
|
|
+ };
|
|
|
+ var dInsert = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["Domain"] = domain,
|
|
|
+ ["PurOrd"] = Trunc(purOrd, 48),
|
|
|
+ ["Potype"] = Trunc(potype, 8),
|
|
|
+ ["Line"] = d.Line,
|
|
|
+ ["BlanketLine"] = 0,
|
|
|
+ ["ItemNum"] = Trunc(d.ItemNum, 60),
|
|
|
+ ["Descr"] = Trunc(d.Descr, 255),
|
|
|
+ ["UM"] = Trunc(d.UM, 8),
|
|
|
+ ["Rev"] = Trunc(d.Rev, 8),
|
|
|
+ ["Drawing"] = Trunc(d.Drawing, 24),
|
|
|
+ ["Location"] = Trunc(d.Location, 8),
|
|
|
+ ["LotSerial"] = Trunc(d.LotSerial, 120),
|
|
|
+ ["Req"] = Trunc(d.Req, 20),
|
|
|
+ ["QtyOrded"] = d.QtyOrded,
|
|
|
+ // 收货累计列建行给 0,之后归 WMS
|
|
|
+ ["RctQty"] = 0m,
|
|
|
+ ["ReceiptQty"] = 0m,
|
|
|
+ ["QtyReturned"] = 0m,
|
|
|
+ ["Status"] = "",
|
|
|
+ ["IsActive"] = d.IsActive != 0,
|
|
|
+ ["IsConfirm"] = d.IsConfirm != 0,
|
|
|
+ ["CreateUser"] = Trunc(d.CreateUser ?? "aidop", 24),
|
|
|
+ ["UpdateUser"] = Trunc(d.UpdateUser ?? d.CreateUser ?? "aidop", 24),
|
|
|
+ ["CreateTime"] = Fmt(d.CreateTime ?? now),
|
|
|
+ ["UpdateTime"] = Fmt(d.UpdateTime ?? now)
|
|
|
+ };
|
|
|
+ if (d.DueDate != null) dInsert["DueDate"] = Fmt(d.DueDate);
|
|
|
+ if (d.NeedDate != null) dInsert["NeedDate"] = Fmt(d.NeedDate);
|
|
|
+
|
|
|
+ var dUpdate = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["ItemNum"] = Trunc(d.ItemNum, 60),
|
|
|
+ ["Descr"] = Trunc(d.Descr, 255),
|
|
|
+ ["UM"] = Trunc(d.UM, 8),
|
|
|
+ ["Rev"] = Trunc(d.Rev, 8),
|
|
|
+ ["Drawing"] = Trunc(d.Drawing, 24),
|
|
|
+ ["Location"] = Trunc(d.Location, 8),
|
|
|
+ ["QtyOrded"] = d.QtyOrded,
|
|
|
+ ["IsActive"] = d.IsActive != 0,
|
|
|
+ ["UpdateUser"] = Trunc(d.UpdateUser ?? "aidop", 24),
|
|
|
+ ["UpdateTime"] = Fmt(now)
|
|
|
+ };
|
|
|
+ if (d.DueDate != null) dUpdate["DueDate"] = Fmt(d.DueDate);
|
|
|
+ if (d.NeedDate != null) dUpdate["NeedDate"] = Fmt(d.NeedDate);
|
|
|
+
|
|
|
+ // 165 的 RecID 是 IDENTITY,明细外键必须在目标库现查主表 RecID
|
|
|
+ var resolve = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["PurOrdRecID"] = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["table"] = "PurOrdMaster",
|
|
|
+ ["column"] = "RecID",
|
|
|
+ ["match"] = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["Domain"] = domain,
|
|
|
+ ["PurOrd"] = purOrd,
|
|
|
+ ["Potype"] = potype
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ if (await EnqueueRowAsync(tenantId, $"po|{domain}|{potype}|{purOrd}|pod|{d.Line}",
|
|
|
+ ActionPod, "PurOrdDetail", dKeys, dInsert, dUpdate, resolve, ct))
|
|
|
+ result.Enqueued++;
|
|
|
+ result.DetailCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ── 交货计划 ──────────────────────────────────────────────────────
|
|
|
+
|
|
|
+ private async Task EnqueueSchedulesByIdsAsync(
|
|
|
+ long tenantId, List<long> ids, PushResult result, CancellationToken ct)
|
|
|
+ {
|
|
|
+ var rows = await _db.Ado.SqlQueryAsync<DeliveryScheduleRow>(
|
|
|
+ $"""
|
|
|
+ SELECT
|
|
|
+ Id, TRIM(IFNULL(domain, '')) AS Domain, IFNULL(icdsid, 0) AS Icdsid,
|
|
|
+ TRIM(IFNULL(dsnum, '')) AS Dsnum, TRIM(IFNULL(status, '')) AS Status,
|
|
|
+ TRIM(IFNULL(itemnum, '')) AS Itemnum, TRIM(IFNULL(um, '')) AS Um,
|
|
|
+ TRIM(IFNULL(purgroup, '')) AS Purgroup,
|
|
|
+ TRIM(IFNULL(suppliercode, '')) AS Suppliercode, TRIM(IFNULL(supplier, '')) AS Supplier,
|
|
|
+ submitdate, requestdate, needdate,
|
|
|
+ TRIM(IFNULL(ponumber, '')) AS Ponumber, IFNULL(poline, 0) AS Poline,
|
|
|
+ IFNULL(schedqty, 0) AS Schedqty,
|
|
|
+ TRIM(IFNULL(remarks, '')) AS Remarks, IFNULL(isactive, 1) AS Isactive,
|
|
|
+ createuser, createtime, updateuser, updatetime
|
|
|
+ FROM srm_polist_ds
|
|
|
+ WHERE tenant_id = @TenantId AND Id IN ({string.Join(",", ids.Select((_, i) => $"@i{i}"))})
|
|
|
+ """,
|
|
|
+ ids.Select((v, i) => new SugarParameter($"@i{i}", v))
|
|
|
+ .Append(new SugarParameter("@TenantId", tenantId))
|
|
|
+ .ToList());
|
|
|
+
|
|
|
+ foreach (var ds in rows)
|
|
|
+ await EnqueueScheduleRowAsync(tenantId, ds, result, ct);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task EnqueueSchedulesByPurOrdAsync(
|
|
|
+ long tenantId, string purOrd, PushResult result, CancellationToken ct)
|
|
|
+ {
|
|
|
+ var rows = await _db.Ado.SqlQueryAsync<DeliveryScheduleRow>(
|
|
|
+ """
|
|
|
+ SELECT
|
|
|
+ Id, TRIM(IFNULL(domain, '')) AS Domain, IFNULL(icdsid, 0) AS Icdsid,
|
|
|
+ TRIM(IFNULL(dsnum, '')) AS Dsnum, TRIM(IFNULL(status, '')) AS Status,
|
|
|
+ TRIM(IFNULL(itemnum, '')) AS Itemnum, TRIM(IFNULL(um, '')) AS Um,
|
|
|
+ TRIM(IFNULL(purgroup, '')) AS Purgroup,
|
|
|
+ TRIM(IFNULL(suppliercode, '')) AS Suppliercode, TRIM(IFNULL(supplier, '')) AS Supplier,
|
|
|
+ submitdate, requestdate, needdate,
|
|
|
+ TRIM(IFNULL(ponumber, '')) AS Ponumber, IFNULL(poline, 0) AS Poline,
|
|
|
+ IFNULL(schedqty, 0) AS Schedqty,
|
|
|
+ TRIM(IFNULL(remarks, '')) AS Remarks, IFNULL(isactive, 1) AS Isactive,
|
|
|
+ createuser, createtime, updateuser, updatetime
|
|
|
+ FROM srm_polist_ds
|
|
|
+ WHERE tenant_id = @TenantId AND ponumber = @PurOrd AND IFNULL(isactive, 1) = 1
|
|
|
+ ORDER BY poline
|
|
|
+ """,
|
|
|
+ new SugarParameter("@TenantId", tenantId),
|
|
|
+ new SugarParameter("@PurOrd", purOrd));
|
|
|
+
|
|
|
+ foreach (var ds in rows)
|
|
|
+ await EnqueueScheduleRowAsync(tenantId, ds, result, ct);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task EnqueueScheduleRowAsync(
|
|
|
+ long tenantId, DeliveryScheduleRow ds, PushResult result, CancellationToken ct)
|
|
|
+ {
|
|
|
+ var domain = string.IsNullOrWhiteSpace(ds.Domain) ? "8010" : ds.Domain;
|
|
|
+ var now = DateTime.Now;
|
|
|
+
|
|
|
+ var keys = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["domain"] = domain,
|
|
|
+ ["dsnum"] = ds.Dsnum
|
|
|
+ };
|
|
|
+ var insert = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["Id"] = ds.Id,
|
|
|
+ ["domain"] = domain,
|
|
|
+ ["icdsid"] = ds.Icdsid,
|
|
|
+ ["dsnum"] = Trunc(ds.Dsnum, 128),
|
|
|
+ ["status"] = Trunc(ds.Status, 10),
|
|
|
+ ["itemnum"] = Trunc(ds.Itemnum, 128),
|
|
|
+ ["um"] = Trunc(ds.Um, 124),
|
|
|
+ ["purgroup"] = Trunc(ds.Purgroup, 50),
|
|
|
+ ["suppliercode"] = Trunc(ds.Suppliercode, 50),
|
|
|
+ ["supplier"] = Trunc(ds.Supplier, 50),
|
|
|
+ ["ponumber"] = Trunc(ds.Ponumber, 50),
|
|
|
+ ["poline"] = ds.Poline,
|
|
|
+ ["schedqty"] = ds.Schedqty,
|
|
|
+ // 收货累计列建行给 0 / 全额待交,之后归 WMS
|
|
|
+ ["lastsentqty"] = 0m,
|
|
|
+ ["sentqty"] = 0m,
|
|
|
+ ["restqty"] = ds.Schedqty,
|
|
|
+ ["remarks"] = Trunc(ds.Remarks, 500),
|
|
|
+ ["isactive"] = ds.Isactive,
|
|
|
+ ["createuser"] = Trunc(ds.Createuser ?? "aidop", 24),
|
|
|
+ ["updateuser"] = Trunc(ds.Updateuser ?? ds.Createuser ?? "aidop", 24),
|
|
|
+ ["createtime"] = Fmt(ds.Createtime ?? now),
|
|
|
+ ["updatetime"] = Fmt(ds.Updatetime ?? now)
|
|
|
+ };
|
|
|
+ if (ds.Submitdate != null) insert["submitdate"] = Fmt(ds.Submitdate);
|
|
|
+ if (ds.Requestdate != null) insert["requestdate"] = Fmt(ds.Requestdate);
|
|
|
+ if (ds.Needdate != null) insert["needdate"] = Fmt(ds.Needdate);
|
|
|
+
|
|
|
+ // sentqty / restqty / lastsentdate 归 WMS,更新时一律不碰
|
|
|
+ var update = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["status"] = Trunc(ds.Status, 10),
|
|
|
+ ["schedqty"] = ds.Schedqty,
|
|
|
+ ["isactive"] = ds.Isactive,
|
|
|
+ ["remarks"] = Trunc(ds.Remarks, 500),
|
|
|
+ ["updateuser"] = Trunc(ds.Updateuser ?? "aidop", 24),
|
|
|
+ ["updatetime"] = Fmt(now)
|
|
|
+ };
|
|
|
+ if (ds.Needdate != null) update["needdate"] = Fmt(ds.Needdate);
|
|
|
+ if (ds.Submitdate != null) update["submitdate"] = Fmt(ds.Submitdate);
|
|
|
+
|
|
|
+ if (await EnqueueRowAsync(tenantId, $"po|{domain}|ds|{ds.Dsnum}",
|
|
|
+ ActionDs, "srm_polist_ds", keys, insert, update, null, ct))
|
|
|
+ result.Enqueued++;
|
|
|
+ result.ScheduleCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ── 送货单 + 明细 + 装箱标签 ───────────────────────────────────────
|
|
|
+
|
|
|
+ private async Task EnqueueShipmentChainAsync(
|
|
|
+ long tenantId, string shddh, PushResult result, CancellationToken ct)
|
|
|
+ {
|
|
|
+ var masters = await _db.Ado.SqlQueryAsync<ShipmentRow>(
|
|
|
+ """
|
|
|
+ SELECT
|
|
|
+ id AS Id,
|
|
|
+ IFNULL(sh_purchase_id, 0) AS ShPurchaseId,
|
|
|
+ TRIM(IFNULL(sh_purchase_name, '')) AS ShPurchaseName,
|
|
|
+ TRIM(IFNULL(sh_purchase_num, '')) AS ShPurchaseNum,
|
|
|
+ TRIM(IFNULL(sh_purchase_address, '')) AS ShPurchaseAddress,
|
|
|
+ TRIM(IFNULL(sh_purchase_lxr, '')) AS ShPurchaseLxr,
|
|
|
+ TRIM(IFNULL(sh_purchase_phone, '')) AS ShPurchasePhone,
|
|
|
+ TRIM(IFNULL(delivery_Address, '')) AS DeliveryAddress,
|
|
|
+ TRIM(IFNULL(expected_consignee, '')) AS ExpectedConsignee,
|
|
|
+ TRIM(IFNULL(consignee_phone, '')) AS ConsigneePhone,
|
|
|
+ estimated_delivery_date AS EstimatedDeliveryDate,
|
|
|
+ TRIM(IFNULL(po_billno, '')) AS PoBillno,
|
|
|
+ TRIM(IFNULL(shddh, '')) AS Shddh,
|
|
|
+ TRIM(IFNULL(jhshrq, '')) AS Jhshrq,
|
|
|
+ TRIM(IFNULL(tjrid, '')) AS Tjrid,
|
|
|
+ TRIM(IFNULL(tjrxm, '')) AS Tjrxm,
|
|
|
+ TRIM(IFNULL(tjrq, '')) AS Tjrq,
|
|
|
+ IFNULL(scbq, 0) AS Scbq,
|
|
|
+ TRIM(IFNULL(chbg, '')) AS Chbg,
|
|
|
+ IFNULL(sfpc, 0) AS Sfpc,
|
|
|
+ TRIM(IFNULL(pcsm, '')) AS Pcsm,
|
|
|
+ TRIM(IFNULL(wlsc, '')) AS Wlsc,
|
|
|
+ TRIM(IFNULL(yjdhrq, '')) AS Yjdhrq,
|
|
|
+ IFNULL(state, 0) AS State,
|
|
|
+ TRIM(IFNULL(shzt, '')) AS Shzt,
|
|
|
+ TRIM(IFNULL(wldh, '')) AS Wldh,
|
|
|
+ IFNULL(dycs, 0) AS Dycs
|
|
|
+ FROM scm_shd
|
|
|
+ WHERE tenant_id = @TenantId AND shddh = @Shddh
|
|
|
+ LIMIT 1
|
|
|
+ """,
|
|
|
+ new SugarParameter("@TenantId", tenantId),
|
|
|
+ new SugarParameter("@Shddh", shddh));
|
|
|
+ if (masters.Count == 0)
|
|
|
+ throw Oops.Oh($"本库未找到送货单 {shddh}");
|
|
|
+ var s = masters[0];
|
|
|
+
|
|
|
+ var lines = await _db.Ado.SqlQueryAsync<ShipmentLineRow>(
|
|
|
+ """
|
|
|
+ SELECT
|
|
|
+ id AS Id, TRIM(IFNULL(glid, '')) AS Glid,
|
|
|
+ TRIM(IFNULL(sh_material_code, '')) AS ShMaterialCode,
|
|
|
+ TRIM(IFNULL(sh_material_name, '')) AS ShMaterialName,
|
|
|
+ TRIM(IFNULL(sh_material_ggxh, '')) AS ShMaterialGgxh,
|
|
|
+ IFNULL(sh_delivery_quantity, 0) AS ShDeliveryQuantity,
|
|
|
+ TRIM(IFNULL(sh_material_dw, '')) AS ShMaterialDw,
|
|
|
+ TRIM(IFNULL(remarks, '')) AS Remarks,
|
|
|
+ IFNULL(bzsl, 0) AS Bzsl, IFNULL(bqsl, 0) AS Bqsl,
|
|
|
+ TRIM(IFNULL(order_type, '')) AS OrderType,
|
|
|
+ TRIM(IFNULL(po_bill, '')) AS PoBill, TRIM(IFNULL(po_billline, '')) AS PoBillline,
|
|
|
+ IFNULL(hh, 0) AS Hh,
|
|
|
+ TRIM(IFNULL(scrq, '')) AS Scrq, TRIM(IFNULL(scph, '')) AS Scph,
|
|
|
+ TRIM(IFNULL(th, '')) AS Th, TRIM(IFNULL(bbh, '')) AS Bbh,
|
|
|
+ IFNULL(djsl, 0) AS Djsl,
|
|
|
+ TRIM(IFNULL(ccrq, '')) AS Ccrq, TRIM(IFNULL(cgyt, '')) AS Cgyt,
|
|
|
+ TRIM(IFNULL(jybb, '')) AS Jybb,
|
|
|
+ TRIM(IFNULL(jhdbh, '')) AS Jhdbh, TRIM(IFNULL(jhdhh, '')) AS Jhdhh,
|
|
|
+ TRIM(IFNULL(shpc, '')) AS Shpc, TRIM(IFNULL(shzt, '')) AS Shzt,
|
|
|
+ IFNULL(rksl, 0) AS Rksl, IFNULL(thsl, 0) AS Thsl
|
|
|
+ FROM scm_shdzb
|
|
|
+ WHERE glid = @Glid
|
|
|
+ ORDER BY hh, id
|
|
|
+ """,
|
|
|
+ new SugarParameter("@Glid", s.Id.ToString()));
|
|
|
+
|
|
|
+ var labels = await _db.Ado.SqlQueryAsync<ShipmentLabelRow>(
|
|
|
+ """
|
|
|
+ SELECT
|
|
|
+ id AS Id, TRIM(IFNULL(xh, '')) AS Xh, TRIM(IFNULL(wlbm, '')) AS Wlbm,
|
|
|
+ TRIM(IFNULL(scph, '')) AS Scph, TRIM(IFNULL(shdh, '')) AS Shdh,
|
|
|
+ TRIM(IFNULL(shpc, '')) AS Shpc, TRIM(IFNULL(gysbm, '')) AS Gysbm,
|
|
|
+ TRIM(IFNULL(csrq, '')) AS Csrq
|
|
|
+ FROM scm_shdshph
|
|
|
+ WHERE shdh = @Shddh
|
|
|
+ ORDER BY id
|
|
|
+ """,
|
|
|
+ new SugarParameter("@Shddh", shddh));
|
|
|
+
|
|
|
+ // 先补上游:送货明细引用的采购单与交货计划,否则 WMS 查不到可收行
|
|
|
+ var purOrds = lines.Select(x => (x.PoBill ?? "").Trim())
|
|
|
+ .Where(x => x.Length > 0)
|
|
|
+ .Distinct(StringComparer.OrdinalIgnoreCase)
|
|
|
+ .ToList();
|
|
|
+ foreach (var po in purOrds)
|
|
|
+ {
|
|
|
+ await EnqueuePurchaseOrderAsync(tenantId, po, result, ct);
|
|
|
+ await EnqueueSchedulesByPurOrdAsync(tenantId, po, result, ct);
|
|
|
+ }
|
|
|
+
|
|
|
+ var now = DateTime.Now;
|
|
|
+ var sInsert = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["id"] = s.Id,
|
|
|
+ ["sh_purchase_id"] = s.ShPurchaseId,
|
|
|
+ ["sh_purchase_name"] = Trunc(s.ShPurchaseName, 255),
|
|
|
+ ["sh_purchase_num"] = Trunc(s.ShPurchaseNum, 255),
|
|
|
+ ["sh_purchase_address"] = Trunc(s.ShPurchaseAddress, 255),
|
|
|
+ ["sh_purchase_lxr"] = Trunc(s.ShPurchaseLxr, 255),
|
|
|
+ ["sh_purchase_phone"] = Trunc(s.ShPurchasePhone, 255),
|
|
|
+ ["delivery_Address"] = Trunc(s.DeliveryAddress, 255),
|
|
|
+ ["expected_consignee"] = Trunc(s.ExpectedConsignee, 255),
|
|
|
+ ["consignee_phone"] = Trunc(s.ConsigneePhone, 255),
|
|
|
+ ["po_billno"] = Trunc(s.PoBillno, 255),
|
|
|
+ ["shddh"] = Trunc(s.Shddh, 255),
|
|
|
+ ["jhshrq"] = Trunc(s.Jhshrq, 50),
|
|
|
+ ["tjrid"] = Trunc(s.Tjrid, 50),
|
|
|
+ ["tjrxm"] = Trunc(s.Tjrxm, 50),
|
|
|
+ ["tjrq"] = Trunc(s.Tjrq, 50),
|
|
|
+ ["scbq"] = s.Scbq,
|
|
|
+ ["chbg"] = Trunc(s.Chbg, 255),
|
|
|
+ ["sfpc"] = s.Sfpc,
|
|
|
+ ["pcsm"] = Trunc(s.Pcsm, 255),
|
|
|
+ ["wlsc"] = Trunc(s.Wlsc, 500),
|
|
|
+ ["yjdhrq"] = Trunc(s.Yjdhrq, 500),
|
|
|
+ ["state"] = s.State,
|
|
|
+ ["shzt"] = Trunc(s.Shzt, 50),
|
|
|
+ ["wldh"] = Trunc(s.Wldh, 50),
|
|
|
+ ["dycs"] = s.Dycs
|
|
|
+ };
|
|
|
+ if (s.EstimatedDeliveryDate != null) sInsert["estimated_delivery_date"] = Fmt(s.EstimatedDeliveryDate);
|
|
|
+
|
|
|
+ // shzt / state 收货开始后归 WMS('收货中'),更新时不回写覆盖
|
|
|
+ var sUpdate = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["sh_purchase_name"] = Trunc(s.ShPurchaseName, 255),
|
|
|
+ ["sh_purchase_num"] = Trunc(s.ShPurchaseNum, 255),
|
|
|
+ ["delivery_Address"] = Trunc(s.DeliveryAddress, 255),
|
|
|
+ ["expected_consignee"] = Trunc(s.ExpectedConsignee, 255),
|
|
|
+ ["consignee_phone"] = Trunc(s.ConsigneePhone, 255),
|
|
|
+ ["po_billno"] = Trunc(s.PoBillno, 255),
|
|
|
+ ["jhshrq"] = Trunc(s.Jhshrq, 50),
|
|
|
+ ["wlsc"] = Trunc(s.Wlsc, 500),
|
|
|
+ ["yjdhrq"] = Trunc(s.Yjdhrq, 500)
|
|
|
+ };
|
|
|
+ if (s.EstimatedDeliveryDate != null) sUpdate["estimated_delivery_date"] = Fmt(s.EstimatedDeliveryDate);
|
|
|
+
|
|
|
+ if (await EnqueueRowAsync(tenantId, $"po|shd|{shddh}", ActionShd, "scm_shd",
|
|
|
+ new Dictionary<string, object?> { ["id"] = s.Id }, sInsert, sUpdate, null, ct))
|
|
|
+ result.Enqueued++;
|
|
|
+ result.ShipmentCount++;
|
|
|
+
|
|
|
+ foreach (var l in lines)
|
|
|
+ {
|
|
|
+ var lInsert = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["id"] = l.Id,
|
|
|
+ ["glid"] = Trunc(l.Glid, 255),
|
|
|
+ ["sh_material_code"] = Trunc(l.ShMaterialCode, 255),
|
|
|
+ ["sh_material_name"] = Trunc(l.ShMaterialName, 255),
|
|
|
+ ["sh_material_ggxh"] = Trunc(l.ShMaterialGgxh, 255),
|
|
|
+ ["sh_delivery_quantity"] = l.ShDeliveryQuantity,
|
|
|
+ ["sh_material_dw"] = Trunc(l.ShMaterialDw, 255),
|
|
|
+ ["remarks"] = Trunc(l.Remarks, 255),
|
|
|
+ ["bzsl"] = l.Bzsl,
|
|
|
+ ["bqsl"] = l.Bqsl,
|
|
|
+ ["order_type"] = Trunc(l.OrderType, 255),
|
|
|
+ ["po_bill"] = Trunc(l.PoBill, 255),
|
|
|
+ ["po_billline"] = Trunc(l.PoBillline, 50),
|
|
|
+ ["hh"] = l.Hh,
|
|
|
+ ["scrq"] = Trunc(l.Scrq, 255),
|
|
|
+ ["scph"] = Trunc(l.Scph, 255),
|
|
|
+ ["th"] = Trunc(l.Th, 255),
|
|
|
+ ["bbh"] = Trunc(l.Bbh, 255),
|
|
|
+ ["djsl"] = l.Djsl,
|
|
|
+ ["ccrq"] = Trunc(l.Ccrq, 255),
|
|
|
+ ["cgyt"] = Trunc(l.Cgyt, 255),
|
|
|
+ ["jybb"] = Trunc(l.Jybb, 255),
|
|
|
+ ["jhdbh"] = Trunc(l.Jhdbh, 50),
|
|
|
+ ["jhdhh"] = Trunc(l.Jhdhh, 50),
|
|
|
+ ["shpc"] = Trunc(l.Shpc, 50),
|
|
|
+ ["shzt"] = Trunc(l.Shzt, 255),
|
|
|
+ // 入库 / 退货数量归 WMS
|
|
|
+ ["rksl"] = 0m,
|
|
|
+ ["thsl"] = 0m
|
|
|
+ };
|
|
|
+ var lUpdate = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["sh_material_code"] = Trunc(l.ShMaterialCode, 255),
|
|
|
+ ["sh_material_name"] = Trunc(l.ShMaterialName, 255),
|
|
|
+ ["sh_material_ggxh"] = Trunc(l.ShMaterialGgxh, 255),
|
|
|
+ ["sh_delivery_quantity"] = l.ShDeliveryQuantity,
|
|
|
+ ["sh_material_dw"] = Trunc(l.ShMaterialDw, 255),
|
|
|
+ ["bzsl"] = l.Bzsl,
|
|
|
+ ["bqsl"] = l.Bqsl,
|
|
|
+ ["po_bill"] = Trunc(l.PoBill, 255),
|
|
|
+ ["po_billline"] = Trunc(l.PoBillline, 50),
|
|
|
+ ["jhdbh"] = Trunc(l.Jhdbh, 50),
|
|
|
+ ["jhdhh"] = Trunc(l.Jhdhh, 50),
|
|
|
+ ["shpc"] = Trunc(l.Shpc, 50)
|
|
|
+ };
|
|
|
+ if (await EnqueueRowAsync(tenantId, $"po|shdzb|{shddh}|{l.Id}", ActionShdzb, "scm_shdzb",
|
|
|
+ new Dictionary<string, object?> { ["id"] = l.Id }, lInsert, lUpdate, null, ct))
|
|
|
+ result.Enqueued++;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var lb in labels)
|
|
|
+ {
|
|
|
+ // 165 的 scm_shdshph.id 是 IDENTITY,只能按 (shdh, xh) 自然键幂等
|
|
|
+ var bKeys = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["shdh"] = Trunc(lb.Shdh, 500),
|
|
|
+ ["xh"] = Trunc(lb.Xh, 50)
|
|
|
+ };
|
|
|
+ var bInsert = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["xh"] = Trunc(lb.Xh, 50),
|
|
|
+ ["wlbm"] = Trunc(lb.Wlbm, 500),
|
|
|
+ ["scph"] = Trunc(lb.Scph, 500),
|
|
|
+ ["shdh"] = Trunc(lb.Shdh, 500),
|
|
|
+ ["shpc"] = Trunc(lb.Shpc, 500),
|
|
|
+ ["gysbm"] = Trunc(lb.Gysbm, 500),
|
|
|
+ ["csrq"] = Trunc(lb.Csrq, 50)
|
|
|
+ };
|
|
|
+ if (await EnqueueRowAsync(tenantId, $"po|shph|{shddh}|{lb.Xh}", ActionShph, "scm_shdshph",
|
|
|
+ bKeys, bInsert, bInsert, null, ct))
|
|
|
+ result.Enqueued++;
|
|
|
+ result.LabelCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ await EnqueueBarcodesAsync(tenantId, shddh, result, ct);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 箱码入 165 MissedPrint:WMS 扫码就是拿箱码查这张表(pr_WMS_GetBarCodes),
|
|
|
+ /// 查不到即报「该标签不存在」。唯一索引 IX_MissedPrint=(Domain,BarCode),RecID 是 IDENTITY 不推。
|
|
|
+ /// </summary>
|
|
|
+ private async Task EnqueueBarcodesAsync(
|
|
|
+ long tenantId, string shddh, PushResult result, CancellationToken ct)
|
|
|
+ {
|
|
|
+ var rows = await _db.Ado.SqlQueryAsync<BarcodeRow>(
|
|
|
+ """
|
|
|
+ SELECT
|
|
|
+ TRIM(IFNULL(`Domain`, '')) AS Domain,
|
|
|
+ TRIM(IFNULL(Site, '')) AS Site,
|
|
|
+ TRIM(IFNULL(BarCode, '')) AS BarCode,
|
|
|
+ TRIM(IFNULL(ItemNum, '')) AS ItemNum,
|
|
|
+ TRIM(IFNULL(Descr, '')) AS Descr,
|
|
|
+ TRIM(IFNULL(Product, '')) AS Product,
|
|
|
+ TRIM(IFNULL(Carton, '')) AS Carton,
|
|
|
+ TRIM(IFNULL(OrdNbr, '')) AS OrdNbr,
|
|
|
+ IFNULL(PackingQty, 0) AS PackingQty,
|
|
|
+ IFNULL(Qty, 0) AS Qty,
|
|
|
+ TRIM(IFNULL(Status, '')) AS Status,
|
|
|
+ TRIM(IFNULL(Supply, '')) AS Supply,
|
|
|
+ TRIM(IFNULL(LotSerial, '')) AS LotSerial,
|
|
|
+ IFNULL(CartonQty, 1) AS CartonQty,
|
|
|
+ TRIM(IFNULL(SuppLotSerial, '')) AS SuppLotSerial,
|
|
|
+ TRIM(IFNULL(ShipperNbr, '')) AS ShipperNbr,
|
|
|
+ IFNULL(ShipperLine, 0) AS ShipperLine,
|
|
|
+ ProdDate, ExpireDate,
|
|
|
+ TRIM(IFNULL(PurOrd, '')) AS PurOrd,
|
|
|
+ IFNULL(PurLine, 0) AS PurLine,
|
|
|
+ IFNULL(PurQty, 0) AS PurQty,
|
|
|
+ TRIM(IFNULL(LabelFormat, '')) AS LabelFormat,
|
|
|
+ TRIM(IFNULL(StandItem, '')) AS StandItem,
|
|
|
+ TRIM(IFNULL(EffSize, '')) AS EffSize,
|
|
|
+ IFNULL(GP12CheckedQty, 0) AS GP12CheckedQty,
|
|
|
+ IFNULL(NetWeight, 0) AS NetWeight,
|
|
|
+ TRIM(IFNULL(Remark, '')) AS Remark,
|
|
|
+ TRIM(IFNULL(LevelChar, '')) AS LevelChar,
|
|
|
+ TRIM(IFNULL(PurOrdDetBatchNbr, '')) AS PurOrdDetBatchNbr,
|
|
|
+ TRIM(IFNULL(FirmString5, '')) AS FirmString5,
|
|
|
+ CreateUser, CreateTime, UpdateUser, UpdateTime
|
|
|
+ FROM MissedPrint
|
|
|
+ WHERE ShipperNbr = @Shddh
|
|
|
+ AND IFNULL(Status, '') = 'U'
|
|
|
+ AND IFNULL(PurOrd, '') NOT LIKE '作废%'
|
|
|
+ ORDER BY ShipperLine, Carton
|
|
|
+ """,
|
|
|
+ new SugarParameter("@Shddh", shddh));
|
|
|
+
|
|
|
+ var now = DateTime.Now;
|
|
|
+ foreach (var b in rows)
|
|
|
+ {
|
|
|
+ var domain = string.IsNullOrWhiteSpace(b.Domain) ? "8010" : b.Domain;
|
|
|
+ var keys = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["Domain"] = domain,
|
|
|
+ ["BarCode"] = b.BarCode
|
|
|
+ };
|
|
|
+ var insert = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["Domain"] = domain,
|
|
|
+ ["Site"] = Trunc(b.Site, 24),
|
|
|
+ ["BarCode"] = Trunc(b.BarCode, 250),
|
|
|
+ ["ItemNum"] = Trunc(b.ItemNum, 60),
|
|
|
+ ["Descr"] = Trunc(b.Descr, 255),
|
|
|
+ ["Product"] = Trunc(b.Product, 255),
|
|
|
+ ["Carton"] = Trunc(b.Carton, 20),
|
|
|
+ ["OrdNbr"] = Trunc(b.OrdNbr, 48),
|
|
|
+ ["PackingQty"] = b.PackingQty,
|
|
|
+ ["Qty"] = b.Qty,
|
|
|
+ // 标签状态归 WMS:建行给待收货 U,收货后由 WMS 改,更新时不回写
|
|
|
+ ["Status"] = "U",
|
|
|
+ ["RelatedBarCode"] = "",
|
|
|
+ ["Supply"] = Trunc(b.Supply, 40),
|
|
|
+ ["LotSerial"] = Trunc(b.LotSerial, 120),
|
|
|
+ ["CartonQty"] = b.CartonQty,
|
|
|
+ ["SuppLotSerial"] = Trunc(b.SuppLotSerial, 120),
|
|
|
+ ["ShipperNbr"] = Trunc(b.ShipperNbr, 60),
|
|
|
+ ["ShipperLine"] = b.ShipperLine,
|
|
|
+ ["PurOrd"] = Trunc(b.PurOrd, 48),
|
|
|
+ ["PurLine"] = b.PurLine,
|
|
|
+ ["PurQty"] = b.PurQty,
|
|
|
+ ["LabelFormat"] = Trunc(b.LabelFormat, 20),
|
|
|
+ ["StandItem"] = Trunc(b.StandItem, 60),
|
|
|
+ ["EffSize"] = Trunc(b.EffSize, 20),
|
|
|
+ ["GP12CheckedQty"] = b.GP12CheckedQty,
|
|
|
+ ["NetWeight"] = b.NetWeight,
|
|
|
+ ["Remark"] = Trunc(b.Remark, 200),
|
|
|
+ ["LevelChar"] = Trunc(b.LevelChar, 20),
|
|
|
+ ["PurOrdDetBatchNbr"] = Trunc(b.PurOrdDetBatchNbr, 48),
|
|
|
+ ["FirmString5"] = Trunc(b.FirmString5, 48),
|
|
|
+ ["CreateUser"] = Trunc(b.CreateUser ?? "aidop", 24),
|
|
|
+ ["UpdateUser"] = Trunc(b.UpdateUser ?? b.CreateUser ?? "aidop", 24),
|
|
|
+ ["CreateTime"] = Fmt(b.CreateTime ?? now),
|
|
|
+ ["UpdateTime"] = Fmt(b.UpdateTime ?? now)
|
|
|
+ };
|
|
|
+ if (b.ProdDate != null) insert["ProdDate"] = Fmt(b.ProdDate);
|
|
|
+ if (b.ExpireDate != null) insert["ExpireDate"] = Fmt(b.ExpireDate);
|
|
|
+
|
|
|
+ var update = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
+ {
|
|
|
+ ["ItemNum"] = Trunc(b.ItemNum, 60),
|
|
|
+ ["Descr"] = Trunc(b.Descr, 255),
|
|
|
+ ["Product"] = Trunc(b.Product, 255),
|
|
|
+ ["PackingQty"] = b.PackingQty,
|
|
|
+ ["Qty"] = b.Qty,
|
|
|
+ ["LotSerial"] = Trunc(b.LotSerial, 120),
|
|
|
+ ["SuppLotSerial"] = Trunc(b.SuppLotSerial, 120),
|
|
|
+ ["ShipperNbr"] = Trunc(b.ShipperNbr, 60),
|
|
|
+ ["ShipperLine"] = b.ShipperLine,
|
|
|
+ ["PurOrd"] = Trunc(b.PurOrd, 48),
|
|
|
+ ["PurLine"] = b.PurLine,
|
|
|
+ ["PurQty"] = b.PurQty,
|
|
|
+ ["StandItem"] = Trunc(b.StandItem, 60),
|
|
|
+ ["EffSize"] = Trunc(b.EffSize, 20),
|
|
|
+ ["LevelChar"] = Trunc(b.LevelChar, 20),
|
|
|
+ ["PurOrdDetBatchNbr"] = Trunc(b.PurOrdDetBatchNbr, 48),
|
|
|
+ ["Remark"] = Trunc(b.Remark, 200),
|
|
|
+ ["UpdateUser"] = Trunc(b.UpdateUser ?? "aidop", 24),
|
|
|
+ ["UpdateTime"] = Fmt(now)
|
|
|
+ };
|
|
|
+ if (b.ProdDate != null) update["ProdDate"] = Fmt(b.ProdDate);
|
|
|
+
|
|
|
+ if (await EnqueueRowAsync(tenantId, $"po|mp|{domain}|{b.BarCode}", ActionMp, "MissedPrint",
|
|
|
+ keys, insert, update, null, ct))
|
|
|
+ result.Enqueued++;
|
|
|
+ result.BarcodeCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ── 基础设施 ──────────────────────────────────────────────────────
|
|
|
+
|
|
|
+ /// <summary>自建单口径:SAP 下发单不回推 165(决策:SAP 维持现状)。</summary>
|
|
|
+ private static bool IsSelfBuilt(string? potype, string? reqBy)
|
|
|
+ {
|
|
|
+ var p = (potype ?? "").Trim();
|
|
|
+ var r = (reqBy ?? "").Trim();
|
|
|
+ return string.Equals(p, "po", StringComparison.OrdinalIgnoreCase)
|
|
|
+ && (string.Equals(r, "PO", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(r, "DO", StringComparison.OrdinalIgnoreCase));
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<bool> EnqueueRowAsync(
|
|
|
+ long tenantId,
|
|
|
+ string idem,
|
|
|
+ string action,
|
|
|
+ string table,
|
|
|
+ Dictionary<string, object?> keys,
|
|
|
+ Dictionary<string, object?> insert,
|
|
|
+ Dictionary<string, object?> update,
|
|
|
+ Dictionary<string, object?>? resolve,
|
|
|
+ CancellationToken ct)
|
|
|
+ {
|
|
|
+ var payload = new Dictionary<string, object?>
|
|
|
+ {
|
|
|
+ ["op"] = "UPSERT",
|
|
|
+ ["table"] = table,
|
|
|
+ ["keys"] = keys,
|
|
|
+ ["insert"] = insert,
|
|
|
+ ["update"] = update,
|
|
|
+ ["expect"] = new Dictionary<string, object?>()
|
|
|
+ };
|
|
|
+ if (resolve != null && resolve.Count > 0) payload["resolve"] = resolve;
|
|
|
+
|
|
|
+ var item = new MdpOutbox
|
|
|
+ {
|
|
|
+ TenantId = tenantId,
|
|
|
+ TargetSourceCode = TargetSource,
|
|
|
+ ActionCode = action,
|
|
|
+ IdemKey = idem.Length > 200 ? idem[..200] : idem,
|
|
|
+ PayloadJson = JsonSerializer.Serialize(payload)
|
|
|
+ };
|
|
|
+ return await _enqueue.TryEnqueueOrRefreshAsync(item, ct, pulse: false);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static string Fmt(DateTime? dt) => (dt ?? DateTime.Now).ToString("yyyy-MM-dd HH:mm:ss");
|
|
|
+
|
|
|
+ private static string Trunc(string? s, int max)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(s)) return "";
|
|
|
+ return s.Length <= max ? s : s[..max];
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class PurOrdMasterRow
|
|
|
+ {
|
|
|
+ public string? Domain { get; set; }
|
|
|
+ public string? PurOrd { get; set; }
|
|
|
+ public string? Potype { get; set; }
|
|
|
+ public string? Typed { get; set; }
|
|
|
+ public string? Supp { get; set; }
|
|
|
+ public string? Buyer { get; set; }
|
|
|
+ public string? ReqBy { get; set; }
|
|
|
+ public string? UsageText { get; set; }
|
|
|
+ public string? FSTID { get; set; }
|
|
|
+ public string? Remark { get; set; }
|
|
|
+ public DateTime? OrdDate { get; set; }
|
|
|
+ public DateTime? DueDate { get; set; }
|
|
|
+ public int IsActive { get; set; }
|
|
|
+ public int IsConfirm { get; set; }
|
|
|
+ public string? CreateUser { get; set; }
|
|
|
+ public DateTime? CreateTime { get; set; }
|
|
|
+ public string? UpdateUser { get; set; }
|
|
|
+ public DateTime? UpdateTime { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class PurOrdDetailRow
|
|
|
+ {
|
|
|
+ public string? Domain { get; set; }
|
|
|
+ public int Line { get; set; }
|
|
|
+ public string? ItemNum { get; set; }
|
|
|
+ public string? Descr { get; set; }
|
|
|
+ public string? UM { get; set; }
|
|
|
+ public string? Rev { get; set; }
|
|
|
+ public string? Drawing { get; set; }
|
|
|
+ public string? Location { get; set; }
|
|
|
+ public string? LotSerial { get; set; }
|
|
|
+ public string? Req { get; set; }
|
|
|
+ public decimal QtyOrded { get; set; }
|
|
|
+ public DateTime? DueDate { get; set; }
|
|
|
+ public DateTime? NeedDate { get; set; }
|
|
|
+ public int IsActive { get; set; }
|
|
|
+ public int IsConfirm { get; set; }
|
|
|
+ public string? CreateUser { get; set; }
|
|
|
+ public DateTime? CreateTime { get; set; }
|
|
|
+ public string? UpdateUser { get; set; }
|
|
|
+ public DateTime? UpdateTime { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class DeliveryScheduleRow
|
|
|
+ {
|
|
|
+ public long Id { get; set; }
|
|
|
+ public string? Domain { get; set; }
|
|
|
+ public long Icdsid { get; set; }
|
|
|
+ public string? Dsnum { get; set; }
|
|
|
+ public string? Status { get; set; }
|
|
|
+ public string? Itemnum { get; set; }
|
|
|
+ public string? Um { get; set; }
|
|
|
+ public string? Purgroup { get; set; }
|
|
|
+ public string? Suppliercode { get; set; }
|
|
|
+ public string? Supplier { get; set; }
|
|
|
+ public DateTime? Submitdate { get; set; }
|
|
|
+ public DateTime? Requestdate { get; set; }
|
|
|
+ public DateTime? Needdate { get; set; }
|
|
|
+ public string? Ponumber { get; set; }
|
|
|
+ public int Poline { get; set; }
|
|
|
+ public decimal Schedqty { get; set; }
|
|
|
+ public string? Remarks { get; set; }
|
|
|
+ public int Isactive { get; set; }
|
|
|
+ public string? Createuser { get; set; }
|
|
|
+ public DateTime? Createtime { get; set; }
|
|
|
+ public string? Updateuser { get; set; }
|
|
|
+ public DateTime? Updatetime { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class ShipmentRow
|
|
|
+ {
|
|
|
+ public long Id { get; set; }
|
|
|
+ public long ShPurchaseId { get; set; }
|
|
|
+ public string? ShPurchaseName { get; set; }
|
|
|
+ public string? ShPurchaseNum { get; set; }
|
|
|
+ public string? ShPurchaseAddress { get; set; }
|
|
|
+ public string? ShPurchaseLxr { get; set; }
|
|
|
+ public string? ShPurchasePhone { get; set; }
|
|
|
+ public string? DeliveryAddress { get; set; }
|
|
|
+ public string? ExpectedConsignee { get; set; }
|
|
|
+ public string? ConsigneePhone { get; set; }
|
|
|
+ public DateTime? EstimatedDeliveryDate { get; set; }
|
|
|
+ public string? PoBillno { get; set; }
|
|
|
+ public string? Shddh { get; set; }
|
|
|
+ public string? Jhshrq { get; set; }
|
|
|
+ public string? Tjrid { get; set; }
|
|
|
+ public string? Tjrxm { get; set; }
|
|
|
+ public string? Tjrq { get; set; }
|
|
|
+ public int Scbq { get; set; }
|
|
|
+ public string? Chbg { get; set; }
|
|
|
+ public int Sfpc { get; set; }
|
|
|
+ public string? Pcsm { get; set; }
|
|
|
+ public string? Wlsc { get; set; }
|
|
|
+ public string? Yjdhrq { get; set; }
|
|
|
+ public int State { get; set; }
|
|
|
+ public string? Shzt { get; set; }
|
|
|
+ public string? Wldh { get; set; }
|
|
|
+ public int Dycs { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class ShipmentLineRow
|
|
|
+ {
|
|
|
+ public long Id { get; set; }
|
|
|
+ public string? Glid { get; set; }
|
|
|
+ public string? ShMaterialCode { get; set; }
|
|
|
+ public string? ShMaterialName { get; set; }
|
|
|
+ public string? ShMaterialGgxh { get; set; }
|
|
|
+ public decimal ShDeliveryQuantity { get; set; }
|
|
|
+ public string? ShMaterialDw { get; set; }
|
|
|
+ public string? Remarks { get; set; }
|
|
|
+ public decimal Bzsl { get; set; }
|
|
|
+ public decimal Bqsl { get; set; }
|
|
|
+ public string? OrderType { get; set; }
|
|
|
+ public string? PoBill { get; set; }
|
|
|
+ public string? 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 decimal Djsl { get; set; }
|
|
|
+ public string? Ccrq { get; set; }
|
|
|
+ public string? Cgyt { get; set; }
|
|
|
+ public string? Jybb { get; set; }
|
|
|
+ public string? Jhdbh { get; set; }
|
|
|
+ public string? Jhdhh { get; set; }
|
|
|
+ public string? Shpc { get; set; }
|
|
|
+ public string? Shzt { get; set; }
|
|
|
+ public decimal Rksl { get; set; }
|
|
|
+ public decimal Thsl { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class BarcodeRow
|
|
|
+ {
|
|
|
+ public string? Domain { get; set; }
|
|
|
+ public string? Site { get; set; }
|
|
|
+ public string? BarCode { get; set; }
|
|
|
+ public string? ItemNum { get; set; }
|
|
|
+ public string? Descr { get; set; }
|
|
|
+ public string? Product { get; set; }
|
|
|
+ public string? Carton { get; set; }
|
|
|
+ public string? OrdNbr { get; set; }
|
|
|
+ public decimal PackingQty { get; set; }
|
|
|
+ public decimal Qty { get; set; }
|
|
|
+ public string? Status { get; set; }
|
|
|
+ public string? Supply { get; set; }
|
|
|
+ public string? LotSerial { get; set; }
|
|
|
+ public int CartonQty { get; set; }
|
|
|
+ public string? SuppLotSerial { get; set; }
|
|
|
+ public string? ShipperNbr { get; set; }
|
|
|
+ public int ShipperLine { get; set; }
|
|
|
+ public DateTime? ProdDate { get; set; }
|
|
|
+ public DateTime? ExpireDate { get; set; }
|
|
|
+ public string? PurOrd { get; set; }
|
|
|
+ public int PurLine { get; set; }
|
|
|
+ public decimal PurQty { get; set; }
|
|
|
+ public string? LabelFormat { get; set; }
|
|
|
+ public string? StandItem { get; set; }
|
|
|
+ public string? EffSize { get; set; }
|
|
|
+ public decimal GP12CheckedQty { get; set; }
|
|
|
+ public decimal NetWeight { get; set; }
|
|
|
+ public string? Remark { get; set; }
|
|
|
+ public string? LevelChar { get; set; }
|
|
|
+ public string? PurOrdDetBatchNbr { get; set; }
|
|
|
+ public string? FirmString5 { get; set; }
|
|
|
+ public string? CreateUser { get; set; }
|
|
|
+ public DateTime? CreateTime { get; set; }
|
|
|
+ public string? UpdateUser { get; set; }
|
|
|
+ public DateTime? UpdateTime { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class ShipmentLabelRow
|
|
|
+ {
|
|
|
+ public long Id { get; set; }
|
|
|
+ public string? Xh { get; set; }
|
|
|
+ public string? Wlbm { get; set; }
|
|
|
+ public string? Scph { get; set; }
|
|
|
+ public string? Shdh { get; set; }
|
|
|
+ public string? Shpc { get; set; }
|
|
|
+ public string? Gysbm { get; set; }
|
|
|
+ public string? Csrq { get; set; }
|
|
|
+ }
|
|
|
+}
|