Commit 5c9cbe75edf9040f0d877744d4d121a81443f8cc

Authored by YunaiV
1 parent c5728f11

✨ feat(mes): 添加外协发料单状态管理功能

新增提交到待拣货、执行拣货和完成外协发料出库的功能,优化了状态管理逻辑,确保发料单在不同状态下的操作流程更加清晰和安全。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java
... ... @@ -109,12 +109,30 @@ public class MesWmOutsourceIssueController {
109 109 BeanUtils.toBean(pageResult.getList(), MesWmOutsourceIssueExcelVO.class));
110 110 }
111 111  
  112 + @PutMapping("/submit")
  113 + @Operation(summary = "提交到待拣货")
  114 + @Parameter(name = "id", description = "编号", required = true)
  115 + @PreAuthorize("@ss.hasPermission('mes:wm-outsource-issue:update')")
  116 + public CommonResult<Boolean> submitOutsourceIssue(@RequestParam("id") Long id) {
  117 + outsourceIssueService.submitOutsourceIssue(id);
  118 + return success(true);
  119 + }
  120 +
  121 + @PutMapping("/stock")
  122 + @Operation(summary = "执行拣货")
  123 + @Parameter(name = "id", description = "编号", required = true)
  124 + @PreAuthorize("@ss.hasPermission('mes:wm-outsource-issue:update')")
  125 + public CommonResult<Boolean> stockOutsourceIssue(@RequestParam("id") Long id) {
  126 + outsourceIssueService.stockOutsourceIssue(id);
  127 + return success(true);
  128 + }
  129 +
112 130 @PutMapping("/finish")
113 131 @Operation(summary = "完成外协发料出库")
114 132 @Parameter(name = "id", description = "编号", required = true)
115 133 @PreAuthorize("@ss.hasPermission('mes:wm-outsource-issue:finish')")
116 134 public CommonResult<Boolean> finishOutsourceIssue(@RequestParam("id") Long id) {
117   - outsourceIssueService.executeOutsourceIssue(id);
  135 + outsourceIssueService.finishOutsourceIssue(id);
118 136 return success(true);
119 137 }
120 138  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueExcelVO.java
... ... @@ -7,6 +7,7 @@ import lombok.Data;
7 7  
8 8 import java.time.LocalDateTime;
9 9  
  10 +// TODO @AI:删除掉,融合到 MesWmOutsourceIssueRespVO 中,保持一个 VO 就好
10 11 @Schema(description = "管理后台 - MES 外协发料单 Excel VO")
11 12 @Data
12 13 @ExcelIgnoreUnannotated
... ... @@ -28,10 +29,10 @@ public class MesWmOutsourceIssueExcelVO {
28 29 private String vendorName;
29 30  
30 31 @ExcelProperty("生产工单编码")
31   - private String workorderCode;
  32 + private String workOrderCode;
32 33  
33 34 @ExcelProperty("生产工单名称")
34   - private String workorderName;
  35 + private String workOrderName;
35 36  
36 37 @ExcelProperty("发料日期")
37 38 private LocalDateTime issueDate;
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssuePageReqVO.java
... ... @@ -27,15 +27,17 @@ public class MesWmOutsourceIssuePageReqVO extends PageParam {
27 27 private Long vendorId;
28 28  
29 29 @Schema(description = "生产工单ID", example = "1")
30   - private Long workorderId;
  30 + private Long workOrderId;
31 31  
  32 + // TODO @AI:这个字段,去掉筛选
32 33 @Schema(description = "生产工单编码", example = "WO202603020001")
33   - private String workorderCode;
  34 + private String workOrderCode;
34 35  
35 36 @Schema(description = "发料日期")
36 37 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
37 38 private LocalDateTime[] issueDate;
38 39  
  40 + // TODO @AI:这个字段,去掉筛选
39 41 @Schema(description = "单据状态", example = "0")
40 42 private Integer status;
41 43  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueRespVO.java
... ... @@ -36,15 +36,15 @@ public class MesWmOutsourceIssueRespVO {
36 36 private String vendorName;
37 37  
38 38 @Schema(description = "生产工单ID", example = "1")
39   - private Long workorderId;
  39 + private Long workOrderId;
40 40  
41 41 @Schema(description = "生产工单编码", example = "WO202603020001")
42 42 @ExcelProperty("生产工单编码")
43   - private String workorderCode;
  43 + private String workOrderCode;
44 44  
45 45 @Schema(description = "生产工单名称", example = "生产工单001")
46 46 @ExcelProperty("生产工单名称")
47   - private String workorderName;
  47 + private String workOrderName;
48 48  
49 49 @Schema(description = "发料日期", requiredMode = Schema.RequiredMode.REQUIRED)
50 50 @ExcelProperty("发料日期")
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/vo/MesWmOutsourceIssueSaveReqVO.java
... ... @@ -22,27 +22,14 @@ public class MesWmOutsourceIssueSaveReqVO {
22 22 @NotEmpty(message = "发料单名称不能为空")
23 23 private String name;
24 24  
25   - @Schema(description = "供应商ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
26   - @NotNull(message = "供应商ID不能为空")
  25 + @Schema(description = "供应商ID", example = "1")
27 26 private Long vendorId;
28 27  
29   - @Schema(description = "供应商编码", example = "V001")
30   - private String vendorCode;
  28 + @Schema(description = "生产工单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  29 + @NotNull(message = "生产工单ID不能为空")
  30 + private Long workOrderId;
31 31  
32   - @Schema(description = "供应商名称", example = "供应商A")
33   - private String vendorName;
34   -
35   - @Schema(description = "生产工单ID", example = "1")
36   - private Long workorderId;
37   -
38   - @Schema(description = "生产工单编码", example = "WO202603020001")
39   - private String workorderCode;
40   -
41   - @Schema(description = "生产工单名称", example = "生产工单001")
42   - private String workorderName;
43   -
44   - @Schema(description = "发料日期", requiredMode = Schema.RequiredMode.REQUIRED)
45   - @NotNull(message = "发料日期不能为空")
  32 + @Schema(description = "发料日期")
46 33 private LocalDateTime issueDate;
47 34  
48 35 @Schema(description = "备注", example = "备注")
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourceissue/MesWmOutsourceIssueDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourceissue;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
5 6 import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceIssueStatusEnum;
6 7 import com.baomidou.mybatisplus.annotation.KeySequence;
7 8 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -44,29 +45,12 @@ public class MesWmOutsourceIssueDO extends BaseDO {
44 45 * 关联 {@link MesMdVendorDO#getId()}
45 46 */
46 47 private Long vendorId;
47   - // TODO @AI:去掉冗余的 vendorCode、vendorName 字段
48   - /**
49   - * 供应商编码
50   - */
51   - private String vendorCode;
52   - /**
53   - * 供应商名称
54   - */
55   - private String vendorName;
56   - // TODO @AI:去掉冗余的 workorderCode、workorderName 字段
57   - // TODO @AI:拼写不对;应该是 workOrderId;
58 48 /**
59 49 * 生产工单ID
  50 + *
  51 + * 关联 {@link MesProWorkOrderDO#getId()}
60 52 */
61   - private Long workorderId;
62   - /**
63   - * 生产工单编码
64   - */
65   - private String workorderCode;
66   - /**
67   - * 生产工单名称
68   - */
69   - private String workorderName;
  53 + private Long workOrderId;
70 54 /**
71 55 * 发料日期
72 56 */
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -358,6 +358,8 @@ public interface ErrorCodeConstants {
358 358 ErrorCode WM_OUTSOURCE_ISSUE_STATUS_NOT_PREPARE = new ErrorCode(1_040_707_002, "只有草稿状态才允许此操作");
359 359 ErrorCode WM_OUTSOURCE_ISSUE_NO_LINE = new ErrorCode(1_040_707_003, "至少需要一条发料行");
360 360 ErrorCode WM_OUTSOURCE_ISSUE_QUANTITY_MISMATCH = new ErrorCode(1_040_707_004, "发料单行数量与明细数量不一致");
  361 + ErrorCode WM_OUTSOURCE_ISSUE_STATUS_NOT_APPROVING = new ErrorCode(1_040_707_005, "只有待拣货状态才允许此操作");
  362 + ErrorCode WM_OUTSOURCE_ISSUE_STATUS_NOT_APPROVED = new ErrorCode(1_040_707_006, "只有待执行出库状态才允许此操作");
361 363 ErrorCode WM_OUTSOURCE_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_707_100, "外协发料单行不存在");
362 364 ErrorCode WM_OUTSOURCE_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_707_200, "外协发料单明细不存在");
363 365  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmOutsourceIssueStatusEnum.java
1 1 package cn.iocoder.yudao.module.mes.enums.wm;
2 2  
3 3 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueSaveReqVO;
4 5 import cn.iocoder.yudao.module.mes.enums.MesOrderStatusConstants;
  6 +import cn.iocoder.yudao.module.mes.service.wm.outsourceissue.MesWmOutsourceIssueService;
5 7 import lombok.AllArgsConstructor;
6 8 import lombok.Getter;
7 9  
... ... @@ -10,6 +12,8 @@ import java.util.Arrays;
10 12 /**
11 13 * MES 外协发料单状态枚举
12 14 *
  15 + * 对应字典 mes_wm_outsource_issue_status
  16 + *
13 17 * @author 芋道源码
14 18 */
15 19 @Getter
... ... @@ -18,13 +22,34 @@ public enum MesWmOutsourceIssueStatusEnum implements ArrayValuable&lt;Integer&gt; {
18 22  
19 23 /**
20 24 * 草稿
  25 + *
  26 + * 对应方法:{@link MesWmOutsourceIssueService#createOutsourceIssue(MesWmOutsourceIssueSaveReqVO)}
21 27 */
22 28 PREPARE(MesOrderStatusConstants.PREPARE, "草稿"),
23   - // TODO @AI:状态缺失了;
  29 + /**
  30 + * 待拣货
  31 + *
  32 + * 对应方法:TODO 待实现
  33 + */
  34 + APPROVING(MesOrderStatusConstants.APPROVING, "待拣货"),
  35 + /**
  36 + * 待执行出库
  37 + *
  38 + * 对应方法:TODO 待实现
  39 + */
  40 + APPROVED(MesOrderStatusConstants.APPROVED, "待执行出库"),
24 41 /**
25 42 * 已完成
  43 + *
  44 + * 对应方法:{@link MesWmOutsourceIssueService#executeOutsourceIssue(Long)}
  45 + */
  46 + FINISHED(MesOrderStatusConstants.FINISHED, "已完成"),
  47 + /**
  48 + * 已取消
  49 + *
  50 + * 对应方法:TODO 待实现
26 51 */
27   - FINISHED(MesOrderStatusConstants.FINISHED, "已完成");
  52 + CANCELLED(MesOrderStatusConstants.CANCELLED, "已取消");
28 53  
29 54 public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesWmOutsourceIssueStatusEnum::getStatus).toArray(Integer[]::new);
30 55  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueService.java
... ... @@ -52,10 +52,24 @@ public interface MesWmOutsourceIssueService {
52 52 PageResult<MesWmOutsourceIssueDO> getOutsourceIssuePage(MesWmOutsourceIssuePageReqVO pageReqVO);
53 53  
54 54 /**
55   - * 执行外协发料出库(草稿 → 已完成,扣减库存)
  55 + * 提交到待拣货(草稿 → 待拣货)
56 56 *
57 57 * @param id 编号
58 58 */
59   - void executeOutsourceIssue(Long id);
  59 + void submitOutsourceIssue(Long id);
  60 +
  61 + /**
  62 + * 执行拣货(待拣货 → 待执行出库)
  63 + *
  64 + * @param id 编号
  65 + */
  66 + void stockOutsourceIssue(Long id);
  67 +
  68 + /**
  69 + * 完成外协发料出库(待执行出库 → 已完成,扣减库存)
  70 + *
  71 + * @param id 编号
  72 + */
  73 + void finishOutsourceIssue(Long id);
60 74  
61 75 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueServiceImpl.java
... ... @@ -97,29 +97,56 @@ public class MesWmOutsourceIssueServiceImpl implements MesWmOutsourceIssueServic
97 97  
98 98 @Override
99 99 @Transactional(rollbackFor = Exception.class)
100   - public void executeOutsourceIssue(Long id) {
101   - // 1. 校验存在 + 草稿状态
102   - MesWmOutsourceIssueDO issue = validateOutsourceIssueExistsAndDraft(id);
103   -
104   - // 2. 检查是否有发料行
  100 + public void submitOutsourceIssue(Long id) {
  101 + // 1.1 校验存在 + 草稿状态
  102 + validateOutsourceIssueExistsAndDraft(id);
  103 + // 1.2 检查是否有发料行
105 104 List<MesWmOutsourceIssueLineDO> lines = outsourceIssueLineService.getOutsourceIssueLineListByIssueId(id);
106 105 if (CollUtil.isEmpty(lines)) {
107 106 throw exception(WM_OUTSOURCE_ISSUE_NO_LINE);
108 107 }
109 108  
110   - // 3. 校验数量一致性(行数量 = 明细数量之和)
  109 + // 2. 更新单据状态为待拣货
  110 + outsourceIssueMapper.updateById(new MesWmOutsourceIssueDO()
  111 + .setId(id).setStatus(MesWmOutsourceIssueStatusEnum.APPROVING.getStatus()));
  112 + }
  113 +
  114 + @Override
  115 + @Transactional(rollbackFor = Exception.class)
  116 + public void stockOutsourceIssue(Long id) {
  117 + // 1.1 校验存在 + 待拣货状态
  118 + MesWmOutsourceIssueDO issue = validateOutsourceIssueExists(id);
  119 + if (ObjUtil.notEqual(MesWmOutsourceIssueStatusEnum.APPROVING.getStatus(), issue.getStatus())) {
  120 + throw exception(WM_OUTSOURCE_ISSUE_STATUS_NOT_APPROVING);
  121 + }
  122 + // 1.2 校验数量一致性(行数量 = 明细数量之和)
  123 + List<MesWmOutsourceIssueLineDO> lines = outsourceIssueLineService.getOutsourceIssueLineListByIssueId(id);
111 124 validateQuantityMatch(id, lines);
112 125  
113   - // 4. 扣减库存(遍历明细表,逐条扣减)
  126 + // 2. 更新单据状态为待执行出库
  127 + outsourceIssueMapper.updateById(new MesWmOutsourceIssueDO()
  128 + .setId(id).setStatus(MesWmOutsourceIssueStatusEnum.APPROVED.getStatus()));
  129 + }
  130 +
  131 + @Override
  132 + @Transactional(rollbackFor = Exception.class)
  133 + public void finishOutsourceIssue(Long id) {
  134 + // 1. 校验存在 + 待执行出库状态
  135 + MesWmOutsourceIssueDO issue = validateOutsourceIssueExists(id);
  136 + if (ObjUtil.notEqual(MesWmOutsourceIssueStatusEnum.APPROVED.getStatus(), issue.getStatus())) {
  137 + throw exception(WM_OUTSOURCE_ISSUE_STATUS_NOT_APPROVED);
  138 + }
  139 +
  140 + // 2. 扣减库存(遍历明细表,逐条扣减)
114 141 List<MesWmOutsourceIssueDetailDO> details = outsourceIssueDetailService.getOutsourceIssueDetailListByIssueId(id);
115 142 for (MesWmOutsourceIssueDetailDO detail : details) {
116 143 // TODO: 调用库存服务扣减库存
117 144 // materialStockService.decreaseStock(detail.getMaterialStockId(), detail.getQuantity());
118   - log.info("[executeOutsourceIssue][发料单({}) 扣减库存 - 库存ID: {}, 数量: {}]",
  145 + log.info("[finishOutsourceIssue][发料单({}) 扣减库存 - 库存ID: {}, 数量: {}]",
119 146 id, detail.getMaterialStockId(), detail.getQuantity());
120 147 }
121 148  
122   - // 5. 更新单据状态为已完成
  149 + // 3. 更新单据状态为已完成
123 150 outsourceIssueMapper.updateById(new MesWmOutsourceIssueDO()
124 151 .setId(id).setStatus(MesWmOutsourceIssueStatusEnum.FINISHED.getStatus()));
125 152 }
... ... @@ -162,6 +189,7 @@ public class MesWmOutsourceIssueServiceImpl implements MesWmOutsourceIssueServic
162 189 /**
163 190 * 校验数量一致性(行数量 = 明细数量之和)
164 191 */
  192 + // TODO @AI:参考别的模块,不要抽成小方法;
165 193 private void validateQuantityMatch(Long issueId, List<MesWmOutsourceIssueLineDO> lines) {
166 194 // 获取所有明细
167 195 List<MesWmOutsourceIssueDetailDO> details = outsourceIssueDetailService.getOutsourceIssueDetailListByIssueId(issueId);
... ...