From 4d5893d12407f9a433ed1887bbda6194f7ca7f77 Mon Sep 17 00:00:00 2001 From: git_rabbit Date: Wed, 28 Jan 2026 14:52:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=BA=93=E5=AD=98=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=87=BA=E8=B4=A7=E6=93=8D=E4=BD=9C=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现物料出货功能,包括库存校验、事务处理、单据生成和关联订单/工单更新 支持蓝单正向和红单逆向操作,包含完整的参数校验和错误处理 --- .../MmInventoryController.cs | 27 +++ DOAN.Model/MES/Material/Dto/ShipmentDto.cs | 52 +++++ .../Material/IService/IMmInventoryService.cs | 6 + .../MES/Material/MmInventoryService.cs | 219 +++++++++++++++++- 4 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 DOAN.Model/MES/Material/Dto/ShipmentDto.cs diff --git a/DOAN.Admin.WebApi/Controllers/MES/Material/productionMaterial/MmInventoryController.cs b/DOAN.Admin.WebApi/Controllers/MES/Material/productionMaterial/MmInventoryController.cs index e59b661..e10132c 100644 --- a/DOAN.Admin.WebApi/Controllers/MES/Material/productionMaterial/MmInventoryController.cs +++ b/DOAN.Admin.WebApi/Controllers/MES/Material/productionMaterial/MmInventoryController.cs @@ -288,5 +288,32 @@ namespace DOAN.Admin.WebApi.Controllers.BZFM return SUCCESS(response); } + + /// + /// 出货操作 + /// + /// + [HttpPost("Shipment")] + [AllowAnonymous] + [Log(Title = "出货操作", BusinessType = BusinessType.INSERT)] + public IActionResult Shipment([FromBody] ShipmentDto parm) + { + try + { + string response = _MmInventoryService.Shipment(parm); + if (response == "ok") + { + return ToResponse(new ApiResult(200, "ok")); + } + else + { + return ToResponse(new ApiResult(500, response)); + } + } + catch (Exception) + { + throw; + } + } } } diff --git a/DOAN.Model/MES/Material/Dto/ShipmentDto.cs b/DOAN.Model/MES/Material/Dto/ShipmentDto.cs new file mode 100644 index 0000000..52335c6 --- /dev/null +++ b/DOAN.Model/MES/Material/Dto/ShipmentDto.cs @@ -0,0 +1,52 @@ +using System.ComponentModel.DataAnnotations; + +namespace DOAN.Model.BZFM.Dto +{ + /// + /// 出货请求参数 + /// + public class ShipmentDto + { + [Required(ErrorMessage = "物料编码不能为空")] + public string MaterialCode { get; set; } + + [Required(ErrorMessage = "出货数量不能为空")] + public decimal Quantity { get; set; } + + [Required(ErrorMessage = "仓库编码不能为空")] + public string WarehouseCode { get; set; } + + [Required(ErrorMessage = "库位编码不能为空")] + public string LocationCode { get; set; } + + public string BatchNo { get; set; } + + [Required(ErrorMessage = "操作员不能为空")] + public string Operator { get; set; } + + public string Workorder { get; set; } + + public string WorkorderRaw { get; set; } + + public string OrderNo { get; set; } + + [Required(ErrorMessage = "交易类型不能为空")] + public string TransactionType { get; set; } + + /// + /// 备注 + /// + public string Remarks { get; set; } + + /// + /// 客户订单号 + /// + [Required(ErrorMessage = "客户订单号不能为空")] + public string CustomerOrder { get; set; } + + /// + /// 单据类型:1-蓝单正向,2-红单逆向 + /// + public int ReceiptType { get; set; } = 1; + } +} \ No newline at end of file diff --git a/DOAN.Service/MES/Material/IService/IMmInventoryService.cs b/DOAN.Service/MES/Material/IService/IMmInventoryService.cs index e8b69fd..3ef9c7a 100644 --- a/DOAN.Service/MES/Material/IService/IMmInventoryService.cs +++ b/DOAN.Service/MES/Material/IService/IMmInventoryService.cs @@ -51,6 +51,12 @@ namespace DOAN.Service.BZFM.IBZFMService /// /// string RevokeReceipt(MmInventoryRevokeDto parm); + /// + /// 出货操作 成功返回ok + /// + /// + /// + string Shipment(ShipmentDto parm); /// /// 导入 diff --git a/DOAN.Service/MES/Material/MmInventoryService.cs b/DOAN.Service/MES/Material/MmInventoryService.cs index 23783b6..6b1a335 100644 --- a/DOAN.Service/MES/Material/MmInventoryService.cs +++ b/DOAN.Service/MES/Material/MmInventoryService.cs @@ -1,7 +1,8 @@ using DOAN.Model; using DOAN.Model.BZFM; using DOAN.Model.BZFM.Dto; -using DOAN.Model.Mobile.Dto; +using DOAN.Model.MES.order; +using DOAN.Model.MES.product; using DOAN.Repository; using DOAN.Service.BZFM.IBZFMService; using Infrastructure.Attribute; @@ -896,5 +897,221 @@ namespace DOAN.Service.BZFM return e.Message; } } + + /// + /// 出货操作 成功返回ok + /// + /// + /// + public string Shipment(ShipmentDto parm) + { + try + { + DateTime nowDate = DateTime.Now; + + // 计算有符号变动量(蓝单为正,红单为负) + decimal delta = GetSignedQuantity(parm.ReceiptType, parm.Quantity); + + // 校验物料和库位 + var mmMaterial = Context + .Queryable() + .Where(it => it.MaterialCode == parm.MaterialCode) + .First(); + if (mmMaterial == null) + return "物料不存在!"; + + var mmLocation = Context + .Queryable() + .Where(it => it.WarehouseCode == parm.WarehouseCode) + .Where(it => it.LocationCode == parm.LocationCode) + .First(); + if (mmLocation == null) + return "仓库编码或库位编码不存在!"; + + // 启用事务 + Context.Ado.BeginTran(); + + // 订单关联处理 + if (!string.IsNullOrEmpty(parm.CustomerOrder)) + { + // 判断订单号是否存在 + var orderPurchase = Context + .Queryable() + .Where(o => o.OrderNoMes == parm.CustomerOrder) + .First(); + if (orderPurchase == null) + { + Context.Ado.RollbackTran(); + return "订单号不存在"; + } + + // 判断工单是否存在 + if (!string.IsNullOrEmpty(parm.Workorder)) + { + var workorderInfo = Context + .Queryable() + .Where(it => it.Workorder == parm.Workorder) + .First(); + if (workorderInfo == null) + { + Context.Ado.RollbackTran(); + return "工单不存在"; + } + + // 判断工单主体型号和订单物料号是否匹配 + if (workorderInfo.productionCode != orderPurchase.MaterialCode) + { + Context.Ado.RollbackTran(); + return "工单主体型号和订单物料号不匹配"; + } + } + } + + // 获取现有库存 + var mmInventory = Context + .Queryable() + .Where(it => it.MaterialCode == parm.MaterialCode) + .Where(it => it.BatchNo == parm.BatchNo) + .Where(it => it.WarehouseCode == parm.WarehouseCode) + .Where(it => it.LocationCode == parm.LocationCode) + .First(); + + // 库存检查 + if (mmInventory == null) + { + if (parm.ReceiptType == 1) + { + Context.Ado.RollbackTran(); + return "库存不存在,禁止出货!"; + } + // 红单处理 + var newInventory = new MmInventory() + { + MaterialCode = mmMaterial.MaterialCode, + MaterialName = mmMaterial.MaterialName, + SupplierCode = mmMaterial.SupplierCode, + SupplierName = mmMaterial.SupplierName, + LocationCode = mmLocation.LocationCode, + LocationName = mmLocation.LocationName, + WarehouseCode = mmLocation.WarehouseCode, + WarehouseName = mmLocation.WarehouseName, + BatchNo = parm.BatchNo, + CurrentQty = -delta, + Unit = mmMaterial.Unit, + LastUpdatedTime = null, + CreatedTime = nowDate, + }; + Context.Insertable(newInventory).ExecuteCommand(); + } + else + { + if (mmInventory.CurrentQty - delta < 0) + { + Context.Ado.RollbackTran(); + return "库存不足,无法出货!"; + } + // 更新库存 + mmInventory.CurrentQty -= delta; + Context + .Updateable(mmInventory) + .UpdateColumns(it => it.CurrentQty) + .ExecuteCommand(); + } + + // 生成出货单号 + var shipmentNo = GenerateReceiptNo("CH"); + + // 创建出货记录 + MmRecordOutbound newRecord = new() + { + OutboundNo = shipmentNo, + BatchNo = parm.BatchNo, + Operator = parm.Operator, + MaterialCode = mmMaterial.MaterialCode, + MaterialName = mmMaterial.MaterialName, + LocationCode = mmLocation.LocationCode, + LocationName = mmLocation.LocationName, + WarehouseCode = mmLocation.WarehouseCode, + WarehouseName = mmLocation.WarehouseName, + Quantity = -delta, + Unit = mmMaterial.Unit, + CreatedTime = nowDate, + TransactionType = parm.TransactionType, + Workorder = parm.Workorder, + WorkorderRaw = parm.WorkorderRaw, + OrderNo = parm.CustomerOrder, + Remarks = parm.Remarks, + }; + Context.Insertable(newRecord).ExecuteCommand(); + + // 更新工单和订单信息 + if (!string.IsNullOrEmpty(parm.CustomerOrder) && !string.IsNullOrEmpty(parm.Workorder)) + { + // 获取当前工单信息 + var workorderInfo = Context + .Queryable() + .Where(it => it.Workorder == parm.Workorder) + .First(); + + // 计算累计出货数量(使用delta值,考虑单据类型的影响) + int currentShipmentNum = workorderInfo.ShipmentNum ?? 0; + int newShipmentNum = currentShipmentNum + (int)delta; + + // 验证出货数量有效性 + if (newShipmentNum < 0) + { + Context.Ado.RollbackTran(); + return "累计出货数量不能为负数"; + } + + // 更新工单信息 + Context + .Updateable() + .Where(it => it.Workorder == parm.Workorder) + .SetColumns(it => it.ShipmentNum == newShipmentNum) + .SetColumns(it => it.CustomerOrder == parm.CustomerOrder) + .ExecuteCommand(); + + // 修改采购订单信息 + var orderPurchase = Context + .Queryable() + .Where(o => o.OrderNoMes == parm.CustomerOrder) + .First(); + if (orderPurchase != null) + { + int newQuantity = Context + .Queryable() + .Where(it => it.CustomerOrder == parm.CustomerOrder) + .Sum(it => it.ShipmentNum) ?? 0; + + orderPurchase.DeliveryQuantity = newQuantity; + if (orderPurchase.DeliveryQuantity > orderPurchase.DemandQuantity) + { + Context.Ado.RollbackTran(); + return "交货数量超过订单需求数量"; + } + if (orderPurchase.DeliveryQuantity == orderPurchase.DemandQuantity) + { + orderPurchase.Orderindicator = 1; + } + else + { + orderPurchase.Orderindicator = 0; + } + Context.Updateable(orderPurchase).ExecuteCommand(); + } + } + + // 提交事务 + Context.Ado.CommitTran(); + return "ok"; + } + catch (Exception ex) + { + // 回滚操作 + Context.Ado.RollbackTran(); + return ex.Message; + } + } } }