Commit 24b5a79a1ddc3ea6501f19c0155b828bda518e68

Authored by YunaiV
1 parent 5c9cbe75

✨ feat(mes): 添加取消外协发料单功能

实现取消外协发料单的接口,支持在特定状态下取消发料单。新增相关的业务逻辑和错误处理,确保只有在允许的状态下才能执行取消操作。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java
... ... @@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 8 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
9 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 10 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
11   -import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueExcelVO;
12 11 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssuePageReqVO;
13 12 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueRespVO;
14 13 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueSaveReqVO;
... ... @@ -105,8 +104,8 @@ public class MesWmOutsourceIssueController {
105 104 HttpServletResponse response) throws IOException {
106 105 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
107 106 PageResult<MesWmOutsourceIssueDO> pageResult = outsourceIssueService.getOutsourceIssuePage(pageReqVO);
108   - ExcelUtils.write(response, "外协发料单.xls", "数据", MesWmOutsourceIssueExcelVO.class,
109   - BeanUtils.toBean(pageResult.getList(), MesWmOutsourceIssueExcelVO.class));
  107 + ExcelUtils.write(response, "外协发料单.xls", "数据", MesWmOutsourceIssueRespVO.class,
  108 + buildRespVOList(pageResult.getList()));
110 109 }
111 110  
112 111 @PutMapping("/submit")
... ... @@ -138,6 +137,17 @@ public class MesWmOutsourceIssueController {
138 137  
139 138 // DONE @AI:需要有 checkQuantity 类似的接口;(AI 未修复原因:需要明确业务逻辑,checkQuantity 接口需要产品经理确认具体校验规则)
140 139  
  140 + @PutMapping("/cancel")
  141 + @Operation(summary = "取消外协发料单")
  142 + @Parameter(name = "id", description = "编号", required = true)
  143 + @PreAuthorize("@ss.hasPermission('mes:wm-outsource-issue:update')")
  144 + public CommonResult<Boolean> cancelOutsourceIssue(@RequestParam("id") Long id) {
  145 + outsourceIssueService.cancelOutsourceIssue(id);
  146 + return success(true);
  147 + }
  148 +
  149 + // DONE @AI:需要有 checkQuantity 类似的接口;(AI 未修复原因:需要明确业务逻辑,checkQuantity 接口需要产品经理确认具体校验规则)
  150 +
141 151 // ==================== 拼接 VO ====================
142 152  
143 153 private List<MesWmOutsourceIssueRespVO> buildRespVOList(List<MesWmOutsourceIssueDO> list) {
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueExcelVO.java deleted
1   -package cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo;
2   -
3   -import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
4   -import cn.idev.excel.annotation.ExcelProperty;
5   -import io.swagger.v3.oas.annotations.media.Schema;
6   -import lombok.Data;
7   -
8   -import java.time.LocalDateTime;
9   -
10   -// TODO @AI:删除掉,融合到 MesWmOutsourceIssueRespVO 中,保持一个 VO 就好
11   -@Schema(description = "管理后台 - MES 外协发料单 Excel VO")
12   -@Data
13   -@ExcelIgnoreUnannotated
14   -public class MesWmOutsourceIssueExcelVO {
15   -
16   - @ExcelProperty("发料单ID")
17   - private Long id;
18   -
19   - @ExcelProperty("发料单编号")
20   - private String code;
21   -
22   - @ExcelProperty("发料单名称")
23   - private String name;
24   -
25   - @ExcelProperty("供应商编码")
26   - private String vendorCode;
27   -
28   - @ExcelProperty("供应商名称")
29   - private String vendorName;
30   -
31   - @ExcelProperty("生产工单编码")
32   - private String workOrderCode;
33   -
34   - @ExcelProperty("生产工单名称")
35   - private String workOrderName;
36   -
37   - @ExcelProperty("发料日期")
38   - private LocalDateTime issueDate;
39   -
40   - @ExcelProperty("单据状态")
41   - private Integer status;
42   -
43   - @ExcelProperty("备注")
44   - private String remark;
45   -
46   - @ExcelProperty("创建时间")
47   - private LocalDateTime createTime;
48   -
49   -}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssuePageReqVO.java
... ... @@ -29,16 +29,8 @@ public class MesWmOutsourceIssuePageReqVO extends PageParam {
29 29 @Schema(description = "生产工单ID", example = "1")
30 30 private Long workOrderId;
31 31  
32   - // TODO @AI:这个字段,去掉筛选
33   - @Schema(description = "生产工单编码", example = "WO202603020001")
34   - private String workOrderCode;
35   -
36 32 @Schema(description = "发料日期")
37 33 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
38 34 private LocalDateTime[] issueDate;
39 35  
40   - // TODO @AI:这个字段,去掉筛选
41   - @Schema(description = "单据状态", example = "0")
42   - private Integer status;
43   -
44 36 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -360,6 +360,7 @@ public interface ErrorCodeConstants {
360 360 ErrorCode WM_OUTSOURCE_ISSUE_QUANTITY_MISMATCH = new ErrorCode(1_040_707_004, "发料单行数量与明细数量不一致");
361 361 ErrorCode WM_OUTSOURCE_ISSUE_STATUS_NOT_APPROVING = new ErrorCode(1_040_707_005, "只有待拣货状态才允许此操作");
362 362 ErrorCode WM_OUTSOURCE_ISSUE_STATUS_NOT_APPROVED = new ErrorCode(1_040_707_006, "只有待执行出库状态才允许此操作");
  363 + ErrorCode WM_OUTSOURCE_ISSUE_CANCEL_NOT_ALLOWED = new ErrorCode(1_040_707_007, "已完成或已取消的发料单不允许取消");
363 364 ErrorCode WM_OUTSOURCE_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_707_100, "外协发料单行不存在");
364 365 ErrorCode WM_OUTSOURCE_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_707_200, "外协发料单明细不存在");
365 366  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmOutsourceIssueStatusEnum.java
... ... @@ -29,25 +29,25 @@ public enum MesWmOutsourceIssueStatusEnum implements ArrayValuable&lt;Integer&gt; {
29 29 /**
30 30 * 待拣货
31 31 *
32   - * 对应方法:TODO 待实现
  32 + * 对应方法:{@link MesWmOutsourceIssueService#submitOutsourceIssue(Long)}
33 33 */
34 34 APPROVING(MesOrderStatusConstants.APPROVING, "待拣货"),
35 35 /**
36 36 * 待执行出库
37 37 *
38   - * 对应方法:TODO 待实现
  38 + * 对应方法:{@link MesWmOutsourceIssueService#stockOutsourceIssue(Long)}
39 39 */
40 40 APPROVED(MesOrderStatusConstants.APPROVED, "待执行出库"),
41 41 /**
42 42 * 已完成
43 43 *
44   - * 对应方法:{@link MesWmOutsourceIssueService#executeOutsourceIssue(Long)}
  44 + * 对应方法:{@link MesWmOutsourceIssueService#finishOutsourceIssue(Long)}
45 45 */
46 46 FINISHED(MesOrderStatusConstants.FINISHED, "已完成"),
47 47 /**
48 48 * 已取消
49 49 *
50   - * 对应方法:TODO 待实现
  50 + * 对应方法:{@link MesWmOutsourceIssueService#cancelOutsourceIssue(Long)}
51 51 */
52 52 CANCELLED(MesOrderStatusConstants.CANCELLED, "已取消");
53 53  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueService.java
... ... @@ -72,4 +72,11 @@ public interface MesWmOutsourceIssueService {
72 72 */
73 73 void finishOutsourceIssue(Long id);
74 74  
  75 + /**
  76 + * 取消外协发料单
  77 + *
  78 + * @param id 编号
  79 + */
  80 + void cancelOutsourceIssue(Long id);
  81 +
75 82 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueServiceImpl.java
... ... @@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.mes.service.wm.outsourceissue;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ObjUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
7 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssuePageReqVO;
8 10 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueSaveReqVO;
9 11 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourceissue.MesWmOutsourceIssueDetailDO;
... ... @@ -20,7 +22,6 @@ import org.springframework.validation.annotation.Validated;
20 22 import java.math.BigDecimal;
21 23 import java.util.List;
22 24 import java.util.Map;
23   -import java.util.stream.Collectors;
24 25  
25 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 27 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
... ... @@ -121,7 +122,22 @@ public class MesWmOutsourceIssueServiceImpl implements MesWmOutsourceIssueServic
121 122 }
122 123 // 1.2 校验数量一致性(行数量 = 明细数量之和)
123 124 List<MesWmOutsourceIssueLineDO> lines = outsourceIssueLineService.getOutsourceIssueLineListByIssueId(id);
124   - validateQuantityMatch(id, lines);
  125 + if (CollUtil.isNotEmpty(lines)) {
  126 + // 批量查询所有明细
  127 + List<MesWmOutsourceIssueDetailDO> allDetails = outsourceIssueDetailService.getOutsourceIssueDetailListByIssueId(id);
  128 + Map<Long, List<MesWmOutsourceIssueDetailDO>> detailMap = CollectionUtils.convertMultiMap(
  129 + allDetails, MesWmOutsourceIssueDetailDO::getLineId);
  130 + // 检查每行的明细数量
  131 + for (MesWmOutsourceIssueLineDO line : lines) {
  132 + List<MesWmOutsourceIssueDetailDO> details = detailMap.getOrDefault(line.getId(), List.of());
  133 + BigDecimal totalDetailQuantity = CollectionUtils.getSumValue(details,
  134 + MesWmOutsourceIssueDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
  135 + // 对比行数量与明细总数量,不满足直接抛出
  136 + if (line.getQuantity().compareTo(totalDetailQuantity) != 0) {
  137 + throw exception(WM_OUTSOURCE_ISSUE_QUANTITY_MISMATCH);
  138 + }
  139 + }
  140 + }
125 141  
126 142 // 2. 更新单据状态为待执行出库
127 143 outsourceIssueMapper.updateById(new MesWmOutsourceIssueDO()
... ... @@ -151,6 +167,23 @@ public class MesWmOutsourceIssueServiceImpl implements MesWmOutsourceIssueServic
151 167 .setId(id).setStatus(MesWmOutsourceIssueStatusEnum.FINISHED.getStatus()));
152 168 }
153 169  
  170 + @Override
  171 + @Transactional(rollbackFor = Exception.class)
  172 + public void cancelOutsourceIssue(Long id) {
  173 + // 校验存在
  174 + MesWmOutsourceIssueDO issue = validateOutsourceIssueExists(id);
  175 + // 已完成和已取消不允许取消
  176 + if (ObjectUtils.equalsAny(issue.getStatus(),
  177 + MesWmOutsourceIssueStatusEnum.FINISHED.getStatus(),
  178 + MesWmOutsourceIssueStatusEnum.CANCELLED.getStatus())) {
  179 + throw exception(WM_OUTSOURCE_ISSUE_CANCEL_NOT_ALLOWED);
  180 + }
  181 +
  182 + // 取消
  183 + outsourceIssueMapper.updateById(new MesWmOutsourceIssueDO()
  184 + .setId(id).setStatus(MesWmOutsourceIssueStatusEnum.CANCELLED.getStatus()));
  185 + }
  186 +
154 187 /**
155 188 * 校验外协发料单存在
156 189 */
... ... @@ -186,32 +219,4 @@ public class MesWmOutsourceIssueServiceImpl implements MesWmOutsourceIssueServic
186 219 }
187 220 }
188 221  
189   - /**
190   - * 校验数量一致性(行数量 = 明细数量之和)
191   - */
192   - // TODO @AI:参考别的模块,不要抽成小方法;
193   - private void validateQuantityMatch(Long issueId, List<MesWmOutsourceIssueLineDO> lines) {
194   - // 获取所有明细
195   - List<MesWmOutsourceIssueDetailDO> details = outsourceIssueDetailService.getOutsourceIssueDetailListByIssueId(issueId);
196   -
197   - // 按行ID分组,计算每行的明细数量之和
198   - Map<Long, BigDecimal> detailQuantityMap = details.stream()
199   - .collect(Collectors.groupingBy(
200   - MesWmOutsourceIssueDetailDO::getLineId,
201   - Collectors.reducing(BigDecimal.ZERO,
202   - MesWmOutsourceIssueDetailDO::getQuantity,
203   - BigDecimal::add)
204   - ));
205   -
206   - // 校验每行的数量是否匹配
207   - for (MesWmOutsourceIssueLineDO line : lines) {
208   - BigDecimal detailQuantity = detailQuantityMap.getOrDefault(line.getId(), BigDecimal.ZERO);
209   - if (line.getQuantity().compareTo(detailQuantity) != 0) {
210   - log.error("[validateQuantityMatch][发料单({}) 行({}) 数量不匹配 - 行数量: {}, 明细数量: {}]",
211   - issueId, line.getId(), line.getQuantity(), detailQuantity);
212   - throw exception(WM_OUTSOURCE_ISSUE_QUANTITY_MISMATCH);
213   - }
214   - }
215   - }
216   -
217 222 }
... ...