| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService; |
| | | import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum; |
| | | import cn.iocoder.yudao.module.mes.service.wm.itemreceipt.MesWmItemReceiptService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.context.annotation.Lazy; |
| | |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | |
| | | @Validated |
| | | @Slf4j |
| | | public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService { |
| | | |
| | | @Resource |
| | | private cn.iocoder.yudao.module.mes.service.qc.ncr.MesQcArrivalDispositionService arrivalDispositionService; |
| | | |
| | | @Resource |
| | | private MesWmArrivalNoticeMapper arrivalNoticeMapper; |
| | |
| | | @Resource |
| | | @Lazy |
| | | private MesQcIqcService iqcService; |
| | | |
| | | @Resource |
| | | @Lazy |
| | | private MesWmItemReceiptService itemReceiptService; |
| | | |
| | | @Override |
| | | public Long createArrivalNotice(MesWmArrivalNoticeSaveReqVO createReqVO) { |
| | |
| | | } |
| | | |
| | | // 2. 检查所有行的 iqcCheckFlag:如果有需要检验的行,进入待质检状态 |
| | | // 注意:来料检验单不在提交时生成,由库管点击「执行质检」时通过 createIqcForArrivalNotice 生成 |
| | | boolean needCheck = CollectionUtils.anyMatch(lines, |
| | | line -> Boolean.TRUE.equals(line.getIqcCheckFlag())); |
| | | if (needCheck) { |
| | | // 2.1 需要检验,进入待质检 |
| | | arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO() |
| | | .setId(id).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus())); |
| | | // 2.2 为需要质检的行自动创建 IQC 检验单 |
| | | for (MesWmArrivalNoticeLineDO line : lines) { |
| | | if (Boolean.TRUE.equals(line.getIqcCheckFlag()) && line.getIqcId() == null) { |
| | | try { |
| | | Long iqcId = iqcService.createIqcFromArrivalNotice( |
| | | id, line, notice.getCode(), notice.getVendorId()); |
| | | // 更新行上的 iqcId |
| | | arrivalNoticeLineService.updateArrivalNoticeLineIqcId(line.getId(), iqcId); |
| | | log.info("[submitArrivalNotice][到货通知单({}) 行({}) 自动创建 IQC 检验单({})]", |
| | | id, line.getId(), iqcId); |
| | | } catch (Exception e) { |
| | | log.warn("[submitArrivalNotice][到货通知单({}) 行({}) 创建 IQC 失败: {}]", |
| | | id, line.getId(), e.getMessage()); |
| | | // 继续处理其他行,不中断流程 |
| | | } |
| | | } |
| | | } |
| | | return; |
| | | } |
| | | // 不需要检验,直接进入待入库 |
| | | // 不需要检验,直接进入待入库,并自动生成采购入库单交给库管确认入库 |
| | | arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO() |
| | | .setId(id).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus())); |
| | | autoGenerateItemReceipt(notice); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Integer createIqcForArrivalNotice(Long id) { |
| | | // 1. 校验到货通知单存在且为待质检状态 |
| | | MesWmArrivalNoticeDO notice = validateArrivalNoticeExists(id); |
| | | if (ObjUtil.notEqual(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus(), notice.getStatus())) { |
| | | String statusName = Arrays.stream(MesWmArrivalNoticeStatusEnum.values()) |
| | | .filter(item -> ObjUtil.equal(item.getStatus(), notice.getStatus())) |
| | | .map(MesWmArrivalNoticeStatusEnum::getName).findFirst() |
| | | .orElse(String.valueOf(notice.getStatus())); |
| | | throw new ServiceException(WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_QC.getCode(), |
| | | "到货通知单[" + notice.getCode() + "]当前状态为[" + statusName + "],只有「待质检」状态才能执行质检。" |
| | | + "请刷新列表确认单据状态;若该单据已执行过质检,请直接到【质量管理 - IQC检验】录入检测结果。"); |
| | | } |
| | | // 2. 校验需检行 |
| | | List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(id); |
| | | List<MesWmArrivalNoticeLineDO> needCheckLines = lines.stream() |
| | | .filter(line -> Boolean.TRUE.equals(line.getIqcCheckFlag())) |
| | | .toList(); |
| | | if (needCheckLines.isEmpty()) { |
| | | throw new ServiceException(WM_ARRIVAL_NOTICE_NO_IQC_LINE.getCode(), |
| | | "到货通知单[" + notice.getCode() + "]的明细行均为「不需检验」,无需生成来料检验单。" |
| | | + "请直接到该单据执行入库;若本应检验,请核对采购订单行的检验标识(该标识由采购订单带入,到货通知单上不可修改)。"); |
| | | } |
| | | List<MesWmArrivalNoticeLineDO> pendingLines = needCheckLines.stream() |
| | | .filter(line -> line.getIqcId() == null) |
| | | .toList(); |
| | | if (pendingLines.isEmpty()) { |
| | | throw new ServiceException(WM_ARRIVAL_NOTICE_IQC_ALREADY_CREATED.getCode(), |
| | | "到货通知单[" + notice.getCode() + "]的 " + needCheckLines.size() |
| | | + " 条需检行均已生成来料检验单,无需重复生成。请直接到【质量管理 - IQC检验】录入检测结果;" |
| | | + "如需重新生成,请先删除原检验单。"); |
| | | } |
| | | // 3. 逐行生成来料检验单 |
| | | int createdCount = 0; |
| | | for (MesWmArrivalNoticeLineDO line : pendingLines) { |
| | | Long iqcId; |
| | | try { |
| | | iqcId = iqcService.createIqcFromArrivalNotice(id, line, notice.getCode(), notice.getVendorId()); |
| | | } catch (Exception ex) { |
| | | log.error("[createIqcForArrivalNotice][到货通知单({}) 行({}) 创建 IQC 失败]", id, line.getId(), ex); |
| | | throw new ServiceException(WM_ARRIVAL_NOTICE_IQC_CREATE_FAILED.getCode(), |
| | | "到货通知单[" + notice.getCode() + "]的明细行(物料ID=" + line.getItemId() |
| | | + ")生成来料检验单失败:" + ex.getMessage() |
| | | + "。请稍后重试;若持续失败,请到【质量管理 - IQC检验】手工新建检验单。"); |
| | | } |
| | | arrivalNoticeLineService.updateArrivalNoticeLineIqcId(line.getId(), iqcId); |
| | | createdCount++; |
| | | log.info("[createIqcForArrivalNotice][到货通知单({}) 行({}) 生成 IQC 检验单({})]", |
| | | id, line.getId(), iqcId); |
| | | } |
| | | return createdCount; |
| | | } |
| | | |
| | | @Override |
| | |
| | | // 2. 更新到货通知单行:绑定 iqcId + 质检结果 + 合格数量 |
| | | arrivalNoticeLineService.updateArrivalNoticeLineWhenIqcFinish(lineId, iqcId, checkResult, qualifiedQuantity); |
| | | |
| | | // 同时认可仓库已确认的来料退货;保留原始不合格结论。 |
| | | arrivalDispositionService.refresh(id); |
| | | // 3.1 判断是否所有需检行都已完成质检 |
| | | List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(id); |
| | | boolean hasUnchecked = CollectionUtils.anyMatch(lines, |
| | |
| | | // 3.3 所有行检验完成且可入库,更新主表状态 PENDING_QC → PENDING_RECEIPT |
| | | arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO() |
| | | .setId(id).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus())); |
| | | // 3.4 质检合格后直接生成采购入库单(待入库),库管只需核对数量并确认入库 |
| | | autoGenerateItemReceipt(notice); |
| | | } |
| | | |
| | | /** |
| | | * 到货通知单进入「待入库」后自动生成采购入库单。 |
| | | * 库管无需再回到到货通知点「执行入库」,直接到【采购入库】核对数量并确认入库即可。 |
| | | */ |
| | | private void autoGenerateItemReceipt(MesWmArrivalNoticeDO notice) { |
| | | try { |
| | | Long receiptId = itemReceiptService.generateItemReceiptFromArrivalNotice(notice.getId()); |
| | | log.info("[autoGenerateItemReceipt][到货通知单({}) 已自动生成采购入库单({})]", |
| | | notice.getId(), receiptId); |
| | | } catch (ServiceException ex) { |
| | | log.error("[autoGenerateItemReceipt][到货通知单({}) 自动生成采购入库单失败]", |
| | | notice.getId(), ex); |
| | | throw new ServiceException(ex.getCode(), |
| | | "到货通知单[" + notice.getCode() + "]转入待入库后自动生成采购入库单失败:" + ex.getMessage() |
| | | + "。本次操作未生效,请稍后重试;若持续失败请联系管理员。"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | arrivalNoticeLineService.createArrivalNoticeLineFromPurchaseOrderItem(line); |
| | | } |
| | | |
| | | // 7. 如果有需要质检的行,更新状态为待质检并自动创建 IQC 单 |
| | | // 7. 如果有需要质检的行,更新状态为待质检 |
| | | // 注意:来料检验单不在生成时创建,由库管点击「执行质检」时通过 createIqcForArrivalNotice 生成 |
| | | if (hasNeedQc) { |
| | | arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO() |
| | | .setId(notice.getId()).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus())); |
| | | // 为需要质检的行自动创建 IQC 检验单 |
| | | List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(notice.getId()); |
| | | for (MesWmArrivalNoticeLineDO line : lines) { |
| | | if (Boolean.TRUE.equals(line.getIqcCheckFlag())) { |
| | | Long iqcId = iqcService.createIqcFromArrivalNotice( |
| | | notice.getId(), line, notice.getCode(), notice.getVendorId()); |
| | | arrivalNoticeLineService.updateArrivalNoticeLineIqcId(line.getId(), iqcId); |
| | | log.info("[generateArrivalNoticeFromPurchaseOrder][到货通知单({}) 行({}) 自动创建 IQC 检验单({})]", |
| | | notice.getId(), line.getId(), iqcId); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return notice.getId(); |