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.qc.iqc.MesQcIqcDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ncr.MesQcNcrDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.arrivalnotice.MesWmArrivalNoticeLineMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.arrivalnotice.MesWmArrivalNoticeMapper;
|
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
|
import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
|
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
|
import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Component;
|
|
import java.math.BigDecimal;
|
import java.util.Objects;
|
|
/** 采购处置前锁定并核对检验单和到货行;调用者须处于事务中。 */
|
@Component
|
public class MesQcNcrPurchaseSourceValidator {
|
@Resource private MesQcIqcMapper iqcMapper;
|
@Resource private MesWmArrivalNoticeMapper arrivalNoticeMapper;
|
@Resource private MesWmArrivalNoticeLineMapper lineMapper;
|
|
public record Source(MesQcIqcDO iqc, MesWmArrivalNoticeDO notice, MesWmArrivalNoticeLineDO line) {}
|
|
public Source validate(MesQcNcrDO ncr) {
|
if (!Objects.equals(ncr.getQcType(), MesQcTypeEnum.IQC.getType()) || ncr.getQcId() == null)
|
throw new ServiceException(400, "采购不合格处置必须关联来料检验单");
|
var iqc = iqcMapper.selectOne(new LambdaQueryWrapperX<MesQcIqcDO>()
|
.eq(MesQcIqcDO::getId, ncr.getQcId()).last("FOR UPDATE"));
|
if (iqc == null || !Objects.equals(iqc.getStatus(), MesQcStatusEnum.FINISHED.getStatus())
|
|| !Objects.equals(iqc.getSourceDocType(), MesBizTypeConstants.WM_ARRIVAL_NOTICE)
|
|| iqc.getSourceDocId() == null || iqc.getSourceLineId() == null)
|
throw new ServiceException(400, "采购处置仅支持已完成且关联到货通知单行的来料检验");
|
var notice = arrivalNoticeMapper.selectOne(new LambdaQueryWrapperX<MesWmArrivalNoticeDO>()
|
.eq(MesWmArrivalNoticeDO::getId, iqc.getSourceDocId()).last("FOR UPDATE"));
|
var line = lineMapper.selectOne(new LambdaQueryWrapperX<MesWmArrivalNoticeLineDO>()
|
.eq(MesWmArrivalNoticeLineDO::getId, iqc.getSourceLineId()).last("FOR UPDATE"));
|
if (notice == null || line == null || !Objects.equals(line.getNoticeId(), notice.getId())
|
|| !Objects.equals(line.getIqcId(), iqc.getId())
|
|| !isDispositionAllowedNoticeStatus(notice.getStatus())
|
|| !Objects.equals(line.getItemId(), iqc.getItemId())
|
|| !Objects.equals(ncr.getItemId(), iqc.getItemId())
|
|| !samePositive(ncr.getUnqualifiedQuantity(), iqc.getUnqualifiedQuantity())
|
|| !samePositive(iqc.getCheckQuantity(), line.getArrivalQuantity()))
|
throw new ServiceException(400, "到货单、质检单及不合格单来源或数量不一致,无法自动处置");
|
return new Source(iqc, notice, line);
|
}
|
|
/**
|
* 不合格处置允许的到货通知状态。
|
*
|
* 来料检验完成时,到货通知会在同一事务里从「待质检」推进到「待入库」并自动生成采购入库单
|
* (只取合格数量),随后确认入库后变为「已完成」。不合格部分的退货/让步由不合格单单独处置,
|
* 不再受通知状态限制,否则单行通知(或最后一行检验完成时)的不合格单将永远无法处置。
|
*/
|
private boolean isDispositionAllowedNoticeStatus(Integer status) {
|
return Objects.equals(status, MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus())
|
|| Objects.equals(status, MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus())
|
|| Objects.equals(status, MesWmArrivalNoticeStatusEnum.FINISHED.getStatus());
|
}
|
|
private boolean samePositive(BigDecimal a, BigDecimal b) {
|
return a != null && b != null && a.signum() > 0 && a.compareTo(b) == 0;
|
}
|
}
|