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,6 +366,7 @@ public interface ErrorCodeConstants { | ||
| 366 | ErrorCode QC_OQC_QUANTITY_MISMATCH = new ErrorCode(1_040_607_004, "合格品与不合格品数量之和须等于检测数量"); | 366 | ErrorCode QC_OQC_QUANTITY_MISMATCH = new ErrorCode(1_040_607_004, "合格品与不合格品数量之和须等于检测数量"); |
| 367 | ErrorCode QC_OQC_NO_TEMPLATE = new ErrorCode(1_040_607_005, "当前产品未配置 OQC 检测模板"); | 367 | ErrorCode QC_OQC_NO_TEMPLATE = new ErrorCode(1_040_607_005, "当前产品未配置 OQC 检测模板"); |
| 368 | ErrorCode QC_OQC_CHECK_RESULT_EMPTY = new ErrorCode(1_040_607_006, "完成检验单前,检测结果必须填写"); | 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 | // ========== MES 质量管理-出货检验行(1-040-607-100) ========== | 371 | // ========== MES 质量管理-出货检验行(1-040-607-100) ========== |
| 371 | ErrorCode QC_OQC_LINE_NOT_EXISTS = new ErrorCode(1_040_607_100, "出货检验行不存在"); | 372 | ErrorCode QC_OQC_LINE_NOT_EXISTS = new ErrorCode(1_040_607_100, "出货检验行不存在"); |
| @@ -491,6 +492,7 @@ public interface ErrorCodeConstants { | @@ -491,6 +492,7 @@ public interface ErrorCodeConstants { | ||
| 491 | ErrorCode WM_PRODUCT_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在"); | 492 | ErrorCode WM_PRODUCT_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在"); |
| 492 | ErrorCode WM_PRODUCT_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_708_201, "拣货明细的物料与领料单行的物料不一致"); | 493 | ErrorCode WM_PRODUCT_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_708_201, "拣货明细的物料与领料单行的物料不一致"); |
| 493 | ErrorCode WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_708_202, "拣货明细不属于指定的领料出库单"); | 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 | // ========== MES 仓库管理-生产入库单(1-040-709-000) ========== | 497 | // ========== MES 仓库管理-生产入库单(1-040-709-000) ========== |
| 496 | ErrorCode WM_PRODUCT_PRODUCE_NOT_EXISTS = new ErrorCode(1_040_709_000, "生产入库单不存在"); | 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,8 +233,10 @@ public class MesQcOqcServiceImpl implements MesQcOqcService { | ||
| 233 | MesWmProductSalesDO sales = productSalesService.getProductSales(salesLine.getSalesId()); | 233 | MesWmProductSalesDO sales = productSalesService.getProductSales(salesLine.getSalesId()); |
| 234 | return sales != null ? sales.getCode() : null; | 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 | @Override | 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,11 +38,13 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService | ||
| 38 | 38 | ||
| 39 | @Resource | 39 | @Resource |
| 40 | private MesWmArrivalNoticeLineService arrivalNoticeLineService; | 40 | private MesWmArrivalNoticeLineService arrivalNoticeLineService; |
| 41 | + @Resource | ||
| 42 | + private cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService vendorService; | ||
| 41 | 43 | ||
| 42 | @Override | 44 | @Override |
| 43 | public Long createArrivalNotice(MesWmArrivalNoticeSaveReqVO createReqVO) { | 45 | public Long createArrivalNotice(MesWmArrivalNoticeSaveReqVO createReqVO) { |
| 44 | - // 校验编码唯一 | ||
| 45 | - validateCodeUnique(null, createReqVO.getCode()); | 46 | + // 校验数据 |
| 47 | + validateArrivalNoticeSaveData(createReqVO); | ||
| 46 | 48 | ||
| 47 | // 插入 | 49 | // 插入 |
| 48 | MesWmArrivalNoticeDO notice = BeanUtils.toBean(createReqVO, MesWmArrivalNoticeDO.class); | 50 | MesWmArrivalNoticeDO notice = BeanUtils.toBean(createReqVO, MesWmArrivalNoticeDO.class); |
| @@ -55,14 +57,21 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService | @@ -55,14 +57,21 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService | ||
| 55 | public void updateArrivalNotice(MesWmArrivalNoticeSaveReqVO updateReqVO) { | 57 | public void updateArrivalNotice(MesWmArrivalNoticeSaveReqVO updateReqVO) { |
| 56 | // 校验存在 + 草稿状态 | 58 | // 校验存在 + 草稿状态 |
| 57 | validateArrivalNoticeExistsAndDraft(updateReqVO.getId()); | 59 | validateArrivalNoticeExistsAndDraft(updateReqVO.getId()); |
| 58 | - // 校验编码唯一 | ||
| 59 | - validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); | 60 | + // 校验数据 |
| 61 | + validateArrivalNoticeSaveData(updateReqVO); | ||
| 60 | 62 | ||
| 61 | // 更新 | 63 | // 更新 |
| 62 | MesWmArrivalNoticeDO updateObj = BeanUtils.toBean(updateReqVO, MesWmArrivalNoticeDO.class); | 64 | MesWmArrivalNoticeDO updateObj = BeanUtils.toBean(updateReqVO, MesWmArrivalNoticeDO.class); |
| 63 | arrivalNoticeMapper.updateById(updateObj); | 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 | @Override | 75 | @Override |
| 67 | @Transactional(rollbackFor = Exception.class) | 76 | @Transactional(rollbackFor = Exception.class) |
| 68 | public void deleteArrivalNotice(Long id) { | 77 | public void deleteArrivalNotice(Long id) { |
| @@ -144,6 +153,13 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService | @@ -144,6 +153,13 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService | ||
| 144 | if (ObjUtil.notEqual(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus(), notice.getStatus())) { | 153 | if (ObjUtil.notEqual(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus(), notice.getStatus())) { |
| 145 | throw exception(WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT); | 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 | arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO() | 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,6 +125,11 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta | ||
| 125 | if (ObjUtil.notEqual(line.getIssueId(), reqVO.getIssueId())) { | 125 | if (ObjUtil.notEqual(line.getIssueId(), reqVO.getIssueId())) { |
| 126 | throw exception(WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH); | 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 | // 校验物料匹配(明细 itemId 必须与行 itemId 一致) | 133 | // 校验物料匹配(明细 itemId 必须与行 itemId 一致) |
| 129 | validateDetailItemMatch(reqVO.getItemId(), line.getItemId()); | 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,7 +109,9 @@ public class MesWmProductIssueLineServiceImpl implements MesWmProductIssueLineSe | ||
| 109 | * 校验保存时的关联数据 | 109 | * 校验保存时的关联数据 |
| 110 | */ | 110 | */ |
| 111 | private void validateProductIssueLineSaveData(MesWmProductIssueLineSaveReqVO reqVO) { | 111 | private void validateProductIssueLineSaveData(MesWmProductIssueLineSaveReqVO reqVO) { |
| 112 | - // 校验父数据存在 + 校验物料在工单 BOM 中 | 112 | + // 校验父数据存在 + 草稿状态才允许新增/修改行 |
| 113 | + issueService.validateProductIssueExistsAndPrepare(reqVO.getIssueId()); | ||
| 114 | + // 校验物料在工单 BOM 中 | ||
| 113 | validateItemInWorkOrderBom(reqVO.getIssueId(), reqVO.getItemId()); | 115 | validateItemInWorkOrderBom(reqVO.getIssueId(), reqVO.getItemId()); |
| 114 | // 校验物料存在 | 116 | // 校验物料存在 |
| 115 | itemService.validateItemExistsAndEnable(reqVO.getItemId()); | 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,6 +138,11 @@ public class MesWmProductIssueServiceImpl implements MesWmProductIssueService { | ||
| 138 | if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVING.getStatus(), issue.getStatus())) { | 138 | if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVING.getStatus(), issue.getStatus())) { |
| 139 | throw exception(WM_PRODUCT_ISSUE_STATUS_INVALID); | 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 | issueMapper.updateById(new MesWmProductIssueDO() | 147 | issueMapper.updateById(new MesWmProductIssueDO() |
| 143 | .setId(id).setStatus(MesWmProductIssueStatusEnum.APPROVED.getStatus())); | 148 | .setId(id).setStatus(MesWmProductIssueStatusEnum.APPROVED.getStatus())); |
| @@ -151,6 +156,15 @@ public class MesWmProductIssueServiceImpl implements MesWmProductIssueService { | @@ -151,6 +156,15 @@ public class MesWmProductIssueServiceImpl implements MesWmProductIssueService { | ||
| 151 | if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVED.getStatus(), issue.getStatus())) { | 156 | if (ObjUtil.notEqual(MesWmProductIssueStatusEnum.APPROVED.getStatus(), issue.getStatus())) { |
| 152 | throw exception(WM_PRODUCT_ISSUE_STATUS_INVALID); | 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 | // 2. 遍历所有明细,创建库存事务(扣减库存 + 记录流水) | 169 | // 2. 遍历所有明细,创建库存事务(扣减库存 + 记录流水) |
| 156 | createTransactionList(issue); | 170 | createTransactionList(issue); |