Files
kunshan-bzfm-mes-backend/DOAN.Service/MES/Material/MmInventoryService.cs
git_rabbit 17efcdf2cf feat(材料管理): 添加原始工单号字段并优化注释
- 在MmInventoryDto中添加WorkorderRaw字段用于记录成品号之前的原始工单号
- 更新MmRecordOutboundDto中WorkorderRaw字段的注释说明
- 在库存服务中处理WorkorderRaw字段的映射
- 移除出库单处理中的冗余注释
2026-01-30 14:05:09 +08:00

1120 lines
46 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using DOAN.Model;
using DOAN.Model.BZFM;
using DOAN.Model.BZFM.Dto;
using DOAN.Model.MES.order;
using DOAN.Model.MES.product;
using DOAN.Repository;
using DOAN.Service.BZFM.IBZFMService;
using Infrastructure.Attribute;
using Infrastructure.Converter;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
namespace DOAN.Service.BZFM
{
/// <summary>
/// 库存表Service业务层处理
/// </summary>
[AppService(ServiceType = typeof(IMmInventoryService), ServiceLifetime = LifeTime.Transient)]
public class MmInventoryService : BaseService<MmInventory>, IMmInventoryService
{
/// <summary>
/// 查询库存表列表
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
public PagedInfo<MmInventoryDto> GetList(MmInventoryQueryDto parm)
{
var predicate = QueryExp(parm);
var response = Queryable()
.Where(predicate.ToExpression())
.ToPage<MmInventory, MmInventoryDto>(parm);
return response;
}
/// <summary>
/// 获取详情
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public MmInventory GetInfo(int Id)
{
var response = Queryable().Where(x => x.Id == Id).First();
return response;
}
/// <summary>
/// 添加库存表
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public MmInventory AddMmInventory(MmInventory model)
{
return Insertable(model).ExecuteReturnEntity();
}
/// <summary>
/// 修改库存表
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public int UpdateMmInventory(MmInventory model)
{
return Update(model, true);
}
/// <summary>
/// 查询导出表达式
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
private static Expressionable<MmInventory> QueryExp(MmInventoryQueryDto parm)
{
var predicate = Expressionable
.Create<MmInventory>()
.AndIF(
!string.IsNullOrEmpty(parm.MaterialCode),
m => m.MaterialCode.Contains(parm.MaterialCode)
)
.AndIF(
!string.IsNullOrEmpty(parm.SupplierCode),
m => m.SupplierCode.Contains(parm.SupplierCode)
)
.AndIF(
!string.IsNullOrEmpty(parm.WarehouseName),
m => m.WarehouseName.Contains(parm.WarehouseName)
)
.AndIF(
!string.IsNullOrEmpty(parm.WarehouseCode),
m => m.WarehouseCode.Contains(parm.WarehouseCode)
)
.AndIF(
!string.IsNullOrEmpty(parm.LocationCode),
m => m.LocationCode.Contains(parm.LocationCode)
)
.AndIF(
!string.IsNullOrEmpty(parm.SupplierName),
m => m.SupplierName.Contains(parm.SupplierName)
)
.AndIF(!string.IsNullOrEmpty(parm.BatchNo), m => m.BatchNo.Contains(parm.BatchNo));
return predicate;
}
public List<MmMaterialOption> GetMaterialOption()
{
try
{
return Context
.Queryable<MmMaterial>()
.Where(it => it.Status == "启用")
.Select(it => new MmMaterialOption
{
Id = it.Id,
MaterialCode = it.MaterialCode,
MaterialName = it.MaterialName,
CategoryCode = it.CategoryCode,
Specification = it.Specification,
SupplierCode = it.SupplierCode,
SupplierName = it.SupplierName,
Type = it.Type,
})
.OrderBy(it => it.Type)
.ToList();
}
catch (Exception)
{
throw;
}
}
public List<MmLocationOption> GetLocationOption()
{
try
{
return Context
.Queryable<MmLocation>()
.Where(it => it.Status == "启用")
.Select(it => new MmLocationOption
{
Id = it.Id,
WarehouseCode = it.WarehouseCode,
WarehouseName = it.WarehouseName,
LocationCode = it.LocationCode,
LocationName = it.LocationName,
LocationType = it.LocationType,
})
.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;
// 计算有符号变动量(蓝单为正,红单为负)
decimal delta = GetSignedQuantity(parm.ReceiptType, parm.Quantity);
// 校验物料和库位
var mmMaterial = Context
.Queryable<MmMaterial>()
.Where(it => it.MaterialCode == parm.MaterialCode)
.WhereIF(
!string.IsNullOrEmpty(parm.SupplierCode),
it => it.SupplierCode == parm.SupplierCode
)
.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();
// 获取现有库存(同物料、批次、库位)
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)
{
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 = parm.Unit,
ExpiryDate = parm.ExpiryDate,
LastUpdatedTime = null,
ProductionDate = parm.ProductionDate,
CreatedTime = nowDate,
};
Context.Insertable(newInventory).ExecuteCommand();
}
else
{
mmInventory.CurrentQty += delta;
Context
.Updateable(mmInventory)
.UpdateColumns(it => it.CurrentQty)
.ExecuteCommand();
}
// 插入入库记录,入库单号使用自动增长策略(同日期按序号)
var inboundNo = GenerateReceiptNo("RK");
MmRecordInbound newRecord = new()
{
InboundNo = inboundNo,
BatchNo = parm.BatchNo,
Operator = parm.Operator,
StoveCode = parm.StoveCode,
MaterialCode = mmMaterial.MaterialCode,
MaterialName = mmMaterial.MaterialName,
SupplierCode = mmMaterial.SupplierCode,
SupplierName = mmMaterial.SupplierName,
LocationCode = mmLocation.LocationCode,
LocationName = mmLocation.LocationName,
WarehouseCode = mmLocation.WarehouseCode,
WarehouseName = mmLocation.WarehouseName,
//TODO 待调整(可能涉及记录汇总)
Quantity = delta,
Unit = parm.Unit,
ProductionDate = parm.ProductionDate,
Workorder = parm.Workorder,
WorkorderRaw = parm.WorkorderRaw,
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;
// 计算有符号变动量(蓝单为正,红单为负)
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();
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)
{
//库存为0或者不存在不允许出库
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 = parm.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 outboundNo = GenerateReceiptNo("CK");
MmRecordOutbound newRecord = new()
{
OutboundNo = outboundNo,
BatchNo = parm.BatchNo,
Operator = parm.Operator,
MaterialCode = mmMaterial.MaterialCode,
MaterialName = mmMaterial.MaterialName,
LocationCode = mmLocation.LocationCode,
LocationName = mmLocation.LocationName,
WarehouseCode = mmLocation.WarehouseCode,
WarehouseName = mmLocation.WarehouseName,
//TODO 待调整(可能涉及记录汇总)
Quantity = -delta,
Unit = parm.Unit,
CreatedTime = nowDate,
TransactionType = parm.TransactionType,
Workorder = parm.Workorder,
WorkorderRaw = parm.WorkorderRaw,
OrderNo = parm.OrderNo,
Remarks = parm.Remarks,
};
Context.Insertable(newRecord).ExecuteCommand();
Context.Ado.CommitTran();
return "ok";
}
catch (Exception ex)
{
// 回滚操作
Context.Ado.RollbackTran();
return ex.Message;
}
}
/// <summary>
/// 根据单据类型与数量计算有符号变动量(蓝单为正,红单为负)
/// </summary>
/// <param name="receiptType">1 表示蓝单(正),其它为红单(负)</param>
/// <param name="quantity">1 表示蓝单(正),其它为红单(负)</param>
private static decimal GetSignedQuantity(int receiptType, decimal quantity)
{
return receiptType == 1 ? Math.Abs(quantity) : -Math.Abs(quantity);
}
/// <summary>
/// 生成单据编号,格式:{prefix}{yyyyMMdd}-{nnn}
/// 例如RK20251225-001
/// </summary>
private string GenerateReceiptNo(string prefix)
{
var datePart = DateTime.Now.ToString("yyyyMMdd");
var baseNo = prefix + datePart + "-";
// 尝试从入库/出库表中获取当天最大的编号后缀
try
{
if (prefix == "RK")
{
var last = Context
.Queryable<MmRecordInbound>()
.Where(it => it.InboundNo.StartsWith(prefix + datePart))
.OrderByDescending(it => it.InboundNo)
.Select(it => it.InboundNo)
.First();
if (string.IsNullOrEmpty(last))
{
return baseNo + "001";
}
var suf = last.Substring((prefix + datePart).Length).TrimStart('-', '_');
if (int.TryParse(suf, out var n))
{
return baseNo + (n + 1).ToString("D3");
}
return baseNo + "001";
}
else
{
var last = Context
.Queryable<MmRecordOutbound>()
.Where(it => it.OutboundNo.StartsWith(prefix + datePart))
.OrderByDescending(it => it.OutboundNo)
.Select(it => it.OutboundNo)
.First();
if (string.IsNullOrEmpty(last))
{
return baseNo + "001";
}
var suf = last.Substring((prefix + datePart).Length).TrimStart('-', '_');
if (int.TryParse(suf, out var n))
{
return baseNo + (n + 1).ToString("D3");
}
return baseNo + "001";
}
}
catch
{
return baseNo + "001";
}
}
/// <summary>
/// 导入数据
/// </summary>
/// <param name="formFile"></param>
/// <returns></returns>
public ImportResultDto ImportInventory(IFormFile formFile, string username)
{
string message = "导入成功";
List<MmInventoryExcelDto> inventoryList = new();
using (var stream = formFile.OpenReadStream())
{
try
{
IWorkbook workbook = new XSSFWorkbook(stream);
ISheet sheet = workbook.GetSheetAt(0);
#region excel
// 遍历每一行
for (int row = 1; row <= sheet.LastRowNum; row++)
{
IRow currentRow = sheet.GetRow(row);
if (currentRow != null) // 确保行不为空
{
MmInventoryExcelDto inventory = new MmInventoryExcelDto();
//00 ID
NPOI.SS.UserModel.ICell currentCell_00 = currentRow.GetCell(0);
inventory.Id = (int)currentCell_00?.NumericCellValue;
//02物料编码
NPOI.SS.UserModel.ICell currentCell_01 = currentRow.GetCell(1);
inventory.MaterialCode = currentCell_01?.ToString();
if (
currentCell_01 == null
|| string.IsNullOrEmpty(inventory.MaterialCode)
)
{
message = $"物料编码不可为空,第{row + 1}行";
break;
}
//03批次号
NPOI.SS.UserModel.ICell currentCell_02 = currentRow.GetCell(2);
inventory.BatchNo = currentCell_02?.ToString() ?? string.Empty;
//04当前库存量
NPOI.SS.UserModel.ICell currentCell_03 = currentRow.GetCell(3);
string currentQtyStr = currentCell_03?.ToString();
if (currentCell_03 == null || string.IsNullOrWhiteSpace(currentQtyStr))
{
message = $"当前库存量不可为空,第{row + 1}行";
break;
}
// 尝试转换为decimal
if (!decimal.TryParse(currentQtyStr, out decimal currentQty))
{
message = $"当前库存量格式错误(必须是数字),第{row + 1}行";
break;
}
// 验证数值范围(可根据业务需求调整)
//if (currentQty < 0)
//{
// message = $"当前库存量不能为负数,第{row + 1}行";
// break;
//}
inventory.CurrentQty = currentQty;
//05 仓库编码
NPOI.SS.UserModel.ICell currentCell_04 = currentRow.GetCell(4);
inventory.WarehouseCode = currentCell_04?.ToString();
if (
currentCell_04 == null
|| string.IsNullOrEmpty(inventory.WarehouseCode)
)
{
message = $"仓库编码不可为空,第{row + 1}行";
break;
}
//06 仓库名称
NPOI.SS.UserModel.ICell currentCell_05 = currentRow.GetCell(5);
inventory.WarehouseName = currentCell_05?.ToString();
if (
currentCell_05 == null
|| string.IsNullOrEmpty(inventory.WarehouseName)
)
{
message = $"仓库名称不可为空,第{row + 1}行";
break;
}
//07 库位编码
NPOI.SS.UserModel.ICell currentCell_06 = currentRow.GetCell(6);
inventory.LocationCode = currentCell_06?.ToString();
if (
currentCell_06 == null
|| string.IsNullOrEmpty(inventory.LocationCode)
)
{
message = $"仓库编码不可为空,第{row + 1}行";
break;
}
//08 库位名称
NPOI.SS.UserModel.ICell currentCell_07 = currentRow.GetCell(7);
inventory.LocationName = currentCell_07?.ToString();
if (
currentCell_07 == null
|| string.IsNullOrEmpty(inventory.LocationName)
)
{
message = $"库位名称不可为空,第{row + 1}行";
break;
}
//09 创建时间
NPOI.SS.UserModel.ICell currentCell_08 = currentRow.GetCell(8);
inventory.CreatedTime = currentCell_08?.DateCellValue ?? DateTime.Now;
inventoryList.Add(inventory);
}
}
#endregion
}
catch (Exception ex)
{
return null;
}
}
// TODO 3.调用SplitInsert方法实现导入操作,注意主键列的配置(建议优化为ID相同则修改不同则新增)
var x = Context
.Storageable(inventoryList)
//.SplitInsert(it => !it.Any())
//.WhereColumns(it => it.Id) //如果不是主键可以这样实现多字段it=>new{it.x1,it.x2}
.ToStorage();
var result = x.AsInsertable.ExecuteCommand();
var result2 = x.AsUpdateable.IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommand(); //插入可插入部分;
var importResult = new ImportResultDto
{
Message = message,
Inserted = x.InsertList.Count,
Updated = x.UpdateList.Count,
ErrorCount = x.ErrorList.Count,
IgnoredCount = x.IgnoreList.Count,
Deleted = x.DeleteList.Count,
Total = x.TotalList.Count,
};
//输出统计
Console.WriteLine(importResult);
// 4.收集错误与忽略信息返回导入结果ImportResultDto 提示需要修改IServer相关返回格式
foreach (var item in x.ErrorList)
{
importResult.Errors.Add(
new ImportErrorDto
{
MaterialCode = item.Item.MaterialCode,
Message = item.StorageMessage,
}
);
}
foreach (var item in x.IgnoreList)
{
importResult.Ignored.Add(
new ImportErrorDto
{
MaterialCode = item.Item.MaterialCode,
Message = item.StorageMessage,
}
);
}
return importResult;
}
/// <summary>
/// 导出物料表列表
/// </summary>
/// <returns></returns>
public PagedInfo<MmInventoryExcelDto> SelectInventoryList(
MmInventoryQueryDto inventory,
PagerInfo pager
)
{
// Use the same predicate builder as GetList to support consistent filtering
var predicate = QueryExp(inventory);
var query = Queryable()
.Where(predicate.ToExpression())
.Select(it => new MmInventoryExcelDto
{
Id = it.Id,
MaterialCode = it.MaterialCode,
BatchNo = it.BatchNo,
CurrentQty = it.CurrentQty,
WarehouseCode = it.WarehouseCode,
WarehouseName = it.WarehouseName,
LocationCode = it.LocationCode,
LocationName = it.LocationName,
CreatedTime = it.CreatedTime,
});
return query.ToPage(pager);
}
/// <summary>
/// 查询出/入记录列表
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
public PagedInfo<MmInventoryRecordDto> GetInOrOutRecord(MmInventoryRecordQueryDto parm)
{
PagedInfo<MmInventoryRecordDto> result = new PagedInfo<MmInventoryRecordDto>();
// 处理日期
if (parm.StartTime != null && parm.StartTime.Value > DateTime.MinValue)
{
parm.StartTime = DOANConvertDate.ConvertLocalDate(parm.StartTime.Value);
}
if (parm.EndTime != null && parm.EndTime.Value > DateTime.MinValue)
{
parm.EndTime = DOANConvertDate.ConvertLocalDate(parm.EndTime.Value);
}
// 查询入库记录
if (parm.SearchType == 1)
{
result = Context
.Queryable<MmRecordInbound>()
.WhereIF(
parm.StartTime != null && parm.StartTime.Value > DateTime.MinValue,
it => it.CreatedTime >= parm.StartTime
)
.WhereIF(
parm.EndTime != null && parm.EndTime.Value > DateTime.MinValue,
it => it.CreatedTime <= parm.EndTime
)
.WhereIF(
!string.IsNullOrEmpty(parm.TransactionType),
it => it.TransactionType == parm.TransactionType
)
.Where(it => it.MaterialCode == parm.MaterialCode)
.Where(it => it.SupplierCode == parm.SupplierCode)
.Where(it => it.LocationCode == parm.LocationCode)
.Where(it => it.BatchNo == parm.BatchNo)
.Select(it => new MmInventoryRecordDto()
{
Id = it.Id,
InboundNo = it.InboundNo,
MaterialCode = it.MaterialCode,
MaterialName = it.MaterialName,
SupplierCode = it.SupplierCode,
SupplierName = it.SupplierName,
LocationCode = it.LocationCode,
LocationName = it.LocationName,
WarehouseCode = it.WarehouseCode,
WarehouseName = it.WarehouseName,
BatchNo = it.BatchNo,
Quantity = it.Quantity,
TransactionType = it.TransactionType,
Operator = it.Operator,
CreatedTime = it.CreatedTime,
Workorder = it.Workorder,
WorkorderRaw = it.WorkorderRaw,
StoveCode = it.StoveCode,
Remarks = it.Remarks,
})
.OrderByDescending(it => it.CreatedTime)
.ToPage(parm);
}
// 查询出库记录
else if (parm.SearchType == 2)
{
result = Context
.Queryable<MmRecordOutbound>()
.WhereIF(
parm.StartTime != null && parm.StartTime.Value > DateTime.MinValue,
it => it.CreatedTime >= parm.StartTime
)
.WhereIF(
parm.EndTime != null && parm.EndTime.Value > DateTime.MinValue,
it => it.CreatedTime <= parm.EndTime
)
.WhereIF(
!string.IsNullOrEmpty(parm.TransactionType),
it => it.TransactionType == parm.TransactionType
)
.Where(it => it.MaterialCode == parm.MaterialCode)
//.Where(it => it.SupplierCode == parm.SupplierCode)
.Where(it => it.LocationCode == parm.LocationCode)
.Where(it => it.BatchNo == parm.BatchNo)
.Select(it => new MmInventoryRecordDto()
{
Id = it.Id,
OutboundNo = it.OutboundNo,
MaterialCode = it.MaterialCode,
MaterialName = it.MaterialName,
LocationCode = it.LocationCode,
LocationName = it.LocationName,
WarehouseCode = it.WarehouseCode,
WarehouseName = it.WarehouseName,
BatchNo = it.BatchNo,
Quantity = it.Quantity,
TransactionType = it.TransactionType,
Operator = it.Operator,
CreatedTime = it.CreatedTime,
Workorder = it.Workorder,
WorkorderRaw = it.WorkorderRaw,
OrderNo = it.OrderNo,
Remarks = it.Remarks,
})
.OrderByDescending(it => it.CreatedTime)
.ToPage(parm);
}
return result;
}
/// <summary>
/// 撤销单据,传入单据类型(1-入库单2-出库单)和ID
/// </summary>
/// <param name="parm"></param>
/// <returns>返回ok即为成功其他都是不成功</returns>
/// <exception cref="NotImplementedException"></exception>
public string RevokeReceipt(MmInventoryRevokeDto parm)
{
try
{
int _type = parm.Type;
int _id = parm.Id;
if (_type < -1 && _id < -1)
{
return $"传入参数有误,请检查:type-{_type},id-{_id}";
}
// type == 1 入库单
if (_type == 1)
{
MmRecordInbound recordInbound = Context
.Queryable<MmRecordInbound>()
.Where(it => it.Id == _id)
.First();
if (recordInbound == null)
{
return $"无此入库记录,请检查:type-{_type},id-{_id}";
}
if (recordInbound.Remarks == "已撤销")
{
return $"此记录已撤销过,不可重复撤销";
}
//做出库红单
InboundReceiptDto revokeRecepitDto = new()
{
ReceiptType = 2,
MaterialCode = recordInbound.MaterialCode,
BatchNo = recordInbound.BatchNo,
LocationCode = recordInbound.LocationCode,
WarehouseCode = recordInbound.WarehouseCode,
SupplierCode = recordInbound.SupplierCode,
StoveCode = recordInbound.StoveCode,
Workorder = recordInbound.Workorder,
WorkorderRaw = recordInbound.WorkorderRaw,
Operator = recordInbound.Operator,
Quantity = recordInbound.Quantity,
TransactionType = "入库红单",
Remarks = $"撤销操作,入库单号:{recordInbound.InboundNo}",
};
string result = CreateInboundReceipt(revokeRecepitDto);
if (result == "ok")
{
recordInbound.Remarks = "已撤销";
Context.Updateable(recordInbound).ExecuteCommand();
return result;
}
else
{
return result;
}
}
else
{
MmRecordOutbound recordOutbound = Context
.Queryable<MmRecordOutbound>()
.Where(it => it.Id == _id)
.First();
if (recordOutbound == null)
{
return $"无此出库记录,请检查:type-{_type},id-{_id}";
}
if (recordOutbound.Remarks == "已撤销")
{
return $"此记录已撤销过,不可重复撤销";
}
//做出库红单
OutboundReceiptDto revokeRecepitDto = new()
{
ReceiptType = 2,
MaterialCode = recordOutbound.MaterialCode,
BatchNo = recordOutbound.BatchNo,
LocationCode = recordOutbound.LocationCode,
WarehouseCode = recordOutbound.WarehouseCode,
OrderNo = recordOutbound.OrderNo,
Workorder = recordOutbound.Workorder,
WorkorderRaw = recordOutbound.WorkorderRaw,
Operator = recordOutbound.Operator,
Quantity = recordOutbound.Quantity,
TransactionType = "出库红单",
Remarks = $"撤销操作,出库单号:{recordOutbound.OutboundNo}",
};
string result = CreateOutboundReceipt(revokeRecepitDto);
if (result == "ok")
{
recordOutbound.Remarks = "已撤销";
Context.Updateable(recordOutbound).ExecuteCommand();
return result;
}
else
{
return result;
}
}
}
catch (Exception e)
{
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;
}
}
}
}