Files
kunshan-bzfm-mes-backend/DOAN.Service/MES/Quality/IPQC/QcScrapRecordsService.cs
git_rabbit 5011447292 feat(工单): 实现工单标签打印功能并优化相关逻辑
- 添加Bartender打印工具类实现工单标签打印功能
- 修改PrintTicketsByTemplate方法返回类型为string并实现完整打印逻辑
- 优化工单领料逻辑,增加原材料工单信息获取
- 调整工单查询条件,移除PlanNum>0的限制
- 修复出库单操作符赋值错误
- 优化不良品处理流程,统一使用不良库代替报废库
- 完善领料报工逻辑,增加计划数校验和原材料工单处理
2026-02-24 15:36:35 +08:00

621 lines
25 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.BZFM;
using DOAN.Model.BZFM.Dto;
using DOAN.Model.MES.product;
using DOAN.Repository;
using DOAN.Service.BZFM.IService;
using Infrastructure.Attribute;
using Infrastructure.Extensions;
using SqlSugar.DistributedSystem.Snowflake;
namespace DOAN.Service.BZFM
{
/// <summary>
/// Service业务层处理
/// </summary>
[AppService(ServiceType = typeof(IQcScrapRecordsService), ServiceLifetime = LifeTime.Transient)]
public class QcScrapRecordsService : BaseService<QcScrapRecords>, IQcScrapRecordsService
{
/// <summary>
/// 查询报废记录表列表
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
public PagedInfo<QcScrapRecordsDto> GetList(QcScrapRecordsQueryDto parm)
{
var predicate = QueryExp(parm);
var response = Queryable()
.Where(predicate.ToExpression())
.OrderByDescending(it => it.CreatedTime)
.ToPage<QcScrapRecords, QcScrapRecordsDto>(parm);
return response;
}
/// <summary>
/// 获取详情
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public QcScrapRecords GetInfo(long Id)
{
var response = Queryable().Where(x => x.Id == Id).First();
return response;
}
/// <summary>
/// 添加报废记录表
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public QcScrapRecords AddQcScrapRecords(QcScrapRecords model)
{
// 如果没有提供报废单号,则生成一个
if (string.IsNullOrEmpty(model.ScrapOrderNo))
{
// 根据报废类型选择前缀
string prefix = model.ScrapType == "转用" ? "ZY" : "BF";
model.ScrapOrderNo = GenerateScrapOrderNo(prefix);
}
return Context.Insertable(model).ExecuteReturnEntity();
}
/// <summary>
/// 修改报废记录表
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public int UpdateQcScrapRecords(QcScrapRecords model)
{
return Update(model, true);
}
/// <summary>
/// 查询导出表达式
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
private static Expressionable<QcScrapRecords> QueryExp(QcScrapRecordsQueryDto parm)
{
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)
{
try
{
QcScrapRecords newQcScrapRecords = parm;
var workorderInfo = Context
.Queryable<ProWorkorder>()
.Where(x => x.Workorder == parm.Workorder)
.First();
// 工单号创建,根据工单信息填充报废单的相关字段
if (workorderInfo != null)
{
newQcScrapRecords.ProductCode = workorderInfo.productionCode;
newQcScrapRecords.ProductName = workorderInfo.productionName;
newQcScrapRecords.LineCode = workorderInfo.RouteCode;
newQcScrapRecords.StoveCode = workorderInfo.StoveCode;
newQcScrapRecords.BatchNo = workorderInfo.FeedOrder;
newQcScrapRecords.MaterialCode = workorderInfo.MaterialCode;
newQcScrapRecords.MaterialName = workorderInfo.MaterialName;
// 如有领料记录,则绑定采购记录
var purchaseInfo = Context
.Queryable<MmRecordInbound>()
.Where(it => it.BatchNo == workorderInfo.FeedOrder)
.Where(it => it.TransactionType == "采购入库")
.Where(it => it.Remarks != "已撤销")
.First();
if (purchaseInfo != null)
{
newQcScrapRecords.SupplierCode = purchaseInfo.SupplierCode;
newQcScrapRecords.SupplierName = purchaseInfo.SupplierName;
newQcScrapRecords.StoveCode = purchaseInfo.StoveCode;
newQcScrapRecords.Unit = purchaseInfo.Unit;
}
}
// 生成报废单号
newQcScrapRecords.ScrapOrderNo = GenerateScrapOrderNo();
// 设置状态为待审批
newQcScrapRecords.Status = "待审批";
newQcScrapRecords.ScrapType = "报废";
newQcScrapRecords.TenantId = 0;
newQcScrapRecords.Version = 1;
return Context.Insertable(newQcScrapRecords).ExecuteReturnEntity();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
/// <summary>
/// 创建转用单
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
public QcScrapRecords CreateTransferOrder(QcScrapRecords parm)
{
// 验证工单号
if (string.IsNullOrEmpty(parm.Workorder))
{
throw new Exception("工单号不能为空");
}
QcScrapRecords newQcScrapRecords = parm;
var workorderInfo = Context
.Queryable<ProWorkorder>()
.Where(x => x.Workorder == parm.Workorder)
.First();
// 工单号创建,根据工单信息填充报废单的相关字段
if (workorderInfo != null)
{
newQcScrapRecords.ProductCode = workorderInfo.productionCode;
newQcScrapRecords.ProductName = workorderInfo.productionName;
newQcScrapRecords.LineCode = workorderInfo.RouteCode;
newQcScrapRecords.StoveCode = workorderInfo.StoveCode;
newQcScrapRecords.BatchNo = workorderInfo.FeedOrder;
newQcScrapRecords.MaterialCode = workorderInfo.MaterialCode;
newQcScrapRecords.MaterialName = workorderInfo.MaterialName;
// 如有领料记录,则绑定采购记录
var purchaseInfo = Context
.Queryable<MmRecordInbound>()
.Where(it => it.BatchNo == workorderInfo.FeedOrder)
.Where(it => it.TransactionType == "采购入库")
.Where(it => it.Remarks != "已撤销")
.First();
if (purchaseInfo != null)
{
newQcScrapRecords.SupplierCode = purchaseInfo.SupplierCode;
newQcScrapRecords.SupplierName = purchaseInfo.SupplierName;
newQcScrapRecords.StoveCode = purchaseInfo.StoveCode;
newQcScrapRecords.Unit = purchaseInfo.Unit;
}
}
// 生成单号
newQcScrapRecords.ScrapOrderNo = GenerateScrapOrderNo("ZY"); // ZY表示转用
// 设置状态为待审批,类型为转用
newQcScrapRecords.Status = "待审批";
newQcScrapRecords.ScrapType = "转用";
newQcScrapRecords.TenantId = 0;
newQcScrapRecords.Version = 1;
return Context.Insertable(newQcScrapRecords).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 (!string.IsNullOrEmpty(record.Workorder))
{
var workorderInfo = Context
.Queryable<ProWorkorder>()
.Where(x => x.Workorder == record.Workorder)
.First();
if (workorderInfo != null)
{
workorderInfo.DefectNum += (int)record.ScrapQuantity;
Context
.Updateable(workorderInfo)
.UpdateColumns(x => new { x.DefectNum })
.ExecuteCommand();
}
}
// 调用入库单服务添加到不良库
InboundReceiptDto revokeRecepitDto = new()
{
ReceiptType = 1,
MaterialCode = record.ProductCode,
BatchNo = "000",
LocationCode = "BL001",
WarehouseCode = "WH007",
SupplierCode = record.SupplierCode,
StoveCode = record.StoveCode,
Workorder = record.Workorder,
WorkorderRaw = record.ScrapOrderNo,
Operator = approver,
Quantity = record.ScrapQuantity,
TransactionType = "不良入库",
Remarks = $"不良入库,不良单号:{record.ScrapOrderNo}",
};
MmInventoryService mmInventoryService = new();
string createReceiptresult = mmInventoryService.CreateInboundReceipt(
revokeRecepitDto
);
if (createReceiptresult != "ok")
{
throw new Exception(createReceiptresult);
}
// 根据报废类型处理
//if (record.ScrapType == "转用")
//{
// InboundReceiptDto revokeRecepitDto = new()
// {
// ReceiptType = 1,
// MaterialCode = record.ProductCode,
// BatchNo = "000",
// LocationCode = "ZYK001",
// WarehouseCode = "WH005",
// SupplierCode = record.SupplierCode,
// StoveCode = record.StoveCode,
// Workorder = record.Workorder,
// WorkorderRaw = record.ScrapOrderNo,
// Operator = approver,
// Quantity = record.ScrapQuantity,
// TransactionType = "转用入库",
// Remarks = $"转用入库,转用单号:{record.ScrapOrderNo}",
// };
// MmInventoryService mmInventoryService = new();
// string createReceiptresult = mmInventoryService.CreateInboundReceipt(
// revokeRecepitDto
// );
// if (createReceiptresult != "ok")
// {
// throw new Exception(createReceiptresult);
// }
//}
//else if (record.ScrapType == "报废")
//{
// InboundReceiptDto revokeRecepitDto = new()
// {
// ReceiptType = 1,
// MaterialCode = record.ProductCode,
// BatchNo = "000",
// LocationCode = "BFK001",
// WarehouseCode = "WH006",
// SupplierCode = record.SupplierCode,
// StoveCode = record.StoveCode,
// Workorder = record.Workorder,
// WorkorderRaw = record.ScrapOrderNo,
// Operator = approver,
// Quantity = record.ScrapQuantity,
// TransactionType = "报废入库",
// Remarks = $"报废入库,报废单号:{record.ScrapOrderNo}",
// };
// MmInventoryService mmInventoryService = new();
// string createReceiptresult = mmInventoryService.CreateInboundReceipt(
// revokeRecepitDto
// );
// if (createReceiptresult != "ok")
// {
// throw new Exception(createReceiptresult);
// }
//}
}
});
return result;
}
/// <summary>
/// 撤销报废记录
/// </summary>
/// <param name="id">记录ID</param>
/// <returns></returns>
public int RevokeScrapRecord(long id)
{
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 workorderInfo = Context
.Queryable<ProWorkorder>()
.Where(it => it.Workorder == record.Workorder)
.First();
if (workorderInfo != null)
{
workorderInfo.DefectNum -= (int)Math.Abs(record.ScrapQuantity);
Context
.Updateable(workorderInfo)
.UpdateColumns(it => new { it.DefectNum })
.ExecuteCommand();
}
MmInventoryService mmInventoryService = new();
int InBoundId = Context
.Queryable<MmRecordInbound>()
.Where(it => it.WorkorderRaw == record.ScrapOrderNo)
.Select(it => it.Id)
.First();
if (InBoundId > 0)
{
mmInventoryService.RevokeReceipt(
new MmInventoryRevokeDto { Id = InBoundId, Type = 1 }
);
}
// 撤销:将状态改为待审批
var updateObj = new QcScrapRecords
{
Id = id,
Status = "待审批",
UpdatedTime = DateTime.Now,
};
result = Context
.Updateable(updateObj)
.UpdateColumns(x => new { x.Status, x.UpdatedTime })
.ExecuteCommand();
});
return result;
}
/// <summary>
/// 根据工单号查询不良品记录
/// </summary>
/// <param name="workorder">工单号</param>
/// <returns></returns>
public PagedInfo<QcScrapRecordsDto> GetQcScrapRecordsByWorkorder(string workorder)
{
var queryDto = new QcScrapRecordsQueryDto { Workorder = workorder };
var result = Context
.Queryable<QcScrapRecords>()
.Where(x => x.Workorder == workorder)
.ToPage<QcScrapRecords, QcScrapRecordsDto>(queryDto);
return result;
}
/// <summary>
/// 根据工单号填写报废单
/// </summary>
/// <param name="parm">报废单数据</param>
/// <returns></returns>
public QcScrapRecords CreateScrapOrderByWorkorder(QcScrapRecords parm)
{
// 验证工单号
if (string.IsNullOrEmpty(parm.Workorder))
{
throw new Exception("工单号不能为空");
}
// 调用现有的创建报废单方法
return CreateScrapOrder(parm);
}
/// <summary>
/// 根据工单号填写转用单
/// </summary>
/// <param name="parm">转用单数据</param>
/// <returns></returns>
public QcScrapRecords CreateTransferOrderByWorkorder(QcScrapRecords parm)
{
// 验证工单号
if (string.IsNullOrEmpty(parm.Workorder))
{
throw new Exception("工单号不能为空");
}
// 调用现有的创建转用单方法
return CreateTransferOrder(parm);
}
/// <summary>
/// 根据工单号填写不良品单据
/// </summary>
/// <param name="parm">不良品单数据</param>
/// <returns></returns>
public QcScrapRecords CreateDefectOrderByWorkorder(QcScrapRecords parm)
{
// 验证工单号
if (string.IsNullOrEmpty(parm.Workorder))
{
throw new Exception("工单号不能为空");
}
QcScrapRecords newQcScrapRecords = parm;
var workorderInfo = Context
.Queryable<ProWorkorder>()
.Where(x => x.Workorder == parm.Workorder)
.First();
// 单据不良数校验
if (parm.ScrapQuantity + workorderInfo.DefectNum > workorderInfo.PlanNum)
{
throw new Exception("不良数量总和大于计划数,请检查输入!");
}
if (parm.ScrapQuantity < 0)
{
throw new Exception("不良数量需要为正数!");
}
// 工单号创建,根据工单信息填充报废单的相关字段
if (workorderInfo != null)
{
newQcScrapRecords.ProductCode = workorderInfo.productionCode;
newQcScrapRecords.ProductName = workorderInfo.productionName;
newQcScrapRecords.LineCode = workorderInfo.RouteCode;
newQcScrapRecords.StoveCode = workorderInfo.StoveCode;
newQcScrapRecords.BatchNo = workorderInfo.FeedOrder;
newQcScrapRecords.MaterialCode = workorderInfo.MaterialCode;
newQcScrapRecords.MaterialName = workorderInfo.MaterialName;
// 如有领料记录,则绑定采购记录
var purchaseInfo = Context
.Queryable<MmRecordInbound>()
.Where(it => it.BatchNo == workorderInfo.FeedOrder)
.Where(it => it.TransactionType == "采购入库")
.Where(it => it.Remarks != "已撤销")
.First();
if (purchaseInfo != null)
{
newQcScrapRecords.SupplierCode = purchaseInfo.SupplierCode;
newQcScrapRecords.SupplierName = purchaseInfo.SupplierName;
newQcScrapRecords.StoveCode = purchaseInfo.StoveCode;
newQcScrapRecords.Unit = purchaseInfo.Unit;
}
}
// 生成单号
newQcScrapRecords.ScrapOrderNo = GenerateScrapOrderNo("BL"); // BL表示不良
// 设置状态为待审批,类型为转用
newQcScrapRecords.Status = "待审批";
newQcScrapRecords.ScrapType = "不良品";
newQcScrapRecords.TenantId = 0;
newQcScrapRecords.Version = 1;
return Context.Insertable(newQcScrapRecords).ExecuteReturnEntity();
}
/// <summary>
/// 根据不良品记录ID撤销不良品记录
/// </summary>
/// <param name="id">不良品记录ID</param>
/// <returns></returns>
public int RevokeScrapRecordById(long id)
{
// 调用现有的撤销方法
return RevokeScrapRecord(id);
}
/// <summary>
/// 根据不良记录进行审批
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public string ApproveDefectRecordById(DefectApproveDto parm)
{
throw new NotImplementedException();
}
}
}