Commit b8dbd7cdce563d95da9e43db9fcd045b1482955d
1 parent
92ed8c2e
✨ feat(mes): 新增 OQC 来源单据类型错误码及相关校验
Showing
6 changed files
with
47 additions
and
6 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
| ... | ... | @@ -366,6 +366,7 @@ public interface ErrorCodeConstants { |
| 366 | 366 | ErrorCode QC_OQC_QUANTITY_MISMATCH = new ErrorCode(1_040_607_004, "合格品与不合格品数量之和须等于检测数量"); |
| 367 | 367 | ErrorCode QC_OQC_NO_TEMPLATE = new ErrorCode(1_040_607_005, "当前产品未配置 OQC 检测模板"); |
| 368 | 368 | ErrorCode QC_OQC_CHECK_RESULT_EMPTY = new ErrorCode(1_040_607_006, "完成检验单前,检测结果必须填写"); |
| 369 | + ErrorCode QC_OQC_SOURCE_DOC_TYPE_UNKNOWN = new ErrorCode(1_040_607_007, "未知的 OQC 来源单据类型"); | |
| 369 | 370 | |
| 370 | 371 | // ========== MES 质量管理-出货检验行(1-040-607-100) ========== |
| 371 | 372 | ErrorCode QC_OQC_LINE_NOT_EXISTS = new ErrorCode(1_040_607_100, "出货检验行不存在"); |
| ... | ... | @@ -491,6 +492,7 @@ public interface ErrorCodeConstants { |
| 491 | 492 | ErrorCode WM_PRODUCT_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在"); |
| 492 | 493 | ErrorCode WM_PRODUCT_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_708_201, "拣货明细的物料与领料单行的物料不一致"); |
| 493 | 494 | ErrorCode WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_708_202, "拣货明细不属于指定的领料出库单"); |
| 495 | + ErrorCode WM_PRODUCT_ISSUE_NO_DETAIL = new ErrorCode(1_040_708_203, "领料出库单没有拣货明细,无法执行领出"); | |
| 494 | 496 | |
| 495 | 497 | // ========== MES 仓库管理-生产入库单(1-040-709-000) ========== |
| 496 | 498 | ErrorCode WM_PRODUCT_PRODUCE_NOT_EXISTS = new ErrorCode(1_040_709_000, "生产入库单不存在"); | ... | ... |
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 { |
| 233 | 233 | MesWmProductSalesDO sales = productSalesService.getProductSales(salesLine.getSalesId()); |
| 234 | 234 | return sales != null ? sales.getCode() : null; |
| 235 | 235 | } |
| 236 | + return null; | |
| 236 | 237 | } |
| 237 | - return null; | |
| 238 | + // 未知来源类型应报错,而不是静默忽略 | |
| 239 | + throw exception(QC_OQC_SOURCE_DOC_TYPE_UNKNOWN); | |
| 238 | 240 | } |
| 239 | 241 | |
| 240 | 242 | @Override | ... | ... |
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 |
| 38 | 38 | |
| 39 | 39 | @Resource |
| 40 | 40 | private MesWmArrivalNoticeLineService arrivalNoticeLineService; |
| 41 | + @Resource | |
| 42 | + private cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService vendorService; | |
| 41 | 43 | |
| 42 | 44 | @Override |
| 43 | 45 | public Long createArrivalNotice(MesWmArrivalNoticeSaveReqVO createReqVO) { |
| 44 | - // 校验编码唯一 | |
| 45 | - validateCodeUnique(null, createReqVO.getCode()); | |
| 46 | + // 校验数据 | |
| 47 | + validateArrivalNoticeSaveData(createReqVO); | |
| 46 | 48 | |
| 47 | 49 | // 插入 |
| 48 | 50 | MesWmArrivalNoticeDO notice = BeanUtils.toBean(createReqVO, MesWmArrivalNoticeDO.class); |
| ... | ... | @@ -55,14 +57,21 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService |
| 55 | 57 | public void updateArrivalNotice(MesWmArrivalNoticeSaveReqVO updateReqVO) { |
| 56 | 58 | // 校验存在 + 草稿状态 |
| 57 | 59 | validateArrivalNoticeExistsAndDraft(updateReqVO.getId()); |
| 58 | - // 校验编码唯一 | |
| 59 | - validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); | |
| 60 | + // 校验数据 | |
| 61 | + validateArrivalNoticeSaveData(updateReqVO); | |
| 60 | 62 | |
| 61 | 63 | // 更新 |
| 62 | 64 | MesWmArrivalNoticeDO updateObj = BeanUtils.toBean(updateReqVO, MesWmArrivalNoticeDO.class); |
| 63 | 65 | arrivalNoticeMapper.updateById(updateObj); |
| 64 | 66 | } |
| 65 | 67 | |
| 68 | + private void validateArrivalNoticeSaveData(MesWmArrivalNoticeSaveReqVO reqVO) { | |
| 69 | + // 校验编码唯一 | |
| 70 | + validateCodeUnique(reqVO.getId(), reqVO.getCode()); | |
| 71 | + // 校验供应商存在且启用 | |
| 72 | + vendorService.validateVendorExistsAndEnable(reqVO.getVendorId()); | |
| 73 | + } | |
| 74 | + | |
| 66 | 75 | @Override |
| 67 | 76 | @Transactional(rollbackFor = Exception.class) |
| 68 | 77 | public void deleteArrivalNotice(Long id) { |
| ... | ... | @@ -144,6 +153,13 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService |
| 144 | 153 | if (ObjUtil.notEqual(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus(), notice.getStatus())) { |
| 145 | 154 | throw exception(WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT); |
| 146 | 155 | } |
| 156 | + // 行级防御校验:确保所有需检行都已完成 IQC | |
| 157 | + List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(id); | |
| 158 | + boolean hasUnchecked = CollectionUtils.anyMatch(lines, | |
| 159 | + line -> Boolean.TRUE.equals(line.getIqcCheckFlag()) && line.getIqcId() == null); | |
| 160 | + if (hasUnchecked) { | |
| 161 | + throw exception(WM_ARRIVAL_NOTICE_IQC_PENDING); | |
| 162 | + } | |
| 147 | 163 | |
| 148 | 164 | // 完成 |
| 149 | 165 | arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO() | ... | ... |
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 |
| 125 | 125 | if (ObjUtil.notEqual(line.getIssueId(), reqVO.getIssueId())) { |
| 126 | 126 | throw exception(WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH); |
| 127 | 127 | } |
| 128 | + // 校验主单状态为待拣货(只有 APPROVING 状态才允许新增/修改明细) | |
| 129 | + MesWmProductIssueDO issue = issueService.validateProductIssueExists(line.getIssueId()); | |
| 130 | + if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVING.getStatus(), issue.getStatus())) { | |
| 131 | + throw exception(WM_PRODUCT_ISSUE_STATUS_INVALID); | |
| 132 | + } | |
| 128 | 133 | // 校验物料匹配(明细 itemId 必须与行 itemId 一致) |
| 129 | 134 | validateDetailItemMatch(reqVO.getItemId(), line.getItemId()); |
| 130 | 135 | // 校验仓库、库区、库位的关联关系 | ... | ... |
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 |
| 109 | 109 | * 校验保存时的关联数据 |
| 110 | 110 | */ |
| 111 | 111 | private void validateProductIssueLineSaveData(MesWmProductIssueLineSaveReqVO reqVO) { |
| 112 | - // 校验父数据存在 + 校验物料在工单 BOM 中 | |
| 112 | + // 校验父数据存在 + 草稿状态才允许新增/修改行 | |
| 113 | + issueService.validateProductIssueExistsAndPrepare(reqVO.getIssueId()); | |
| 114 | + // 校验物料在工单 BOM 中 | |
| 113 | 115 | validateItemInWorkOrderBom(reqVO.getIssueId(), reqVO.getItemId()); |
| 114 | 116 | // 校验物料存在 |
| 115 | 117 | itemService.validateItemExistsAndEnable(reqVO.getItemId()); | ... | ... |
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 { |
| 138 | 138 | if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVING.getStatus(), issue.getStatus())) { |
| 139 | 139 | throw exception(WM_PRODUCT_ISSUE_STATUS_INVALID); |
| 140 | 140 | } |
| 141 | + // 校验拣货明细闭环:行数量 = 明细数量 | |
| 142 | + if (!checkProductIssueQuantity(id)) { | |
| 143 | + throw exception(WM_PRODUCT_ISSUE_DETAIL_QUANTITY_MISMATCH); | |
| 144 | + } | |
| 145 | + | |
| 141 | 146 | // 执行拣货(待拣货 → 待执行领出) |
| 142 | 147 | issueMapper.updateById(new MesWmProductIssueDO() |
| 143 | 148 | .setId(id).setStatus(MesWmProductIssueStatusEnum.APPROVED.getStatus())); |
| ... | ... | @@ -151,6 +156,15 @@ public class MesWmProductIssueServiceImpl implements MesWmProductIssueService { |
| 151 | 156 | if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVED.getStatus(), issue.getStatus())) { |
| 152 | 157 | throw exception(WM_PRODUCT_ISSUE_STATUS_INVALID); |
| 153 | 158 | } |
| 159 | + // 校验至少有一条明细 | |
| 160 | + List<MesWmProductIssueDetailDO> details = issueDetailService.getProductIssueDetailListByIssueId(id); | |
| 161 | + if (CollUtil.isEmpty(details)) { | |
| 162 | + throw exception(WM_PRODUCT_ISSUE_NO_DETAIL); | |
| 163 | + } | |
| 164 | + // 校验行数量 = 明细数量 | |
| 165 | + if (!checkProductIssueQuantity(id)) { | |
| 166 | + throw exception(WM_PRODUCT_ISSUE_DETAIL_QUANTITY_MISMATCH); | |
| 167 | + } | |
| 154 | 168 | |
| 155 | 169 | // 2. 遍历所有明细,创建库存事务(扣减库存 + 记录流水) |
| 156 | 170 | createTransactionList(issue); | ... | ... |