feat(库存管理): 添加出货操作功能
实现物料出货功能,包括库存校验、事务处理、单据生成和关联订单/工单更新 支持蓝单正向和红单逆向操作,包含完整的参数校验和错误处理
This commit is contained in:
@@ -288,5 +288,32 @@ namespace DOAN.Admin.WebApi.Controllers.BZFM
|
||||
|
||||
return SUCCESS(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 出货操作
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
DOAN.Model/MES/Material/Dto/ShipmentDto.cs
Normal file
52
DOAN.Model/MES/Material/Dto/ShipmentDto.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace DOAN.Model.BZFM.Dto
|
||||
{
|
||||
/// <summary>
|
||||
/// 出货请求参数
|
||||
/// </summary>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Remarks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户订单号
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "客户订单号不能为空")]
|
||||
public string CustomerOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 单据类型:1-蓝单正向,2-红单逆向
|
||||
/// </summary>
|
||||
public int ReceiptType { get; set; } = 1;
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,12 @@ namespace DOAN.Service.BZFM.IBZFMService
|
||||
/// <param name="parm"></param>
|
||||
/// <returns></returns>
|
||||
string RevokeReceipt(MmInventoryRevokeDto parm);
|
||||
/// <summary>
|
||||
/// 出货操作 成功返回ok
|
||||
/// </summary>
|
||||
/// <param name="parm"></param>
|
||||
/// <returns></returns>
|
||||
string Shipment(ShipmentDto parm);
|
||||
|
||||
/// <summary>
|
||||
/// 导入
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 出货操作 成功返回ok
|
||||
/// </summary>
|
||||
/// <param name="parm"></param>
|
||||
/// <returns></returns>
|
||||
public string Shipment(ShipmentDto parm)
|
||||
{
|
||||
try
|
||||
{
|
||||
DateTime nowDate = DateTime.Now;
|
||||
|
||||
// 计算有符号变动量(蓝单为正,红单为负)
|
||||
decimal delta = GetSignedQuantity(parm.ReceiptType, parm.Quantity);
|
||||
|
||||
// 校验物料和库位
|
||||
var mmMaterial = Context
|
||||
.Queryable<MmMaterial>()
|
||||
.Where(it => it.MaterialCode == parm.MaterialCode)
|
||||
.First();
|
||||
if (mmMaterial == null)
|
||||
return "物料不存在!";
|
||||
|
||||
var mmLocation = Context
|
||||
.Queryable<MmLocation>()
|
||||
.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<OrderPurchase>()
|
||||
.Where(o => o.OrderNoMes == parm.CustomerOrder)
|
||||
.First();
|
||||
if (orderPurchase == null)
|
||||
{
|
||||
Context.Ado.RollbackTran();
|
||||
return "订单号不存在";
|
||||
}
|
||||
|
||||
// 判断工单是否存在
|
||||
if (!string.IsNullOrEmpty(parm.Workorder))
|
||||
{
|
||||
var workorderInfo = Context
|
||||
.Queryable<ProWorkorder>()
|
||||
.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<MmInventory>()
|
||||
.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<ProWorkorder>()
|
||||
.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<ProWorkorder>()
|
||||
.Where(it => it.Workorder == parm.Workorder)
|
||||
.SetColumns(it => it.ShipmentNum == newShipmentNum)
|
||||
.SetColumns(it => it.CustomerOrder == parm.CustomerOrder)
|
||||
.ExecuteCommand();
|
||||
|
||||
// 修改采购订单信息
|
||||
var orderPurchase = Context
|
||||
.Queryable<OrderPurchase>()
|
||||
.Where(o => o.OrderNoMes == parm.CustomerOrder)
|
||||
.First();
|
||||
if (orderPurchase != null)
|
||||
{
|
||||
int newQuantity = Context
|
||||
.Queryable<ProWorkorder>()
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user