Commit deb6cac081fd2e6b2ad459e5b3050b5fa4042144

Authored by YunaiV
1 parent b759c474

✨ feat(mes): 增强产品入库单功能,新增数量校验和明细检查

新增上架数量的正数校验,确保入库单的数量大于0。同时,增加了收货单明细为空的错误提示,提升数据完整性和用户体验。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/detail/MesWmProductReceiptDetailSaveReqVO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.detail
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import jakarta.validation.constraints.NotNull;
  5 +import jakarta.validation.constraints.Positive;
5 6 import lombok.Data;
6 7  
7 8 import java.math.BigDecimal;
... ... @@ -27,6 +28,7 @@ public class MesWmProductReceiptDetailSaveReqVO {
27 28  
28 29 @Schema(description = "上架数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
29 30 @NotNull(message = "上架数量不能为空")
  31 + @Positive(message = "上架数量必须大于 0")
30 32 private BigDecimal quantity;
31 33  
32 34 @Schema(description = "批次编号", example = "1")
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -491,6 +491,7 @@ public interface ErrorCodeConstants {
491 491 ErrorCode WM_PRODUCT_RECPT_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_712_004, "明细上架总数与行收货数量不匹配");
492 492 ErrorCode WM_PRODUCT_RECPT_STATUS_ERROR = new ErrorCode(1_040_712_005, "收货单状态不正确");
493 493 ErrorCode WM_PRODUCT_RECPT_CANCEL_NOT_ALLOWED = new ErrorCode(1_040_712_006, "已完成或已取消的收货单不允许取消");
  494 + ErrorCode WM_PRODUCT_RECPT_NO_DETAIL = new ErrorCode(1_040_712_007, "收货单没有上架明细,无法执行入库");
494 495 ErrorCode WM_PRODUCT_RECPT_LINE_NOT_EXISTS = new ErrorCode(1_040_712_100, "产品收货单行不存在");
495 496 ErrorCode WM_PRODUCT_RECPT_DETAIL_NOT_EXISTS = new ErrorCode(1_040_712_200, "产品收货明细不存在");
496 497  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptDetailServiceImpl.java
... ... @@ -34,12 +34,10 @@ public class MesWmProductReceiptDetailServiceImpl implements MesWmProductReceipt
34 34  
35 35 @Override
36 36 public Long createProductReceiptDetail(MesWmProductReceiptDetailSaveReqVO createReqVO) {
37   - // 校验父单据存在且为可编辑状态
38   - productReceiptService.validateProductReceiptEditable(createReqVO.getReceiptId());
39   - // 校验库区关系
40   - warehouseAreaService.validateWarehouseAreaExists(
41   - createReqVO.getWarehouseId(), createReqVO.getLocationId(), createReqVO.getAreaId());
  37 + // 1. 校验关联数据
  38 + validateProductReceiptDetailSaveData(createReqVO);
42 39  
  40 + // 2. 新增
43 41 MesWmProductReceiptDetailDO detail = BeanUtils.toBean(createReqVO, MesWmProductReceiptDetailDO.class);
44 42 productReceiptDetailMapper.insert(detail);
45 43 return detail.getId();
... ... @@ -47,15 +45,13 @@ public class MesWmProductReceiptDetailServiceImpl implements MesWmProductReceipt
47 45  
48 46 @Override
49 47 public void updateProductReceiptDetail(MesWmProductReceiptDetailSaveReqVO updateReqVO) {
50   - // 校验存在
  48 + // 1.1 校验存在
51 49 MesWmProductReceiptDetailDO detail = validateProductReceiptDetailExists(updateReqVO.getId());
52   - // 校验父单据存在且为可编辑状态
53   - productReceiptService.validateProductReceiptEditable(detail.getReceiptId());
54   - // 校验库区关系
55   - warehouseAreaService.validateWarehouseAreaExists(
56   - updateReqVO.getWarehouseId(), updateReqVO.getLocationId(), updateReqVO.getAreaId());
  50 + // 1.2 校验关联数据
  51 + updateReqVO.setReceiptId(detail.getReceiptId());
  52 + validateProductReceiptDetailSaveData(updateReqVO);
57 53  
58   - // 更新
  54 + // 2. 更新
59 55 MesWmProductReceiptDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductReceiptDetailDO.class);
60 56 productReceiptDetailMapper.updateById(updateObj);
61 57 }
... ... @@ -63,7 +59,10 @@ public class MesWmProductReceiptDetailServiceImpl implements MesWmProductReceipt
63 59 @Override
64 60 public void deleteProductReceiptDetail(Long id) {
65 61 // 校验存在
66   - validateProductReceiptDetailExists(id);
  62 + MesWmProductReceiptDetailDO detail = validateProductReceiptDetailExists(id);
  63 + // 校验父单据存在且为可编辑状态
  64 + productReceiptService.validateProductReceiptEditable(detail.getReceiptId());
  65 +
67 66 // 删除
68 67 productReceiptDetailMapper.deleteById(id);
69 68 }
... ... @@ -101,4 +100,15 @@ public class MesWmProductReceiptDetailServiceImpl implements MesWmProductReceipt
101 100 return detail;
102 101 }
103 102  
  103 + /**
  104 + * 校验明细保存时的关联数据
  105 + */
  106 + private void validateProductReceiptDetailSaveData(MesWmProductReceiptDetailSaveReqVO reqVO) {
  107 + // 校验父单据存在且为可编辑状态
  108 + productReceiptService.validateProductReceiptEditable(reqVO.getReceiptId());
  109 + // 校验仓库层级关系
  110 + warehouseAreaService.validateWarehouseAreaExists(
  111 + reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
  112 + }
  113 +
104 114 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptLineServiceImpl.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.line.Me
6 6 import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.line.MesWmProductReceiptLineSaveReqVO;
7 7 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productreceipt.MesWmProductReceiptLineDO;
8 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productreceipt.MesWmProductReceiptLineMapper;
  9 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
9 10 import jakarta.annotation.Resource;
10 11 import org.springframework.context.annotation.Lazy;
11 12 import org.springframework.stereotype.Service;
... ... @@ -34,12 +35,15 @@ public class MesWmProductReceiptLineServiceImpl implements MesWmProductReceiptLi
34 35 @Resource
35 36 private MesWmProductReceiptDetailService productReceiptDetailService;
36 37  
  38 + @Resource
  39 + private MesMdItemService itemService;
  40 +
37 41 @Override
38 42 public Long createProductReceiptLine(MesWmProductReceiptLineSaveReqVO createReqVO) {
39   - // 校验父单据存在且为可编辑状态
40   - productReceiptService.validateProductReceiptEditable(createReqVO.getReceiptId());
  43 + // 1. 校验关联数据
  44 + validateProductReceiptLineSaveData(createReqVO);
41 45  
42   - // 新增
  46 + // 2. 新增
43 47 MesWmProductReceiptLineDO line = BeanUtils.toBean(createReqVO, MesWmProductReceiptLineDO.class);
44 48 productReceiptLineMapper.insert(line);
45 49 return line.getId();
... ... @@ -47,12 +51,13 @@ public class MesWmProductReceiptLineServiceImpl implements MesWmProductReceiptLi
47 51  
48 52 @Override
49 53 public void updateProductReceiptLine(MesWmProductReceiptLineSaveReqVO updateReqVO) {
50   - // 校验存在
  54 + // 1.1 校验存在
51 55 MesWmProductReceiptLineDO line = validateProductReceiptLineExists(updateReqVO.getId());
52   - // 校验父单据存在且为可编辑状态
53   - productReceiptService.validateProductReceiptEditable(line.getReceiptId());
  56 + // 1.2 校验关联数据
  57 + updateReqVO.setReceiptId(line.getReceiptId());
  58 + validateProductReceiptLineSaveData(updateReqVO);
54 59  
55   - // 更新
  60 + // 2. 更新
56 61 MesWmProductReceiptLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductReceiptLineDO.class);
57 62 productReceiptLineMapper.updateById(updateObj);
58 63 }
... ... @@ -61,7 +66,9 @@ public class MesWmProductReceiptLineServiceImpl implements MesWmProductReceiptLi
61 66 @Transactional(rollbackFor = Exception.class)
62 67 public void deleteProductReceiptLine(Long id) {
63 68 // 校验存在
64   - validateProductReceiptLineExists(id);
  69 + MesWmProductReceiptLineDO line = validateProductReceiptLineExists(id);
  70 + // 校验父单据存在且为可编辑状态
  71 + productReceiptService.validateProductReceiptEditable(line.getReceiptId());
65 72  
66 73 // 级联删除明细
67 74 productReceiptDetailService.deleteProductReceiptDetailByLineId(id);
... ... @@ -97,4 +104,14 @@ public class MesWmProductReceiptLineServiceImpl implements MesWmProductReceiptLi
97 104 return line;
98 105 }
99 106  
  107 + /**
  108 + * 校验行保存时的关联数据
  109 + */
  110 + private void validateProductReceiptLineSaveData(MesWmProductReceiptLineSaveReqVO reqVO) {
  111 + // 校验父单据存在且为可编辑状态
  112 + productReceiptService.validateProductReceiptEditable(reqVO.getReceiptId());
  113 + // 校验物料存在
  114 + itemService.validateItemExists(reqVO.getItemId());
  115 + }
  116 +
100 117 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
... ... @@ -12,15 +12,14 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderD
12 12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productreceipt.MesWmProductReceiptDO;
13 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productreceipt.MesWmProductReceiptDetailDO;
14 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productreceipt.MesWmProductReceiptLineDO;
15   -import cn.iocoder.yudao.module.mes.dal.mysql.wm.productreceipt.MesWmProductReceiptMapper;
16 15 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
17 16 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
18 17 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
19   -import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductReceiptStatusEnum;
  18 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.productreceipt.MesWmProductReceiptMapper;
20 19 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
  20 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductReceiptStatusEnum;
21 21 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
22 22 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
23   -import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
24 23 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
25 24 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
26 25 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
... ... @@ -54,9 +53,6 @@ public class MesWmProductReceiptServiceImpl implements MesWmProductReceiptServic
54 53 private MesWmProductReceiptDetailService productReceiptDetailService;
55 54  
56 55 @Resource
57   - private MesWmMaterialStockService materialStockService;
58   -
59   - @Resource
60 56 private MesWmTransactionService wmTransactionService;
61 57  
62 58 @Resource
... ... @@ -70,13 +66,10 @@ public class MesWmProductReceiptServiceImpl implements MesWmProductReceiptServic
70 66  
71 67 @Override
72 68 public Long createProductReceipt(MesWmProductReceiptSaveReqVO createReqVO) {
73   - // 校验编码唯一
74   - validateCodeUnique(null, createReqVO.getCode());
75   - // 校验工单存在并设置 itemId
76   - MesProWorkOrderDO workOrder = createReqVO.getWorkOrderId() != null ?
77   - workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId()) : null;
  69 + // 1. 校验关联数据
  70 + MesProWorkOrderDO workOrder = validateProductReceiptSaveData(createReqVO);
78 71  
79   - // 插入
  72 + // 2. 插入
80 73 MesWmProductReceiptDO receipt = BeanUtils.toBean(createReqVO, MesWmProductReceiptDO.class);
81 74 if (workOrder != null) {
82 75 receipt.setItemId(workOrder.getProductId());
... ... @@ -88,15 +81,12 @@ public class MesWmProductReceiptServiceImpl implements MesWmProductReceiptServic
88 81  
89 82 @Override
90 83 public void updateProductReceipt(MesWmProductReceiptSaveReqVO updateReqVO) {
91   - // 校验存在 + 草稿状态
  84 + // 1.1 校验存在 + 草稿状态
92 85 validateProductReceiptExistsAndDraft(updateReqVO.getId());
93   - // 校验编码唯一
94   - validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
95   - // 校验工单存在
96   - MesProWorkOrderDO workOrder = updateReqVO.getWorkOrderId() != null ?
97   - workOrderService.validateWorkOrderExists(updateReqVO.getWorkOrderId()) : null;
  86 + // 1.2 校验关联数据
  87 + MesProWorkOrderDO workOrder = validateProductReceiptSaveData(updateReqVO);
98 88  
99   - // 更新
  89 + // 2. 更新
100 90 MesWmProductReceiptDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductReceiptDO.class);
101 91 if (workOrder != null) {
102 92 updateObj.setItemId(workOrder.getProductId());
... ... @@ -179,23 +169,27 @@ public class MesWmProductReceiptServiceImpl implements MesWmProductReceiptServic
179 169 if (ObjUtil.notEqual(MesWmProductReceiptStatusEnum.APPROVED.getStatus(), receipt.getStatus())) {
180 170 throw exception(WM_PRODUCT_RECPT_STATUS_ERROR);
181 171 }
  172 + // 校验明细非空
  173 + List<MesWmProductReceiptDetailDO> details = productReceiptDetailService.getProductReceiptDetailListByRecptId(id);
  174 + if (CollUtil.isEmpty(details)) {
  175 + throw exception(WM_PRODUCT_RECPT_NO_DETAIL);
  176 + }
182 177  
183 178 // 创建库存事务
184   - createTransactionList(receipt);
  179 + createTransactionList(receipt, details);
185 180  
186 181 // 更新收货单状态
187 182 productReceiptMapper.updateById(new MesWmProductReceiptDO()
188 183 .setId(id).setStatus(MesWmProductReceiptStatusEnum.FINISHED.getStatus()));
189 184 }
190 185  
191   - private void createTransactionList(MesWmProductReceiptDO receipt) {
  186 + private void createTransactionList(MesWmProductReceiptDO receipt, List<MesWmProductReceiptDetailDO> details) {
192 187 // 1. 查询虚拟线边库
193 188 MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
194 189 MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
195 190 MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
196 191  
197 192 // 2. 遍历明细,每条明细产生 OUT(虚拟线边库扣减)+ IN(实际仓库增加)
198   - List<MesWmProductReceiptDetailDO> details = productReceiptDetailService.getProductReceiptDetailListByRecptId(receipt.getId());
199 193 for (MesWmProductReceiptDetailDO detail : details) {
200 194 // 2.1 先从虚拟线边库出库(库存减少)
201 195 Long outTransactionId = wmTransactionService.createTransaction(new MesWmTransactionSaveReqDTO()
... ... @@ -263,6 +257,18 @@ public class MesWmProductReceiptServiceImpl implements MesWmProductReceiptServic
263 257 }
264 258 return receipt;
265 259 }
  260 + /**
  261 + * 校验保存时的关联数据
  262 + *
  263 + * @return 工单对象(可能为 null)
  264 + */
  265 + private MesProWorkOrderDO validateProductReceiptSaveData(MesWmProductReceiptSaveReqVO reqVO) {
  266 + // 校验编码唯一
  267 + validateCodeUnique(reqVO.getId(), reqVO.getCode());
  268 + // 校验工单存在
  269 + return reqVO.getWorkOrderId() != null ?
  270 + workOrderService.validateWorkOrderExists(reqVO.getWorkOrderId()) : null;
  271 + }
266 272  
267 273 private void validateCodeUnique(Long id, String code) {
268 274 MesWmProductReceiptDO receipt = productReceiptMapper.selectByCode(code);
... ...