From cd03b4d5dc6fee4c5004dcc361794ee212fc76c6 Mon Sep 17 00:00:00 2001 From: git_rabbit Date: Sat, 7 Mar 2026 17:16:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=B7=A5=E5=8D=95=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=89=B9=E9=87=8F=E5=88=A0=E9=99=A4=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E5=92=8C=E6=89=B9=E9=87=8F=E9=A2=86=E6=96=99=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在工单DTO中增加领料数和成品在库数字段 - 实现批量删除工单功能并进行前置校验 - 实现根据工单ID批量领料功能 - 优化原材料领料外联查询显示炉号 - 修复领料查询条件忽略备注为null的记录问题 --- .../MES/Product/ProWorkorderController.cs | 49 +++++++ DOAN.Model/MES/Material/Dto/MmInventoryDto.cs | 3 + DOAN.Model/MES/Product/Dto/ProWorkorderDto.cs | 31 +++++ .../MES/Material/MmInventoryService.cs | 9 +- .../IService/IProWorkorderCoreService.cs | 8 ++ .../IService/IProWorkorderMaterialService.cs | 7 + .../Product/IService/IProWorkorderService.cs | 2 + .../MES/Product/ProWorkorderCoreService.cs | 93 +++++++++++-- .../Product/ProWorkorderMaterialService.cs | 128 +++++++++++++++++- .../MES/Product/ProWorkorderQueryService.cs | 21 ++- .../MES/Product/ProWorkorderService.cs | 12 ++ .../MES/Quality/IPQC/QcScrapRecordsService.cs | 6 +- DOAN.Service/Mobile/ReportFlowService.cs | 5 +- 13 files changed, 353 insertions(+), 21 deletions(-) diff --git a/DOAN.Admin.WebApi/Controllers/MES/Product/ProWorkorderController.cs b/DOAN.Admin.WebApi/Controllers/MES/Product/ProWorkorderController.cs index a6d70c6..15927f5 100644 --- a/DOAN.Admin.WebApi/Controllers/MES/Product/ProWorkorderController.cs +++ b/DOAN.Admin.WebApi/Controllers/MES/Product/ProWorkorderController.cs @@ -131,6 +131,29 @@ namespace DOAN.Admin.WebApi.Controllers return ToResponse(response); } + /// + /// 批量安全删除工单 + /// + /// + [HttpPost("BatchDeleteWorkOrder")] + [ActionPermissionFilter(Permission = "productManagement:proworkorder:add")] + [Log(Title = "删除工单", BusinessType = BusinessType.DELETE)] + public IActionResult BatchDeleteWorkOrder([FromBody] WorkOrderBatchDto parm) + { + try + { + var response = _ProWorkorderService.BatchDeleteWorkOrder(parm); + + return SUCCESS(response); + } + catch (Exception ex) + { + + return ToResponse(new ApiResult(500,ex.Message)); + } + + } + ///// ///// 生成工单号 ///// @@ -553,6 +576,32 @@ namespace DOAN.Admin.WebApi.Controllers } } + /// + /// 批量领料接口 + /// + /// 领料请求参数 + /// 操作结果 + [HttpPost("BatchTakeMaterialByIds")] + [ActionPermissionFilter(Permission = "productManagement:proworkorder:edit")] + [Log(Title = "批量领料", BusinessType = BusinessType.UPDATE)] + public IActionResult BatchTakeMaterialByIds([FromBody] WorkOrderBatchDto parm) + { + try + { + if (parm == null) + { + return ToResponse(ApiResult.Error($"领料请求参数不能为空")); + } + + var response = _ProWorkorderMaterialService.BatchTakeMaterialByIds(parm); + return SUCCESS(response); + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message)); + } + } + /// /// 成品入库接口 /// diff --git a/DOAN.Model/MES/Material/Dto/MmInventoryDto.cs b/DOAN.Model/MES/Material/Dto/MmInventoryDto.cs index 6bee835..c61e7ec 100644 --- a/DOAN.Model/MES/Material/Dto/MmInventoryDto.cs +++ b/DOAN.Model/MES/Material/Dto/MmInventoryDto.cs @@ -52,6 +52,9 @@ namespace DOAN.Model.BZFM.Dto public string SupplierName { get; set; } + // 炉号(原材料领料外联查询) + public string StoveCode { get; set; } + public string BatchNo { get; set; } public decimal CurrentQty { get; set; } diff --git a/DOAN.Model/MES/Product/Dto/ProWorkorderDto.cs b/DOAN.Model/MES/Product/Dto/ProWorkorderDto.cs index 496b62a..44416e7 100644 --- a/DOAN.Model/MES/Product/Dto/ProWorkorderDto.cs +++ b/DOAN.Model/MES/Product/Dto/ProWorkorderDto.cs @@ -60,6 +60,11 @@ namespace DOAN.Model.MES.product.Dto public int PlanNum { get; set; } = 0; public int ShipmentNum { get; set; } = 0; + // 领料数 + public int MaterialNum { get; set; } = 0; + // 成品在库数 + public int InInventoryNum { get; set; } = 0; + public int ProductNum { get; set; } = 0; public int DefectNum { get; set; } = 0; public int? Sort { get; set; } @@ -230,6 +235,32 @@ namespace DOAN.Model.MES.product.Dto public string BOMVersion { get; set; } } + /// + /// 工单批量操作Dto + /// + public class WorkOrderBatchDto + { + /// + /// ID号数组 + /// + public List Ids { get; set; } + + /// + /// 批号 + /// + public string BatchNo { get; set; } + + /// + /// 数量 + /// + public decimal Quantity { get; set; } + + /// + /// 操作人 + /// + public string Operator { get; set; } + } + /// /// 工单进度追溯 /// diff --git a/DOAN.Service/MES/Material/MmInventoryService.cs b/DOAN.Service/MES/Material/MmInventoryService.cs index 27d2c65..0947d7e 100644 --- a/DOAN.Service/MES/Material/MmInventoryService.cs +++ b/DOAN.Service/MES/Material/MmInventoryService.cs @@ -32,7 +32,14 @@ namespace DOAN.Service.BZFM var response = Queryable() .Where(predicate.ToExpression()) - .ToPage(parm); + .Select(it => new MmInventoryDto + { + Id = it.Id, + StoveCode = SqlFunc.Subqueryable() + .Where(sub=>sub.BatchNo == it.BatchNo) + .Select(sub => sub.StoveCode) + },true) + .ToPage(parm); return response; } diff --git a/DOAN.Service/MES/Product/IService/IProWorkorderCoreService.cs b/DOAN.Service/MES/Product/IService/IProWorkorderCoreService.cs index cf3eec5..6a839c8 100644 --- a/DOAN.Service/MES/Product/IService/IProWorkorderCoreService.cs +++ b/DOAN.Service/MES/Product/IService/IProWorkorderCoreService.cs @@ -44,5 +44,13 @@ namespace DOAN.Service.MES.product.IService /// /// int Insert_workOrder2(ProWorkorder proWorkorder, string next_id); + + /// + /// 批量删除工单 + /// + /// + /// + /// + int BatchDeleteWorkOrder(WorkOrderBatchDto parm); } } \ No newline at end of file diff --git a/DOAN.Service/MES/Product/IService/IProWorkorderMaterialService.cs b/DOAN.Service/MES/Product/IService/IProWorkorderMaterialService.cs index 15e78a3..20e0559 100644 --- a/DOAN.Service/MES/Product/IService/IProWorkorderMaterialService.cs +++ b/DOAN.Service/MES/Product/IService/IProWorkorderMaterialService.cs @@ -75,6 +75,13 @@ namespace DOAN.Service.MES.product.IService /// 操作结果 bool TakeMaterial(MaterialTakeRequestDto request); + /// + /// 根据工单Ids批量领料 + /// + /// 领料请求参数 + /// 操作结果 + int BatchTakeMaterialByIds(WorkOrderBatchDto request); + /// /// 根据工单成品入库 /// diff --git a/DOAN.Service/MES/Product/IService/IProWorkorderService.cs b/DOAN.Service/MES/Product/IService/IProWorkorderService.cs index 09eef6a..e2c5d30 100644 --- a/DOAN.Service/MES/Product/IService/IProWorkorderService.cs +++ b/DOAN.Service/MES/Product/IService/IProWorkorderService.cs @@ -33,6 +33,8 @@ namespace DOAN.Service.MES.product.IService int UpdateProWorkorder(ProWorkorder parm); + int BatchDeleteWorkOrder(WorkOrderBatchDto parm); + int Generate_workorder(ProWorkorderQueryDto2 parm); int Insert_workOrder(ProWorkorder proWorkorder, string next_id); diff --git a/DOAN.Service/MES/Product/ProWorkorderCoreService.cs b/DOAN.Service/MES/Product/ProWorkorderCoreService.cs index c716654..49c1519 100644 --- a/DOAN.Service/MES/Product/ProWorkorderCoreService.cs +++ b/DOAN.Service/MES/Product/ProWorkorderCoreService.cs @@ -4,13 +4,17 @@ using DOAN.Model.MES.product.Dto; using DOAN.Service.MES.product.IService; using Infrastructure.Attribute; using Infrastructure.Converter; +using Microsoft.IdentityModel.Tokens; namespace DOAN.Service.MES.product { /// /// 核心工单管理服务 /// - [AppService(ServiceType = typeof(IProWorkorderCoreService), ServiceLifetime = LifeTime.Transient)] + [AppService( + ServiceType = typeof(IProWorkorderCoreService), + ServiceLifetime = LifeTime.Transient + )] public class ProWorkorderCoreService : BaseService, IProWorkorderCoreService { /// @@ -51,7 +55,7 @@ namespace DOAN.Service.MES.product Workorder = model.Workorder, Log = "修改生产工单", ChangeTime = DateTime.Now, - Operator = model.CreatedBy + Operator = model.CreatedBy, }; Context.Insertable(logObj).ExecuteCommand(); @@ -210,7 +214,7 @@ namespace DOAN.Service.MES.product Log = "手动新增", ChangeTime = DateTime.Now, Operator = proWorkorder.CreatedBy, - CreatedBy = proWorkorder.CreatedBy + CreatedBy = proWorkorder.CreatedBy, }; UseTran2(() => { @@ -373,7 +377,8 @@ namespace DOAN.Service.MES.product } // 批量获取工单信息 - var workorders = Context.Queryable() + var workorders = Context + .Queryable() .Where(it => workorderIds.Contains(it.Id)) .ToList(); @@ -412,11 +417,81 @@ namespace DOAN.Service.MES.product int progress = CalculateProductionProgressByStatus(workorder.Status); // 返回包含生产进度的工单信息 - return new + return new { Workorder = workorder, ProductionProgress = progress }; + } + + public int BatchDeleteWorkOrder(WorkOrderBatchDto parm) + { + try { - Workorder = workorder, - ProductionProgress = progress - }; + // 删除参数校验 + if (parm == null) + { + throw new Exception("传入参数parm为空"); + } + List workorderIds = parm.Ids; + if (workorderIds.Count == 0) + { + throw new Exception("传入参数Ids为空,无修改工单"); + } + List deleteWorkOrderList = new(); + // 删除业务前置校验 + foreach (string workOrderId in workorderIds) + { + ProWorkorder workOrderInfo = Context + .Queryable() + .Where(it => it.Id == workOrderId) + .First(); + if (workOrderInfo == null || string.IsNullOrEmpty(workOrderInfo.Workorder)) + { + throw new Exception($"工单异常{workOrderId},未找到工单"); + } + string workOrder = workOrderInfo.Workorder; + // 是否有领料记录 + bool hasOutMaterialRecord = Context.Queryable() + .Where(it => it.Workorder == workOrder) + .Where(it => it.TransactionType == "领料出库") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) + .Any(); + if (hasOutMaterialRecord) + { + throw new Exception($"该工单{workOrder}存在领料记录,无法删除"); + } + // 是否存在成品入库记录 + bool hasWarehousingRecord = Context.Queryable() + .Where(it => it.Workorder == workOrder) + .Where(it => it.TransactionType == "生产入库") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) + .Any(); + if (hasWarehousingRecord) + { + throw new Exception($"该工单{workOrder}存在生产入库记录,无法删除"); + } + // 是否存在出货记录 + bool hasOutRecord = Context.Queryable() + .Where(it => it.Workorder == workOrder) + .Where(it => it.TransactionType == "出货出库") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) + .Any(); + if (hasOutRecord) + { + throw new Exception($"该工单{workOrder}存在出货记录,无法删除"); + } + bool hasReportRecord = Context.Queryable() + .Where(it => it.Workorder == workOrder) + .Any(); + if (hasReportRecord) + { + throw new Exception($"该工单{workOrder}存在报工记录,无法删除"); + } + deleteWorkOrderList.Add(workOrderInfo); + } + return Context.Deleteable(deleteWorkOrderList).ExecuteCommand(); + } + catch (Exception) + { + throw; + } } } -} \ No newline at end of file +} diff --git a/DOAN.Service/MES/Product/ProWorkorderMaterialService.cs b/DOAN.Service/MES/Product/ProWorkorderMaterialService.cs index 1050daa..3999ff6 100644 --- a/DOAN.Service/MES/Product/ProWorkorderMaterialService.cs +++ b/DOAN.Service/MES/Product/ProWorkorderMaterialService.cs @@ -1,3 +1,4 @@ +using Aliyun.OSS; using DOAN.Model.BZFM; using DOAN.Model.MES.order; using DOAN.Model.MES.order.Dto; @@ -9,6 +10,7 @@ using DOAN.Service.MES.product.IService; using DOAN.Service.Mobile; using Infrastructure.Attribute; using MailKit.Search; +using Microsoft.Data.SqlClient; using Microsoft.IdentityModel.Tokens; using SqlSugar.DistributedSystem.Snowflake; using System.Collections.Generic; @@ -37,7 +39,7 @@ namespace DOAN.Service.MES.product .Queryable() .Where(it => it.Workorder == workorder) .Where(it => it.TransactionType == "领料出库") - .Where(it => it.Remarks != "已撤销") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) //.WhereIF(isHideZero, it => it != "已撤销") .Select(it => new MaterialTakeDto { @@ -88,7 +90,7 @@ namespace DOAN.Service.MES.product .Queryable() .Where(it => it.Workorder == workorder) .Where(it => it.TransactionType == "生产入库") - .Where(it => it.Remarks != "已撤销") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) .Select(it => new ProductStorageDto { Id = it.Id, @@ -119,7 +121,7 @@ namespace DOAN.Service.MES.product .Queryable() .Where(it => it.Workorder == workorder) .Where(it => it.TransactionType == "出货出库") - .Where(it => it.Remarks != "已撤销") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) .Select(it => new ShipmentDto { Id = it.Id, @@ -271,7 +273,7 @@ namespace DOAN.Service.MES.product .LeftJoin((mri,pro)=>mri.Workorder == pro.Workorder) .Where((mri, pro) => mri.MaterialCode == workorderInfo.MaterialCode) .Where((mri, pro) => mri.TransactionType == "生产入库") - .Where((mri, pro) => mri.Remarks != "已撤销") + .Where((mri, pro) => mri.Remarks != "已撤销" || mri.Remarks == null) //.Where((mri, pro) => pro.ShipmentNum < pro.PlanNum) .OrderByDescending((mri, pro) => mri.Workorder) .Select( @@ -476,6 +478,7 @@ namespace DOAN.Service.MES.product ); // 示例返回true return result; + } catch (Exception ex) { @@ -485,6 +488,122 @@ namespace DOAN.Service.MES.product } } + /// + /// 工单批量领料 + /// + /// + /// + public int BatchTakeMaterialByIds(WorkOrderBatchDto parm) + { + try + { + // 删除参数校验 + if (parm == null) + { + throw new Exception("传入参数parm为空"); + } + List workorderIds = parm.Ids; + if (workorderIds.Count == 0) + { + throw new Exception("传入参数Ids为空,无修改工单"); + } + // 检查是否有采购记录 + var inboundRecordInfo = Context.Queryable() + .Where(it => it.BatchNo == parm.BatchNo) + .Where(it => it.TransactionType == "采购入库") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) + .First(); + if (inboundRecordInfo == null) + { + throw new Exception($"批次号{parm.BatchNo}无对应采购记录,请检查"); + } + // 批量领料业务 + int result = 0; + string standardMaterialCode = string.Empty; + // 业务处理前校验 + List batchTakeMaterialWorkOrderList = new(); + foreach (string workOrderId in workorderIds) + { + ProWorkorder workOrderInfo = Context + .Queryable() + .Where(it => it.Id == workOrderId) + .First(); + if (workOrderInfo == null || string.IsNullOrEmpty(workOrderInfo.Workorder)) + { + throw new Exception($"工单异常{workOrderId},未找到工单"); + } + if(workOrderInfo.RouteCode == "10") + { + throw new Exception("10线工单不可批量领料"); + } + if (string.IsNullOrEmpty(standardMaterialCode)) + { + standardMaterialCode = workOrderInfo.MaterialCode; + } + else + { + if(standardMaterialCode != workOrderInfo.MaterialCode) + { + throw new Exception($"工单{workOrderInfo.Workorder}原材料与上一个工单不一致,上一个工单{standardMaterialCode}"); + } + } + string workOrder = workOrderInfo.Workorder; + // 是否有领料记录 + bool hasOutMaterialRecord = Context.Queryable() + .Where(it => it.Workorder == workOrder) + .Where(it => it.TransactionType == "领料出库") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) + .Any(); + if (hasOutMaterialRecord) + { + throw new Exception($"该工单{workOrder}已存在领料记录,无法再次领料"); + } + batchTakeMaterialWorkOrderList.Add(workOrderInfo); + } + if(batchTakeMaterialWorkOrderList.Count == 0) + { + throw new Exception($"待领料工单数量异常"); + } + // 检查库存容量是否足够 + var inventoryInfo = Context.Queryable() + .Where(it => it.BatchNo == parm.BatchNo) + .Where(it => it.MaterialCode == batchTakeMaterialWorkOrderList[0].MaterialCode) + .Where(it => it.LocationCode == "YCL001") + .First(); + if (inventoryInfo.CurrentQty < batchTakeMaterialWorkOrderList.Count * parm.Quantity) + { + throw new Exception($"该批次库存数不足,目前库存数{inventoryInfo.CurrentQty},需要{batchTakeMaterialWorkOrderList.Count * parm.Quantity}"); + } + + foreach (var workorderInfo in batchTakeMaterialWorkOrderList) + { + MaterialTakeRequestDto takeDto = new() + { + Workorder = workorderInfo.Workorder, + WorkorderRaw = string.Empty, + Quantity = parm.Quantity, + MaterialInventoryId = inventoryInfo.Id, + StoveCode = inboundRecordInfo.StoveCode, + Operator = parm.Operator + }; + bool isOk = TakeMaterial(takeDto); + if (isOk) + { + result++; + } + else + { + throw new Exception($"领料异常结束,只成功{result}个工单"); + } + } + return result; + } + catch (Exception) + { + throw; + } + } + /// /// 工单成品入库接口 /// @@ -617,5 +736,6 @@ namespace DOAN.Service.MES.product throw new Exception(ex.Message); } } + } } diff --git a/DOAN.Service/MES/Product/ProWorkorderQueryService.cs b/DOAN.Service/MES/Product/ProWorkorderQueryService.cs index 1cce63b..df72d3d 100644 --- a/DOAN.Service/MES/Product/ProWorkorderQueryService.cs +++ b/DOAN.Service/MES/Product/ProWorkorderQueryService.cs @@ -1,3 +1,4 @@ +using DOAN.Model.BZFM; using DOAN.Model.MES.product; using DOAN.Model.MES.product.Dto; using DOAN.Repository; @@ -131,9 +132,25 @@ namespace DOAN.Service.MES.product var response = Context .Queryable() .Where(predicate) + .Select( + it => new ProWorkorderDto + { + Id = it.Id, + MaterialNum = (int) + SqlFunc.Abs( + SqlFunc + .Subqueryable() + .Where(sub => sub.Workorder == it.Workorder) + .Where(sub => sub.TransactionType == "领料出库") + .Where(sub => sub.Remarks != "已撤销" || sub.Remarks == null) + .Select(sub => sub.Quantity) + ), + InInventoryNum = it.ProductNum - it.ShipmentNum, + }, + true + ) .OrderBy(it => it.Sort) - .ToPage(parm); - + .ToPage(parm); return response; } diff --git a/DOAN.Service/MES/Product/ProWorkorderService.cs b/DOAN.Service/MES/Product/ProWorkorderService.cs index 137a7f7..40d29d2 100644 --- a/DOAN.Service/MES/Product/ProWorkorderService.cs +++ b/DOAN.Service/MES/Product/ProWorkorderService.cs @@ -98,6 +98,18 @@ namespace DOAN.Service.MES.product return _coreService.UpdateProWorkorder(model); } + /// + /// 安全批量删除工单 + /// + /// + /// + public int BatchDeleteWorkOrder(WorkOrderBatchDto parm) + { + return _coreService.BatchDeleteWorkOrder(parm); + } + + + /// /// 插入工单、新增工单 /// diff --git a/DOAN.Service/MES/Quality/IPQC/QcScrapRecordsService.cs b/DOAN.Service/MES/Quality/IPQC/QcScrapRecordsService.cs index 7a5da6a..8236f5a 100644 --- a/DOAN.Service/MES/Quality/IPQC/QcScrapRecordsService.cs +++ b/DOAN.Service/MES/Quality/IPQC/QcScrapRecordsService.cs @@ -183,7 +183,7 @@ namespace DOAN.Service.BZFM .Queryable() .Where(it => it.BatchNo == workorderInfo.FeedOrder) .Where(it => it.TransactionType == "采购入库") - .Where(it => it.Remarks != "已撤销") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) .First(); if (purchaseInfo != null) { @@ -241,7 +241,7 @@ namespace DOAN.Service.BZFM .Queryable() .Where(it => it.BatchNo == workorderInfo.FeedOrder) .Where(it => it.TransactionType == "采购入库") - .Where(it => it.Remarks != "已撤销") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) .First(); if (purchaseInfo != null) { @@ -725,7 +725,7 @@ namespace DOAN.Service.BZFM .Queryable() .Where(it => it.BatchNo == workorderInfo.FeedOrder) .Where(it => it.TransactionType == "采购入库") - .Where(it => it.Remarks != "已撤销") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) .First(); if (purchaseInfo != null) { diff --git a/DOAN.Service/Mobile/ReportFlowService.cs b/DOAN.Service/Mobile/ReportFlowService.cs index 9d90a8c..cb150d5 100644 --- a/DOAN.Service/Mobile/ReportFlowService.cs +++ b/DOAN.Service/Mobile/ReportFlowService.cs @@ -11,6 +11,7 @@ using DOAN.Service.BZFM; using DOAN.Service.Mobile.IService; using Infrastructure.Attribute; using Mapster; +using System; namespace DOAN.Service.Mobile; @@ -175,7 +176,7 @@ public class ReportFlowService : BaseService, IReportFlowServic .Queryable() .Where(it => it.Workorder == workorder) .Where(it => it.TransactionType == "领料出库") - .Where(it => it.Remarks != "已撤销") + .Where(it => it.Remarks != "已撤销" || it.Remarks == null) .First(); //1.检查领料是否超出限额 @@ -198,7 +199,7 @@ public class ReportFlowService : BaseService, IReportFlowServic .Queryable() .Where(it => it.Workorder == workorderRaw) .Where(it => it.TransactionType == "生产入库") - .Where(it => it.Remarks != "已撤销") + .Where(it => it.Remarks != "已撤销"|| it.Remarks == null) .First(); if (inboundRecord == null) {