Commit a1c14c4977c6fd7320e8932857171924dd6bb764

Authored by YunaiV
1 parent e40bb7e7

♻️ refactor(mes): 重构 approveFeedback 方法,移除用户 ID 参数

简化 approveFeedback 方法,移除不必要的用户 ID 参数,改为使用表单保存的报工人和时间。此变更提高了代码的可读性和维护性,同时确保审批状态更新逻辑的清晰性。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/MesProFeedbackController.java
@@ -49,7 +49,7 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPOR @@ -49,7 +49,7 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPOR
49 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 49 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
50 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 50 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
51 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; 51 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
52 -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 52 +
53 53
54 @Tag(name = "管理后台 - MES 生产报工") 54 @Tag(name = "管理后台 - MES 生产报工")
55 @RestController 55 @RestController
@@ -158,7 +158,7 @@ public class MesProFeedbackController { @@ -158,7 +158,7 @@ public class MesProFeedbackController {
158 @Parameter(name = "id", description = "编号", required = true) 158 @Parameter(name = "id", description = "编号", required = true)
159 @PreAuthorize("@ss.hasPermission('mes:pro-feedback:approve')") 159 @PreAuthorize("@ss.hasPermission('mes:pro-feedback:approve')")
160 public CommonResult<Boolean> approveFeedback(@RequestParam("id") Long id) { 160 public CommonResult<Boolean> approveFeedback(@RequestParam("id") Long id) {
161 - return success(feedbackService.approveFeedback(id, getLoginUserId())); 161 + return success(feedbackService.approveFeedback(id));
162 } 162 }
163 163
164 // ==================== 拼接 VO ==================== 164 // ==================== 拼接 VO ====================
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackService.java
@@ -91,11 +91,10 @@ public interface MesProFeedbackService { @@ -91,11 +91,10 @@ public interface MesProFeedbackService {
91 * </li> 91 * </li>
92 * </ol> 92 * </ol>
93 * 93 *
94 - * @param id 报工单编号  
95 - * @param userId 当前操作用户编号(审核人) 94 + * @param id 报工单编号
96 * @return true=已完成, false=待检验(需等质检回调) 95 * @return true=已完成, false=待检验(需等质检回调)
97 */ 96 */
98 - boolean approveFeedback(Long id, Long userId); 97 + boolean approveFeedback(Long id);
99 98
100 /** 99 /**
101 * IPQC 完成后回调:完成报工单并更新任务/工单进度 100 * IPQC 完成后回调:完成报工单并更新任务/工单进度
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -31,11 +31,9 @@ import org.springframework.transaction.annotation.Transactional; @@ -31,11 +31,9 @@ import org.springframework.transaction.annotation.Transactional;
31 import org.springframework.validation.annotation.Validated; 31 import org.springframework.validation.annotation.Validated;
32 32
33 import java.math.BigDecimal; 33 import java.math.BigDecimal;
34 -import java.time.LocalDateTime;  
35 import java.util.List; 34 import java.util.List;
36 35
37 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 36 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
38 -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;  
39 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 37 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
40 38
41 /** 39 /**
@@ -116,11 +114,9 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { @@ -116,11 +114,9 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
116 // 1. 校验存在 + 草稿状态 114 // 1. 校验存在 + 草稿状态
117 validateFeedbackStatusPrepare(id); 115 validateFeedbackStatusPrepare(id);
118 116
119 - // 2. 更新状态为审批中,记录报工人和报工时间 117 + // 2. 更新状态为审批中(报工人和报工时间由表单保存时确定,提交不覆盖)
120 feedbackMapper.updateById(new MesProFeedbackDO().setId(id) 118 feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
121 - .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus())  
122 - .setFeedbackUserId(getLoginUserId())  
123 - .setFeedbackTime(LocalDateTime.now())); 119 + .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus()));
124 } 120 }
125 121
126 @Override 122 @Override
@@ -135,34 +131,35 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { @@ -135,34 +131,35 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
135 131
136 @Override 132 @Override
137 @Transactional(rollbackFor = Exception.class) 133 @Transactional(rollbackFor = Exception.class)
138 - public boolean approveFeedback(Long id, Long userId) {  
139 - // 1.1.a 校验存在 + 审批中状态 134 + public boolean approveFeedback(Long id) {
  135 + // 1.1 校验存在 + 审批中状态
140 MesProFeedbackDO feedback = validateFeedbackStatusApproving(id); 136 MesProFeedbackDO feedback = validateFeedbackStatusApproving(id);
141 - // 1.1.b 校验报工数量 > 0 137 + // 1.2 校验报工数量 > 0
142 if (feedback.getFeedbackQuantity() == null 138 if (feedback.getFeedbackQuantity() == null
143 || feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) { 139 || feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
144 throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE); 140 throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
145 } 141 }
146 - // 1.2.a 校验任务未完成 142 + // 1.3 校验任务未完成
147 taskService.validateTaskNotFinished(feedback.getTaskId()); 143 taskService.validateTaskNotFinished(feedback.getTaskId());
148 - // 1.2.b 仍有待检数量时不能执行  
149 - if (feedback.getUncheckQuantity() != null 144 +
  145 + // 2.1 查询工序的关键工序标识 + 检验标识(需在 uncheckQuantity 校验之前,因为质检工序允许 uncheckQuantity > 0)
  146 + MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
  147 + feedback.getRouteId(), feedback.getProcessId());
  148 + boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag());
  149 + boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());
  150 + // 2.2 非质检工序:仍有待检数量时不能审批(质检工序的 uncheckQuantity > 0 是正常状态,不做拦截)
  151 + if (!checkFlag
  152 + && feedback.getUncheckQuantity() != null
150 && feedback.getUncheckQuantity().compareTo(BigDecimal.ZERO) > 0) { 153 && feedback.getUncheckQuantity().compareTo(BigDecimal.ZERO) > 0) {
151 throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity()); 154 throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity());
152 } 155 }
153 156
154 - // 2. 物料消耗:根据工序 BOM 生成消耗记录并执行扣减 157 + // 3. 物料消耗:根据工序 BOM 生成消耗记录并执行扣减
155 MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback); 158 MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback);
156 if (itemConsume != null) { 159 if (itemConsume != null) {
157 itemConsumeService.finishItemConsume(itemConsume.getId()); 160 itemConsumeService.finishItemConsume(itemConsume.getId());
158 } 161 }
159 162
160 - // 3. 查询工序的关键工序标识 + 检验标识  
161 - MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(  
162 - feedback.getRouteId(), feedback.getProcessId());  
163 - boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag());  
164 - boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());  
165 -  
166 // 4. 关键工序:生成产出单,并根据是否需要检验决定入库方式 163 // 4. 关键工序:生成产出单,并根据是否需要检验决定入库方式
167 if (keyFlag) { 164 if (keyFlag) {
168 // 4.1 需要检验:生成产出单(质量状态=待检验),更新报工状态为待检验,等质检完成回调后再入库 165 // 4.1 需要检验:生成产出单(质量状态=待检验),更新报工状态为待检验,等质检完成回调后再入库
@@ -179,10 +176,10 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { @@ -179,10 +176,10 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
179 updateTaskAndWorkOrderByFeedback(feedback); 176 updateTaskAndWorkOrderByFeedback(feedback);
180 } 177 }
181 178
182 - // 5. 非关键工序:不生成产出单,不更新任务/工单数量,直接完成 179 + // 5. 非关键工序 / 关键非质检工序:直接完成(清零 uncheckQuantity 防止 !key+check 留脏数据)
183 feedbackMapper.updateById(new MesProFeedbackDO().setId(id) 180 feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
184 .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()) 181 .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())
185 - .setApproveUserId(userId)); 182 + .setUncheckQuantity(BigDecimal.ZERO));
186 return true; // 已完成 183 return true; // 已完成
187 } 184 }
188 185
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImplTest.java
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.mes.service.pro.feedback; @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.mes.service.pro.feedback;
3 import cn.hutool.core.collection.ListUtil; 3 import cn.hutool.core.collection.ListUtil;
4 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 4 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
5 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; 5 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO;
6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; 8 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
7 import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; 9 import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
8 import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum; 10 import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
@@ -23,8 +25,7 @@ import java.math.BigDecimal; @@ -23,8 +25,7 @@ import java.math.BigDecimal;
23 25
24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; 26 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
25 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 27 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
26 -import static org.junit.jupiter.api.Assertions.assertEquals;  
27 -import static org.junit.jupiter.api.Assertions.assertThrows; 28 +import static org.junit.jupiter.api.Assertions.*;
28 import static org.mockito.Mockito.*; 29 import static org.mockito.Mockito.*;
29 30
30 /** 31 /**
@@ -194,4 +195,203 @@ public class MesProFeedbackServiceImplTest extends BaseDbUnitTest { @@ -194,4 +195,203 @@ public class MesProFeedbackServiceImplTest extends BaseDbUnitTest {
194 BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO)); 195 BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO));
195 } 196 }
196 197
  198 + // ==================== approveFeedback 测试 ====================
  199 +
  200 + @Test
  201 + public void testApproveFeedback_keyNonCheck_success() {
  202 + // 准备数据:关键非质检工序,常规审批
  203 + Long routeId = randomLongId();
  204 + Long processId = randomLongId();
  205 + MesProFeedbackDO feedback = randomPojo(MesProFeedbackDO.class, o -> {
  206 + o.setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus());
  207 + o.setFeedbackQuantity(BigDecimal.valueOf(100));
  208 + o.setQualifiedQuantity(BigDecimal.valueOf(80));
  209 + o.setUnqualifiedQuantity(BigDecimal.valueOf(20));
  210 + o.setUncheckQuantity(BigDecimal.ZERO);
  211 + o.setRouteId(routeId);
  212 + o.setProcessId(processId);
  213 + });
  214 + feedbackMapper.insert(feedback);
  215 +
  216 + // mock: 工序配置 key=true, check=false
  217 + MesProRouteProcessDO routeProcess = MesProRouteProcessDO.builder()
  218 + .routeId(routeId).processId(processId)
  219 + .keyFlag(true).checkFlag(false).build();
  220 + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId))
  221 + .thenReturn(routeProcess);
  222 +
  223 + // mock: 产品产出单
  224 + MesWmProductProduceDO produce = randomPojo(MesWmProductProduceDO.class);
  225 + when(productProduceService.generateProductProduce(any(), eq(false))).thenReturn(produce);
  226 +
  227 + // mock: 产出行(用于 updateTaskAndWorkOrderByFeedback)
  228 + MesWmProductProduceLineDO qualifiedLine = MesWmProductProduceLineDO.builder()
  229 + .quantity(BigDecimal.valueOf(80))
  230 + .qualityStatus(MesWmQualityStatusEnum.PASS.getStatus()).build();
  231 + when(produceLineService.getProductProduceLineListByFeedbackId(feedback.getId()))
  232 + .thenReturn(ListUtil.of(qualifiedLine));
  233 +
  234 + // 调用
  235 + boolean result = feedbackService.approveFeedback(feedback.getId());
  236 +
  237 + // 断言 1:返回 true(已完成)
  238 + assertTrue(result);
  239 +
  240 + // 断言 2:状态为已完成,uncheckQuantity 清零
  241 + MesProFeedbackDO updated = feedbackMapper.selectById(feedback.getId());
  242 + assertEquals(MesProFeedbackStatusEnum.FINISHED.getStatus(), updated.getStatus());
  243 + assertEquals(0, BigDecimal.ZERO.compareTo(updated.getUncheckQuantity()));
  244 +
  245 + // 断言 3:调用了产出单生成 + 入库 + 任务/工单更新
  246 + verify(productProduceService).generateProductProduce(any(), eq(false));
  247 + verify(productProduceService).finishProductProduce(produce.getId());
  248 + verify(taskService).updateProducedQuantity(eq(feedback.getTaskId()),
  249 + any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class));
  250 + verify(workOrderService).updateProducedQuantity(eq(feedback.getWorkOrderId()),
  251 + any(BigDecimal.class));
  252 + }
  253 +
  254 + @Test
  255 + public void testApproveFeedback_keyCheck_enterUncheck() {
  256 + // 准备数据:关键质检工序,应进入待检验
  257 + Long routeId = randomLongId();
  258 + Long processId = randomLongId();
  259 + MesProFeedbackDO feedback = randomPojo(MesProFeedbackDO.class, o -> {
  260 + o.setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus());
  261 + o.setFeedbackQuantity(BigDecimal.valueOf(50));
  262 + o.setUncheckQuantity(BigDecimal.valueOf(50)); // 质检工序 uncheckQuantity > 0
  263 + o.setRouteId(routeId);
  264 + o.setProcessId(processId);
  265 + });
  266 + feedbackMapper.insert(feedback);
  267 +
  268 + // mock: key=true, check=true
  269 + MesProRouteProcessDO routeProcess = MesProRouteProcessDO.builder()
  270 + .routeId(routeId).processId(processId)
  271 + .keyFlag(true).checkFlag(true).build();
  272 + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId))
  273 + .thenReturn(routeProcess);
  274 +
  275 + // 调用
  276 + boolean result = feedbackService.approveFeedback(feedback.getId());
  277 +
  278 + // 断言 1:返回 false(待检验)
  279 + assertFalse(result);
  280 +
  281 + // 断言 2:状态为待检验
  282 + MesProFeedbackDO updated = feedbackMapper.selectById(feedback.getId());
  283 + assertEquals(MesProFeedbackStatusEnum.UNCHECK.getStatus(), updated.getStatus());
  284 +
  285 + // 断言 3:生成了待检产出单,但没有 finishProductProduce
  286 + verify(productProduceService).generateProductProduce(any(), eq(true));
  287 + verify(productProduceService, never()).finishProductProduce(anyLong());
  288 +
  289 + // 断言 4:没有更新任务/工单数量(等 IPQC 回调)
  290 + verify(taskService, never()).updateProducedQuantity(anyLong(), any(), any(), any());
  291 + verify(workOrderService, never()).updateProducedQuantity(anyLong(), any());
  292 + }
  293 +
  294 + @Test
  295 + public void testApproveFeedback_nonKey_directFinish() {
  296 + // 准备数据:非关键工序,直接完结
  297 + Long routeId = randomLongId();
  298 + Long processId = randomLongId();
  299 + MesProFeedbackDO feedback = randomPojo(MesProFeedbackDO.class, o -> {
  300 + o.setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus());
  301 + o.setFeedbackQuantity(BigDecimal.valueOf(30));
  302 + o.setUncheckQuantity(BigDecimal.ZERO);
  303 + o.setRouteId(routeId);
  304 + o.setProcessId(processId);
  305 + });
  306 + feedbackMapper.insert(feedback);
  307 +
  308 + // mock: key=false, check=false
  309 + MesProRouteProcessDO routeProcess = MesProRouteProcessDO.builder()
  310 + .routeId(routeId).processId(processId)
  311 + .keyFlag(false).checkFlag(false).build();
  312 + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId))
  313 + .thenReturn(routeProcess);
  314 +
  315 + // 调用
  316 + boolean result = feedbackService.approveFeedback(feedback.getId());
  317 +
  318 + // 断言 1:返回 true(已完成)
  319 + assertTrue(result);
  320 +
  321 + // 断言 2:状态为已完成
  322 + MesProFeedbackDO updated = feedbackMapper.selectById(feedback.getId());
  323 + assertEquals(MesProFeedbackStatusEnum.FINISHED.getStatus(), updated.getStatus());
  324 +
  325 + // 断言 3:不生成产出单,不更新任务/工单
  326 + verify(productProduceService, never()).generateProductProduce(any(), anyBoolean());
  327 + verify(taskService, never()).updateProducedQuantity(anyLong(), any(), any(), any());
  328 + }
  329 +
  330 + @Test
  331 + public void testApproveFeedback_nonCheck_uncheckQuantityReject() {
  332 + // 准备数据:非质检工序,但 uncheckQuantity > 0(异常数据),应被拦截
  333 + Long routeId = randomLongId();
  334 + Long processId = randomLongId();
  335 + MesProFeedbackDO feedback = randomPojo(MesProFeedbackDO.class, o -> {
  336 + o.setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus());
  337 + o.setFeedbackQuantity(BigDecimal.valueOf(10));
  338 + o.setUncheckQuantity(BigDecimal.valueOf(10)); // 非质检工序不应有待检数量
  339 + o.setRouteId(routeId);
  340 + o.setProcessId(processId);
  341 + });
  342 + feedbackMapper.insert(feedback);
  343 +
  344 + // mock: key=true, check=false(非质检)
  345 + MesProRouteProcessDO routeProcess = MesProRouteProcessDO.builder()
  346 + .routeId(routeId).processId(processId)
  347 + .keyFlag(true).checkFlag(false).build();
  348 + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId))
  349 + .thenReturn(routeProcess);
  350 +
  351 + // 调用,应该抛异常
  352 + assertThrows(Exception.class, () ->
  353 + feedbackService.approveFeedback(feedback.getId()));
  354 +
  355 + // 断言:不应该执行任何后续操作
  356 + verify(productProduceService, never()).generateProductProduce(any(), anyBoolean());
  357 + verify(itemConsumeService, never()).generateItemConsume(any());
  358 + }
  359 +
  360 + @Test
  361 + public void testApproveFeedback_nonKeyCheck_directFinishAndCleanUncheck() {
  362 + // 准备数据:非关键 + 质检工序(!key+check),uncheckQuantity > 0
  363 + // 应放行(checkFlag=true 不拦截 uncheckQuantity),直接完结并清零 uncheckQuantity
  364 + Long routeId = randomLongId();
  365 + Long processId = randomLongId();
  366 + MesProFeedbackDO feedback = randomPojo(MesProFeedbackDO.class, o -> {
  367 + o.setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus());
  368 + o.setFeedbackQuantity(BigDecimal.valueOf(20));
  369 + o.setUncheckQuantity(BigDecimal.valueOf(20));
  370 + o.setRouteId(routeId);
  371 + o.setProcessId(processId);
  372 + });
  373 + feedbackMapper.insert(feedback);
  374 +
  375 + // mock: key=false, check=true
  376 + MesProRouteProcessDO routeProcess = MesProRouteProcessDO.builder()
  377 + .routeId(routeId).processId(processId)
  378 + .keyFlag(false).checkFlag(true).build();
  379 + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId))
  380 + .thenReturn(routeProcess);
  381 +
  382 + // 调用
  383 + boolean result = feedbackService.approveFeedback(feedback.getId());
  384 +
  385 + // 断言 1:返回 true(直接完成,不走 UNCHECK)
  386 + assertTrue(result);
  387 +
  388 + // 断言 2:状态为已完成,uncheckQuantity 被清零
  389 + MesProFeedbackDO updated = feedbackMapper.selectById(feedback.getId());
  390 + assertEquals(MesProFeedbackStatusEnum.FINISHED.getStatus(), updated.getStatus());
  391 + assertEquals(0, BigDecimal.ZERO.compareTo(updated.getUncheckQuantity()));
  392 +
  393 + // 断言 3:非关键工序不生成产出单
  394 + verify(productProduceService, never()).generateProductProduce(any(), anyBoolean());
  395 + }
  396 +
197 } 397 }
yudao-module-mes/src/test/resources/sql/create_tables.sql
@@ -705,17 +705,17 @@ CREATE TABLE IF NOT EXISTS &quot;mes_wm_batch&quot; ( @@ -705,17 +705,17 @@ CREATE TABLE IF NOT EXISTS &quot;mes_wm_batch&quot; (
705 -- ---------------------------- 705 -- ----------------------------
706 CREATE TABLE IF NOT EXISTS "mes_wm_material_stock" ( 706 CREATE TABLE IF NOT EXISTS "mes_wm_material_stock" (
707 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 707 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
708 - \"item_type_id\" bigint DEFAULT NULL,  
709 - \"item_id\" bigint DEFAULT NULL,  
710 - \"batch_id\" bigint DEFAULT NULL,  
711 - \"batch_code\" varchar(255) DEFAULT NULL,  
712 - \"warehouse_id\" bigint DEFAULT NULL,  
713 - \"location_id\" bigint DEFAULT NULL,  
714 - \"area_id\" bigint DEFAULT NULL,  
715 - \"vendor_id\" bigint DEFAULT NULL,  
716 - \"quantity\" decimal(14,2) DEFAULT NULL,  
717 - \"receipt_time\" timestamp DEFAULT NULL,  
718 - \"frozen\" bit DEFAULT FALSE, 708 + "item_type_id" bigint DEFAULT NULL,
  709 + "item_id" bigint DEFAULT NULL,
  710 + "batch_id" bigint DEFAULT NULL,
  711 + "batch_code" varchar(255) DEFAULT NULL,
  712 + "warehouse_id" bigint DEFAULT NULL,
  713 + "location_id" bigint DEFAULT NULL,
  714 + "area_id" bigint DEFAULT NULL,
  715 + "vendor_id" bigint DEFAULT NULL,
  716 + "quantity" decimal(14,2) DEFAULT NULL,
  717 + "receipt_time" timestamp DEFAULT NULL,
  718 + "frozen" bit DEFAULT FALSE,
719 "creator" varchar(64) DEFAULT '', 719 "creator" varchar(64) DEFAULT '',
720 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 720 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
721 "updater" varchar(64) DEFAULT '', 721 "updater" varchar(64) DEFAULT '',