| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- using System.Text.Json;
- using Admin.NET.Plugin.AiDOP.DataPlatform.Executors;
- using Admin.NET.Plugin.AiDOP.Entity.DataPlatform;
- using Admin.NET.Plugin.AiDOP.WorkOrder;
- using Microsoft.AspNetCore.Http;
- using Microsoft.Extensions.Logging;
- using SqlSugar;
- namespace Admin.NET.Plugin.AiDOP.DataPlatform.Wms;
- /// <summary>
- /// WP4 · 计划侧工单入 165:本库 WorkOrdMaster/Detail/Routing 快照 → Outbox UPSERT。
- /// 挂接点:订单评审 / 计划刷新 / 交期确认;下达侧只推进状态(CreatePickBill)。
- /// </summary>
- [ApiDescriptionSettings(Order = 328, Description = "工单推送 MES")]
- [Route("api/aidop/wms-workord")]
- [AllowAnonymous]
- [NonUnify]
- public class WorkOrdMesPushService : IDynamicApiController, ITransient
- {
- public const string TargetSource = "DOPDEMORQ_SQLSERVER";
- public const string ActionWom = "WO_MES_WOM";
- public const string ActionWod = "WO_MES_WOD";
- public const string ActionWor = "WO_MES_WOR";
- private readonly ISqlSugarClient _db;
- private readonly MdpSourceScopeFactory _scopeFactory;
- private readonly MdpOutboxEnqueueService _enqueue;
- private readonly MdpOutboxWakeSignal _wake;
- private readonly UserManager _userManager;
- private readonly IHttpContextAccessor _httpContextAccessor;
- private readonly ILogger<WorkOrdMesPushService> _logger;
- public WorkOrdMesPushService(
- ISqlSugarClient db,
- MdpSourceScopeFactory scopeFactory,
- MdpOutboxEnqueueService enqueue,
- MdpOutboxWakeSignal wake,
- UserManager userManager,
- IHttpContextAccessor httpContextAccessor,
- ILogger<WorkOrdMesPushService> logger)
- {
- _db = db;
- _scopeFactory = scopeFactory;
- _enqueue = enqueue;
- _wake = wake;
- _userManager = userManager;
- _httpContextAccessor = httpContextAccessor;
- _logger = logger;
- }
- public sealed class PushInput
- {
- public string WorkOrd { get; set; } = "";
- public long TenantId { get; set; }
- public string? Domain { get; set; }
- }
- public sealed class PushResult
- {
- public bool Ok { get; set; }
- public string WorkOrd { get; set; } = "";
- public string Domain { get; set; } = "";
- public int Enqueued { get; set; }
- public int DetailCount { get; set; }
- public int RoutingCount { get; set; }
- public string? Message { get; set; }
- }
- /// <summary>补偿 / 联调:按本库快照入队推送 165。</summary>
- [DisplayName("推送工单到 MES")]
- [HttpPost("push")]
- public async Task<PushResult> Push([FromBody] PushInput input, CancellationToken ct = default)
- {
- if (input == null || string.IsNullOrWhiteSpace(input.WorkOrd))
- 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(
- "[WorkOrdMesPush] 无登录态推送 audit ip={ClientIp} workOrd={WorkOrd} tenantId={TenantId}",
- clientIp, input.WorkOrd.Trim(), input.TenantId);
- }
- var tenantId = input.TenantId;
- var workOrd = input.WorkOrd.Trim();
- var domain = string.IsNullOrWhiteSpace(input.Domain)
- ? await ResolveDomainAsync(tenantId, workOrd)
- : input.Domain.Trim();
- return await EnqueueSnapshotAsync(tenantId, domain, workOrd, ct);
- }
- /// <summary>评审/刷新/确认后调用:失败只记日志,不抛(避免拖垮本库评审事务)。</summary>
- public async Task TryEnqueueSnapshotSafeAsync(long tenantId, string workOrd, List<string>? warnings = null)
- {
- try
- {
- if (string.IsNullOrWhiteSpace(workOrd)) return;
- var domain = await ResolveDomainAsync(tenantId, workOrd);
- var r = await EnqueueSnapshotAsync(tenantId, domain, workOrd);
- warnings?.Add($"工单 {workOrd} 已入队推送 MES({r.Enqueued} 条 Outbox,明细 {r.DetailCount}/工序 {r.RoutingCount})");
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, "[WorkOrdMesPush] enqueue failed tenant={Tenant} wo={WorkOrd}", tenantId, workOrd);
- warnings?.Add($"工单 {workOrd} 推送 MES 入队失败:{ex.Message}(可稍后调用 /api/aidop/wms-workord/push 补偿)");
- }
- }
- /// <summary>从本库读三人组并入队(主表 → 明细 → 工序);末尾 Pulse 一次。</summary>
- public async Task<PushResult> EnqueueSnapshotAsync(
- long tenantId, string domain, string workOrd, CancellationToken ct = default)
- {
- domain = string.IsNullOrWhiteSpace(domain) ? "8010" : domain.Trim();
- workOrd = workOrd.Trim();
- var masters = await _db.Queryable<WorkOrdMaster>()
- .Where(m => m.TenantId == tenantId && m.WorkOrd == workOrd)
- .Take(1)
- .ToListAsync(ct);
- if (masters.Count == 0)
- throw Oops.Oh($"本库未找到工单 {workOrd}");
- var master = masters[0];
- var details = await _db.Ado.SqlQueryAsync<DetailRow>(
- """
- SELECT
- TRIM(IFNULL(Domain, '')) AS Domain,
- WorkOrd,
- IFNULL(LineNum, 0) AS LineNum,
- TRIM(IFNULL(ItemNum, '')) AS ItemNum,
- IFNULL(Op, 0) AS Op,
- TRIM(IFNULL(Location, '')) AS Location,
- TRIM(IFNULL(Site, '')) AS Site,
- IFNULL(QtyRequired, 0) AS QtyRequired,
- IFNULL(FrozenBOMQty, 0) AS FrozenBOMQty,
- TRIM(IFNULL(LotSerial, '')) AS LotSerial,
- TRIM(IFNULL(Status, '')) AS Status,
- TRIM(IFNULL(Typed, '')) AS Typed,
- TRIM(IFNULL(UM, '')) AS UM,
- IFNULL(IsActive, 0) AS IsActive,
- IFNULL(BusinessID, 0) AS BusinessID,
- CreateUser, CreateTime, UpdateUser, UpdateTime
- FROM WorkOrdDetail
- WHERE tenant_id = @TenantId AND WorkOrd = @WorkOrd
- AND IFNULL(IsActive, 0) = 1
- ORDER BY LineNum, Op, ItemNum
- """,
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@WorkOrd", workOrd));
- var routings = await _db.Ado.SqlQueryAsync<RoutingRow>(
- """
- SELECT
- TRIM(IFNULL(Domain, '')) AS Domain,
- WorkOrd,
- OP,
- TRIM(IFNULL(ItemNum, '')) AS ItemNum,
- TRIM(IFNULL(Descr, '')) AS Descr,
- IFNULL(MilestoneOp, 0) AS MilestoneOp,
- IFNULL(ParentOp, 0) AS ParentOp,
- IFNULL(QtyOrded, 0) AS QtyOrded,
- TRIM(IFNULL(Status, '')) AS Status,
- TRIM(IFNULL(ProdLine, '')) AS ProdLine,
- TRIM(IFNULL(WorkCtr, '')) AS WorkCtr,
- TRIM(IFNULL(Machine, '')) AS Machine,
- IFNULL(RunCrew, 0) AS RunCrew,
- IFNULL(StdSetupTime, 0) AS StdSetupTime,
- IFNULL(ProcessOut, 0) AS ProcessOut,
- IFNULL(IsActive, 0) AS IsActive,
- CreateUser, CreateTime, UpdateUser, UpdateTime
- FROM WorkOrdRouting
- WHERE tenant_id = @TenantId AND WorkOrd = @WorkOrd
- AND IFNULL(IsActive, 0) = 1
- ORDER BY OP
- """,
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@WorkOrd", workOrd));
- if (details.Count == 0)
- throw Oops.Oh($"工单 {workOrd} 无活跃物料明细,无法推送 MES");
- if (routings.Count == 0)
- throw Oops.Oh($"工单 {workOrd} 无活跃工序,无法推送 MES");
- var masterStatus = StatusForMasterPush(master.Status);
- var now = DateTime.Now;
- var enqueued = 0;
- var womInsert = BuildMasterInsert(domain, master, masterStatus, now);
- var womUpdate = BuildMasterUpdate(master, masterStatus, now);
- // 165 唯一索引 IX_WorkOrdMaster = (Domain, Typed, WorkOrd)
- var typed = string.IsNullOrWhiteSpace(master.Typed) ? "" : master.Typed.Trim();
- if (await EnqueueRowAsync(tenantId, $"wo|{domain}|{workOrd}|wom", ActionWom, "WorkOrdMaster",
- new Dictionary<string, object?>
- {
- ["Domain"] = domain,
- ["Typed"] = typed,
- ["WorkOrd"] = workOrd
- },
- womInsert, womUpdate, ct))
- enqueued++;
- foreach (var d in details)
- {
- var line = d.LineNum;
- var item = d.ItemNum ?? "";
- var op = d.Op;
- // 165 唯一索引 IX_WorkOrdDetail = (Domain, ID, WorkOrd, Op, Line, ItemNum, Dimension1, Dimension2)
- var keys = new Dictionary<string, object?>
- {
- ["Domain"] = domain,
- ["ID"] = "",
- ["WorkOrd"] = workOrd,
- ["Op"] = op,
- ["Line"] = line,
- ["ItemNum"] = item,
- ["Dimension1"] = "",
- ["Dimension2"] = ""
- };
- var insert = BuildDetailInsert(domain, workOrd, d, now);
- var update = BuildDetailUpdate(d, now);
- var idem = $"wo|{domain}|{workOrd}|wod|{line}|{op}|{item}";
- if (await EnqueueRowAsync(tenantId, idem, ActionWod, "WorkOrdDetail", keys, insert, update, ct))
- enqueued++;
- }
- foreach (var r in routings)
- {
- var routingStatus = StatusForRoutingPush(masterStatus, r.Status);
- // 165 唯一索引 IX_WorkOrdRouting_1 = (Domain, ID, OP, WorkOrd)
- var keys = new Dictionary<string, object?>
- {
- ["Domain"] = domain,
- ["ID"] = "",
- ["OP"] = r.OP,
- ["WorkOrd"] = workOrd
- };
- var insert = BuildRoutingInsert(domain, workOrd, r, routingStatus, now);
- var update = BuildRoutingUpdate(r, routingStatus, now);
- var idem = $"wo|{domain}|{workOrd}|wor|{r.OP}";
- if (await EnqueueRowAsync(tenantId, idem, ActionWor, "WorkOrdRouting", keys, insert, update, ct))
- enqueued++;
- }
- _wake.Pulse();
- return new PushResult
- {
- Ok = true,
- WorkOrd = workOrd,
- Domain = domain,
- Enqueued = enqueued,
- DetailCount = details.Count,
- RoutingCount = routings.Count,
- Message = $"已入队 {enqueued} 条(明细 {details.Count} / 工序 {routings.Count})"
- };
- }
- /// <summary>探测 165 是否已有完整工单三人组。</summary>
- public async Task<(bool Ready, int MasterCnt, int DetailCnt, int RoutingCnt)> ProbeReadyOnMesAsync(
- string domain, string workOrd, CancellationToken ct = default)
- {
- domain = string.IsNullOrWhiteSpace(domain) ? "8010" : domain.Trim();
- workOrd = workOrd.Trim();
- var ss = await _scopeFactory.GetScopeAsync(TargetSource, ct);
- var masterCnt = await ss.Ado.GetIntAsync(
- "SELECT COUNT(1) FROM WorkOrdMaster WHERE Domain=@d AND WorkOrd=@w",
- new SugarParameter("@d", domain),
- new SugarParameter("@w", workOrd));
- var detailCnt = await ss.Ado.GetIntAsync(
- "SELECT COUNT(1) FROM WorkOrdDetail WHERE Domain=@d AND WorkOrd=@w AND ISNULL(IsActive,0)=1",
- new SugarParameter("@d", domain),
- new SugarParameter("@w", workOrd));
- var routingCnt = await ss.Ado.GetIntAsync(
- "SELECT COUNT(1) FROM WorkOrdRouting WHERE Domain=@d AND WorkOrd=@w AND ISNULL(IsActive,0)=1",
- new SugarParameter("@d", domain),
- new SugarParameter("@w", workOrd));
- return (masterCnt > 0 && detailCnt > 0 && routingCnt > 0, masterCnt, detailCnt, routingCnt);
- }
- /// <summary>Mes 下达前置:165 上须已有完整工单三人组。</summary>
- public async Task AssertReadyOnMesAsync(string domain, string workOrd, CancellationToken ct = default)
- {
- try
- {
- var probe = await ProbeReadyOnMesAsync(domain, workOrd, ct);
- if (!probe.Ready)
- {
- throw Oops.Oh(PickBillUserMessages.ForReleaseFailure("WO_MES_NOT_READY"));
- }
- }
- catch (Exception ex) when (ex is not AppFriendlyException)
- {
- throw Oops.Oh(PickBillUserMessages.ForReleaseFailure("MES_UNREACHABLE"));
- }
- }
- private async Task<string> ResolveDomainAsync(long tenantId, string workOrd)
- {
- var domain = await _db.Ado.GetStringAsync(
- """
- SELECT TRIM(IFNULL(`Domain`, '')) FROM WorkOrdMaster
- WHERE tenant_id = @TenantId AND WorkOrd = @WorkOrd LIMIT 1
- """,
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@WorkOrd", workOrd));
- return string.IsNullOrWhiteSpace(domain) ? "8010" : domain;
- }
- 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,
- CancellationToken ct)
- {
- var payload = new Dictionary<string, object?>
- {
- ["op"] = "UPSERT",
- ["table"] = table,
- ["keys"] = keys,
- ["insert"] = insert,
- ["update"] = update,
- ["expect"] = new Dictionary<string, object?>()
- };
- 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 StatusForMasterPush(string? local)
- {
- var t = (local ?? "").Trim().ToLowerInvariant();
- return t is "r" or "w" or "s" or "c" or "p" ? t : "p";
- }
- private static string StatusForRoutingPush(string masterStatus, string? localRouting)
- {
- var m = (masterStatus ?? "").Trim().ToLowerInvariant();
- if (m is "r" or "w" or "s")
- {
- var r = (localRouting ?? "").Trim().ToLowerInvariant();
- return string.IsNullOrEmpty(r) ? "r" : r;
- }
- // 计划态:不写执行态;空串表示未下达
- return "";
- }
- private static Dictionary<string, object?> BuildMasterInsert(
- string domain, WorkOrdMaster m, string status, DateTime now)
- {
- var insert = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
- {
- ["Domain"] = domain,
- ["WorkOrd"] = m.WorkOrd,
- ["ID"] = m.WorkOrd,
- ["Status"] = status,
- ["Batch"] = m.Batch ?? "",
- ["ItemNum"] = m.ItemNum ?? "",
- ["Site"] = m.Site ?? "",
- ["Typed"] = string.IsNullOrWhiteSpace(m.Typed) ? "" : m.Typed.Trim(),
- ["QtyOrded"] = m.QtyOrded,
- ["ProdLine"] = m.ProdLine ?? "",
- ["Location"] = m.Location ?? "",
- ["Priority"] = m.Priority,
- ["Urgent"] = m.Urgent,
- ["ItemName"] = Trunc(m.ItemName, 256),
- ["Department"] = Trunc(m.Department, 64),
- ["RoutingCode"] = m.RoutingCode ?? "",
- ["BOMFormula"] = m.BOMFormula ?? "",
- ["Project"] = m.Project ?? "",
- ["Drawing"] = Trunc(m.Drawing, 64),
- ["Rev"] = m.Rev ?? "",
- ["CustNo"] = Trunc(m.CustNo, 64),
- ["BusinessID"] = m.BusinessID,
- ["IsActive"] = m.IsActive,
- ["IsConfirm"] = m.IsConfirm,
- ["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);
- if (m.ReleaseDate != null) insert["ReleaseDate"] = Fmt(m.ReleaseDate);
- return insert;
- }
- private static Dictionary<string, object?> BuildMasterUpdate(WorkOrdMaster m, string status, DateTime now)
- {
- var update = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
- {
- ["Status"] = status,
- ["Batch"] = m.Batch ?? "",
- ["ItemNum"] = m.ItemNum ?? "",
- ["Site"] = m.Site ?? "",
- ["Typed"] = m.Typed ?? "",
- ["QtyOrded"] = m.QtyOrded,
- ["ProdLine"] = m.ProdLine ?? "",
- ["Location"] = m.Location ?? "",
- ["Priority"] = m.Priority,
- ["Urgent"] = m.Urgent,
- ["ItemName"] = Trunc(m.ItemName, 256),
- ["Department"] = Trunc(m.Department, 64),
- ["RoutingCode"] = m.RoutingCode ?? "",
- ["BOMFormula"] = m.BOMFormula ?? "",
- ["Project"] = m.Project ?? "",
- ["Drawing"] = Trunc(m.Drawing, 64),
- ["Rev"] = m.Rev ?? "",
- ["CustNo"] = Trunc(m.CustNo, 64),
- ["IsActive"] = m.IsActive,
- ["UpdateUser"] = Trunc(m.UpdateUser ?? "aidop", 24),
- ["UpdateTime"] = Fmt(now)
- };
- if (m.OrdDate != null) update["OrdDate"] = Fmt(m.OrdDate);
- if (m.DueDate != null) update["DueDate"] = Fmt(m.DueDate);
- if (m.ReleaseDate != null) update["ReleaseDate"] = Fmt(m.ReleaseDate);
- return update;
- }
- private static Dictionary<string, object?> BuildDetailInsert(string domain, string workOrd, DetailRow d, DateTime now)
- {
- return new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
- {
- ["Domain"] = domain,
- ["ID"] = "",
- ["WorkOrd"] = workOrd,
- ["LineNum"] = d.LineNum,
- ["Line"] = d.LineNum,
- ["ItemNum"] = d.ItemNum ?? "",
- ["Op"] = d.Op,
- ["Dimension1"] = "",
- ["Dimension2"] = "",
- ["Location"] = d.Location ?? "",
- ["Site"] = d.Site ?? "",
- ["QtyRequired"] = d.QtyRequired,
- ["FrozenBOMQty"] = d.FrozenBOMQty,
- ["LotSerial"] = Trunc(d.LotSerial, 25),
- ["Status"] = "",
- ["Typed"] = d.Typed ?? "",
- ["UM"] = d.UM ?? "",
- ["IsActive"] = d.IsActive != 0,
- ["BusinessID"] = d.BusinessID,
- ["QtyReturned"] = 0m,
- ["CreateUser"] = Trunc(d.CreateUser ?? "aidop", 24),
- ["UpdateUser"] = Trunc(d.UpdateUser ?? d.CreateUser ?? "aidop", 24),
- ["CreateTime"] = Fmt(d.CreateTime ?? now),
- ["UpdateTime"] = Fmt(d.UpdateTime ?? now)
- };
- }
- private static Dictionary<string, object?> BuildDetailUpdate(DetailRow d, DateTime now)
- {
- return new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
- {
- ["Location"] = d.Location ?? "",
- ["Site"] = d.Site ?? "",
- ["QtyRequired"] = d.QtyRequired,
- ["FrozenBOMQty"] = d.FrozenBOMQty,
- ["LotSerial"] = Trunc(d.LotSerial, 25),
- ["Typed"] = d.Typed ?? "",
- ["UM"] = d.UM ?? "",
- ["IsActive"] = d.IsActive != 0,
- ["UpdateUser"] = Trunc(d.UpdateUser ?? "aidop", 24),
- ["UpdateTime"] = Fmt(now)
- };
- }
- private static Dictionary<string, object?> BuildRoutingInsert(
- string domain, string workOrd, RoutingRow r, string status, DateTime now)
- {
- return new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
- {
- ["Domain"] = domain,
- ["ID"] = "",
- ["WorkOrd"] = workOrd,
- ["OP"] = r.OP,
- ["ItemNum"] = r.ItemNum ?? "",
- ["Descr"] = Trunc(r.Descr, 256),
- ["MilestoneOp"] = r.MilestoneOp != 0 ? 1 : 0,
- ["ParentOp"] = r.ParentOp,
- ["QtyOrded"] = r.QtyOrded,
- ["Status"] = status,
- ["ProdLine"] = Trunc(r.ProdLine, 8),
- ["WorkCtr"] = Trunc(r.WorkCtr, 8),
- ["Machine"] = Trunc(r.Machine, 800),
- ["RunCrew"] = r.RunCrew,
- ["StdSetupTime"] = r.StdSetupTime,
- ["ProcessOut"] = r.ProcessOut,
- ["IsActive"] = r.IsActive != 0,
- ["CreateUser"] = Trunc(r.CreateUser ?? "aidop", 24),
- ["UpdateUser"] = Trunc(r.UpdateUser ?? r.CreateUser ?? "aidop", 24),
- ["CreateTime"] = Fmt(r.CreateTime ?? now),
- ["UpdateTime"] = Fmt(r.UpdateTime ?? now)
- };
- }
- private static Dictionary<string, object?> BuildRoutingUpdate(RoutingRow r, string status, DateTime now)
- {
- return new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
- {
- ["ItemNum"] = r.ItemNum ?? "",
- ["Descr"] = Trunc(r.Descr, 256),
- ["MilestoneOp"] = r.MilestoneOp != 0 ? 1 : 0,
- ["ParentOp"] = r.ParentOp,
- ["QtyOrded"] = r.QtyOrded,
- ["Status"] = status,
- ["ProdLine"] = Trunc(r.ProdLine, 8),
- ["WorkCtr"] = Trunc(r.WorkCtr, 8),
- ["Machine"] = Trunc(r.Machine, 800),
- ["RunCrew"] = r.RunCrew,
- ["StdSetupTime"] = r.StdSetupTime,
- ["ProcessOut"] = r.ProcessOut,
- ["IsActive"] = r.IsActive != 0,
- ["UpdateUser"] = Trunc(r.UpdateUser ?? "aidop", 24),
- ["UpdateTime"] = Fmt(now)
- };
- }
- 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 s ?? "";
- return s.Length <= max ? s : s[..max];
- }
- private sealed class DetailRow
- {
- public string? Domain { get; set; }
- public string? WorkOrd { get; set; }
- public int LineNum { get; set; }
- public string? ItemNum { get; set; }
- public int Op { get; set; }
- public string? Location { get; set; }
- public string? Site { get; set; }
- public decimal QtyRequired { get; set; }
- public decimal FrozenBOMQty { get; set; }
- public string? LotSerial { get; set; }
- public string? Status { get; set; }
- public string? Typed { get; set; }
- public string? UM { get; set; }
- public int IsActive { get; set; }
- public long BusinessID { 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 RoutingRow
- {
- public string? Domain { get; set; }
- public string? WorkOrd { get; set; }
- public int OP { get; set; }
- public string? ItemNum { get; set; }
- public string? Descr { get; set; }
- public int MilestoneOp { get; set; }
- public int ParentOp { get; set; }
- public decimal QtyOrded { get; set; }
- public string? Status { get; set; }
- public string? ProdLine { get; set; }
- public string? WorkCtr { get; set; }
- public string? Machine { get; set; }
- public decimal RunCrew { get; set; }
- public decimal StdSetupTime { get; set; }
- public int ProcessOut { 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; }
- }
- }
|