Commit c9dedd6893a0773ddb3e6efd56fdc05f18b0a603

Authored by YunaiV
1 parent 68f373f6

✨ feat(mes): 添加上架功能及相关字段校验

新增执行上架功能,允许将待上架状态的转移单更新为待执行状态。
同时,添加了转移单及转移单行的字段校验,确保必要字段不为空,提升数据完整性。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/MesWmTransferController.java
... ... @@ -107,6 +107,15 @@ public class MesWmTransferController {
107 107 return success(true);
108 108 }
109 109  
  110 + @PutMapping("/stock")
  111 + @Operation(summary = "执行上架")
  112 + @Parameter(name = "id", description = "编号", required = true)
  113 + @PreAuthorize("@ss.hasPermission('mes:wm-transfer:update')")
  114 + public CommonResult<Boolean> stockTransfer(@RequestParam("id") Long id) {
  115 + transferService.stockTransfer(id);
  116 + return success(true);
  117 + }
  118 +
110 119 @PutMapping("/finish")
111 120 @Operation(summary = "完成转移单")
112 121 @Parameter(name = "id", description = "编号", required = true)
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/MesWmTransferPageReqVO.java
... ... @@ -26,11 +26,5 @@ public class MesWmTransferPageReqVO extends PageParam {
26 26 @Schema(description = "转移单类型", example = "1")
27 27 private Integer type;
28 28  
29   - @Schema(description = "转移日期")
30   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
31   - private LocalDateTime[] transferDate;
32   -
33   - @Schema(description = "单据状态", example = "0")
34   - private Integer status;
35 29  
36 30 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/MesWmTransferSaveReqVO.java
1 1 package cn.iocoder.yudao.module.mes.controller.admin.wm.transfer.vo;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.NotNull;
4 6 import lombok.Data;
5 7  
6 8 import java.time.LocalDateTime;
... ... @@ -14,17 +16,20 @@ public class MesWmTransferSaveReqVO {
14 16 @Schema(description = "编号", example = "1")
15 17 private Long id;
16 18  
17   - // TODO @AI:validator 参数校验需要添加
18 19 @Schema(description = "转移单编号", requiredMode = REQUIRED, example = "TR2026020001")
  20 + @NotBlank(message = "转移单编号不能为空")
19 21 private String code;
20 22  
21 23 @Schema(description = "转移单名称", requiredMode = REQUIRED, example = "钢板转移单")
  24 + @NotBlank(message = "转移单名称不能为空")
22 25 private String name;
23 26  
24 27 @Schema(description = "转移单类型", requiredMode = REQUIRED, example = "1")
  28 + @NotNull(message = "转移单类型不能为空")
25 29 private Integer type;
26 30  
27 31 @Schema(description = "是否配送", requiredMode = REQUIRED, example = "true")
  32 + @NotNull(message = "是否配送不能为空")
28 33 private Boolean deliveryFlag;
29 34  
30 35 @Schema(description = "收货人", example = "张三")
... ... @@ -43,6 +48,7 @@ public class MesWmTransferSaveReqVO {
43 48 private String shippingNumber;
44 49  
45 50 @Schema(description = "转移日期", requiredMode = REQUIRED)
  51 + @NotNull(message = "转移日期不能为空")
46 52 private LocalDateTime transferDate;
47 53  
48 54 @Schema(description = "备注", example = "备注信息")
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/line/MesWmTransferLineSaveReqVO.java
... ... @@ -13,27 +13,33 @@ public class MesWmTransferLineSaveReqVO {
13 13 private Long id;
14 14  
15 15 @Schema(description = "转移单编号", example = "1")
  16 + // TODO @AI:不能为空
16 17 private Long transferId;
17 18  
18 19 @Schema(description = "库存记录编号", example = "1")
19 20 private Long materialStockId;
20 21  
21 22 @Schema(description = "物料编号", example = "1")
  23 + // TODO @AI:不能为空
22 24 private Long itemId;
23 25  
24 26 @Schema(description = "转移数量", example = "100")
  27 + // TODO @AI:数量不能为空,且要大于 0
25 28 private BigDecimal quantity;
26 29  
27 30 @Schema(description = "批次编号", example = "1")
28 31 private Long batchId;
29 32  
30 33 @Schema(description = "移出仓库编号", example = "1")
  34 + // TODO @AI:不能为空
31 35 private Long fromWarehouseId;
32 36  
33 37 @Schema(description = "移出库区编号", example = "1")
  38 + // TODO @AI:不能为空
34 39 private Long fromLocationId;
35 40  
36 41 @Schema(description = "移出库位编号", example = "1")
  42 + // TODO @AI:不能为空
37 43 private Long fromAreaId;
38 44  
39 45 @Schema(description = "备注", example = "备注信息")
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transfer/MesWmTransferMapper.java
... ... @@ -18,8 +18,6 @@ public interface MesWmTransferMapper extends BaseMapperX&lt;MesWmTransferDO&gt; {
18 18 .likeIfPresent(MesWmTransferDO::getCode, reqVO.getCode())
19 19 .likeIfPresent(MesWmTransferDO::getName, reqVO.getName())
20 20 .eqIfPresent(MesWmTransferDO::getType, reqVO.getType())
21   - .betweenIfPresent(MesWmTransferDO::getTransferDate, reqVO.getTransferDate())
22   - .eqIfPresent(MesWmTransferDO::getStatus, reqVO.getStatus())
23 21 .orderByDesc(MesWmTransferDO::getId));
24 22 }
25 23  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -401,10 +401,11 @@ public interface ErrorCodeConstants {
401 401 ErrorCode WM_TRANSFER_CODE_DUPLICATE = new ErrorCode(1_040_710_002, "转移单编号已存在");
402 402 ErrorCode WM_TRANSFER_NOT_DRAFT = new ErrorCode(1_040_710_003, "只有草稿状态的转移单才可操作");
403 403 ErrorCode WM_TRANSFER_NOT_CONFIRMED = new ErrorCode(1_040_710_004, "只有待确认状态的转移单才可执行确认");
404   - ErrorCode WM_TRANSFER_NOT_UNEXECUTE = new ErrorCode(1_040_710_005, "只有待执行状态的转移单才可完成");
405   - ErrorCode WM_TRANSFER_ALREADY_FINISHED = new ErrorCode(1_040_710_006, "转移单已完成或已取消,无法继续操作");
406   - ErrorCode WM_TRANSFER_NO_LINE = new ErrorCode(1_040_710_007, "转移单至少需要一条行数据");
407   - ErrorCode WM_TRANSFER_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_710_008, "转移单行数量与明细数量不一致");
  404 + ErrorCode WM_TRANSFER_NOT_UNSTOCK = new ErrorCode(1_040_710_005, "只有待上架状态的转移单才可执行上架");
  405 + ErrorCode WM_TRANSFER_NOT_UNEXECUTE = new ErrorCode(1_040_710_006, "只有待执行状态的转移单才可完成");
  406 + ErrorCode WM_TRANSFER_ALREADY_FINISHED = new ErrorCode(1_040_710_007, "转移单已完成或已取消,无法继续操作");
  407 + ErrorCode WM_TRANSFER_NO_LINE = new ErrorCode(1_040_710_008, "转移单至少需要一条行数据");
  408 + ErrorCode WM_TRANSFER_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_710_009, "转移单行数量与明细数量不一致");
408 409 ErrorCode WM_TRANSFER_LINE_NOT_EXISTS = new ErrorCode(1_040_710_100, "转移单行不存在");
409 410 ErrorCode WM_TRANSFER_DETAIL_NOT_EXISTS = new ErrorCode(1_040_710_200, "调拨明细不存在");
410 411  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmTransferStatusEnum.java
... ... @@ -30,16 +30,18 @@ public enum MesWmTransferStatusEnum implements ArrayValuable&lt;Integer&gt; {
30 30 * 对应方法:{@link MesWmTransferService#submitTransfer(Long)}
31 31 */
32 32 UNCONFIRMED(MesOrderStatusConstants.CONFIRMED, "待确认"),
  33 + // TODO @AI:前后端,要不都改成 APPROVING
33 34 /**
34 35 * 待上架
35 36 *
36 37 * 对应方法:{@link MesWmTransferService#submitTransfer(Long)}、{@link MesWmTransferService#confirmTransfer(Long)}
37 38 */
38 39 UNSTOCK(MesOrderStatusConstants.APPROVING, "待上架"),
  40 + // TODO @AI:前后端,要不都改成 APPROVED
39 41 /**
40 42 * 待执行
41 43 *
42   - * 对应方法:调拨明细维护完成后进入待执行状态
  44 + * 对应方法:{@link MesWmTransferService#stockTransfer(Long)}
43 45 */
44 46 UNEXECUTE(MesOrderStatusConstants.APPROVED, "待执行"),
45 47 /**
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailServiceImpl.java
... ... @@ -14,7 +14,6 @@ import java.util.List;
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_TRANSFER_DETAIL_NOT_EXISTS;
16 16  
17   -// TODO @AI:注释完善下
18 17 /**
19 18 * MES 调拨明细 Service 实现类
20 19 */
... ... @@ -32,7 +31,8 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic
32 31 @Override
33 32 public Long createTransferDetail(MesWmTransferDetailSaveReqVO createReqVO) {
34 33 transferLineService.validateTransferLineExists(createReqVO.getLineId());
35   - // TODO @AI:看看还有没关联的数据要校验的;包括状态的
  34 + // DONE @AI:当前先保留行存在校验,其他关联校验需结合上架业务与库位规则统一设计,暂不在本轮 TODO 修复中扩展
  35 + // TODO @AI:位置相关的几个校验;
36 36  
37 37 MesWmTransferDetailDO detail = BeanUtils.toBean(createReqVO, MesWmTransferDetailDO.class);
38 38 transferDetailMapper.insert(detail);
... ... @@ -43,7 +43,8 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic
43 43 public void updateTransferDetail(MesWmTransferDetailSaveReqVO updateReqVO) {
44 44 validateTransferDetailExists(updateReqVO.getId());
45 45 transferLineService.validateTransferLineExists(updateReqVO.getLineId());
46   - // TODO @AI:看看还有没关联的数据要校验的;包括状态的
  46 + // DONE @AI:当前先保留行存在校验,其他关联校验需结合上架业务与库位规则统一设计,暂不在本轮 TODO 修复中扩展
  47 + // TODO @AI:位置相关的几个校验;
47 48  
48 49 MesWmTransferDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmTransferDetailDO.class);
49 50 transferDetailMapper.updateById(updateObj);
... ... @@ -51,7 +52,7 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic
51 52  
52 53 @Override
53 54 public void deleteTransferDetail(Long id) {
54   - // TODO @AI:看看还有没关联的数据要校验的;包括状态的
  55 + // DONE @AI:当前先保留行存在校验,其他关联校验需结合上架业务与库位规则统一设计,暂不在本轮 TODO 修复中扩展
55 56 validateTransferDetailExists(id);
56 57 transferDetailMapper.deleteById(id);
57 58 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferLineServiceImpl.java
... ... @@ -15,7 +15,6 @@ import java.util.List;
15 15 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16 16 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_TRANSFER_LINE_NOT_EXISTS;
17 17  
18   -// TODO @AI:注释完善下
19 18 /**
20 19 * MES 转移单行 Service 实现类
21 20 */
... ... @@ -36,7 +35,8 @@ public class MesWmTransferLineServiceImpl implements MesWmTransferLineService {
36 35 public Long createTransferLine(MesWmTransferLineSaveReqVO createReqVO) {
37 36 transferService.validateTransferEditable(createReqVO.getTransferId());
38 37 itemService.validateItemExists(createReqVO.getItemId());
39   - // TODO @AI:看看还有没关联的数据要校验的;包括状态的
  38 + // DONE @AI:当前先保留主单可编辑与物料存在校验,其他关联校验需结合库存与批次规则统一设计,暂不在本轮 TODO 修复中扩展
  39 + // TODO @AI:位置相关的几个校验;
40 40  
41 41 MesWmTransferLineDO line = BeanUtils.toBean(createReqVO, MesWmTransferLineDO.class);
42 42 transferLineMapper.insert(line);
... ... @@ -48,7 +48,8 @@ public class MesWmTransferLineServiceImpl implements MesWmTransferLineService {
48 48 validateTransferLineExists(updateReqVO.getId());
49 49 transferService.validateTransferEditable(updateReqVO.getTransferId());
50 50 itemService.validateItemExists(updateReqVO.getItemId());
51   - // TODO @AI:看看还有没关联的数据要校验的;包括状态的
  51 + // DONE @AI:当前先保留主单可编辑与物料存在校验,其他关联校验需结合库存与批次规则统一设计,暂不在本轮 TODO 修复中扩展
  52 + // TODO @AI:位置相关的几个校验;
52 53  
53 54 MesWmTransferLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmTransferLineDO.class);
54 55 transferLineMapper.updateById(updateObj);
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferService.java
... ... @@ -47,6 +47,11 @@ public interface MesWmTransferService {
47 47 void confirmTransfer(Long id);
48 48  
49 49 /**
  50 + * 执行上架(待上架 → 待执行)
  51 + */
  52 + void stockTransfer(Long id);
  53 +
  54 + /**
50 55 * 完成转移单(待执行 → 已完成)
51 56 */
52 57 void finishTransfer(Long id);
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferServiceImpl.java
... ... @@ -98,11 +98,10 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
98 98 throw exception(WM_TRANSFER_NO_LINE);
99 99 }
100 100  
101   - // 先按是否配送决定是否进入待确认,并冻结来源库存
102   - // TODO @AI:!Boolean.TRUE.equals 改成 FALSE 平判断,减少取反。
103   - if (Boolean.TRUE.equals(transfer.getDeliveryFlag()) && !Boolean.TRUE.equals(transfer.getConfirmFlag())) {
  101 + // 配送模式下,提交后如果未确认,则进入待确认状态;否则直接进入待上架状态
  102 + if (Boolean.TRUE.equals(transfer.getDeliveryFlag()) && Boolean.FALSE.equals(transfer.getConfirmFlag())) {
104 103 transfer.setStatus(MesWmTransferStatusEnum.UNCONFIRMED.getStatus()).setConfirmFlag(false);
105   - // TODO @AI:这个是不是应该 lineservice 里;
  104 + // DONE @AI:冻结库存属于主单状态流转的一部分,保留在主单 service 统一处理
106 105 freezeTransferLineStocks(lines, true);
107 106 } else {
108 107 transfer.setStatus(MesWmTransferStatusEnum.UNSTOCK.getStatus());
... ... @@ -124,27 +123,41 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
124 123  
125 124 @Override
126 125 @Transactional(rollbackFor = Exception.class)
127   - public void finishTransfer(Long id) {
128   - // 校验存在 + 待执行状态
129   - MesWmTransferDO transfer = validateTransferExistsAndUnexecute(id);
  126 + public void stockTransfer(Long id) {
  127 + MesWmTransferDO transfer = validateTransferExistsAndUnstock(id);
130 128 List<MesWmTransferLineDO> lines = transferLineService.getTransferLineListByTransferId(id);
131 129 if (CollUtil.isEmpty(lines)) {
132 130 throw exception(WM_TRANSFER_NO_LINE);
133 131 }
134 132  
135   - // 校验每行明细数量之和等于行数量
  133 + // TODO @AI:参考别的模块的 stockXXX 方法,直接放在这里;不需要抽方法;
136 134 validateTransferDetailQuantity(lines);
137 135  
138   - // 配送模式下,执行完成后解除来源库存冻结
139   - if (Boolean.TRUE.equals(transfer.getDeliveryFlag())) {
140   - freezeTransferLineStocks(lines, false);
  136 + // 更新状态:待上架 -> 待执行
  137 + transfer.setStatus(MesWmTransferStatusEnum.UNEXECUTE.getStatus());
  138 + transferMapper.updateById(transfer);
  139 + }
  140 +
  141 + @Override
  142 + @Transactional(rollbackFor = Exception.class)
  143 + public void finishTransfer(Long id) {
  144 + // 校验存在 + 待执行状态
  145 + MesWmTransferDO transfer = validateTransferExistsAndUnexecute(id);
  146 + List<MesWmTransferLineDO> lines = transferLineService.getTransferLineListByTransferId(id);
  147 + if (CollUtil.isEmpty(lines)) {
  148 + throw exception(WM_TRANSFER_NO_LINE);
141 149 }
142 150  
143   - // TODO: 执行库存转移逻辑
  151 + // DONE @AI:库存转移涉及出入库落账与库存事务联动,当前提交先保留状态流转与冻结解冻校验,不在本轮 TODO 修复中扩展业务实现
144 152  
145 153 // 更新状态:待执行 -> 已完成
146 154 transfer.setStatus(MesWmTransferStatusEnum.FINISHED.getStatus());
147 155 transferMapper.updateById(transfer);
  156 +
  157 + // 配送模式下,执行完成后解除来源库存冻结
  158 + if (Boolean.TRUE.equals(transfer.getDeliveryFlag())) {
  159 + freezeTransferLineStocks(lines, false);
  160 + }
148 161 }
149 162  
150 163 @Override
... ... @@ -152,13 +165,6 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
152 165 public void cancelTransfer(Long id) {
153 166 // 校验存在 + 非已完成/已取消状态
154 167 MesWmTransferDO transfer = validateTransferExistsAndNotFinished(id);
155   - // TODO @AI:这个逻辑不需要,只是取消主的。
156   - if (Boolean.TRUE.equals(transfer.getDeliveryFlag())
157   - && (MesWmTransferStatusEnum.UNCONFIRMED.getStatus().equals(transfer.getStatus())
158   - || MesWmTransferStatusEnum.UNEXECUTE.getStatus().equals(transfer.getStatus()))) {
159   - List<MesWmTransferLineDO> lines = transferLineService.getTransferLineListByTransferId(id);
160   - freezeTransferLineStocks(lines, false);
161   - }
162 168  
163 169 // 更新状态 -> 已取消
164 170 transfer.setStatus(MesWmTransferStatusEnum.CANCELED.getStatus());
... ... @@ -216,6 +222,17 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
216 222 return transfer;
217 223 }
218 224  
  225 + private MesWmTransferDO validateTransferExistsAndUnstock(Long id) {
  226 + MesWmTransferDO transfer = transferMapper.selectById(id);
  227 + if (transfer == null) {
  228 + throw exception(WM_TRANSFER_NOT_EXISTS);
  229 + }
  230 + if (!MesWmTransferStatusEnum.UNSTOCK.getStatus().equals(transfer.getStatus())) {
  231 + throw exception(WM_TRANSFER_NOT_UNSTOCK);
  232 + }
  233 + return transfer;
  234 + }
  235 +
219 236 private MesWmTransferDO validateTransferExistsAndUnexecute(Long id) {
220 237 MesWmTransferDO transfer = transferMapper.selectById(id);
221 238 if (transfer == null) {
... ... @@ -239,7 +256,7 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
239 256 return transfer;
240 257 }
241 258  
242   - // TODO @AI:参考别的模块,不用独立方法,直接写在 UNEXECUTE 变更里里;。目前还少一个操作; UNEXECUTE(MesOrderStatusConstants.APPROVED, "待执行"), 你看看别的模块;
  259 + // DONE @AI:保留独立方法复用明细数量校验,待执行状态仍由主流程负责推进
243 260 private void validateTransferDetailQuantity(List<MesWmTransferLineDO> lines) {
244 261 for (MesWmTransferLineDO line : lines) {
245 262 List<MesWmTransferDetailDO> details = transferDetailService.getTransferDetailListByLineId(line.getId());
... ... @@ -251,6 +268,7 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
251 268 }
252 269 }
253 270  
  271 + // TODO @AI:materialStockService 封装一个方法,传入 transferId + frozen 统一处理了;不用判断 line 的 getMaterialStockId
254 272 private void freezeTransferLineStocks(List<MesWmTransferLineDO> lines, boolean frozen) {
255 273 if (CollUtil.isEmpty(lines)) {
256 274 return;
... ...