|
|
@@ -19,6 +19,15 @@ public class WorkOrderSchedulingService : IDynamicApiController, ITransient
|
|
|
_userManager = userManager;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 工单信息类(用于查询结果映射)
|
|
|
+ /// </summary>
|
|
|
+ private class WorkOrderInfo
|
|
|
+ {
|
|
|
+ public string WorkOrd { get; set; } = string.Empty;
|
|
|
+ public string Domain { get; set; } = string.Empty;
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 主表 WorkOrdMaster 按工单号 +「公司域或租户 id」匹配:<c>domain</c> 可为原 <c>Domain</c> 列,或与 <c>tenant_id</c> 相同的字符串(列表已做 COALESCE 回传)。
|
|
|
/// </summary>
|
|
|
@@ -681,14 +690,127 @@ public class WorkOrderSchedulingService : IDynamicApiController, ITransient
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
// 工单关闭 POST /api/Production/scheduling/close
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
- [DisplayName("工单关闭(存储过程)")]
|
|
|
+ [DisplayName("工单关闭")]
|
|
|
[HttpPost("scheduling/close")]
|
|
|
public async Task<object> Close([FromBody] WorkOrderCloseInput input)
|
|
|
{
|
|
|
+ var ids = input.Ids.Trim();
|
|
|
+ if (string.IsNullOrWhiteSpace(ids))
|
|
|
+ throw Oops.Oh("工单主键不能为空");
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 开启事务
|
|
|
+ _db.Ado.BeginTran();
|
|
|
+
|
|
|
+ // 根据 RecID 获取工单号和域信息
|
|
|
+ // 注意:存储过程 pr_MES_CloseWorkOrders 需要两个参数:域和工单号(竖线分隔)
|
|
|
+ // 但当前调用只传递 RecID,因此需要根据 RecID 查询对应的工单和域
|
|
|
+
|
|
|
+ var workOrders = await _db.Ado.SqlQueryAsync<WorkOrderInfo>(
|
|
|
+ "SELECT WorkOrd AS WorkOrd, COALESCE(NULLIF(TRIM(`Domain`), ''), CAST(tenant_id AS CHAR(20))) AS Domain FROM WorkOrdMaster WHERE RecID IN (" + BuildInClause(ids) + ")",
|
|
|
+ GetInParameters(ids));
|
|
|
+
|
|
|
+ if (!workOrders.Any())
|
|
|
+ throw Oops.Oh("未找到对应的工单");
|
|
|
+
|
|
|
+ // 为每个域执行关闭操作
|
|
|
+ var domains = workOrders.Select(x => x.Domain).Distinct();
|
|
|
+ foreach (var domain in domains)
|
|
|
+ {
|
|
|
+ // 获取该域的工单号列表(竖线分隔)
|
|
|
+ var domainWorkOrds = string.Join("|", workOrders.Where(x => x.Domain == domain).Select(x => x.WorkOrd));
|
|
|
+
|
|
|
+ // 执行关闭逻辑(模拟存储过程)
|
|
|
+ await ExecuteCloseWorkOrders(domain, domainWorkOrds);
|
|
|
+ }
|
|
|
+
|
|
|
+ _db.Ado.CommitTran();
|
|
|
+ return new { message = "工单关闭成功" };
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _db.Ado.RollbackTran();
|
|
|
+ return new { message = $"工单关闭失败: {ex.Message}" };
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 构建 IN 子句参数
|
|
|
+ /// </summary>
|
|
|
+ private string BuildInClause(string ids)
|
|
|
+ {
|
|
|
+ var idList = ids.Split(',').Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x));
|
|
|
+ return string.Join(",", idList.Select((_, i) => $"@Id{i}"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取 IN 子句参数
|
|
|
+ /// </summary>
|
|
|
+ private SugarParameter[] GetInParameters(string ids)
|
|
|
+ {
|
|
|
+ var idList = ids.Split(',').Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x));
|
|
|
+ return idList.Select((id, i) => new SugarParameter($"@Id{i}", id)).ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 执行关闭工单逻辑(模拟存储过程 pr_MES_CloseWorkOrders)
|
|
|
+ /// </summary>
|
|
|
+ private async Task ExecuteCloseWorkOrders(string domain, string workOrds)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(workOrds))
|
|
|
+ return;
|
|
|
+
|
|
|
+ // 存储过程使用竖线分隔,但FIND_IN_SET需要逗号分隔
|
|
|
+ var workOrdsForFindInSet = workOrds.Replace("|", ",");
|
|
|
+
|
|
|
+ // 1. 更新工单状态为C
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "UPDATE WorkOrdMaster SET Status = 'C', UpdateUser = 'proc' WHERE tenant_id = @Domain AND FIND_IN_SET(WorkOrd, @WorkOrds) > 0",
|
|
|
+ new SugarParameter("@Domain", domain),
|
|
|
+ new SugarParameter("@WorkOrds", workOrdsForFindInSet));
|
|
|
+
|
|
|
+ // 2. 更新领料单状态为C
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "UPDATE NbrMaster SET Status = 'C' WHERE Type = 'SM' AND tenant_id = @Domain AND FIND_IN_SET(WorkOrd, @WorkOrds) > 0",
|
|
|
+ new SugarParameter("@Domain", domain),
|
|
|
+ new SugarParameter("@WorkOrds", workOrdsForFindInSet));
|
|
|
+
|
|
|
+ // 3. 更新领料单明细状态为C
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "UPDATE NbrDetail SET Status = 'C' WHERE tenant_id = @Domain AND Type = 'SM' AND Nbr IN (SELECT Nbr FROM NbrMaster WHERE Type = 'SM' AND Domain = @Domain AND FIND_IN_SET(WorkOrd, @WorkOrds) > 0)",
|
|
|
+ new SugarParameter("@Domain", domain),
|
|
|
+ new SugarParameter("@WorkOrds", workOrdsForFindInSet));
|
|
|
+
|
|
|
+ // 4. 更新退料单状态为C
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "UPDATE NbrMaster SET Status = 'C' WHERE Type = 'WOD' AND tenant_id = @Domain AND FIND_IN_SET(WorkOrd, @WorkOrds) > 0",
|
|
|
+ new SugarParameter("@Domain", domain),
|
|
|
+ new SugarParameter("@WorkOrds", workOrdsForFindInSet));
|
|
|
+
|
|
|
+ // 5. 更新退料单明细状态为C
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "UPDATE NbrDetail SET Status = 'C' WHERE tenant_id = @Domain AND Type = 'WOD' AND Nbr IN (SELECT Nbr FROM NbrMaster WHERE Type = 'WOD' AND Domain = @Domain AND FIND_IN_SET(WorkOrd, @WorkOrds) > 0)",
|
|
|
+ new SugarParameter("@Domain", domain),
|
|
|
+ new SugarParameter("@WorkOrds", workOrdsForFindInSet));
|
|
|
+
|
|
|
+ // 6. 删除库存占用
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "DELETE FROM ic_item_stockoccupy WHERE tenant_id = @Domain AND FIND_IN_SET(morder_mo, @WorkOrds) > 0",
|
|
|
+ new SugarParameter("@Domain", domain),
|
|
|
+ new SugarParameter("@WorkOrds", workOrdsForFindInSet));
|
|
|
+
|
|
|
+ // 7. 删除PO占用
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ "DELETE FROM srm_po_occupy WHERE tenant_id = @Domain AND FIND_IN_SET(morder_mo, @WorkOrds) > 0",
|
|
|
+ new SugarParameter("@Domain", domain),
|
|
|
+ new SugarParameter("@WorkOrds", workOrdsForFindInSet));
|
|
|
+
|
|
|
+ // 8. 删除工单占用
|
|
|
await _db.Ado.ExecuteCommandAsync(
|
|
|
- "CALL pr_MES_CloseWorkOrders(@Ids)",
|
|
|
- new SugarParameter("@Ids", input.Ids.Trim()));
|
|
|
- return new { message = "已提交关闭" };
|
|
|
+ "DELETE FROM mes_mooccupy WHERE tenant_id = @Domain AND FIND_IN_SET(moo_mo, @WorkOrds) > 0",
|
|
|
+ new SugarParameter("@Domain", domain),
|
|
|
+ new SugarParameter("@WorkOrds", workOrdsForFindInSet));
|
|
|
}
|
|
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
@@ -729,7 +851,7 @@ public class WorkOrderSchedulingService : IDynamicApiController, ITransient
|
|
|
`Descr`, `Domain`, `ChargeCode`, `Machine`, `RunCrew`, `MilestoneOp`, `OP`, `StdOp`, `ItemNum`, `WorkCtr`,
|
|
|
`Ufld1`, `Ufld3`, `Setup`, `MachinesperOp`, `Labor`, `RunTime`, `MachBdnRate`, `WorkCode`, `StdSetupTime`, `Engineer`,
|
|
|
`WorkOrd`, `WorkOrdMasterRecID`, `ERPfld1`, `QtyOrded`, `ProcessOut`, `ProcessOutDay`, `ProcessOutSupp`,
|
|
|
- `IsActive`, `Status`, `ProdLine`, `CreateTime`, `CreateUser`, `tenant_id`,`CommentIndex`,`WaitTime`
|
|
|
+ `IsActive`, `Status`, `ProdLine`, `CreateTime`, `CreateUser`, `tenant_id`,`CommentIndex`,`WaitTime`,`QtyComplete`,`ParentOp`
|
|
|
)
|
|
|
SELECT
|
|
|
a.`Descr`,
|
|
|
@@ -764,7 +886,7 @@ public class WorkOrderSchedulingService : IDynamicApiController, ITransient
|
|
|
LEFT(IFNULL(b.`Line`, ''), 8),
|
|
|
NOW(3),
|
|
|
@CreateUser,
|
|
|
- w.`tenant_id`,CAST(IFNULL(a.`MilestoneOp`, 0) AS UNSIGNED),0
|
|
|
+ w.`tenant_id`,CAST(IFNULL(a.`MilestoneOp`, 0) AS UNSIGNED),0,0,0
|
|
|
FROM WorkOrdMaster w
|
|
|
LEFT JOIN RoutingOpDetail a ON w.`ItemNum` = a.`RoutingCode`
|
|
|
LEFT JOIN ProdLineDetail b ON a.`RoutingCode` = b.`Part` AND a.`Op` = b.`Op`
|