package cn.iocoder.yudao.module.mes.service.qc.ncr;
|
|
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ncr.MesQcNcrDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ncr.MesQcPurchaseReturnDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorLineDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.qc.ncr.MesQcPurchaseReturnMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.qc.ncr.MesQcPurchaseConcessionMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnvendor.MesWmReturnVendorLineMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnvendor.MesWmReturnVendorMapper;
|
import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnVendorStatusEnum;
|
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
|
import cn.hutool.core.util.StrUtil;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import java.time.LocalDateTime;
|
import java.util.Objects;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
|
@Service
|
public class MesQcPurchaseReturnService {
|
/** 采购退货单编码规则(MesMdAutoCodeRule 中的 code) */
|
private static final String RETURN_VENDOR_CODE_RULE = "WM_RETURN_VENDOR_CODE";
|
|
@Resource private MesQcNcrPurchaseSourceValidator sourceValidator;
|
@Resource private MesQcPurchaseReturnMapper mapper;
|
@Resource private MesQcPurchaseConcessionMapper concessionMapper;
|
@Resource private MesQcArrivalDispositionService arrivalDispositionService;
|
@Resource private MesWmReturnVendorMapper returnVendorMapper;
|
@Resource private MesWmReturnVendorLineMapper returnVendorLineMapper;
|
@Resource private MesMdAutoCodeRecordService autoCodeRecordService;
|
|
/** 必须由已锁定 NCR 的处置事务调用。 */
|
@Transactional(rollbackFor = Exception.class)
|
public MesQcPurchaseReturnDO createForNcr(MesQcNcrDO ncr) {
|
if (mapper.selectByNcrId(ncr.getId()) != null)
|
throw new ServiceException(400, "该不合格单已生成采购退货单");
|
var source = sourceValidator.validate(ncr);
|
if (concessionMapper.selectByIqcId(ncr.getQcId()) != null || mapper.selectByIqcId(ncr.getQcId()) != null)
|
throw new ServiceException(400, "该来料检验已有采购处置单,不能重复处置");
|
if (source.notice().getVendorId() == null || source.notice().getPurchaseOrderId() == null
|
|| source.line().getPurchaseOrderItemId() == null)
|
throw new ServiceException(400, "到货单缺少供应商、采购订单或采购明细,无法生成可追溯退货单");
|
|
// 1. 生成「供应商退货单」:不合格品从未入库,不产生库存出库,也无需拣货与审批,
|
// 直接以已完成状态落到采购仓库-采购退货列表中,供仓库/采购追溯并按实物退回供应商。
|
String code = autoCodeRecordService.generateAutoCode(RETURN_VENDOR_CODE_RULE, null);
|
MesWmReturnVendorDO vendorReturn = new MesWmReturnVendorDO();
|
vendorReturn.setCode(code);
|
vendorReturn.setName("来料不合格退货-" + ncr.getCode());
|
vendorReturn.setPurchaseOrderId(source.notice().getPurchaseOrderId());
|
vendorReturn.setPurchaseOrderCode(source.notice().getPurchaseOrderCode());
|
vendorReturn.setContractNo(source.notice().getContractNo());
|
vendorReturn.setVendorId(source.notice().getVendorId());
|
vendorReturn.setReturnDate(LocalDateTime.now());
|
vendorReturn.setReturnReason("来料检验不合格");
|
vendorReturn.setStatus(MesWmReturnVendorStatusEnum.FINISHED.getStatus());
|
vendorReturn.setRemark("由来料不合格单 " + ncr.getCode() + "(来料检验 " + source.iqc().getCode()
|
+ ")退货处置自动生成;不合格品未入库,无需拣货,单据直接完成。");
|
returnVendorMapper.insert(vendorReturn);
|
MesWmReturnVendorLineDO vendorReturnLine = new MesWmReturnVendorLineDO();
|
vendorReturnLine.setReturnId(vendorReturn.getId());
|
vendorReturnLine.setItemId(ncr.getItemId());
|
vendorReturnLine.setQuantity(ncr.getUnqualifiedQuantity());
|
vendorReturnLine.setBatchCode(StrUtil.blankToDefault(source.iqc().getVendorBatch(), null));
|
vendorReturnLine.setRemark("来料检验 " + source.iqc().getCode() + " 不合格数量");
|
returnVendorLineMapper.insert(vendorReturnLine);
|
|
// 2. 记录质量侧处置台账(与退货单同号),因退货单已直接完成,这里同步标记为已确认,
|
// 不合格单关闭时校验的正是这条记录。
|
MesQcPurchaseReturnDO doc = new MesQcPurchaseReturnDO();
|
doc.setCode(code);
|
doc.setNcrId(ncr.getId());
|
doc.setIqcId(ncr.getQcId());
|
doc.setNoticeId(source.notice().getId());
|
doc.setNoticeLineId(source.line().getId());
|
doc.setPurchaseOrderId(source.notice().getPurchaseOrderId());
|
doc.setPurchaseOrderItemId(source.line().getPurchaseOrderItemId());
|
doc.setVendorId(source.notice().getVendorId());
|
doc.setItemId(ncr.getItemId());
|
doc.setVendorBatch(source.iqc().getVendorBatch());
|
doc.setQuantity(ncr.getUnqualifiedQuantity());
|
doc.setStatus(1);
|
doc.setConfirmedBy(getLoginUserId());
|
doc.setConfirmedAt(LocalDateTime.now());
|
doc.setConfirmationRemark("随不合格处置自动生成采购退货单 " + code + ",未发生库存出库");
|
mapper.insert(doc);
|
arrivalDispositionService.refresh(source.notice().getId());
|
return doc;
|
}
|
|
public MesQcPurchaseReturnDO getByNcrId(Long ncrId) { return mapper.selectByNcrId(ncrId); }
|
|
public PageResult<MesQcPurchaseReturnDO> getPage(PageParam page, Integer status) {
|
if (status != null && status != 0 && status != 1) throw new ServiceException(400, "采购退货状态无效");
|
return mapper.selectPage(page, status);
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public void confirm(Long id, String remark) {
|
MesQcPurchaseReturnDO doc = mapper.selectByIdForUpdate(id);
|
if (doc == null) throw new ServiceException(400, "采购退货单不存在");
|
if (!Objects.equals(doc.getStatus(), 0)) throw new ServiceException(400, "采购退货单已确认退货");
|
mapper.updateById(new MesQcPurchaseReturnDO().setId(id).setStatus(1)
|
.setConfirmedBy(getLoginUserId()).setConfirmedAt(LocalDateTime.now()).setConfirmationRemark(remark));
|
arrivalDispositionService.refresh(doc.getNoticeId());
|
}
|
|
public void validateConfirmed(Long ncrId) {
|
MesQcPurchaseReturnDO doc = mapper.selectByNcrId(ncrId);
|
if (doc == null || !Objects.equals(doc.getStatus(), 1))
|
throw new ServiceException(400, "请先由采购仓库确认采购退货完成,再关闭不合格单");
|
}
|
}
|