Commit efde91601411e758e623ad950e865b3b14ddcf38

Authored by YunaiV
1 parent 8d2fc22d

✨ feat(mes): 添加生产领料出库单相关功能与接口

新增生产领料出库单的状态管理、行验证及明细查询功能,优化了相关数据模型和接口,提升了系统的可用性和用户体验。
Showing 18 changed files with 262 additions and 131 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productionissue/MesWmProductionIssueController.java
... ... @@ -109,13 +109,40 @@ public class MesWmProductionIssueController {
109 109 @PutMapping("/finish")
110 110 @Operation(summary = "完成领料出库单")
111 111 @Parameter(name = "id", description = "编号", required = true)
112   - @PreAuthorize("@ss.hasPermission('mes:wm-production-issue:update-status')")
  112 + @PreAuthorize("@ss.hasPermission('mes:wm-production-issue:execute')")
113 113 public CommonResult<Boolean> finishProductionIssue(@RequestParam("id") Long id) {
114 114 issueService.finishProductionIssue(id);
115 115 return success(true);
116 116 }
117 117  
118   - // TODO @AI:check quantity
  118 + @PutMapping("/submit")
  119 + @Operation(summary = "提交领料出库单")
  120 + @Parameter(name = "id", description = "编号", required = true)
  121 + @PreAuthorize("@ss.hasPermission('mes:wm-production-issue:update')")
  122 + public CommonResult<Boolean> submitProductionIssue(@RequestParam("id") Long id) {
  123 + issueService.submitProductionIssue(id);
  124 + return success(true);
  125 + }
  126 +
  127 + @PutMapping("/stock")
  128 + @Operation(summary = "执行拣货")
  129 + @Parameter(name = "id", description = "编号", required = true)
  130 + @PreAuthorize("@ss.hasPermission('mes:wm-production-issue:update')")
  131 + public CommonResult<Boolean> stockProductionIssue(@RequestParam("id") Long id) {
  132 + issueService.stockProductionIssue(id);
  133 + return success(true);
  134 + }
  135 +
  136 + @PutMapping("/cancel")
  137 + @Operation(summary = "取消领料出库单")
  138 + @Parameter(name = "id", description = "编号", required = true)
  139 + @PreAuthorize("@ss.hasPermission('mes:wm-production-issue:update')")
  140 + public CommonResult<Boolean> cancelProductionIssue(@RequestParam("id") Long id) {
  141 + issueService.cancelProductionIssue(id);
  142 + return success(true);
  143 + }
  144 +
  145 + // TODO @AI:【芋艿】check quantity;晚点搞;
119 146  
120 147 // ==================== 拼接 VO ====================
121 148  
... ... @@ -127,15 +154,15 @@ public class MesWmProductionIssueController {
127 154 Map<Long, MesMdWorkstationDO> workstationMap = workstationService.getWorkstationMap(
128 155 convertSet(list, MesWmProductionIssueDO::getWorkstationId));
129 156 Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(
130   - convertSet(list, MesWmProductionIssueDO::getWorkorderId));
  157 + convertSet(list, MesWmProductionIssueDO::getWorkOrderId));
131 158 // 2. 构建结果
132 159 return BeanUtils.toBean(list, MesWmProductionIssueRespVO.class, vo -> {
133 160 // 2.1 填充工作站名称
134 161 MapUtils.findAndThen(workstationMap, vo.getWorkstationId(),
135 162 workstation -> vo.setWorkstationName(workstation.getName()));
136 163 // 2.2 填充工单编号
137   - MapUtils.findAndThen(workOrderMap, vo.getWorkorderId(),
138   - workOrder -> vo.setWorkorderCode(workOrder.getCode()));
  164 + MapUtils.findAndThen(workOrderMap, vo.getWorkOrderId(),
  165 + workOrder -> vo.setWorkOrderCode(workOrder.getCode()));
139 166 });
140 167 }
141 168  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productionissue/vo/MesWmProductionIssuePageReqVO.java
... ... @@ -25,7 +25,7 @@ public class MesWmProductionIssuePageReqVO extends PageParam {
25 25 private Long workstationId;
26 26  
27 27 @Schema(description = "生产工单ID", example = "1")
28   - private Long workorderId;
  28 + private Long workOrderId;
29 29  
30 30 @Schema(description = "生产任务ID", example = "1")
31 31 private Long taskId;
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productionissue/vo/MesWmProductionIssueRespVO.java
... ... @@ -25,10 +25,10 @@ public class MesWmProductionIssueRespVO {
25 25 private String workstationName;
26 26  
27 27 @Schema(description = "生产工单 ID", example = "1")
28   - private Long workorderId;
  28 + private Long workOrderId;
29 29  
30 30 @Schema(description = "生产工单编号", example = "WO20250226001")
31   - private String workorderCode;
  31 + private String workOrderCode;
32 32  
33 33 @Schema(description = "生产任务 ID", example = "1")
34 34 private Long taskId;
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productionissue/vo/MesWmProductionIssueSaveReqVO.java
... ... @@ -23,11 +23,12 @@ public class MesWmProductionIssueSaveReqVO {
23 23 private String name;
24 24  
25 25 @Schema(description = "工作站 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
26   - @NotNull(message = "工作站ID不能为空")
  26 + @NotNull(message = "工作站 ID 不能为空")
27 27 private Long workstationId;
28 28  
29   - @Schema(description = "生产工单 ID", example = "1")
30   - private Long workorderId;
  29 + @Schema(description = "生产工单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  30 + @NotNull(message = "生产工单 ID 不能为空")
  31 + private Long workOrderId;
31 32  
32 33 @Schema(description = "需求时间", requiredMode = Schema.RequiredMode.REQUIRED)
33 34 @NotNull(message = "需求时间不能为空")
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productionissue/vo/line/MesWmProductionIssueLineSaveReqVO.java
... ... @@ -12,6 +12,9 @@ public class MesWmProductionIssueLineSaveReqVO {
12 12 @Schema(description = "行ID", example = "1")
13 13 private Long id;
14 14  
  15 + @Schema(description = "领料单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  16 + private Long issueId;
  17 +
15 18 @Schema(description = "物料ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
16 19 private Long itemId;
17 20  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productionissue/MesWmProductionIssueDO.java
1 1 package cn.iocoder.yudao.module.mes.dal.dataobject.wm.productionissue;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4   -import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
5 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
6 5 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
7 6 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
... ... @@ -13,12 +12,11 @@ import lombok.*;
13 12  
14 13 import java.time.LocalDateTime;
15 14  
16   -// TODO @AI:【需要一起讨论】不要直接改;是不是叫 issueheader 包?主要后续别的表也有 issue,希望区分下;还是加个合理的前缀?就像 wm_outsource_issue 这种;
17 15 /**
18 16 * MES 领料出库单 DO
19 17 */
20   -@TableName("mes_wm_issue_header")
21   -@KeySequence("mes_wm_issue_header_seq")
  18 +@TableName("mes_wm_production_issue")
  19 +@KeySequence("mes_wm_production_issue_seq")
22 20 @Data
23 21 @EqualsAndHashCode(callSuper = true)
24 22 @ToString(callSuper = true)
... ... @@ -46,28 +44,20 @@ public class MesWmProductionIssueDO extends BaseDO {
46 44 * 关联 {@link MesMdWorkstationDO#getId()}
47 45 */
48 46 private Long workstationId;
49   - // TODO @AI:workOrderId;
50 47 /**
51 48 * 生产工单 ID
52 49 *
53 50 * 关联 {@link MesProWorkOrderDO#getId()}
54 51 */
55   - private Long workorderId;
56   - // TODO @芋艿:【疑问】这个字段的来源?
  52 + private Long workOrderId;
  53 + // TODO @芋艿:【疑问,不要动】这个字段的来源?
57 54 /**
58 55 * 生产任务 ID
59 56 *
60 57 * 关联 {@link MesProTaskDO#getId()}
61 58 */
62 59 private Long taskId;
63   - // TODO @AI:应该不用记录?因为 workorderId 字段的;
64   - /**
65   - * 客户 ID
66   - *
67   - * 关联 {@link MesMdClientDO#getId()}
68   - */
69   - private Long clientId;
70   - // TODO @芋艿:【疑问】这个字段的更新时间;
  60 + // TODO @芋艿:【疑问,不要动】这个字段的更新时间;
71 61 /**
72 62 * 领料日期
73 63 */
... ... @@ -86,21 +76,5 @@ public class MesWmProductionIssueDO extends BaseDO {
86 76 * 备注
87 77 */
88 78 private String remark;
89   - /**
90   - * 预留字段 1
91   - */
92   - private String attribute1;
93   - /**
94   - * 预留字段 2
95   - */
96   - private String attribute2;
97   - /**
98   - * 预留字段 3
99   - */
100   - private Integer attribute3;
101   - /**
102   - * 预留字段 4
103   - */
104   - private Integer attribute4;
105 79  
106 80 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productionissue/MesWmProductionIssueDetailDO.java
... ... @@ -16,8 +16,8 @@ import java.math.BigDecimal;
16 16 /**
17 17 * MES 领料出库明细 DO
18 18 */
19   -@TableName("mes_wm_issue_detail")
20   -@KeySequence("mes_wm_issue_detail_seq")
  19 +@TableName("mes_wm_production_issue_detail")
  20 +@KeySequence("mes_wm_production_issue_detail_seq")
21 21 @Data
22 22 @EqualsAndHashCode(callSuper = true)
23 23 @ToString(callSuper = true)
... ... @@ -85,21 +85,5 @@ public class MesWmProductionIssueDetailDO extends BaseDO {
85 85 * 备注
86 86 */
87 87 private String remark;
88   - /**
89   - * 预留字段1
90   - */
91   - private String attribute1;
92   - /**
93   - * 预留字段2
94   - */
95   - private String attribute2;
96   - /**
97   - * 预留字段3
98   - */
99   - private Integer attribute3;
100   - /**
101   - * 预留字段4
102   - */
103   - private Integer attribute4;
104 88  
105 89 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productionissue/MesWmProductionIssueLineDO.java
... ... @@ -12,8 +12,8 @@ import java.math.BigDecimal;
12 12 /**
13 13 * MES 领料出库单行 DO
14 14 */
15   -@TableName("mes_wm_issue_line")
16   -@KeySequence("mes_wm_issue_line_seq")
  15 +@TableName("mes_wm_production_issue_line")
  16 +@KeySequence("mes_wm_production_issue_line_seq")
17 17 @Data
18 18 @EqualsAndHashCode(callSuper = true)
19 19 @ToString(callSuper = true)
... ... @@ -51,21 +51,5 @@ public class MesWmProductionIssueLineDO extends BaseDO {
51 51 * 备注
52 52 */
53 53 private String remark;
54   - /**
55   - * 预留字段 1
56   - */
57   - private String attribute1;
58   - /**
59   - * 预留字段 2
60   - */
61   - private String attribute2;
62   - /**
63   - * 预留字段 3
64   - */
65   - private Integer attribute3;
66   - /**
67   - * 预留字段 4
68   - */
69   - private Integer attribute4;
70 54  
71 55 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productionissue/MesWmProductionIssueDetailMapper.java
... ... @@ -16,6 +16,10 @@ public interface MesWmProductionIssueDetailMapper extends BaseMapperX&lt;MesWmProdu
16 16 return selectList(MesWmProductionIssueDetailDO::getLineId, lineId);
17 17 }
18 18  
  19 + default List<MesWmProductionIssueDetailDO> selectListByIssueId(Long issueId) {
  20 + return selectList(MesWmProductionIssueDetailDO::getIssueId, issueId);
  21 + }
  22 +
19 23 default void deleteByIssueId(Long issueId) {
20 24 delete(MesWmProductionIssueDetailDO::getIssueId, issueId);
21 25 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productionissue/MesWmProductionIssueMapper.java
... ... @@ -18,9 +18,7 @@ public interface MesWmProductionIssueMapper extends BaseMapperX&lt;MesWmProductionI
18 18 .likeIfPresent(MesWmProductionIssueDO::getCode, reqVO.getCode())
19 19 .likeIfPresent(MesWmProductionIssueDO::getName, reqVO.getName())
20 20 .eqIfPresent(MesWmProductionIssueDO::getWorkstationId, reqVO.getWorkstationId())
21   - .eqIfPresent(MesWmProductionIssueDO::getWorkorderId, reqVO.getWorkorderId())
22   - .eqIfPresent(MesWmProductionIssueDO::getTaskId, reqVO.getTaskId())
23   - .eqIfPresent(MesWmProductionIssueDO::getClientId, reqVO.getClientId())
  21 + .eqIfPresent(MesWmProductionIssueDO::getWorkOrderId, reqVO.getWorkOrderId())
24 22 .eqIfPresent(MesWmProductionIssueDO::getStatus, reqVO.getStatus())
25 23 .betweenIfPresent(MesWmProductionIssueDO::getIssueDate, reqVO.getIssueDate())
26 24 .orderByDesc(MesWmProductionIssueDO::getId));
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -360,9 +360,11 @@ public interface ErrorCodeConstants {
360 360 // ========== MES 仓库管理-生产领料出库单(1-040-708-000) ==========
361 361 ErrorCode WM_PRODUCTION_ISSUE_NOT_EXISTS = new ErrorCode(1_040_708_000, "生产领料出库单不存在");
362 362 ErrorCode WM_PRODUCTION_ISSUE_STATUS_INVALID = new ErrorCode(1_040_708_001, "生产领料出库单状态不正确,无法执行该操作");
363   - // DONE @AI:不需要的错误码,需要处理下 - 已删除不需要的错误码(WM_PRODUCTION_ISSUE_WAREHOUSE_NOT_EXISTS, WM_PRODUCTION_ISSUE_ITEM_NOT_EXISTS, WM_PRODUCTION_ISSUE_NO_LINE, WM_PRODUCTION_ISSUE_QUANTITY_MISMATCH),保留需要的错误码
  363 + ErrorCode WM_PRODUCTION_ISSUE_NO_LINE = new ErrorCode(1_040_708_002, "生产领料出库单至少需要一条行数据");
  364 + ErrorCode WM_PRODUCTION_ISSUE_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_708_003, "领料出库单行数量与明细数量不一致");
364 365 ErrorCode WM_PRODUCTION_ISSUE_WORKORDER_NOT_EXISTS = new ErrorCode(1_040_708_004, "生产工单不存在");
365 366 ErrorCode WM_PRODUCTION_ISSUE_WORKSTATION_NOT_EXISTS = new ErrorCode(1_040_708_005, "工作站不存在");
  367 + ErrorCode WM_PRODUCTION_ISSUE_CANCEL_NOT_ALLOWED = new ErrorCode(1_040_708_006, "生产领料出库单已完成或已取消,无法取消");
366 368 ErrorCode WM_PRODUCTION_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_708_100, "生产领料出库单行不存在");
367 369 ErrorCode WM_PRODUCTION_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在");
368 370  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductionIssueStatusEnum.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.productionissue.vo.MesWmProductionIssueSaveReqVO;
4 5 import cn.iocoder.yudao.module.mes.enums.MesOrderStatusConstants;
  6 +import cn.iocoder.yudao.module.mes.service.wm.productionissue.MesWmProductionIssueService;
5 7 import lombok.AllArgsConstructor;
6 8 import lombok.Getter;
7 9  
... ... @@ -9,18 +11,48 @@ import java.util.Arrays;
9 11  
10 12 /**
11 13 * MES 领料出库单状态枚举
  14 + *
  15 + * @author 芋道源码
12 16 */
13 17 @Getter
14 18 @AllArgsConstructor
15 19 public enum MesWmProductionIssueStatusEnum implements ArrayValuable<Integer> {
16 20  
  21 + /**
  22 + * 草稿
  23 + *
  24 + * 对应方法:{@link MesWmProductionIssueService#createProductionIssue(MesWmProductionIssueSaveReqVO)}
  25 + */
17 26 PREPARE(MesOrderStatusConstants.PREPARE, "草稿"),
18   - FINISHED(MesOrderStatusConstants.FINISHED, "已完成");
  27 + /**
  28 + * 待拣货
  29 + *
  30 + * 对应方法:{@link MesWmProductionIssueService#submitProductionIssue(Long)}
  31 + */
  32 + APPROVING(MesOrderStatusConstants.APPROVING, "待拣货"),
  33 + /**
  34 + * 待执行领出
  35 + *
  36 + * 对应方法:{@link MesWmProductionIssueService#stockProductionIssue(Long)}
  37 + */
  38 + APPROVED(MesOrderStatusConstants.APPROVED, "待执行领出"),
  39 + /**
  40 + * 已完成
  41 + *
  42 + * 对应方法:{@link MesWmProductionIssueService#finishProductionIssue(Long)}
  43 + */
  44 + FINISHED(MesOrderStatusConstants.FINISHED, "已完成"),
  45 + /**
  46 + * 已取消
  47 + *
  48 + * 对应方法:{@link MesWmProductionIssueService#cancelProductionIssue(Long)}
  49 + */
  50 + CANCELED(MesOrderStatusConstants.CANCELLED, "已取消");
19 51  
20 52 public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesWmProductionIssueStatusEnum::getStatus).toArray(Integer[]::new);
21 53  
22 54 /**
23   - * 状态
  55 + * 状态值
24 56 */
25 57 private final Integer status;
26 58 /**
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productionissue/MesWmProductionIssueDetailService.java
... ... @@ -50,6 +50,14 @@ public interface MesWmProductionIssueDetailService {
50 50 List<MesWmProductionIssueDetailDO> getProductionIssueDetailListByLineId(Long lineId);
51 51  
52 52 /**
  53 + * 根据领料单ID获取明细列表
  54 + *
  55 + * @param issueId 领料单ID
  56 + * @return 明细列表
  57 + */
  58 + List<MesWmProductionIssueDetailDO> getProductionIssueDetailListByIssueId(Long issueId);
  59 +
  60 + /**
53 61 * 根据领料单ID删除明细
54 62 *
55 63 * @param issueId 领料单ID
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productionissue/MesWmProductionIssueDetailServiceImpl.java
... ... @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 4 import cn.iocoder.yudao.module.mes.controller.admin.wm.productionissue.vo.detail.MesWmProductionIssueDetailSaveReqVO;
5 5 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productionissue.MesWmProductionIssueDetailDO;
6 6 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productionissue.MesWmProductionIssueDetailMapper;
7   -import cn.iocoder.yudao.module.mes.dal.mysql.wm.productionissue.MesWmProductionIssueLineMapper;
8 7 import jakarta.annotation.Resource;
  8 +import org.springframework.context.annotation.Lazy;
9 9 import org.springframework.stereotype.Service;
10 10 import org.springframework.validation.annotation.Validated;
11 11  
... ... @@ -13,7 +13,6 @@ import java.util.List;
13 13  
14 14 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
15 15 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCTION_ISSUE_DETAIL_NOT_EXISTS;
16   -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCTION_ISSUE_LINE_NOT_EXISTS;
17 16  
18 17 /**
19 18 * MES 领料出库明细 Service 实现类
... ... @@ -24,13 +23,15 @@ public class MesWmProductionIssueDetailServiceImpl implements MesWmProductionIss
24 23  
25 24 @Resource
26 25 private MesWmProductionIssueDetailMapper issueDetailMapper;
  26 +
27 27 @Resource
28   - private MesWmProductionIssueLineMapper issueLineMapper;
  28 + @Lazy
  29 + private MesWmProductionIssueLineService issueLineService;
29 30  
30 31 @Override
31 32 public Long createProductionIssueDetail(MesWmProductionIssueDetailSaveReqVO createReqVO) {
32 33 // 校验父数据存在
33   - validateParentIssueLineExists(createReqVO.getLineId());
  34 + issueLineService.validateProductionIssueLineExists(createReqVO.getLineId());
34 35  
35 36 // 插入
36 37 MesWmProductionIssueDetailDO detail = BeanUtils.toBean(createReqVO, MesWmProductionIssueDetailDO.class);
... ... @@ -41,9 +42,9 @@ public class MesWmProductionIssueDetailServiceImpl implements MesWmProductionIss
41 42 @Override
42 43 public void updateProductionIssueDetail(MesWmProductionIssueDetailSaveReqVO updateReqVO) {
43 44 // 校验存在
44   - validateIssueDetailExists(updateReqVO.getId());
  45 + validateProductionIssueDetailExists(updateReqVO.getId());
45 46 // 校验父数据存在
46   - validateParentIssueLineExists(updateReqVO.getLineId());
  47 + issueLineService.validateProductionIssueLineExists(updateReqVO.getLineId());
47 48  
48 49 // 更新
49 50 MesWmProductionIssueDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductionIssueDetailDO.class);
... ... @@ -53,7 +54,7 @@ public class MesWmProductionIssueDetailServiceImpl implements MesWmProductionIss
53 54 @Override
54 55 public void deleteProductionIssueDetail(Long id) {
55 56 // 校验存在
56   - validateIssueDetailExists(id);
  57 + validateProductionIssueDetailExists(id);
57 58 // 删除
58 59 issueDetailMapper.deleteById(id);
59 60 }
... ... @@ -69,20 +70,19 @@ public class MesWmProductionIssueDetailServiceImpl implements MesWmProductionIss
69 70 }
70 71  
71 72 @Override
  73 + public List<MesWmProductionIssueDetailDO> getProductionIssueDetailListByIssueId(Long issueId) {
  74 + return issueDetailMapper.selectListByIssueId(issueId);
  75 + }
  76 +
  77 + @Override
72 78 public void deleteProductionIssueDetailByIssueId(Long issueId) {
73 79 issueDetailMapper.deleteByIssueId(issueId);
74 80 }
75 81  
76   - private void validateIssueDetailExists(Long id) {
  82 + private void validateProductionIssueDetailExists(Long id) {
77 83 if (issueDetailMapper.selectById(id) == null) {
78 84 throw exception(WM_PRODUCTION_ISSUE_DETAIL_NOT_EXISTS);
79 85 }
80 86 }
81 87  
82   - private void validateParentIssueLineExists(Long lineId) {
83   - if (issueLineMapper.selectById(lineId) == null) {
84   - throw exception(WM_PRODUCTION_ISSUE_LINE_NOT_EXISTS);
85   - }
86   - }
87   -
88 88 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productionissue/MesWmProductionIssueLineService.java
... ... @@ -66,4 +66,12 @@ public interface MesWmProductionIssueLineService {
66 66 */
67 67 void deleteProductionIssueLineByIssueId(Long issueId);
68 68  
  69 + /**
  70 + * 校验领料出库单行是否存在
  71 + *
  72 + * @param id 编号
  73 + * @return 领料出库单行
  74 + */
  75 + MesWmProductionIssueLineDO validateProductionIssueLineExists(Long id);
  76 +
69 77 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productionissue/MesWmProductionIssueLineServiceImpl.java
... ... @@ -6,8 +6,9 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.productionissue.vo.line.M
6 6 import cn.iocoder.yudao.module.mes.controller.admin.wm.productionissue.vo.line.MesWmProductionIssueLineSaveReqVO;
7 7 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productionissue.MesWmProductionIssueLineDO;
8 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productionissue.MesWmProductionIssueLineMapper;
9   -import cn.iocoder.yudao.module.mes.dal.mysql.wm.productionissue.MesWmProductionIssueMapper;
  9 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
10 10 import jakarta.annotation.Resource;
  11 +import org.springframework.context.annotation.Lazy;
11 12 import org.springframework.stereotype.Service;
12 13 import org.springframework.validation.annotation.Validated;
13 14  
... ... @@ -15,7 +16,6 @@ import java.util.List;
15 16  
16 17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
17 18 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCTION_ISSUE_LINE_NOT_EXISTS;
18   -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCTION_ISSUE_NOT_EXISTS;
19 19  
20 20 /**
21 21 * MES 领料出库单行 Service 实现类
... ... @@ -26,13 +26,19 @@ public class MesWmProductionIssueLineServiceImpl implements MesWmProductionIssue
26 26  
27 27 @Resource
28 28 private MesWmProductionIssueLineMapper issueLineMapper;
  29 +
  30 + @Resource
  31 + @Lazy
  32 + private MesWmProductionIssueService issueService;
29 33 @Resource
30   - private MesWmProductionIssueMapper issueMapper;
  34 + private MesMdItemService itemService;
31 35  
32 36 @Override
33 37 public Long createProductionIssueLine(MesWmProductionIssueLineSaveReqVO createReqVO) {
34 38 // 校验父数据存在
35   - validateParentIssueExists(createReqVO.getIssueId());
  39 + issueService.validateProductionIssueExists(createReqVO.getIssueId());
  40 + // 校验物料存在
  41 + itemService.validateItemExists(createReqVO.getItemId());
36 42  
37 43 // 插入
38 44 MesWmProductionIssueLineDO line = BeanUtils.toBean(createReqVO, MesWmProductionIssueLineDO.class);
... ... @@ -43,9 +49,11 @@ public class MesWmProductionIssueLineServiceImpl implements MesWmProductionIssue
43 49 @Override
44 50 public void updateProductionIssueLine(MesWmProductionIssueLineSaveReqVO updateReqVO) {
45 51 // 校验存在
46   - validateIssueLineExists(updateReqVO.getId());
  52 + validateProductionIssueLineExists(updateReqVO.getId());
47 53 // 校验父数据存在
48   - validateParentIssueExists(updateReqVO.getIssueId());
  54 + issueService.validateProductionIssueExists(updateReqVO.getIssueId());
  55 + // 校验物料存在
  56 + itemService.validateItemExists(updateReqVO.getItemId());
49 57  
50 58 // 更新
51 59 MesWmProductionIssueLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductionIssueLineDO.class);
... ... @@ -55,7 +63,7 @@ public class MesWmProductionIssueLineServiceImpl implements MesWmProductionIssue
55 63 @Override
56 64 public void deleteProductionIssueLine(Long id) {
57 65 // 校验存在
58   - validateIssueLineExists(id);
  66 + validateProductionIssueLineExists(id);
59 67 // 删除
60 68 issueLineMapper.deleteById(id);
61 69 }
... ... @@ -80,16 +88,13 @@ public class MesWmProductionIssueLineServiceImpl implements MesWmProductionIssue
80 88 issueLineMapper.deleteByIssueId(issueId);
81 89 }
82 90  
83   - private void validateIssueLineExists(Long id) {
84   - if (issueLineMapper.selectById(id) == null) {
  91 + @Override
  92 + public MesWmProductionIssueLineDO validateProductionIssueLineExists(Long id) {
  93 + MesWmProductionIssueLineDO line = issueLineMapper.selectById(id);
  94 + if (line == null) {
85 95 throw exception(WM_PRODUCTION_ISSUE_LINE_NOT_EXISTS);
86 96 }
87   - }
88   -
89   - private void validateParentIssueExists(Long issueId) {
90   - if (issueMapper.selectById(issueId) == null) {
91   - throw exception(WM_PRODUCTION_ISSUE_NOT_EXISTS);
92   - }
  97 + return line;
93 98 }
94 99  
95 100 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productionissue/MesWmProductionIssueService.java
... ... @@ -50,10 +50,39 @@ public interface MesWmProductionIssueService {
50 50 PageResult<MesWmProductionIssueDO> getProductionIssuePage(MesWmProductionIssuePageReqVO pageReqVO);
51 51  
52 52 /**
53   - * 完成领料出库单(草稿 → 已完成,执行出库)
  53 + * 校验领料出库单是否存在
  54 + *
  55 + * @param id 编号
  56 + * @return 领料出库单
  57 + */
  58 + MesWmProductionIssueDO validateProductionIssueExists(Long id);
  59 +
  60 + /**
  61 + * 提交领料出库单(草稿 → 待拣货)
  62 + *
  63 + * @param id 编号
  64 + */
  65 + void submitProductionIssue(Long id);
  66 +
  67 + /**
  68 + * 执行拣货(待拣货 → 待执行领出)
  69 + *
  70 + * @param id 编号
  71 + */
  72 + void stockProductionIssue(Long id);
  73 +
  74 + /**
  75 + * 执行领出/出库(待执行领出 → 已完成),更新库存台账
54 76 *
55 77 * @param id 编号
56 78 */
57 79 void finishProductionIssue(Long id);
58 80  
  81 + /**
  82 + * 取消领料出库单(任意非已完成/已取消状态 → 已取消)
  83 + *
  84 + * @param id 编号
  85 + */
  86 + void cancelProductionIssue(Long id);
  87 +
59 88 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productionissue/MesWmProductionIssueServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.productionissue;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.ObjUtil;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
6 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.productionissue.vo.MesWmProductionIssuePageReqVO;
7 10 import cn.iocoder.yudao.module.mes.controller.admin.wm.productionissue.vo.MesWmProductionIssueSaveReqVO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productionissue.MesWmProductionIssueDetailDO;
8 12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productionissue.MesWmProductionIssueDO;
  13 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productionissue.MesWmProductionIssueLineDO;
9 14 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productionissue.MesWmProductionIssueMapper;
10 15 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductionIssueStatusEnum;
11 16 import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
... ... @@ -15,6 +20,9 @@ import org.springframework.stereotype.Service;
15 20 import org.springframework.transaction.annotation.Transactional;
16 21 import org.springframework.validation.annotation.Validated;
17 22  
  23 +import java.math.BigDecimal;
  24 +import java.util.List;
  25 +
18 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
19 27 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
20 28  
... ... @@ -41,9 +49,9 @@ public class MesWmProductionIssueServiceImpl implements MesWmProductionIssueServ
41 49 @Transactional(rollbackFor = Exception.class)
42 50 public Long createProductionIssue(MesWmProductionIssueSaveReqVO createReqVO) {
43 51 // 1. 校验关联数据
44   - workstationService.validateWorkstationExists(createReqVO.getWorkstationId());
45   - if (createReqVO.getWorkorderId() != null) {
46   - workOrderService.validateWorkOrderExists(createReqVO.getWorkorderId());
  52 + workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId());
  53 + if (createReqVO.getWorkstationId() != null) {
  54 + workstationService.validateWorkstationExists(createReqVO.getWorkstationId());
47 55 }
48 56  
49 57 // 2. 插入主表
... ... @@ -57,11 +65,11 @@ public class MesWmProductionIssueServiceImpl implements MesWmProductionIssueServ
57 65 @Transactional(rollbackFor = Exception.class)
58 66 public void updateProductionIssue(MesWmProductionIssueSaveReqVO updateReqVO) {
59 67 // 1.1 校验存在 + 准备中状态
60   - validateIssueExistsAndPrepare(updateReqVO.getId());
  68 + validateProductionIssueExistsAndPrepare(updateReqVO.getId());
61 69 // 1.2 校验关联数据
62   - workstationService.validateWorkstationExists(updateReqVO.getWorkstationId());
63   - if (updateReqVO.getWorkorderId() != null) {
64   - workOrderService.validateWorkOrderExists(updateReqVO.getWorkorderId());
  70 + workOrderService.validateWorkOrderExists(updateReqVO.getWorkOrderId());
  71 + if (updateReqVO.getWorkstationId() != null) {
  72 + workstationService.validateWorkstationExists(updateReqVO.getWorkstationId());
65 73 }
66 74  
67 75 // 2. 更新主表
... ... @@ -73,7 +81,7 @@ public class MesWmProductionIssueServiceImpl implements MesWmProductionIssueServ
73 81 @Transactional(rollbackFor = Exception.class)
74 82 public void deleteProductionIssue(Long id) {
75 83 // 1. 校验存在 + 准备中状态
76   - validateIssueExistsAndPrepare(id);
  84 + validateProductionIssueExistsAndPrepare(id);
77 85  
78 86 // 2.1 级联删除明细
79 87 issueDetailService.deleteProductionIssueDetailByIssueId(id);
... ... @@ -95,21 +103,85 @@ public class MesWmProductionIssueServiceImpl implements MesWmProductionIssueServ
95 103  
96 104 @Override
97 105 @Transactional(rollbackFor = Exception.class)
  106 + public void submitProductionIssue(Long id) {
  107 + // 校验存在 + 草稿状态
  108 + validateProductionIssueExistsAndPrepare(id);
  109 + // 校验至少有一条行
  110 + List<MesWmProductionIssueLineDO> lines = issueLineService.getProductionIssueLineListByIssueId(id);
  111 + if (CollUtil.isEmpty(lines)) {
  112 + throw exception(WM_PRODUCTION_ISSUE_NO_LINE);
  113 + }
  114 +
  115 + // 提交(草稿 → 待拣货)
  116 + issueMapper.updateById(new MesWmProductionIssueDO()
  117 + .setId(id).setStatus(MesWmProductionIssueStatusEnum.APPROVING.getStatus()));
  118 + }
  119 +
  120 + @Override
  121 + @Transactional(rollbackFor = Exception.class)
  122 + public void stockProductionIssue(Long id) {
  123 + // 校验存在
  124 + MesWmProductionIssueDO issue = validateProductionIssueExists(id);
  125 + if (ObjUtil.notEqual(MesWmProductionIssueStatusEnum.APPROVING.getStatus(), issue.getStatus())) {
  126 + throw exception(WM_PRODUCTION_ISSUE_STATUS_INVALID);
  127 + }
  128 + // 校验每行明细数量之和是否等于行领料数量
  129 + List<MesWmProductionIssueLineDO> lines = issueLineService.getProductionIssueLineListByIssueId(id);
  130 + for (MesWmProductionIssueLineDO line : lines) {
  131 + List<MesWmProductionIssueDetailDO> details = issueDetailService.getProductionIssueDetailListByLineId(line.getId());
  132 + BigDecimal totalDetailQty = CollectionUtils.getSumValue(details,
  133 + MesWmProductionIssueDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
  134 + if (line.getQuantity() != null && totalDetailQty.compareTo(line.getQuantity()) != 0) {
  135 + throw exception(WM_PRODUCTION_ISSUE_DETAIL_QUANTITY_MISMATCH);
  136 + }
  137 + }
  138 +
  139 + // 执行拣货(待拣货 → 待执行领出)
  140 + issueMapper.updateById(new MesWmProductionIssueDO()
  141 + .setId(id).setStatus(MesWmProductionIssueStatusEnum.APPROVED.getStatus()));
  142 + }
  143 +
  144 + @Override
  145 + @Transactional(rollbackFor = Exception.class)
98 146 public void finishProductionIssue(Long id) {
99   - // 1.1 校验存在
100   - MesWmProductionIssueDO issue = validateIssueExists(id);
101   - // 1.2 校验状态:只有草稿才允许完成
102   - if (ObjUtil.notEqual(MesWmProductionIssueStatusEnum.PREPARE.getStatus(), issue.getStatus())) {
  147 + // 校验存在
  148 + MesWmProductionIssueDO issue = validateProductionIssueExists(id);
  149 + if (ObjUtil.notEqual(MesWmProductionIssueStatusEnum.APPROVED.getStatus(), issue.getStatus())) {
103 150 throw exception(WM_PRODUCTION_ISSUE_STATUS_INVALID);
104 151 }
105 152  
106   - // 2. 草稿 → 已完成
107   - // TODO @芋艿:库存扣减逻辑待完善(需要确定仓库ID的来源,可能需要在 line 或 detail 表中添加 warehouseId 字段)
  153 + // 遍历所有明细,更新库存台账(扣减库存)
  154 + // TODO @芋艿:【后续在弄】这里可能有点问题;缺少库存更新;后面在弄;
  155 + List<MesWmProductionIssueDetailDO> details = issueDetailService.getProductionIssueDetailListByIssueId(id);
  156 + for (MesWmProductionIssueDetailDO detail : details) {
  157 + // materialStockService.decreaseStock(
  158 + // detail.getItemId(), detail.getWarehouseId(), detail.getLocationId(), detail.getAreaId(),
  159 + // detail.getBatchId(), detail.getQuantity(), issue.getWorkOrderId(), null, null);
  160 + }
  161 +
  162 + // 更新出库单状态
108 163 issueMapper.updateById(new MesWmProductionIssueDO()
109 164 .setId(id).setStatus(MesWmProductionIssueStatusEnum.FINISHED.getStatus()));
110 165 }
111 166  
112   - private MesWmProductionIssueDO validateIssueExists(Long id) {
  167 + @Override
  168 + @Transactional(rollbackFor = Exception.class)
  169 + public void cancelProductionIssue(Long id) {
  170 + // 校验存在
  171 + MesWmProductionIssueDO issue = validateProductionIssueExists(id);
  172 + // 已完成和已取消不允许取消
  173 + if (ObjectUtils.equalsAny(issue.getStatus(),
  174 + MesWmProductionIssueStatusEnum.FINISHED.getStatus(),
  175 + MesWmProductionIssueStatusEnum.CANCELED.getStatus())) {
  176 + throw exception(WM_PRODUCTION_ISSUE_CANCEL_NOT_ALLOWED);
  177 + }
  178 + // 取消
  179 + issueMapper.updateById(new MesWmProductionIssueDO()
  180 + .setId(id).setStatus(MesWmProductionIssueStatusEnum.CANCELED.getStatus()));
  181 + }
  182 +
  183 + @Override
  184 + public MesWmProductionIssueDO validateProductionIssueExists(Long id) {
113 185 MesWmProductionIssueDO issue = issueMapper.selectById(id);
114 186 if (issue == null) {
115 187 throw exception(WM_PRODUCTION_ISSUE_NOT_EXISTS);
... ... @@ -120,8 +192,8 @@ public class MesWmProductionIssueServiceImpl implements MesWmProductionIssueServ
120 192 /**
121 193 * 校验领料出库单存在且为准备中状态
122 194 */
123   - private MesWmProductionIssueDO validateIssueExistsAndPrepare(Long id) {
124   - MesWmProductionIssueDO issue = validateIssueExists(id);
  195 + private MesWmProductionIssueDO validateProductionIssueExistsAndPrepare(Long id) {
  196 + MesWmProductionIssueDO issue = validateProductionIssueExists(id);
125 197 if (ObjUtil.notEqual(MesWmProductionIssueStatusEnum.PREPARE.getStatus(), issue.getStatus())) {
126 198 throw exception(WM_PRODUCTION_ISSUE_STATUS_INVALID);
127 199 }
... ...