新增库存出入库单据及下拉选项相关接口

本次提交为物料库存管理模块增加了出入库单据的创建接口(入库单、出库单),并补充了物料及出入库类型下拉选项接口。新增了相关DTO数据结构,服务接口与实现,完善了库存变更的业务流程,所有操作均支持事务控制,提升了数据一致性和系统健壮性。
This commit is contained in:
2025-12-29 11:43:25 +08:00
parent 7d69dbc43f
commit 8b13457ff6
7 changed files with 464 additions and 3 deletions

View File

@@ -98,5 +98,86 @@ namespace DOAN.Admin.WebApi.Controllers.BZFM
return ToResponse(_MmInventoryService.Delete(idArr));
}
/// <summary>
/// 获取物料清单下拉数据
/// </summary>
/// <returns></returns>
[HttpPost("GetMaterialOption")]
public IActionResult GetMaterialOption()
{
var response = _MmInventoryService.GetMaterialOption();
return SUCCESS(response);
}
/// <summary>
/// 获取出/入库操作类型下拉数据
/// </summary>
/// <returns></returns>
[HttpPost("GetTransactionOption")]
public IActionResult GetTransactionOption()
{
var response = _MmInventoryService.GetTransactionOption();
return SUCCESS(response);
}
/// <summary>
/// 创建入库单
/// </summary>
/// <returns></returns>
[HttpPost("CreateInboundReceipt")]
[AllowAnonymous]
[Log(Title = "入库单", BusinessType = BusinessType.INSERT)]
public IActionResult CreateInboundReceipt([FromBody] InboundReceiptDto parm)
{
try
{
string response = _MmInventoryService.CreateInboundReceipt(parm);
if(response == "ok")
{
return ToResponse(new ApiResult(200, "ok"));
}
else
{
return ToResponse(new ApiResult(500, response));
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 创建出库单
/// </summary>
/// <returns></returns>
[HttpPost("CreateOutboundReceipt")]
[AllowAnonymous]
[Log(Title = "出库单", BusinessType = BusinessType.INSERT)]
public IActionResult CreateOutboundReceipt([FromBody] OutboundReceiptDto parm)
{
try
{
string response = _MmInventoryService.CreateOutboundReceipt(parm);
if (response == "ok")
{
return ToResponse(new ApiResult(200, "ok"));
}
else
{
return ToResponse(new ApiResult(500, response));
}
}
catch (Exception)
{
throw;
}
}
}
}

View File

@@ -60,4 +60,15 @@ namespace DOAN.Model.BZFM.Dto
[ExcelColumn(Name = "物料类型(原材料/半成品/产成品/打包材料/辅料)")]
public string TypeLabel { get; set; }
}
/// <summary>
/// 物料清单下拉内容
/// </summary>
public class MmMaterialOption
{
public string MaterialCode { get; set; }
public string MaterialName { get; set; }
public string Specification { get; set; }
public string CategoryCode { get; set; }
}
}

View File

@@ -62,4 +62,38 @@ namespace DOAN.Model.BZFM.Dto
[ExcelColumn(Name = "入库类型")]
public string TransactionTypeLabel { get; set; }
}
/// <summary>
/// 入库单
/// </summary>
public class InboundReceiptDto
{
public string Unit { get; set; }
public DateTime? CreatedTime { get; set; }
public string Remarks { get; set; }
public string Operator { get; set; }
public string SupplierName { get; set; }
public string SupplierCode { get; set; }
public DateTime? ExpiryDate { get; set; }
public DateTime? ProductionDate { get; set; }
public string BatchNo { get; set; }
[Required(ErrorMessage = "入库类型不能为空")]
public string TransactionType { get; set; }
[Required(ErrorMessage = "入库数量不能为空")]
public decimal Quantity { get; set; }
public string LocationName { get; set; }
public string LocationCode { get; set; }
public string WarehouseName { get; set; }
[Required(ErrorMessage = "仓库编码不能为空")]
public string WarehouseCode { get; set; }
public string MaterialName { get; set; }
[Required(ErrorMessage = "物料编码不能为空")]
public string MaterialCode { get; set; }
[Required(ErrorMessage = "入库单号不能为空")]
public string InboundNo { get; set; }
[ExcelColumn(Name = "入库类型")]
public string TransactionTypeLabel { get; set; }
// 1-蓝单正向 2-红单逆向
public int ReceiptType { get; set; } = 1;
}
}

View File

@@ -56,4 +56,50 @@ namespace DOAN.Model.BZFM.Dto
[ExcelColumn(Name = "出库类型")]
public string TransactionTypeLabel { get; set; }
}
/// <summary>
/// 出库单
/// </summary>
public class OutboundReceiptDto
{
[Required(ErrorMessage = "出库单号不能为空")]
public string OutboundNo { get; set; }
[Required(ErrorMessage = "物料编码不能为空")]
public string MaterialCode { get; set; }
public string MaterialName { get; set; }
[Required(ErrorMessage = "仓库编码不能为空")]
public string WarehouseCode { get; set; }
public string WarehouseName { get; set; }
public string LocationCode { get; set; }
public string LocationName { get; set; }
[Required(ErrorMessage = "出库数量不能为空")]
public decimal Quantity { get; set; }
public string Unit { get; set; }
[Required(ErrorMessage = "出库类型不能为空")]
public string TransactionType { get; set; }
public string BatchNo { get; set; }
public string OrderNo { get; set; }
public string Operator { get; set; }
public string Remarks { get; set; }
public DateTime? CreatedTime { get; set; }
[ExcelColumn(Name = "出库类型")]
public string TransactionTypeLabel { get; set; }
// 1-蓝单正向 2-红单逆向
public int ReceiptType { get; set; } = 1;
}
}

View File

@@ -38,4 +38,13 @@ namespace DOAN.Model.BZFM.Dto
[ExcelColumn(Name = "状态(停用/启用)")]
public string StatusLabel { get; set; }
}
/// <summary>
/// 出入库类别对照表下拉内容
/// </summary>
public class MmTransactionOption
{
public string Label { get; set; }
public string Value { get; set; }
}
}

View File

@@ -16,6 +16,25 @@ namespace DOAN.Service.BZFM.IBZFMService
MmInventory AddMmInventory(MmInventory parm);
int UpdateMmInventory(MmInventory parm);
/// <summary>
/// 获取物料清单下拉数据
/// </summary>
List<MmMaterialOption> GetMaterialOption();
/// <summary>
/// 获取出/入库操作类型下拉数据
/// </summary>
List<MmTransactionOption> GetTransactionOption();
/// <summary>
/// 创建入库单 成功返回ok ReceiptType = 1蓝单 ReceiptType = 2 红单(逆向)
/// </summary>
string CreateInboundReceipt(InboundReceiptDto parm);
/// <summary>
/// 创建出库单 成功返回ok
/// </summary>
string CreateOutboundReceipt(OutboundReceiptDto parm);
}
}

View File

@@ -1,9 +1,10 @@
using Infrastructure.Attribute;
using Infrastructure.Extensions;
using DOAN.Model.BZFM.Dto;
using DOAN.Model.BZFM;
using DOAN.Model.BZFM.Dto;
using DOAN.Model.Mobile.Dto;
using DOAN.Repository;
using DOAN.Service.BZFM.IBZFMService;
using Infrastructure.Attribute;
using Infrastructure.Extensions;
namespace DOAN.Service.BZFM
{
@@ -78,5 +79,265 @@ namespace DOAN.Service.BZFM
return predicate;
}
public List<MmMaterialOption> GetMaterialOption()
{
try
{
return Context.Queryable<MmMaterial>()
.Where(it => it.Status == "启用")
.Select(it => new MmMaterialOption
{
MaterialCode = it.MaterialCode,
MaterialName = it.MaterialName,
CategoryCode = it.CategoryCode,
Specification = it.Specification
})
.ToList();
}
catch (Exception)
{
throw;
}
}
public List<MmTransactionOption> GetTransactionOption()
{
try
{
return Context.Queryable<MmTransactionType>()
.Where(it => it.Status == "启用")
.Select(it => new MmTransactionOption
{
Label = it.TypeName,
Value = it.TypeCode
})
.ToList();
}
catch (Exception)
{
throw;
}
}
public string CreateInboundReceipt(InboundReceiptDto parm)
{
try
{
DateTime nowDate = DateTime.Now;
// 验证信息
// 校验蓝单红单
int receiptType = parm.ReceiptType;
decimal CurrentQty = parm.Quantity;
if(receiptType == 1)
{
CurrentQty = Math.Abs(CurrentQty);
}
else
{
CurrentQty = - Math.Abs(CurrentQty);
}
MmMaterial mmMaterial = Context.Queryable<MmMaterial>()
.Where(it => it.MaterialCode == parm.MaterialCode)
.First();
if(mmMaterial == null)
{
return "物料不存在!";
}
// 验证信息
MmLocation mmLocation = Context.Queryable<MmLocation>()
.Where(it => it.LocationCode == parm.LocationCode)
.First();
if (mmLocation == null)
{
return "仓库不存在!";
}
bool hasInventory = true;
MmInventory mmInventory = Context.Queryable<MmInventory>()
.Where(it => it.MaterialCode == parm.MaterialCode)
.Where(it => it.BatchNo == parm.BatchNo)
.Where(it => it.LocationCode == parm.LocationCode)
.First();
if(mmInventory == null)
{
hasInventory = false;
}
// 启用事务
Context.Ado.BeginTran();
if (!hasInventory)
{
// 添加库存
MmInventory newInventory = new MmInventory()
{
MaterialCode = mmMaterial.MaterialCode,
LocationCode = mmLocation.LocationCode,
LocationName = mmLocation.LocationName,
WarehouseCode = mmLocation.WarehouseCode,
WarehouseName = mmLocation.WarehouseName,
BatchNo = parm.BatchNo,
CurrentQty = parm.Quantity,
Unit = parm.Unit,
ExpiryDate = parm.ExpiryDate,
LastUpdatedTime = null,
ProductionDate = parm.ProductionDate,
CreatedTime = nowDate
};
Context.Insertable(newInventory).ExecuteCommand();
}
else
{
Context.Updateable(mmInventory)
.SetColumns(it => it.CurrentQty == it.CurrentQty + CurrentQty)
.ExecuteCommand();
}
// 插入记录
MmRecordInbound newRecord = new MmRecordInbound()
{
// TODO处理入库单号自动累计增加
InboundNo = "RK" + DateTime.Now.ToString("yyyyMMdd"),
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 = parm.Quantity,
Unit = parm.Unit,
SupplierCode = parm.SupplierCode,
SupplierName = parm.SupplierName,
ProductionDate = parm.ProductionDate,
ExpiryDate = parm.ExpiryDate,
CreatedTime = nowDate,
TransactionType = parm.TransactionType,
Remarks = parm.Remarks
};
Context.Insertable(newRecord).ExecuteCommand();
Context.Ado.CommitTran();
return "ok";
}
catch (Exception ex)
{
// 回滚操作
Context.Ado.RollbackTran();
return ex.Message;
}
}
public string CreateOutboundReceipt(OutboundReceiptDto parm)
{
try
{
DateTime nowDate = DateTime.Now;
// 验证信息
// 校验蓝单红单
int receiptType = parm.ReceiptType;
decimal CurrentQty = parm.Quantity;
if (receiptType == 1)
{
CurrentQty = Math.Abs(CurrentQty);
}
else
{
CurrentQty = -Math.Abs(CurrentQty);
}
MmMaterial mmMaterial = Context.Queryable<MmMaterial>()
.Where(it => it.MaterialCode == parm.MaterialCode)
.First();
if (mmMaterial == null)
{
return "物料不存在!";
}
// 验证信息
MmLocation mmLocation = Context.Queryable<MmLocation>()
.Where(it => it.LocationCode == parm.LocationCode)
.First();
if (mmLocation == null)
{
return "仓库不存在!";
}
bool hasInventory = true;
MmInventory mmInventory = Context.Queryable<MmInventory>()
.Where(it => it.MaterialCode == parm.MaterialCode)
.Where(it => it.BatchNo == parm.BatchNo)
.Where(it => it.LocationCode == parm.LocationCode)
.First();
if (mmInventory == null)
{
hasInventory = false;
}
// 启用事务
Context.Ado.BeginTran();
if (!hasInventory)
{
// 添加库存
MmInventory newInventory = new MmInventory()
{
MaterialCode = mmMaterial.MaterialCode,
LocationCode = mmLocation.LocationCode,
LocationName = mmLocation.LocationName,
WarehouseCode = mmLocation.WarehouseCode,
WarehouseName = mmLocation.WarehouseName,
BatchNo = parm.BatchNo,
CurrentQty = - parm.Quantity,
Unit = parm.Unit,
LastUpdatedTime = null,
CreatedTime = nowDate
};
Context.Insertable(newInventory).ExecuteCommand();
}
else
{
Context.Updateable(mmInventory)
.SetColumns(it => it.CurrentQty == it.CurrentQty - CurrentQty)
.ExecuteCommand();
}
// 插入记录
MmRecordOutbound newRecord = new MmRecordOutbound()
{
// TODO处理出库单号自动累计增加
OutboundNo = "CK" + DateTime.Now.ToString("yyyyMMdd"),
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 = parm.Quantity,
Unit = parm.Unit,
CreatedTime = nowDate,
TransactionType = parm.TransactionType,
Remarks = parm.Remarks
};
Context.Insertable(newRecord).ExecuteCommand();
Context.Ado.CommitTran();
return "ok";
}
catch (Exception ex)
{
// 回滚操作
Context.Ado.RollbackTran();
return ex.Message;
}
}
}
}