Commit 68f373f6c962aa73583186b86bf6515d13e09797

Authored by YunaiV
1 parent f004611c

✨ feat(mes): 添加转移单及转移单行的字段校验和状态管理

新增转移单及转移单行相关功能,包含字段的必填校验和状态管理逻辑。更新了相关数据结构,确保在创建和提交转移单时进行必要的验证,提升系统的稳定性和数据一致性。

- 添加转移单字段的必填校验
- 更新转移单状态管理逻辑
- 增强数据结构的完整性
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/MesWmTransferSaveReqVO.java
... ... @@ -5,6 +5,8 @@ import lombok.Data;
5 5  
6 6 import java.time.LocalDateTime;
7 7  
  8 +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
  9 +
8 10 @Schema(description = "管理后台 - MES 转移单 Save Request VO")
9 11 @Data
10 12 public class MesWmTransferSaveReqVO {
... ... @@ -12,16 +14,17 @@ public class MesWmTransferSaveReqVO {
12 14 @Schema(description = "编号", example = "1")
13 15 private Long id;
14 16  
15   - @Schema(description = "转移单编号", example = "TR2026020001")
  17 + // TODO @AI:validator 参数校验需要添加
  18 + @Schema(description = "转移单编号", requiredMode = REQUIRED, example = "TR2026020001")
16 19 private String code;
17 20  
18   - @Schema(description = "转移单名称", example = "钢板转移单")
  21 + @Schema(description = "转移单名称", requiredMode = REQUIRED, example = "钢板转移单")
19 22 private String name;
20 23  
21   - @Schema(description = "转移单类型", example = "1")
  24 + @Schema(description = "转移单类型", requiredMode = REQUIRED, example = "1")
22 25 private Integer type;
23 26  
24   - @Schema(description = "是否配送", example = "true")
  27 + @Schema(description = "是否配送", requiredMode = REQUIRED, example = "true")
25 28 private Boolean deliveryFlag;
26 29  
27 30 @Schema(description = "收货人", example = "张三")
... ... @@ -39,15 +42,9 @@ public class MesWmTransferSaveReqVO {
39 42 @Schema(description = "运输单号", example = "SF123456789")
40 43 private String shippingNumber;
41 44  
42   - @Schema(description = "是否已确认", example = "false")
43   - private Boolean confirmFlag;
44   -
45   - @Schema(description = "转移日期")
  45 + @Schema(description = "转移日期", requiredMode = REQUIRED)
46 46 private LocalDateTime transferDate;
47 47  
48   - @Schema(description = "单据状态", example = "0")
49   - private Integer status;
50   -
51 48 @Schema(description = "备注", example = "备注信息")
52 49 private String remark;
53 50  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transfer/MesWmTransferDetailDO.java
... ... @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
5 8 import com.baomidou.mybatisplus.annotation.KeySequence;
6 9 import com.baomidou.mybatisplus.annotation.TableId;
7 10 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -57,17 +60,22 @@ public class MesWmTransferDetailDO extends BaseDO {
57 60 */
58 61 private Long batchId;
59 62  
60   - // TODO @AI:需要 @
61 63 /**
62 64 * 移入仓库编号
  65 + *
  66 + * 关联 {@link MesWmWarehouseDO#getId()}
63 67 */
64 68 private Long toWarehouseId;
65 69 /**
66 70 * 移入库区编号
  71 + *
  72 + * 关联 {@link MesWmWarehouseLocationDO#getId()}
67 73 */
68 74 private Long toLocationId;
69 75 /**
70 76 * 移入库位编号
  77 + *
  78 + * 关联 {@link MesWmWarehouseAreaDO#getId()}
71 79 */
72 80 private Long toAreaId;
73 81  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transfer/MesWmTransferLineDO.java
... ... @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
5 8 import com.baomidou.mybatisplus.annotation.KeySequence;
6 9 import com.baomidou.mybatisplus.annotation.TableId;
7 10 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -54,17 +57,22 @@ public class MesWmTransferLineDO extends BaseDO {
54 57 */
55 58 private Long batchId;
56 59  
57   - // TODO @AI:需要 @
58 60 /**
59 61 * 移出仓库编号
  62 + *
  63 + * 关联 {@link MesWmWarehouseDO#getId()}
60 64 */
61 65 private Long fromWarehouseId;
62 66 /**
63 67 * 移出库区编号
  68 + *
  69 + * 关联 {@link MesWmWarehouseLocationDO#getId()}
64 70 */
65 71 private Long fromLocationId;
66 72 /**
67 73 * 移出库位编号
  74 + *
  75 + * 关联 {@link MesWmWarehouseAreaDO#getId()}
68 76 */
69 77 private Long fromAreaId;
70 78  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -403,6 +403,8 @@ public interface ErrorCodeConstants {
403 403 ErrorCode WM_TRANSFER_NOT_CONFIRMED = new ErrorCode(1_040_710_004, "只有待确认状态的转移单才可执行确认");
404 404 ErrorCode WM_TRANSFER_NOT_UNEXECUTE = new ErrorCode(1_040_710_005, "只有待执行状态的转移单才可完成");
405 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, "转移单行数量与明细数量不一致");
406 408 ErrorCode WM_TRANSFER_LINE_NOT_EXISTS = new ErrorCode(1_040_710_100, "转移单行不存在");
407 409 ErrorCode WM_TRANSFER_DETAIL_NOT_EXISTS = new ErrorCode(1_040_710_200, "调拨明细不存在");
408 410  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmTransferStatusEnum.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.transfer.vo.MesWmTransferSaveReqVO;
4 5 import cn.iocoder.yudao.module.mes.enums.MesOrderStatusConstants;
  6 +import cn.iocoder.yudao.module.mes.service.wm.transfer.MesWmTransferService;
5 7 import lombok.AllArgsConstructor;
6 8 import lombok.Getter;
7 9  
... ... @@ -16,29 +18,40 @@ import java.util.Arrays;
16 18 @AllArgsConstructor
17 19 public enum MesWmTransferStatusEnum implements ArrayValuable<Integer> {
18 20  
19   - // TODO @AI:/Users/yunai/Java/yudao-all-in-one/ruoyi-vue-pro/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmOutsourceReceiptStatusEnum.java,增加 “对应方法”
20 21 /**
21 22 * 草稿
  23 + *
  24 + * 对应方法:{@link MesWmTransferService#createTransfer(MesWmTransferSaveReqVO)}
22 25 */
23 26 PREPARE(MesOrderStatusConstants.PREPARE, "草稿"),
24 27 /**
25 28 * 待确认(仅配送模式)
  29 + *
  30 + * 对应方法:{@link MesWmTransferService#submitTransfer(Long)}
26 31 */
27 32 UNCONFIRMED(MesOrderStatusConstants.CONFIRMED, "待确认"),
28 33 /**
29 34 * 待上架
  35 + *
  36 + * 对应方法:{@link MesWmTransferService#submitTransfer(Long)}、{@link MesWmTransferService#confirmTransfer(Long)}
30 37 */
31 38 UNSTOCK(MesOrderStatusConstants.APPROVING, "待上架"),
32 39 /**
33 40 * 待执行
  41 + *
  42 + * 对应方法:调拨明细维护完成后进入待执行状态
34 43 */
35 44 UNEXECUTE(MesOrderStatusConstants.APPROVED, "待执行"),
36 45 /**
37 46 * 已完成
  47 + *
  48 + * 对应方法:{@link MesWmTransferService#finishTransfer(Long)}
38 49 */
39 50 FINISHED(MesOrderStatusConstants.FINISHED, "已完成"),
40 51 /**
41 52 * 已取消
  53 + *
  54 + * 对应方法:{@link MesWmTransferService#cancelTransfer(Long)}
42 55 */
43 56 CANCELED(MesOrderStatusConstants.CANCELLED, "已取消");
44 57  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.transfer;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockFreezeReqVO;
5 8 import cn.iocoder.yudao.module.mes.controller.admin.wm.transfer.vo.MesWmTransferPageReqVO;
6 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.transfer.vo.MesWmTransferSaveReqVO;
7 10 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer.MesWmTransferDO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer.MesWmTransferDetailDO;
  12 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer.MesWmTransferLineDO;
8 13 import cn.iocoder.yudao.module.mes.dal.mysql.wm.transfer.MesWmTransferMapper;
9 14 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransferStatusEnum;
10   -import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransferTypeEnum;
  15 +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
11 16 import jakarta.annotation.Resource;
12 17 import org.springframework.stereotype.Service;
  18 +import org.springframework.transaction.annotation.Transactional;
13 19 import org.springframework.validation.annotation.Validated;
14 20  
  21 +import java.math.BigDecimal;
  22 +import java.util.List;
  23 +
15 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16 25 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
17 26  
... ... @@ -25,11 +34,12 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
25 34 @Resource
26 35 private MesWmTransferMapper transferMapper;
27 36  
28   - // TODO @AI:MesWmTransferLineService、MesWmTransferDetailService 类不存在;
29 37 @Resource
30 38 private MesWmTransferLineService transferLineService;
31 39 @Resource
32 40 private MesWmTransferDetailService transferDetailService;
  41 + @Resource
  42 + private MesWmMaterialStockService materialStockService;
33 43  
34 44 @Override
35 45 public Long createTransfer(MesWmTransferSaveReqVO createReqVO) {
... ... @@ -55,8 +65,8 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
55 65 transferMapper.updateById(updateObj);
56 66 }
57 67  
58   - // TODO @AI:少了事务;
59 68 @Override
  69 + @Transactional(rollbackFor = Exception.class)
60 70 public void deleteTransfer(Long id) {
61 71 // 校验存在 + 草稿状态
62 72 validateTransferExistsAndDraft(id);
... ... @@ -79,23 +89,29 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
79 89 }
80 90  
81 91 @Override
  92 + @Transactional(rollbackFor = Exception.class)
82 93 public void submitTransfer(Long id) {
83 94 // 校验存在 + 草稿状态
84 95 MesWmTransferDO transfer = validateTransferExistsAndDraft(id);
  96 + List<MesWmTransferLineDO> lines = transferLineService.getTransferLineListByTransferId(id);
  97 + if (CollUtil.isEmpty(lines)) {
  98 + throw exception(WM_TRANSFER_NO_LINE);
  99 + }
85 100  
86   - // TODO @芋艿:这里的逻辑对齐不对;需要再次看看;
87   - // 根据转移单类型设置状态
88   - // 内部调拨:草稿 -> 待上架
89   - // 外部调拨:草稿 -> 待确认
90   - if (MesWmTransferTypeEnum.INNER.getType().equals(transfer.getType())) {
91   - transfer.setStatus(MesWmTransferStatusEnum.UNSTOCK.getStatus());
  101 + // 先按是否配送决定是否进入待确认,并冻结来源库存
  102 + // TODO @AI:!Boolean.TRUE.equals 改成 FALSE 平判断,减少取反。
  103 + if (Boolean.TRUE.equals(transfer.getDeliveryFlag()) && !Boolean.TRUE.equals(transfer.getConfirmFlag())) {
  104 + transfer.setStatus(MesWmTransferStatusEnum.UNCONFIRMED.getStatus()).setConfirmFlag(false);
  105 + // TODO @AI:这个是不是应该 lineservice 里;
  106 + freezeTransferLineStocks(lines, true);
92 107 } else {
93   - transfer.setStatus(MesWmTransferStatusEnum.UNCONFIRMED.getStatus());
  108 + transfer.setStatus(MesWmTransferStatusEnum.UNSTOCK.getStatus());
94 109 }
95 110 transferMapper.updateById(transfer);
96 111 }
97 112  
98 113 @Override
  114 + @Transactional(rollbackFor = Exception.class)
99 115 public void confirmTransfer(Long id) {
100 116 // 校验存在 + 待确认状态
101 117 MesWmTransferDO transfer = validateTransferExistsAndConfirm(id);
... ... @@ -107,9 +123,22 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
107 123 }
108 124  
109 125 @Override
  126 + @Transactional(rollbackFor = Exception.class)
110 127 public void finishTransfer(Long id) {
111 128 // 校验存在 + 待执行状态
112 129 MesWmTransferDO transfer = validateTransferExistsAndUnexecute(id);
  130 + List<MesWmTransferLineDO> lines = transferLineService.getTransferLineListByTransferId(id);
  131 + if (CollUtil.isEmpty(lines)) {
  132 + throw exception(WM_TRANSFER_NO_LINE);
  133 + }
  134 +
  135 + // 校验每行明细数量之和等于行数量
  136 + validateTransferDetailQuantity(lines);
  137 +
  138 + // 配送模式下,执行完成后解除来源库存冻结
  139 + if (Boolean.TRUE.equals(transfer.getDeliveryFlag())) {
  140 + freezeTransferLineStocks(lines, false);
  141 + }
113 142  
114 143 // TODO: 执行库存转移逻辑
115 144  
... ... @@ -119,9 +148,17 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
119 148 }
120 149  
121 150 @Override
  151 + @Transactional(rollbackFor = Exception.class)
122 152 public void cancelTransfer(Long id) {
123 153 // 校验存在 + 非已完成/已取消状态
124 154 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 + }
125 162  
126 163 // 更新状态 -> 已取消
127 164 transfer.setStatus(MesWmTransferStatusEnum.CANCELED.getStatus());
... ... @@ -131,7 +168,6 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
131 168 @Override
132 169 public MesWmTransferDO validateTransferEditable(Long id) {
133 170 MesWmTransferDO transfer = transferMapper.selectById(id);
134   - // TODO @AI:错误码缺少;是不是少了 WM_
135 171 if (transfer == null) {
136 172 throw exception(WM_TRANSFER_NOT_EXISTS);
137 173 }
... ... @@ -203,4 +239,31 @@ public class MesWmTransferServiceImpl implements MesWmTransferService {
203 239 return transfer;
204 240 }
205 241  
  242 + // TODO @AI:参考别的模块,不用独立方法,直接写在 UNEXECUTE 变更里里;。目前还少一个操作; UNEXECUTE(MesOrderStatusConstants.APPROVED, "待执行"), 你看看别的模块;
  243 + private void validateTransferDetailQuantity(List<MesWmTransferLineDO> lines) {
  244 + for (MesWmTransferLineDO line : lines) {
  245 + List<MesWmTransferDetailDO> details = transferDetailService.getTransferDetailListByLineId(line.getId());
  246 + BigDecimal totalDetailQty = CollectionUtils.getSumValue(details,
  247 + MesWmTransferDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
  248 + if (line.getQuantity() != null && totalDetailQty.compareTo(line.getQuantity()) != 0) {
  249 + throw exception(WM_TRANSFER_DETAIL_QUANTITY_MISMATCH);
  250 + }
  251 + }
  252 + }
  253 +
  254 + private void freezeTransferLineStocks(List<MesWmTransferLineDO> lines, boolean frozen) {
  255 + if (CollUtil.isEmpty(lines)) {
  256 + return;
  257 + }
  258 + for (MesWmTransferLineDO line : lines) {
  259 + if (line.getMaterialStockId() == null) {
  260 + continue;
  261 + }
  262 + MesWmMaterialStockFreezeReqVO freezeReqVO = new MesWmMaterialStockFreezeReqVO();
  263 + freezeReqVO.setId(line.getMaterialStockId());
  264 + freezeReqVO.setFrozen(frozen);
  265 + materialStockService.updateMaterialStockFrozen(freezeReqVO);
  266 + }
  267 + }
  268 +
206 269 }
... ...