Commit dfeb3c104d48d1b8844c9850bf55c924f51e37ef

Authored by YunaiV
1 parent b31f78c8

✨ feat(mes): 更新销售出库单状态流转逻辑,添加 OQC 检验支持

优化销售出库单的状态流转逻辑,新增待检测状态以支持 OQC 检验。根据检验结果自动流转状态,提升出库流程的灵活性和准确性。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -533,7 +533,8 @@ public interface ErrorCodeConstants { @@ -533,7 +533,8 @@ public interface ErrorCodeConstants {
533 ErrorCode WM_PRODUCT_SALES_CANNOT_SHIPPING = new ErrorCode(1_040_714_006, "当前状态不允许填写运单"); 533 ErrorCode WM_PRODUCT_SALES_CANNOT_SHIPPING = new ErrorCode(1_040_714_006, "当前状态不允许填写运单");
534 ErrorCode WM_PRODUCT_SALES_CANNOT_FINISH = new ErrorCode(1_040_714_007, "当前状态不允许执行出库"); 534 ErrorCode WM_PRODUCT_SALES_CANNOT_FINISH = new ErrorCode(1_040_714_007, "当前状态不允许执行出库");
535 ErrorCode WM_PRODUCT_SALES_CANNOT_CANCEL = new ErrorCode(1_040_714_008, "当前状态不允许取消"); 535 ErrorCode WM_PRODUCT_SALES_CANNOT_CANCEL = new ErrorCode(1_040_714_008, "当前状态不允许取消");
536 - ErrorCode WM_PRODUCT_SALES_DETAILS_EMPTY = new ErrorCode(1_040_714_009, "拣货明细不能为空"); 536 + ErrorCode WM_PRODUCT_SALES_CANNOT_CONFIRM = new ErrorCode(1_040_714_009, "当前状态不允许确认检验通过");
  537 + ErrorCode WM_PRODUCT_SALES_DETAILS_EMPTY = new ErrorCode(1_040_714_020, "拣货明细不能为空");
537 ErrorCode WM_PRODUCT_SALES_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_714_010, "拣货数量与出库数量不匹配"); 538 ErrorCode WM_PRODUCT_SALES_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_714_010, "拣货数量与出库数量不匹配");
538 ErrorCode WM_PRODUCT_SALES_LINE_NOT_EXISTS = new ErrorCode(1_040_714_011, "销售出库单行不存在"); 539 ErrorCode WM_PRODUCT_SALES_LINE_NOT_EXISTS = new ErrorCode(1_040_714_011, "销售出库单行不存在");
539 ErrorCode WM_PRODUCT_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_714_012, "销售出库明细不存在"); 540 ErrorCode WM_PRODUCT_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_714_012, "销售出库明细不存在");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductSalesStatusEnum.java
@@ -28,10 +28,18 @@ public enum MesWmProductSalesStatusEnum implements ArrayValuable<Integer> { @@ -28,10 +28,18 @@ public enum MesWmProductSalesStatusEnum implements ArrayValuable<Integer> {
28 */ 28 */
29 PREPARE(MesOrderStatusConstants.PREPARE, "草稿"), 29 PREPARE(MesOrderStatusConstants.PREPARE, "草稿"),
30 /** 30 /**
31 - * 待拣货 31 + * 待检测(需要 OQC 检验时),对接 qc 模块的 MesQcOqcDO 后续流程
32 * 32 *
33 * 对应方法:{@link MesWmProductSalesService#submitProductSales(Long)} 33 * 对应方法:{@link MesWmProductSalesService#submitProductSales(Long)}
34 */ 34 */
  35 + CONFIRMED(MesOrderStatusConstants.CONFIRMED, "待检测"),
  36 + /**
  37 + * 待拣货
  38 + *
  39 + * 对应方法:
  40 + * 1. 不需要 OQC 检验时:{@link MesWmProductSalesService#submitProductSales(Long)}
  41 + * 2. 或 OQC 检验完成时:{@link MesWmProductSalesService#confirmProductSales(Long)}
  42 + */
35 APPROVING(MesOrderStatusConstants.APPROVING, "待拣货"), 43 APPROVING(MesOrderStatusConstants.APPROVING, "待拣货"),
36 /** 44 /**
37 * 待填写运单 45 * 待填写运单
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java
@@ -115,19 +115,30 @@ public class MesWmProductSalesLineServiceImpl implements MesWmProductSalesLineSe @@ -115,19 +115,30 @@ public class MesWmProductSalesLineServiceImpl implements MesWmProductSalesLineSe
115 productSalesLineMapper.updateById(new MesWmProductSalesLineDO() 115 productSalesLineMapper.updateById(new MesWmProductSalesLineDO()
116 .setId(id).setOqcId(oqcId).setQualityStatus(qualityStatus)); 116 .setId(id).setOqcId(oqcId).setQualityStatus(qualityStatus));
117 117
118 - // 2. 检查同一出库单下所有需要 OQC 检验的行的质检状态,联动更新出库单整体状态 118 + // 2. 检查同一出库单下所有需要 OQC 检验的行
119 MesWmProductSalesLineDO currentLine = productSalesLineMapper.selectById(id); 119 MesWmProductSalesLineDO currentLine = productSalesLineMapper.selectById(id);
120 if (currentLine == null || currentLine.getSalesId() == null) { 120 if (currentLine == null || currentLine.getSalesId() == null) {
121 return; 121 return;
122 } 122 }
123 Long salesId = currentLine.getSalesId(); 123 Long salesId = currentLine.getSalesId();
124 List<MesWmProductSalesLineDO> allLines = productSalesLineMapper.selectListBySalesId(salesId); 124 List<MesWmProductSalesLineDO> allLines = productSalesLineMapper.selectListBySalesId(salesId);
  125 +
  126 + // 2.1 检查是否有不合格行 → 取消出库单
125 boolean hasNg = CollectionUtils.anyMatch(allLines, 127 boolean hasNg = CollectionUtils.anyMatch(allLines,
126 line -> Boolean.TRUE.equals(line.getOqcCheckFlag()) 128 line -> Boolean.TRUE.equals(line.getOqcCheckFlag())
127 && Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())); 129 && Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus()));
128 if (hasNg) { 130 if (hasNg) {
129 log.info("[updateProductSalesLineWhenOqcFinish][销售出库单({}) 存在质检不合格行,取消出库单]", salesId); 131 log.info("[updateProductSalesLineWhenOqcFinish][销售出库单({}) 存在质检不合格行,取消出库单]", salesId);
130 productSalesService.cancelProductSales(salesId); 132 productSalesService.cancelProductSales(salesId);
  133 + return;
  134 + }
  135 +
  136 + // 2.2 检查是否所有 OQC 行都已完成,自动流转 CONFIRMED → APPROVING
  137 + boolean hasUnchecked = CollectionUtils.anyMatch(allLines,
  138 + line -> Boolean.TRUE.equals(line.getOqcCheckFlag()) && line.getOqcId() == null);
  139 + if (!hasUnchecked) {
  140 + log.info("[updateProductSalesLineWhenOqcFinish][销售出库单({}) 所有 OQC 行检验完成,流转到待拣货]", salesId);
  141 + productSalesService.confirmProductSales(salesId);
131 } 142 }
132 } 143 }
133 144
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesService.java
@@ -53,7 +53,11 @@ public interface MesWmProductSalesService { @@ -53,7 +53,11 @@ public interface MesWmProductSalesService {
53 PageResult<MesWmProductSalesDO> getProductSalesPage(MesWmProductSalesPageReqVO pageReqVO); 53 PageResult<MesWmProductSalesDO> getProductSalesPage(MesWmProductSalesPageReqVO pageReqVO);
54 54
55 /** 55 /**
56 - * 提交销售出库单(草稿 → 待拣货) 56 + * 提交销售出库单
  57 + * <ul>
  58 + * <li>有 OQC 检验行:草稿 → 待检测</li>
  59 + * <li>无 OQC 检验行:草稿 → 待拣货</li>
  60 + * </ul>
57 * 61 *
58 * @param id 编号 62 * @param id 编号
59 */ 63 */
@@ -95,4 +99,11 @@ public interface MesWmProductSalesService { @@ -95,4 +99,11 @@ public interface MesWmProductSalesService {
95 */ 99 */
96 void cancelProductSales(Long id); 100 void cancelProductSales(Long id);
97 101
  102 + /**
  103 + * OQC 检验完成后,自动流转(待检测 → 待拣货)
  104 + *
  105 + * @param id 编号
  106 + */
  107 + void confirmProductSales(Long id);
  108 +
98 } 109 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
@@ -117,7 +117,16 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService { @@ -117,7 +117,16 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
117 throw exception(WM_PRODUCT_SALES_LINES_EMPTY); 117 throw exception(WM_PRODUCT_SALES_LINES_EMPTY);
118 } 118 }
119 119
120 - // 提交(草稿 → 待拣货) 120 + // 检查所有行的 oqcCheckFlag:如果有需要 OQC 检验的行,进入待检测状态
  121 + boolean needOqc = CollectionUtils.anyMatch(lines,
  122 + line -> Boolean.TRUE.equals(line.getOqcCheckFlag()));
  123 + if (needOqc) {
  124 + // 需要检验,进入待检测
  125 + productSalesMapper.updateById(new MesWmProductSalesDO()
  126 + .setId(id).setStatus(MesWmProductSalesStatusEnum.CONFIRMED.getStatus()));
  127 + return;
  128 + }
  129 + // 不需要检验,直接进入待拣货
121 productSalesMapper.updateById(new MesWmProductSalesDO() 130 productSalesMapper.updateById(new MesWmProductSalesDO()
122 .setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus())); 131 .setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus()));
123 } 132 }
@@ -215,6 +224,18 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService { @@ -215,6 +224,18 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
215 .setId(id).setStatus(MesWmProductSalesStatusEnum.CANCELED.getStatus())); 224 .setId(id).setStatus(MesWmProductSalesStatusEnum.CANCELED.getStatus()));
216 } 225 }
217 226
  227 + @Override
  228 + public void confirmProductSales(Long id) {
  229 + // 校验存在 + 待检测状态
  230 + MesWmProductSalesDO sales = validateProductSalesExists(id);
  231 + if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.CONFIRMED.getStatus(), sales.getStatus())) {
  232 + throw exception(WM_PRODUCT_SALES_CANNOT_CONFIRM);
  233 + }
  234 + // OQC 检验完成,流转到待拣货
  235 + productSalesMapper.updateById(new MesWmProductSalesDO()
  236 + .setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus()));
  237 + }
  238 +
218 private MesWmProductSalesDO validateProductSalesExists(Long id) { 239 private MesWmProductSalesDO validateProductSalesExists(Long id) {
219 MesWmProductSalesDO sales = productSalesMapper.selectById(id); 240 MesWmProductSalesDO sales = productSalesMapper.selectById(id);
220 if (sales == null) { 241 if (sales == null) {
yudao-module-mes/src/main/resources/mapper/qc/pendinginspect/MesQcPendingInspectMapper.xml
@@ -4,12 +4,12 @@ @@ -4,12 +4,12 @@
4 4
5 <!-- 5 <!--
6 待检任务查询(UNION ALL 多来源),已实现的来源: 6 待检任务查询(UNION ALL 多来源),已实现的来源:
7 - 1. 到货通知单(IQC):单据状态=待质检(2),行的 iqc_check_flag=1 且 iqc_id 为空  
8 - 2. 外协入库单(IQC):单据状态=待检验(1),行的 iqc_check_flag=1 且 iqc_id 为空  
9 - 3. 生产报工(IPQC):单据状态=待检验(3)  
10 - 4. 销售出库单(OQC):无单据状态要求,行的 oqc_check_flag=1 且 oqc_id 为空  
11 - 5. 生产退料单(RQC):单据状态=待检验(1),行的 rqc_check_flag=1 且 rqc_id 为空 且 quality_status=0(待检)  
12 - 6. 销售退货单(RQC):单据状态=待检验(1),行的 rqc_check_flag=1 且 rqc_id 为空 且 quality_status=0(待检) 7 + 1. 到货通知单(IQC):单据状态=PENDING_QC(2),行的 iqc_check_flag=1 且 iqc_id 为空
  8 + 2. 外协入库单(IQC):单据状态=CONFIRMED(1),行的 iqc_check_flag=1 且 iqc_id 为空
  9 + 3. 生产报工(IPQC):单据状态=UNCHECK(3)
  10 + 4. 销售出库单(OQC):单据状态=CONFIRMED(1),行的 oqc_check_flag=1 且 oqc_id 为空
  11 + 5. 生产退料单(RQC):单据状态=CONFIRMED(1),行的 rqc_check_flag=1 且 rqc_id 为空 且 quality_status=0(待检)
  12 + 6. 销售退货单(RQC):单据状态=CONFIRMED(1),行的 rqc_check_flag=1 且 rqc_id 为空 且 quality_status=0(待检)
13 --> 13 -->
14 <select id="selectQcPendingPage" resultType="cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectRespVO"> 14 <select id="selectQcPendingPage" resultType="cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectRespVO">
15 SELECT * FROM ( 15 SELECT * FROM (
@@ -103,6 +103,7 @@ @@ -103,6 +103,7 @@
103 JOIN mes_wm_product_sales_line sl ON s.id = sl.sales_id 103 JOIN mes_wm_product_sales_line sl ON s.id = sl.sales_id
104 WHERE sl.oqc_check_flag = 1 104 WHERE sl.oqc_check_flag = 1
105 AND sl.oqc_id IS NULL 105 AND sl.oqc_id IS NULL
  106 + AND s.status = ${@cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum@CONFIRMED.status}
106 AND s.deleted = 0 107 AND s.deleted = 0
107 AND sl.deleted = 0 108 AND sl.deleted = 0
108 109