Commit b8dbd7cdce563d95da9e43db9fcd045b1482955d

Authored by YunaiV
1 parent 92ed8c2e

✨ feat(mes): 新增 OQC 来源单据类型错误码及相关校验

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);