fix(物料管理): 修复批次号默认值问题并优化工单导入逻辑

修复QcScrapRecordsService和ReportFlowService中批次号默认值为"000"的问题,改为使用实际批次号
重构ProWorkorderImportService中的工单编号生成逻辑,改为按导入顺序生成而非按产品分组
完善IProWorkorderMaterialService接口定义,增加参数并调整方法位置
优化ProWorkorderMaterialService中的库存查询方法,增加过滤条件和参数
This commit is contained in:
2026-03-02 11:32:13 +08:00
parent b68a277d8e
commit dc8cb4053d
5 changed files with 290 additions and 260 deletions

View File

@@ -10,21 +10,21 @@ namespace DOAN.Service.MES.product.IService
public interface IProWorkorderMaterialService
{
/// <summary>
/// 根据工单号查询领料清单
/// 根据工单号查询领料清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>领料清单数据</returns>
List<MaterialTakeDto> GetMaterialTakeList(string workorder);
/// <summary>
/// 根据工单号查询成品入库清单
/// 根据工单号查询成品入库清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>成品入库清单数据</returns>
List<ProductStorageDto> GetProductStorageList(string workorder);
/// <summary>
/// 根据工单号查询出货清单
/// 根据工单号查询出货清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>出货清单数据</returns>
@@ -34,8 +34,35 @@ namespace DOAN.Service.MES.product.IService
/// 根据工单号查询物料库存接口
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏为0记录</param>
/// <param name="searchType">查询范围 1-物料库 2-转用库</param>
/// <returns>物料库存信息列表</returns>
List<MaterialInventoryDto> GetMaterialInventoryList(string workorder);
List<MaterialInventoryDto> GetMaterialInventoryList(string workorder, bool isHideZero, int searchType);
/// <summary>
/// 根据工单号获取可领料工单清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏为0记录</param>
/// <param name="searchType">查询范围 1-物料库 2-转用库</param>
/// <returns>可领料工单清单</returns>
List<ProWorkorderDto> GetPickableWorkordersByWorkorder(string workorder, bool isHideZero, int searchType);
/// <summary>
/// 根据工单号查询成品库存
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏为0记录</param>
/// <returns>成品库存信息列表</returns>
List<MaterialInventoryDto> GetProductInventoryList(string workorder, bool isHideZero);
/// <summary>
/// 根据工单号获取可出货订单清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏为0记录</param>
/// <returns>可出货订单清单</returns>
List<OrderPurchaseDto> GetShippableOrdersByWorkorder(string workorder, bool isHideZero);
/// <summary>
/// 根据工单领料
@@ -58,25 +85,6 @@ namespace DOAN.Service.MES.product.IService
/// <returns>操作结果</returns>
bool ShipProduct(ShipmentRequestDto request);
/// <summary>
/// 根据工单号获取可领料工单清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>可领料工单清单</returns>
List<ProWorkorderDto> GetPickableWorkordersByWorkorder(string workorder);
/// <summary>
/// 根据工单号获取可出货订单清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>可出货订单清单</returns>
List<OrderPurchaseDto> GetShippableOrdersByWorkorder(string workorder);
/// <summary>
/// 根据工单号查询成品库存
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>成品库存信息列表</returns>
List<MaterialInventoryDto> GetProductInventoryList(string workorder);
}
}

View File

@@ -108,6 +108,32 @@ namespace DOAN.Service.MES.product
return workorderNumbers.Count > 0 ? workorderNumbers.Max() : 0;
}
/// <summary>
/// 获取当天所有工单的最大编号索引
/// </summary>
/// <param name="dateValue">日期值</param>
/// <returns>最大编号索引</returns>
private int GetMaxWorkorderIndex(DateTime dateValue)
{
var workorderNumbers = Context
.Queryable<ProWorkorder>()
.Where(it => it.WorkorderDate == dateValue.Date)
.Select(it => it.Workorder)
.ToList()
.Where(w => w.StartsWith(dateValue.ToString("yyyyMMdd")))
.Select(w =>
{
var parts = w.Split('_');
if (parts.Length >= 4 && int.TryParse(parts[3], out int index))
return index;
return 0;
})
.ToList();
// 如果没有找到记录返回0
return workorderNumbers.Count > 0 ? workorderNumbers.Max() : 0;
}
/// <summary>
/// 从Excel文件读取工单数据
/// </summary>
@@ -419,17 +445,16 @@ namespace DOAN.Service.MES.product
workorderList = ReadWorkordersFromExcel(formFile, username, out dateValue);
Logger.Info($"读取到 {workorderList.Count} 条工单数据");
// 按productionCode分组并顺序编号
// 先按productionCode分组确保同一产品的工单连续排序
var productionCodeGroups = workorderList
.GroupBy(w => w.productionCode)
.ToList();
// 获取当天所有工单的最大编号索引,用于后续编号
int maxIndex = GetMaxWorkorderIndex(dateValue);
// 从最大编号+1开始顺序编号
int currentIndex = maxIndex + 1;
Logger.Info($"当天所有工单最大编号索引: {maxIndex},开始编号: {currentIndex}");
// 获取所有工单的最大sort值用于后续排序
var maxSortNullable = Context
.Queryable<ProWorkorder>()
.Where(it => it.WorkorderDate == dateValue.Date)
//.Select(it => it.Sort)
.Max(it => it.Sort);
// 如果没有找到记录设置默认值0
@@ -438,33 +463,24 @@ namespace DOAN.Service.MES.product
// 从maxSort + 10开始确保sort值按10、20、30...递增
int currentSort = (maxSort / 10) * 10 + 10;
foreach (var group in productionCodeGroups)
// 直接按照Excel导入顺序生成工单号
foreach (var workorder in workorderList)
{
Logger.Info($"处理产品代码: {group.Key},共 {group.Count()} 条工单");
// 获取当前productionCode当天已有的最大编号
int maxIndex = GetMaxWorkorderIndex(group.Key, dateValue);
// 从最大编号+1开始顺序编号
int currentIndex = maxIndex + 1;
Logger.Info($"产品代码: {group.Key},当前最大编号索引: {maxIndex},开始编号: {currentIndex}");
string nickCode = mmMaterials
.Where(it => it.MaterialCode == workorder.productionCode)
.Select(it => it.Type)
.FirstOrDefault();
foreach (var workorder in group)
{
string nickCode = mmMaterials
.Where(it => it.MaterialCode == workorder.productionCode)
.Select(it => it.Type)
.FirstOrDefault();
// 生成唯一的工单编号
var generateResult = GenerateUniqueWorkorderNo(workorder, dateValue, currentIndex, nickCode);
workorder.Workorder = generateResult.Item1;
// 使用连续的sort值不受编号冲突影响
workorder.Sort = currentSort;
// 生成唯一的工单编号
var generateResult = GenerateUniqueWorkorderNo(workorder, dateValue, currentIndex, nickCode);
workorder.Workorder = generateResult.Item1;
// 使用连续的sort值不受编号冲突影响
workorder.Sort = currentSort;
Logger.Info($"生成工单编号: {workorder.Workorder},产品: {workorder.productionName}sort: {currentSort}");
currentIndex = generateResult.Item2 + 1;
// 增加sort值确保下一个工单的sort值为当前值+10
currentSort += 10;
}
Logger.Info($"生成工单编号: {workorder.Workorder},产品: {workorder.productionName}sort: {currentSort}");
currentIndex = generateResult.Item2 + 1;
// 增加sort值确保下一个工单的sort值为当前值+10
currentSort += 10;
}
UseTran2(() =>
@@ -511,14 +527,16 @@ namespace DOAN.Service.MES.product
workorderList = ReadWorkordersFromExcel(formFile, username, out dateValue);
Logger.Info($"读取到 {workorderList.Count} 条工单数据");
// 按productionCode分组并顺序编号
var productionCodeGroups = workorderList.GroupBy(w => w.productionCode).ToList();
// 获取当天所有工单的最大编号索引,用于后续编号
int maxIndex = GetMaxWorkorderIndex(dateValue);
// 从最大编号+1开始顺序编号
int currentIndex = maxIndex + 1;
Logger.Info($"当天所有工单最大编号索引: {maxIndex},开始编号: {currentIndex}");
// 获取所有工单的最大sort值用于后续排序
var maxSortNullable = Context
.Queryable<ProWorkorder>()
.Where(it => it.WorkorderDate == dateValue.Date)
//.Select(it => it.Sort)
.Max(it => it.Sort);
// 如果没有找到记录设置默认值0
@@ -527,33 +545,24 @@ namespace DOAN.Service.MES.product
// 从maxSort + 10开始确保sort值按10、20、30...递增
int currentSort = (maxSort / 10) * 10 + 10;
foreach (var group in productionCodeGroups)
// 直接按照Excel导入顺序生成工单号
foreach (var workorder in workorderList)
{
Logger.Info($"处理产品代码: {group.Key},共 {group.Count()} 条工单");
// 获取当前productionCode当天已有的最大编号
int maxIndex = GetMaxWorkorderIndex(group.Key, dateValue);
// 从最大编号+1开始顺序编号
int currentIndex = maxIndex + 1;
Logger.Info($"产品代码: {group.Key},当前最大编号索引: {maxIndex},开始编号: {currentIndex}");
string nickCode = mmMaterials
.Where(it => it.MaterialCode == workorder.productionCode)
.Select(it => it.Type)
.FirstOrDefault();
foreach (var workorder in group)
{
string nickCode = mmMaterials
.Where(it => it.MaterialCode == workorder.productionCode)
.Select(it => it.Type)
.FirstOrDefault();
// 生成唯一的工单编号
var generateResult = GenerateUniqueWorkorderNo(workorder, dateValue, currentIndex, nickCode);
workorder.Workorder = generateResult.Item1;
// 使用连续的sort值不受编号冲突影响
workorder.Sort = currentSort;
// 生成唯一的工单编号
var generateResult = GenerateUniqueWorkorderNo(workorder, dateValue, currentIndex, nickCode);
workorder.Workorder = generateResult.Item1;
// 使用连续的sort值不受编号冲突影响
workorder.Sort = currentSort;
Logger.Info($"生成工单编号: {workorder.Workorder},产品: {workorder.productionName}sort: {currentSort}");
currentIndex = generateResult.Item2 + 1;
// 增加sort值确保下一个工单的sort值为当前值+10
currentSort += 10;
}
Logger.Info($"生成工单编号: {workorder.Workorder},产品: {workorder.productionName}sort: {currentSort}");
currentIndex = generateResult.Item2 + 1;
// 增加sort值确保下一个工单的sort值为当前值+10
currentSort += 10;
}
UseTran2(() =>

View File

@@ -37,6 +37,7 @@ namespace DOAN.Service.MES.product
.Where(it => it.Workorder == workorder)
.Where(it => it.TransactionType == "领料出库")
.Where(it => it.Remarks != "已撤销")
//.WhereIF(isHideZero, it => it != "已撤销")
.Select(it => new MaterialTakeDto
{
Id = it.Id,
@@ -78,6 +79,7 @@ namespace DOAN.Service.MES.product
/// 根据工单号查询工单已成品入库清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏0库存</param>
/// <returns>成品入库清单数据</returns>
public List<ProductStorageDto> GetProductStorageList(string workorder)
{
@@ -108,6 +110,7 @@ namespace DOAN.Service.MES.product
/// 根据工单号查询工单已出货清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏0库存</param>
/// <returns>出货清单数据</returns>
public List<ShipmentDto> GetShipmentList(string workorder)
{
@@ -138,9 +141,11 @@ namespace DOAN.Service.MES.product
/// <summary>
/// 根据工单查询物料库存接口
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏0</param>
/// <param name="searchType">查询范围 1-物料库 2-转用库</param>
/// <returns>物料库存信息列表</returns>
public List<MaterialInventoryDto> GetMaterialInventoryList(string workorder)
public List<MaterialInventoryDto> GetMaterialInventoryList(string workorder, bool isHideZero = true, int searchType = 1)
{
try
{
@@ -166,7 +171,7 @@ namespace DOAN.Service.MES.product
.Queryable<MmInventory>()
.Where(it => it.MaterialCode == materialCode)
.Where(it => it.LocationCode == "YCL001")
.Where(it => it.CurrentQty > 0)
.WhereIF(isHideZero, it => it.CurrentQty > 0)
.Select(it => new MaterialInventoryDto
{
MaterialId = it.Id,
@@ -196,7 +201,7 @@ namespace DOAN.Service.MES.product
.Where(it => it.MaterialCode == mmMaterial.MaterialCode)
.Where(it => it.SupplierCode == mmMaterial.SupplierCode)
.Where(it => it.LocationCode == "YCL001" || it.LocationCode == "CP001")
.Where(it => it.CurrentQty > 0)
.WhereIF(isHideZero, it => it.CurrentQty > 0)
.Select(it => new MaterialInventoryDto
{
MaterialId = it.Id,
@@ -225,6 +230,187 @@ namespace DOAN.Service.MES.product
}
}
/// <summary>
/// 根据工单号获取可领料工单清单(产成品领取半成品)
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏0记录</param>
/// <param name="searchType">查询范围 1-物料库 2-转用库</param>
/// <returns>可领料工单清单</returns>
public List<ProWorkorderDto> GetPickableWorkordersByWorkorder(string workorder, bool isHideZero = true, int searchType = 1)
{
try
{
// 参数验证
if (string.IsNullOrEmpty(workorder))
{
throw new ArgumentNullException(nameof(workorder), "工单号不能为空");
}
var workorderInfo = Context
.Queryable<ProWorkorder>()
.First(it => it.Workorder == workorder);
if (workorderInfo == null)
{
throw new ArgumentException("工单不存在", nameof(workorder));
}
// 需要领取半成品
if (workorderInfo.RouteCode == "10")
{
return Context
.Queryable<MmRecordInbound>()
.LeftJoin<ProWorkorder>((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) => pro.ShipmentNum < pro.PlanNum)
.OrderByDescending((mri, pro) => mri.Workorder)
.Select(
(mri, pro) => new ProWorkorderDto
{
Id = pro.Id,
Workorder = mri.Workorder,
productionName = mri.MaterialCode,
productionCode = mri.MaterialName,
MaterialCode = mri.MaterialCode,
MaterialName = mri.MaterialName,
ShipmentNum = pro.ShipmentNum,
PlanNum = pro.PlanNum,
Remark01 = mri.Remarks
},
true
)
.Take(30)
.ToList();
}
else
{
// 非10线则返回库存
// 示例返回空列表
return new List<ProWorkorderDto>();
}
}
catch (Exception ex)
{
// 集成现有系统的日志记录
// Log.Error("获取可领料工单清单失败", ex);
throw;
}
}
/// <summary>
/// 根据工单号查询成品库存
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏为0记录</param>
/// <returns>成品库存信息列表</returns>
public List<MaterialInventoryDto> GetProductInventoryList(string workorder, bool isHideZero = true)
{
try
{
// 参数验证
if (string.IsNullOrEmpty(workorder))
{
throw new ArgumentNullException(nameof(workorder), "工单号不能为空");
}
var workorderInfo = Context
.Queryable<ProWorkorder>()
.First(it => it.Workorder == workorder);
if (workorderInfo == null)
{
throw new ArgumentException("工单不存在", nameof(workorder));
}
var result = Context
.Queryable<MmInventory>()
.Where(it => it.MaterialCode == workorderInfo.productionCode)
.Where(it => it.LocationCode == "CP001")
.WhereIF(isHideZero, it => it.CurrentQty > 0)
.Select(it => new MaterialInventoryDto
{
MaterialId = it.Id,
MaterialCode = it.MaterialCode,
MaterialName = it.MaterialName,
CurrentQuantity = it.CurrentQty,
SupplierCode = it.SupplierCode,
SupplierName = it.SupplierName,
Unit = it.Unit,
BatchNo = it.BatchNo,
})
.OrderByDescending(it => it.BatchNo)
.Take(10)
.ToList();
return result;
}
catch (Exception ex)
{
// 集成现有系统的日志记录
// Log.Error("查询成品库存失败", ex);
throw;
}
}
/// <summary>
/// 根据工单号获取可出货订单清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <param name="isHideZero">是否隐藏为0记录</param>
/// <returns>可出货订单清单</returns>
public List<OrderPurchaseDto> GetShippableOrdersByWorkorder(string workorder, bool isHideZero = true)
{
try
{
// 参数验证
if (string.IsNullOrEmpty(workorder))
{
throw new ArgumentNullException(nameof(workorder), "工单号不能为空");
}
var workorderInfo = Context
.Queryable<ProWorkorder>()
.First(it => it.Workorder == workorder);
if (workorderInfo == null)
{
throw new ArgumentException("工单不存在", nameof(workorder));
}
var orderPurchaseList = Context
.Queryable<OrderPurchase>()
.Where(o => o.MaterialCode == workorderInfo.productionCode)
.Where(it => it.Orderindicator != 1)
//.Where(it => it.Orderindicator != -1)
.OrderBy(it => it.DeliveryDate)
.Select(
o => new OrderPurchaseDto
{
Id = o.Id,
OrderNoMes = o.OrderNoMes,
MaterialCode = o.MaterialCode,
MaterialName = o.MaterialName,
DemandQuantity = o.DemandQuantity,
DeliveryQuantity = o.DeliveryQuantity,
DeliveryDate = o.DeliveryDate,
},
true
)
.ToList();
// 示例返回空列表
return orderPurchaseList;
}
catch (Exception ex)
{
// 集成现有系统的日志记录
// Log.Error("获取可出货订单清单失败", ex);
throw;
}
}
/// <summary>
/// 工单领料接口
/// </summary>
@@ -423,178 +609,5 @@ namespace DOAN.Service.MES.product
throw new Exception(ex.Message);
}
}
/// <summary>
/// 根据工单号获取可领料工单清单(产成品领取半成品)
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>可领料工单清单</returns>
public List<ProWorkorderDto> GetPickableWorkordersByWorkorder(string workorder)
{
try
{
// 参数验证
if (string.IsNullOrEmpty(workorder))
{
throw new ArgumentNullException(nameof(workorder), "工单号不能为空");
}
var workorderInfo = Context
.Queryable<ProWorkorder>()
.First(it => it.Workorder == workorder);
if (workorderInfo == null)
{
throw new ArgumentException("工单不存在", nameof(workorder));
}
// 需要领取半成品
if (workorderInfo.RouteCode == "10")
{
return Context
.Queryable<MmRecordInbound>()
.LeftJoin<ProWorkorder>((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) => pro.ShipmentNum < pro.PlanNum)
.OrderByDescending((mri, pro) => mri.Workorder)
.Select(
(mri, pro) => new ProWorkorderDto
{
Id = pro.Id,
Workorder = mri.Workorder,
productionName = mri.MaterialCode,
productionCode = mri.MaterialName,
MaterialCode = mri.MaterialCode,
MaterialName = mri.MaterialName,
ShipmentNum = pro.ShipmentNum,
PlanNum = pro.PlanNum,
Remark01 = mri.Remarks
},
true
)
.Take(30)
.ToList();
}
else
{
// 非10线则返回库存
// 示例返回空列表
return new List<ProWorkorderDto>();
}
}
catch (Exception ex)
{
// 集成现有系统的日志记录
// Log.Error("获取可领料工单清单失败", ex);
throw;
}
}
/// <summary>
/// 根据工单号获取可出货订单清单
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>可出货订单清单</returns>
public List<OrderPurchaseDto> GetShippableOrdersByWorkorder(string workorder)
{
try
{
// 参数验证
if (string.IsNullOrEmpty(workorder))
{
throw new ArgumentNullException(nameof(workorder), "工单号不能为空");
}
var workorderInfo = Context
.Queryable<ProWorkorder>()
.First(it => it.Workorder == workorder);
if (workorderInfo == null)
{
throw new ArgumentException("工单不存在", nameof(workorder));
}
var orderPurchaseList = Context
.Queryable<OrderPurchase>()
.Where(o => o.MaterialCode == workorderInfo.productionCode)
.Where(it => it.Orderindicator != 1)
//.Where(it => it.Orderindicator != -1)
.OrderBy(it => it.DeliveryDate)
.Select(
o => new OrderPurchaseDto
{
Id = o.Id,
OrderNoMes = o.OrderNoMes,
MaterialCode = o.MaterialCode,
MaterialName = o.MaterialName,
DemandQuantity = o.DemandQuantity,
DeliveryQuantity = o.DeliveryQuantity,
DeliveryDate = o.DeliveryDate,
},
true
)
.ToList();
// 示例返回空列表
return orderPurchaseList;
}
catch (Exception ex)
{
// 集成现有系统的日志记录
// Log.Error("获取可出货订单清单失败", ex);
throw;
}
}
/// <summary>
/// 根据工单号查询成品库存
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns>成品库存信息列表</returns>
public List<MaterialInventoryDto> GetProductInventoryList(string workorder)
{
try
{
// 参数验证
if (string.IsNullOrEmpty(workorder))
{
throw new ArgumentNullException(nameof(workorder), "工单号不能为空");
}
var workorderInfo = Context
.Queryable<ProWorkorder>()
.First(it => it.Workorder == workorder);
if (workorderInfo == null)
{
throw new ArgumentException("工单不存在", nameof(workorder));
}
var result = Context
.Queryable<MmInventory>()
.Where(it => it.MaterialCode == workorderInfo.productionCode)
.Where(it => it.LocationCode == "CP001")
//.Where(it => it.CurrentQty > 0)
.Select(it => new MaterialInventoryDto
{
MaterialId = it.Id,
MaterialCode = it.MaterialCode,
MaterialName = it.MaterialName,
CurrentQuantity = it.CurrentQty,
SupplierCode = it.SupplierCode,
SupplierName = it.SupplierName,
Unit = it.Unit,
BatchNo = it.BatchNo,
})
.OrderByDescending(it => it.BatchNo)
.Take(10)
.ToList();
return result;
}
catch (Exception ex)
{
// 集成现有系统的日志记录
// Log.Error("查询成品库存失败", ex);
throw;
}
}
}
}