diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java index 6d6aad3..1bf2792 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java @@ -366,6 +366,7 @@ public interface ErrorCodeConstants { ErrorCode QC_OQC_QUANTITY_MISMATCH = new ErrorCode(1_040_607_004, "合格品与不合格品数量之和须等于检测数量"); ErrorCode QC_OQC_NO_TEMPLATE = new ErrorCode(1_040_607_005, "当前产品未配置 OQC 检测模板"); ErrorCode QC_OQC_CHECK_RESULT_EMPTY = new ErrorCode(1_040_607_006, "完成检验单前,检测结果必须填写"); + ErrorCode QC_OQC_SOURCE_DOC_TYPE_UNKNOWN = new ErrorCode(1_040_607_007, "未知的 OQC 来源单据类型"); // ========== MES 质量管理-出货检验行(1-040-607-100) ========== ErrorCode QC_OQC_LINE_NOT_EXISTS = new ErrorCode(1_040_607_100, "出货检验行不存在"); @@ -491,6 +492,7 @@ public interface ErrorCodeConstants { ErrorCode WM_PRODUCT_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在"); ErrorCode WM_PRODUCT_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_708_201, "拣货明细的物料与领料单行的物料不一致"); ErrorCode WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_708_202, "拣货明细不属于指定的领料出库单"); + ErrorCode WM_PRODUCT_ISSUE_NO_DETAIL = new ErrorCode(1_040_708_203, "领料出库单没有拣货明细,无法执行领出"); // ========== MES 仓库管理-生产入库单(1-040-709-000) ========== ErrorCode WM_PRODUCT_PRODUCE_NOT_EXISTS = new ErrorCode(1_040_709_000, "生产入库单不存在"); diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java index 0fe4c9f..ba304b9 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java @@ -233,8 +233,10 @@ public class MesQcOqcServiceImpl implements MesQcOqcService { MesWmProductSalesDO sales = productSalesService.getProductSales(salesLine.getSalesId()); return sales != null ? sales.getCode() : null; } + return null; } - return null; + // 未知来源类型应报错,而不是静默忽略 + throw exception(QC_OQC_SOURCE_DOC_TYPE_UNKNOWN); } @Override diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java index c2d6bc5..78ae354 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java @@ -38,11 +38,13 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService @Resource private MesWmArrivalNoticeLineService arrivalNoticeLineService; + @Resource + private cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService vendorService; @Override public Long createArrivalNotice(MesWmArrivalNoticeSaveReqVO createReqVO) { - // 校验编码唯一 - validateCodeUnique(null, createReqVO.getCode()); + // 校验数据 + validateArrivalNoticeSaveData(createReqVO); // 插入 MesWmArrivalNoticeDO notice = BeanUtils.toBean(createReqVO, MesWmArrivalNoticeDO.class); @@ -55,14 +57,21 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService public void updateArrivalNotice(MesWmArrivalNoticeSaveReqVO updateReqVO) { // 校验存在 + 草稿状态 validateArrivalNoticeExistsAndDraft(updateReqVO.getId()); - // 校验编码唯一 - validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); + // 校验数据 + validateArrivalNoticeSaveData(updateReqVO); // 更新 MesWmArrivalNoticeDO updateObj = BeanUtils.toBean(updateReqVO, MesWmArrivalNoticeDO.class); arrivalNoticeMapper.updateById(updateObj); } + private void validateArrivalNoticeSaveData(MesWmArrivalNoticeSaveReqVO reqVO) { + // 校验编码唯一 + validateCodeUnique(reqVO.getId(), reqVO.getCode()); + // 校验供应商存在且启用 + vendorService.validateVendorExistsAndEnable(reqVO.getVendorId()); + } + @Override @Transactional(rollbackFor = Exception.class) public void deleteArrivalNotice(Long id) { @@ -144,6 +153,13 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService if (ObjUtil.notEqual(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus(), notice.getStatus())) { throw exception(WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT); } + // 行级防御校验:确保所有需检行都已完成 IQC + List lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(id); + boolean hasUnchecked = CollectionUtils.anyMatch(lines, + line -> Boolean.TRUE.equals(line.getIqcCheckFlag()) && line.getIqcId() == null); + if (hasUnchecked) { + throw exception(WM_ARRIVAL_NOTICE_IQC_PENDING); + } // 完成 arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO() diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueDetailServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueDetailServiceImpl.java index bea4dd4..55d1887 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueDetailServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueDetailServiceImpl.java @@ -125,6 +125,11 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta if (ObjUtil.notEqual(line.getIssueId(), reqVO.getIssueId())) { throw exception(WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH); } + // 校验主单状态为待拣货(只有 APPROVING 状态才允许新增/修改明细) + MesWmProductIssueDO issue = issueService.validateProductIssueExists(line.getIssueId()); + if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVING.getStatus(), issue.getStatus())) { + throw exception(WM_PRODUCT_ISSUE_STATUS_INVALID); + } // 校验物料匹配(明细 itemId 必须与行 itemId 一致) validateDetailItemMatch(reqVO.getItemId(), line.getItemId()); // 校验仓库、库区、库位的关联关系 diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java index a01e43c..374a2ce 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java @@ -109,7 +109,9 @@ public class MesWmProductIssueLineServiceImpl implements MesWmProductIssueLineSe * 校验保存时的关联数据 */ private void validateProductIssueLineSaveData(MesWmProductIssueLineSaveReqVO reqVO) { - // 校验父数据存在 + 校验物料在工单 BOM 中 + // 校验父数据存在 + 草稿状态才允许新增/修改行 + issueService.validateProductIssueExistsAndPrepare(reqVO.getIssueId()); + // 校验物料在工单 BOM 中 validateItemInWorkOrderBom(reqVO.getIssueId(), reqVO.getItemId()); // 校验物料存在 itemService.validateItemExistsAndEnable(reqVO.getItemId()); diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java index d8ad7c7..e794078 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java @@ -138,6 +138,11 @@ public class MesWmProductIssueServiceImpl implements MesWmProductIssueService { if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVING.getStatus(), issue.getStatus())) { throw exception(WM_PRODUCT_ISSUE_STATUS_INVALID); } + // 校验拣货明细闭环:行数量 = 明细数量 + if (!checkProductIssueQuantity(id)) { + throw exception(WM_PRODUCT_ISSUE_DETAIL_QUANTITY_MISMATCH); + } + // 执行拣货(待拣货 → 待执行领出) issueMapper.updateById(new MesWmProductIssueDO() .setId(id).setStatus(MesWmProductIssueStatusEnum.APPROVED.getStatus())); @@ -151,6 +156,15 @@ public class MesWmProductIssueServiceImpl implements MesWmProductIssueService { if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVED.getStatus(), issue.getStatus())) { throw exception(WM_PRODUCT_ISSUE_STATUS_INVALID); } + // 校验至少有一条明细 + List details = issueDetailService.getProductIssueDetailListByIssueId(id); + if (CollUtil.isEmpty(details)) { + throw exception(WM_PRODUCT_ISSUE_NO_DETAIL); + } + // 校验行数量 = 明细数量 + if (!checkProductIssueQuantity(id)) { + throw exception(WM_PRODUCT_ISSUE_DETAIL_QUANTITY_MISMATCH); + } // 2. 遍历所有明细,创建库存事务(扣减库存 + 记录流水) createTransactionList(issue);