feat(报废管理): 实现报废单和转用单的创建与审批流程

添加报废审批DTO和供应商字段
实现创建报废单/转用单功能
新增审批和撤销报废记录接口
优化查询条件并生成唯一报废单号
This commit is contained in:
2026-01-26 17:57:18 +08:00
parent 1e7d768489
commit 722ef40fd2
8 changed files with 355 additions and 17 deletions

View File

@@ -81,6 +81,10 @@ namespace DOAN.Service.BZFM
!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)
@@ -93,10 +97,6 @@ namespace DOAN.Service.BZFM
!string.IsNullOrEmpty(parm.LocationCode),
m => m.LocationCode.Contains(parm.LocationCode)
)
.AndIF(
!string.IsNullOrEmpty(parm.SupplierCode),
m => m.SupplierCode.Contains(parm.SupplierCode)
)
.AndIF(
!string.IsNullOrEmpty(parm.SupplierName),
m => m.SupplierName.Contains(parm.SupplierName)
@@ -734,7 +734,7 @@ namespace DOAN.Service.BZFM
CreatedTime = it.CreatedTime,
Workorder = it.Workorder,
StoveCode = it.StoveCode,
Remarks = it.Remarks
Remarks = it.Remarks,
})
.OrderByDescending(it => it.CreatedTime)
.ToPage(parm);
@@ -777,7 +777,7 @@ namespace DOAN.Service.BZFM
CreatedTime = it.CreatedTime,
Workorder = it.Workorder,
OrderNo = it.OrderNo,
Remarks = it.Remarks
Remarks = it.Remarks,
})
.OrderByDescending(it => it.CreatedTime)
.ToPage(parm);

View File

@@ -15,7 +15,36 @@ namespace DOAN.Service.BZFM.IService
QcScrapRecords AddQcScrapRecords(QcScrapRecords parm);
int UpdateQcScrapRecords(QcScrapRecords parm);
/// <summary>
/// 创建报废单
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
QcScrapRecords CreateScrapOrder(QcScrapRecords parm);
/// <summary>
/// 创建转用单
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
QcScrapRecords CreateTransferOrder(QcScrapRecords parm);
/// <summary>
/// 审批报废记录
/// </summary>
/// <param name="id">记录ID</param>
/// <param name="isApproved">是否批准</param>
/// <param name="approver">审批人</param>
/// <returns></returns>
int ApproveScrapRecord(long id, bool isApproved, string approver);
/// <summary>
/// 撤销报废记录
/// </summary>
/// <param name="id">记录ID</param>
/// <returns></returns>
int RevokeScrapRecord(long id);
}
}

View File

@@ -8,7 +8,7 @@ using DOAN.Service.BZFM.IService;
namespace DOAN.Service.BZFM
{
/// <summary>
/// 报废记录表Service业务层处理
/// Service业务层处理
/// </summary>
[AppService(ServiceType = typeof(IQcScrapRecordsService), ServiceLifetime = LifeTime.Transient)]
public class QcScrapRecordsService : BaseService<QcScrapRecords>, IQcScrapRecordsService
@@ -51,7 +51,15 @@ namespace DOAN.Service.BZFM
/// <returns></returns>
public QcScrapRecords AddQcScrapRecords(QcScrapRecords model)
{
return Insertable(model).ExecuteReturnEntity();
// 如果没有提供报废单号,则生成一个
if (string.IsNullOrEmpty(model.ScrapOrderNo))
{
// 根据报废类型选择前缀
string prefix = model.ScrapType == "转用" ? "ZY" : "BF";
model.ScrapOrderNo = GenerateScrapOrderNo(prefix);
}
return Context.Insertable(model).ExecuteReturnEntity();
}
/// <summary>
@@ -71,9 +79,204 @@ namespace DOAN.Service.BZFM
/// <returns></returns>
private static Expressionable<QcScrapRecords> QueryExp(QcScrapRecordsQueryDto parm)
{
var predicate = Expressionable.Create<QcScrapRecords>();
var predicate = Expressionable
.Create<QcScrapRecords>()
// 模糊搜索字段
.AndIF(!string.IsNullOrEmpty(parm.ScrapOrderNo), it => it.ScrapOrderNo.Contains(parm.ScrapOrderNo))
.AndIF(!string.IsNullOrEmpty(parm.Workorder), it => it.Workorder.Contains(parm.Workorder))
.AndIF(!string.IsNullOrEmpty(parm.StoveCode), it => it.StoveCode.Contains(parm.StoveCode))
.AndIF(!string.IsNullOrEmpty(parm.ProductCode), it => it.ProductCode.Contains(parm.ProductCode))
.AndIF(!string.IsNullOrEmpty(parm.BatchNo), it => it.BatchNo.Contains(parm.BatchNo))
// 精确搜索字段
.AndIF(!string.IsNullOrEmpty(parm.MaterialCode), it => it.MaterialCode == parm.MaterialCode)
.AndIF(!string.IsNullOrEmpty(parm.SupplierCode), it => it.SupplierCode == parm.SupplierCode)
.AndIF(!string.IsNullOrEmpty(parm.ScrapType), it => it.ScrapType == parm.ScrapType)
.AndIF(!string.IsNullOrEmpty(parm.LineCode), it => it.LineCode == parm.LineCode)
.AndIF(!string.IsNullOrEmpty(parm.Status), it => it.Status == parm.Status);
return predicate;
}
/// <summary>
/// 生成报废单号
/// </summary>
/// <param name="prefix">前缀默认BF表示报废</param>
/// <returns></returns>
private string GenerateScrapOrderNo(string prefix = "BF")
{
var datePart = DateTime.Now.ToString("yyyyMMdd");
var baseNo = prefix + datePart + "-";
// 尝试从报废记录表中获取当天最大的编号后缀
try
{
var last = Context
.Queryable<QcScrapRecords>()
.Where(it => it.ScrapOrderNo.StartsWith(prefix + datePart))
.OrderByDescending(it => it.ScrapOrderNo)
.Select(it => it.ScrapOrderNo)
.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="parm"></param>
/// <returns></returns>
public QcScrapRecords CreateScrapOrder(QcScrapRecords parm)
{
// 验证物料编码
if (string.IsNullOrEmpty(parm.MaterialCode))
{
throw new Exception("物料编码不能为空");
}
// 生成报废单号
parm.ScrapOrderNo = GenerateScrapOrderNo();
// 设置状态为待审批
parm.Status = "待审批";
parm.ScrapType = "报废";
return Context.Insertable(parm).ExecuteReturnEntity();
}
/// <summary>
/// 创建转用单
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
public QcScrapRecords CreateTransferOrder(QcScrapRecords parm)
{
// 验证物料编码
if (string.IsNullOrEmpty(parm.MaterialCode))
{
throw new Exception("物料编码不能为空");
}
// 验证工单号
if (string.IsNullOrEmpty(parm.Workorder))
{
throw new Exception("工单号不能为空");
}
// 生成报废单号
parm.ScrapOrderNo = GenerateScrapOrderNo("ZY"); // ZY表示转用
// 设置状态为待审批,报废类型为转用
parm.Status = "待审批";
parm.ScrapType = "转用";
return Context.Insertable(parm).ExecuteReturnEntity();
}
/// <summary>
/// 审批报废记录
/// </summary>
/// <param name="id">记录ID</param>
/// <param name="isApproved">是否批准</param>
/// <param name="approver">审批人</param>
/// <returns></returns>
public int ApproveScrapRecord(long id, bool isApproved, string approver)
{
int result = 0;
UseTran2(() =>
{
// 获取记录信息
var record = Context.Queryable<QcScrapRecords>().Where(x => x.Id == id).First();
if (record == null)
{
throw new Exception("记录不存在");
}
// 只有待审批状态的记录才能被审批
if (record.Status != "待审批")
{
throw new Exception("只有待审批状态的记录才能被审批");
}
// 更新审批信息
var updateObj = new QcScrapRecords
{
Id = id,
Status = isApproved ? "已批准" : "已拒绝",
ApprovalDate = DateTime.Now,
SupervisorName = approver,
UpdatedTime = DateTime.Now
};
result = Context.Updateable(updateObj).UpdateColumns(x => new { x.Status, x.ApprovalDate, x.SupervisorName, x.UpdatedTime }).ExecuteCommand();
if (isApproved)
{
// 根据报废类型处理
if (record.ScrapType == "转用")
{
// 转用单批准:生成新工单(这里需要根据实际业务调整工单生成逻辑)
// 注意:需要确保工单服务的命名空间和方法名正确
// 示例_proWorkorderService.GenerateWorkorder(record);
}
else
{
// 报废单批准:生成入库单到报废库(这里需要根据实际业务调整入库逻辑)
// 示例:调用入库单服务添加到报废库
}
}
});
return result;
}
/// <summary>
/// 撤销报废记录
/// </summary>
/// <param name="id">记录ID</param>
/// <returns></returns>
public int RevokeScrapRecord(long id)
{
// 获取记录信息
var record = Context.Queryable<QcScrapRecords>().Where(x => x.Id == id).First();
if (record == null)
{
throw new Exception("记录不存在");
}
// 只有待审批状态的记录才能被撤销
if (record.Status != "已批准")
{
throw new Exception("只有已批准状态的记录才能被撤销");
}
// 撤销:将状态改为待审批
var updateObj = new QcScrapRecords
{
Id = id,
Status = "待审批",
UpdatedTime = DateTime.Now
};
return Context.Updateable(updateObj).UpdateColumns(x => new { x.Status, x.UpdatedTime }).ExecuteCommand();
}
}
}