From 24b5a79a1ddc3ea6501f19c0155b828bda518e68 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 4 Mar 2026 08:49:00 +0800 Subject: [PATCH] ✨ feat(mes): 添加取消外协发料单功能 --- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java | 16 +++++++++++++--- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueExcelVO.java | 49 ------------------------------------------------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssuePageReqVO.java | 8 -------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java | 1 + yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmOutsourceIssueStatusEnum.java | 8 ++++---- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueService.java | 7 +++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueServiceImpl.java | 65 +++++++++++++++++++++++++++++++++++------------------------------ 7 files changed, 60 insertions(+), 94 deletions(-) delete mode 100644 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueExcelVO.java diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java index 7ac4581..96858ba 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java +++ b/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; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueExcelVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssuePageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueRespVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueSaveReqVO; @@ -105,8 +104,8 @@ public class MesWmOutsourceIssueController { HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); PageResult pageResult = outsourceIssueService.getOutsourceIssuePage(pageReqVO); - ExcelUtils.write(response, "外协发料单.xls", "数据", MesWmOutsourceIssueExcelVO.class, - BeanUtils.toBean(pageResult.getList(), MesWmOutsourceIssueExcelVO.class)); + ExcelUtils.write(response, "外协发料单.xls", "数据", MesWmOutsourceIssueRespVO.class, + buildRespVOList(pageResult.getList())); } @PutMapping("/submit") @@ -138,6 +137,17 @@ public class MesWmOutsourceIssueController { // DONE @AI:需要有 checkQuantity 类似的接口;(AI 未修复原因:需要明确业务逻辑,checkQuantity 接口需要产品经理确认具体校验规则) + @PutMapping("/cancel") + @Operation(summary = "取消外协发料单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('mes:wm-outsource-issue:update')") + public CommonResult cancelOutsourceIssue(@RequestParam("id") Long id) { + outsourceIssueService.cancelOutsourceIssue(id); + return success(true); + } + + // DONE @AI:需要有 checkQuantity 类似的接口;(AI 未修复原因:需要明确业务逻辑,checkQuantity 接口需要产品经理确认具体校验规则) + // ==================== 拼接 VO ==================== private List buildRespVOList(List list) { diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueExcelVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueExcelVO.java deleted file mode 100644 index ff130a3..0000000 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueExcelVO.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo; - -import cn.idev.excel.annotation.ExcelIgnoreUnannotated; -import cn.idev.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -// TODO @AI:删除掉,融合到 MesWmOutsourceIssueRespVO 中,保持一个 VO 就好 -@Schema(description = "管理后台 - MES 外协发料单 Excel VO") -@Data -@ExcelIgnoreUnannotated -public class MesWmOutsourceIssueExcelVO { - - @ExcelProperty("发料单ID") - private Long id; - - @ExcelProperty("发料单编号") - private String code; - - @ExcelProperty("发料单名称") - private String name; - - @ExcelProperty("供应商编码") - private String vendorCode; - - @ExcelProperty("供应商名称") - private String vendorName; - - @ExcelProperty("生产工单编码") - private String workOrderCode; - - @ExcelProperty("生产工单名称") - private String workOrderName; - - @ExcelProperty("发料日期") - private LocalDateTime issueDate; - - @ExcelProperty("单据状态") - private Integer status; - - @ExcelProperty("备注") - private String remark; - - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssuePageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssuePageReqVO.java index 8fdddf0..5419da3 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssuePageReqVO.java +++ b/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 { @Schema(description = "生产工单ID", example = "1") private Long workOrderId; - // TODO @AI:这个字段,去掉筛选 - @Schema(description = "生产工单编码", example = "WO202603020001") - private String workOrderCode; - @Schema(description = "发料日期") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] issueDate; - // TODO @AI:这个字段,去掉筛选 - @Schema(description = "单据状态", example = "0") - private Integer status; - } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java index caf42b6..b3bce83 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java @@ -360,6 +360,7 @@ public interface ErrorCodeConstants { ErrorCode WM_OUTSOURCE_ISSUE_QUANTITY_MISMATCH = new ErrorCode(1_040_707_004, "发料单行数量与明细数量不一致"); ErrorCode WM_OUTSOURCE_ISSUE_STATUS_NOT_APPROVING = new ErrorCode(1_040_707_005, "只有待拣货状态才允许此操作"); ErrorCode WM_OUTSOURCE_ISSUE_STATUS_NOT_APPROVED = new ErrorCode(1_040_707_006, "只有待执行出库状态才允许此操作"); + ErrorCode WM_OUTSOURCE_ISSUE_CANCEL_NOT_ALLOWED = new ErrorCode(1_040_707_007, "已完成或已取消的发料单不允许取消"); ErrorCode WM_OUTSOURCE_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_707_100, "外协发料单行不存在"); ErrorCode WM_OUTSOURCE_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_707_200, "外协发料单明细不存在"); diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmOutsourceIssueStatusEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmOutsourceIssueStatusEnum.java index 8e9cdf2..40ae484 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmOutsourceIssueStatusEnum.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmOutsourceIssueStatusEnum.java @@ -29,25 +29,25 @@ public enum MesWmOutsourceIssueStatusEnum implements ArrayValuable { /** * 待拣货 * - * 对应方法:TODO 待实现 + * 对应方法:{@link MesWmOutsourceIssueService#submitOutsourceIssue(Long)} */ APPROVING(MesOrderStatusConstants.APPROVING, "待拣货"), /** * 待执行出库 * - * 对应方法:TODO 待实现 + * 对应方法:{@link MesWmOutsourceIssueService#stockOutsourceIssue(Long)} */ APPROVED(MesOrderStatusConstants.APPROVED, "待执行出库"), /** * 已完成 * - * 对应方法:{@link MesWmOutsourceIssueService#executeOutsourceIssue(Long)} + * 对应方法:{@link MesWmOutsourceIssueService#finishOutsourceIssue(Long)} */ FINISHED(MesOrderStatusConstants.FINISHED, "已完成"), /** * 已取消 * - * 对应方法:TODO 待实现 + * 对应方法:{@link MesWmOutsourceIssueService#cancelOutsourceIssue(Long)} */ CANCELLED(MesOrderStatusConstants.CANCELLED, "已取消"); diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueService.java index 7278006..b1b3ad9 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueService.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueService.java @@ -72,4 +72,11 @@ public interface MesWmOutsourceIssueService { */ void finishOutsourceIssue(Long id); + /** + * 取消外协发料单 + * + * @param id 编号 + */ + void cancelOutsourceIssue(Long id); + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueServiceImpl.java index 668d1eb..aaccb9f 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueServiceImpl.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssuePageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourceissue.MesWmOutsourceIssueDetailDO; @@ -20,7 +22,6 @@ import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; @@ -121,7 +122,22 @@ public class MesWmOutsourceIssueServiceImpl implements MesWmOutsourceIssueServic } // 1.2 校验数量一致性(行数量 = 明细数量之和) List lines = outsourceIssueLineService.getOutsourceIssueLineListByIssueId(id); - validateQuantityMatch(id, lines); + if (CollUtil.isNotEmpty(lines)) { + // 批量查询所有明细 + List allDetails = outsourceIssueDetailService.getOutsourceIssueDetailListByIssueId(id); + Map> detailMap = CollectionUtils.convertMultiMap( + allDetails, MesWmOutsourceIssueDetailDO::getLineId); + // 检查每行的明细数量 + for (MesWmOutsourceIssueLineDO line : lines) { + List details = detailMap.getOrDefault(line.getId(), List.of()); + BigDecimal totalDetailQuantity = CollectionUtils.getSumValue(details, + MesWmOutsourceIssueDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO); + // 对比行数量与明细总数量,不满足直接抛出 + if (line.getQuantity().compareTo(totalDetailQuantity) != 0) { + throw exception(WM_OUTSOURCE_ISSUE_QUANTITY_MISMATCH); + } + } + } // 2. 更新单据状态为待执行出库 outsourceIssueMapper.updateById(new MesWmOutsourceIssueDO() @@ -151,6 +167,23 @@ public class MesWmOutsourceIssueServiceImpl implements MesWmOutsourceIssueServic .setId(id).setStatus(MesWmOutsourceIssueStatusEnum.FINISHED.getStatus())); } + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelOutsourceIssue(Long id) { + // 校验存在 + MesWmOutsourceIssueDO issue = validateOutsourceIssueExists(id); + // 已完成和已取消不允许取消 + if (ObjectUtils.equalsAny(issue.getStatus(), + MesWmOutsourceIssueStatusEnum.FINISHED.getStatus(), + MesWmOutsourceIssueStatusEnum.CANCELLED.getStatus())) { + throw exception(WM_OUTSOURCE_ISSUE_CANCEL_NOT_ALLOWED); + } + + // 取消 + outsourceIssueMapper.updateById(new MesWmOutsourceIssueDO() + .setId(id).setStatus(MesWmOutsourceIssueStatusEnum.CANCELLED.getStatus())); + } + /** * 校验外协发料单存在 */ @@ -186,32 +219,4 @@ public class MesWmOutsourceIssueServiceImpl implements MesWmOutsourceIssueServic } } - /** - * 校验数量一致性(行数量 = 明细数量之和) - */ - // TODO @AI:参考别的模块,不要抽成小方法; - private void validateQuantityMatch(Long issueId, List lines) { - // 获取所有明细 - List details = outsourceIssueDetailService.getOutsourceIssueDetailListByIssueId(issueId); - - // 按行ID分组,计算每行的明细数量之和 - Map detailQuantityMap = details.stream() - .collect(Collectors.groupingBy( - MesWmOutsourceIssueDetailDO::getLineId, - Collectors.reducing(BigDecimal.ZERO, - MesWmOutsourceIssueDetailDO::getQuantity, - BigDecimal::add) - )); - - // 校验每行的数量是否匹配 - for (MesWmOutsourceIssueLineDO line : lines) { - BigDecimal detailQuantity = detailQuantityMap.getOrDefault(line.getId(), BigDecimal.ZERO); - if (line.getQuantity().compareTo(detailQuantity) != 0) { - log.error("[validateQuantityMatch][发料单({}) 行({}) 数量不匹配 - 行数量: {}, 明细数量: {}]", - issueId, line.getId(), line.getQuantity(), detailQuantity); - throw exception(WM_OUTSOURCE_ISSUE_QUANTITY_MISMATCH); - } - } - } - } -- libgit2 0.21.4