package cn.iocoder.yudao.module.mes.service.qc.ncr;
|
|
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ncr.MesQcNcrDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.qc.ipqc.MesQcIpqcMapper;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction.MesWmTransactionDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceLineMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.transaction.MesWmTransactionMapper;
|
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
|
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
|
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
|
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderSourceTypeEnum;
|
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum;
|
import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
|
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
|
import cn.iocoder.yudao.module.mes.dal.mysql.qc.ncr.MesQcNcrMapper;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Component;
|
|
import java.math.BigDecimal;
|
import java.util.List;
|
import java.util.Objects;
|
|
/** 关闭返工/返修 NCR 前,核实操作者明确指定的整批复检证据。 */
|
@Component
|
public class MesQcNcrReinspectionValidator {
|
|
@Resource private MesProWorkOrderMapper workOrderMapper;
|
@Resource private MesQcIpqcMapper ipqcMapper;
|
@Resource private MesMdItemMapper itemMapper;
|
@Resource private MesWmProductProduceLineMapper produceLineMapper;
|
@Resource private MesWmProductProduceMapper produceMapper;
|
@Resource private MesWmTransactionMapper transactionMapper;
|
@Resource private MesQcNcrMapper ncrMapper;
|
|
public void validate(MesQcNcrDO ncr, Long reinspectionIpqcId) {
|
if (reinspectionIpqcId == null) {
|
throw new ServiceException(400, "返工/返修关闭前请选择关联产出的整批复检单");
|
}
|
// 当前订单仅持有来源编码;存在多个匹配项时不能猜测关联。
|
List<MesProWorkOrderDO> orders = workOrderMapper.selectList(new LambdaQueryWrapperX<MesProWorkOrderDO>()
|
.eq(MesProWorkOrderDO::getOrderSourceType, MesProWorkOrderSourceTypeEnum.STORE.getType())
|
.eq(MesProWorkOrderDO::getOrderSourceCode, ncr.getCode()));
|
if (orders.size() != 1) {
|
throw new ServiceException(400, "无法唯一确定此不合格单对应的返工/返修生产订单,请先核对来源编码");
|
}
|
MesProWorkOrderDO order = orders.get(0);
|
if (!Objects.equals(order.getStatus(), MesProWorkOrderStatusEnum.FINISHED.getStatus())
|
|| order.getFinishDate() == null) {
|
throw new ServiceException(400, "返工/返修生产订单尚未完成或缺少完成时间,不能关闭");
|
}
|
MesMdItemDO product = itemMapper.selectById(order.getProductId());
|
if (product == null || product.getMdmItemId() == null
|
|| !Objects.equals(product.getMdmItemId(), ncr.getItemId())) {
|
throw new ServiceException(400, "返工订单产品与不合格物料的映射不明确,请先核对产品主数据");
|
}
|
MesQcIpqcDO qc = ipqcMapper.selectById(reinspectionIpqcId);
|
// 出货返工同样走报工产出复检:返工工单完成后,需绑定其报工产出行的过程检验单
|
if (!Objects.equals(ncr.getQcType(), MesQcTypeEnum.IPQC.getType())
|
&& !Objects.equals(ncr.getQcType(), MesQcTypeEnum.RQC.getType())
|
&& !Objects.equals(ncr.getQcType(), MesQcTypeEnum.OQC.getType())) {
|
validateLegacy(qc, order, ncr);
|
return;
|
}
|
if (qc == null || !Objects.equals(qc.getWorkOrderId(), order.getId())
|
|| !Objects.equals(qc.getItemId(), order.getProductId())
|
|| !Objects.equals(qc.getSourceDocType(), MesBizTypeConstants.PRO_FEEDBACK)
|
|| qc.getSourceDocId() == null || qc.getSourceLineId() == null) {
|
throw new ServiceException(400, "请选择关联返工订单报工产出行的过程检验单作为复检证据");
|
}
|
if (ncr.getHandleDate() == null || qc.getCreateTime() == null || qc.getCreateTime().isBefore(ncr.getHandleDate())
|
|| qc.getInspectDate() == null || qc.getInspectDate().isBefore(ncr.getHandleDate())) {
|
throw new ServiceException(400, "复检单须在返工处置后创建并检验,请核对检验日期");
|
}
|
BigDecimal quantity = ncr.getUnqualifiedQuantity();
|
if (!Objects.equals(qc.getStatus(), MesQcStatusEnum.FINISHED.getStatus())
|
|| quantity == null || quantity.signum() <= 0
|
|| qc.getCheckQuantity() == null || qc.getCheckQuantity().compareTo(quantity) != 0
|
|| qc.getQualifiedQuantity() == null || qc.getQualifiedQuantity().signum() < 0
|
|| qc.getUnqualifiedQuantity() == null || qc.getUnqualifiedQuantity().signum() < 0
|
|| qc.getQualifiedQuantity().add(qc.getUnqualifiedQuantity()).compareTo(quantity) != 0
|
|| !Objects.equals(qc.getCheckResult(), qc.getUnqualifiedQuantity().signum() == 0 ? 1 : 2)) {
|
throw new ServiceException(400, "复检必须已完成,且合格与不合格数量之和须等于不合格单数量");
|
}
|
MesWmProductProduceLineDO line = produceLineMapper.selectById(qc.getSourceLineId());
|
MesWmProductProduceDO produce = line == null ? null : produceMapper.selectById(line.getProduceId());
|
if (line == null || produce == null || !Objects.equals(line.getFeedbackId(), qc.getSourceDocId())
|
|| !Objects.equals(produce.getWorkOrderId(), order.getId())
|
|| !Objects.equals(line.getItemId(), qc.getItemId())
|
|| !Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())
|
|| line.getQuantity() == null || line.getQuantity().compareTo(qc.getQualifiedQuantity()) != 0) {
|
throw new ServiceException(400, "复检产出行与返工订单或合格数量不一致");
|
}
|
List<MesWmTransactionDO> flows = transactionMapper.selectList(new LambdaQueryWrapperX<MesWmTransactionDO>()
|
.eq(MesWmTransactionDO::getBizType, MesBizTypeConstants.WM_PRODUCT_PRODUCE)
|
.eq(MesWmTransactionDO::getBizId, produce.getId())
|
.eq(MesWmTransactionDO::getBizLineId, line.getId())
|
.eq(MesWmTransactionDO::getWorkOrderId, order.getId())
|
.eq(MesWmTransactionDO::getItemId, qc.getItemId())
|
.eq(MesWmTransactionDO::getType, MesWmTransactionTypeEnum.IN.getType()));
|
BigDecimal stocked = flows.stream().map(MesWmTransactionDO::getQuantity)
|
.filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
|
if (stocked.compareTo(qc.getQualifiedQuantity()) != 0) {
|
throw new ServiceException(400, "返工复检合格产出的入库流水数量不匹配,暂不能关闭");
|
}
|
if (qc.getUnqualifiedQuantity().signum() > 0) {
|
List<MesQcNcrDO> children = ncrMapper.selectList(new LambdaQueryWrapperX<MesQcNcrDO>()
|
.eq(MesQcNcrDO::getQcType, MesQcTypeEnum.IPQC.getType())
|
.eq(MesQcNcrDO::getQcId, qc.getId()));
|
if (children.size() != 1 || !Objects.equals(children.get(0).getItemId(), ncr.getItemId())
|
|| children.get(0).getUnqualifiedQuantity() == null
|
|| children.get(0).getUnqualifiedQuantity().compareTo(qc.getUnqualifiedQuantity()) != 0) {
|
throw new ServiceException(400, "复检不合格数量须全部移交至唯一的新不合格处理单,请核对物料和数量");
|
}
|
}
|
}
|
|
/** 其他检验来源保持原有关闭规则。 */
|
private void validateLegacy(MesQcIpqcDO qc, MesProWorkOrderDO order, MesQcNcrDO ncr) {
|
if (qc == null || !Objects.equals(qc.getWorkOrderId(), order.getId())
|
|| !Objects.equals(qc.getItemId(), order.getProductId())
|
|| qc.getSourceDocType() != null || qc.getSourceDocId() != null || qc.getSourceLineId() != null) {
|
throw new ServiceException(400, "请选择该返工订单产品的独立过程检验单作为复检证据");
|
}
|
if (qc.getCreateTime() == null || qc.getCreateTime().isBefore(order.getFinishDate())
|
|| qc.getInspectDate() == null || qc.getInspectDate().isBefore(order.getFinishDate())) {
|
throw new ServiceException(400, "复检单须在返工订单完成后创建并检验,请核对检验日期");
|
}
|
BigDecimal quantity = ncr.getUnqualifiedQuantity();
|
if (!Objects.equals(qc.getStatus(), MesQcStatusEnum.FINISHED.getStatus())
|
|| !Objects.equals(qc.getCheckResult(), 1)
|
|| quantity == null || quantity.signum() <= 0
|
|| qc.getCheckQuantity() == null || qc.getCheckQuantity().compareTo(quantity) != 0
|
|| qc.getQualifiedQuantity() == null || qc.getQualifiedQuantity().compareTo(quantity) != 0
|
|| qc.getUnqualifiedQuantity() == null || qc.getUnqualifiedQuantity().signum() != 0) {
|
throw new ServiceException(400, "复检必须已完成且整批合格,检测数量和合格数量须等于不合格单数量");
|
}
|
}
|
}
|