Commit 34c1199b762e36afd7b628cf8cebf6677f4ee2bb

Authored by YunaiV
1 parent caf4c2ff

✨ feat(mes): 添加数量校验逻辑,更新入库明细处理

新增对上架数量和入库数量的校验逻辑,确保入库明细的总数量不超过行入库数量。优化了入库单和入库明细的创建与更新流程,提升数据一致性和准确性。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemreceipt/vo/MesWmItemReceiptRespVO.java
... ... @@ -38,10 +38,6 @@ public class MesWmItemReceiptRespVO {
38 38 @ExcelProperty("到货通知单")
39 39 private String noticeCode;
40 40  
41   - @Schema(description = "采购订单编号", example = "PO20260101")
42   - @ExcelProperty("采购订单编号")
43   - private String purchaseOrderCode;
44   -
45 41 @Schema(description = "供应商编号", example = "1")
46 42 private Long vendorId;
47 43  
... ... @@ -49,27 +45,6 @@ public class MesWmItemReceiptRespVO {
49 45 @ExcelProperty("供应商")
50 46 private String vendorName;
51 47  
52   - @Schema(description = "仓库编号", example = "1")
53   - private Long warehouseId;
54   -
55   - @Schema(description = "仓库名称", example = "原料仓")
56   - @ExcelProperty("仓库")
57   - private String warehouseName;
58   -
59   - @Schema(description = "库区编号", example = "1")
60   - private Long locationId;
61   -
62   - @Schema(description = "库区名称", example = "A 区")
63   - @ExcelProperty("库区")
64   - private String locationName;
65   -
66   - @Schema(description = "库位编号", example = "1")
67   - private Long areaId;
68   -
69   - @Schema(description = "库位名称", example = "A-01")
70   - @ExcelProperty("库位")
71   - private String areaName;
72   -
73 48 @Schema(description = "入库日期")
74 49 @ExcelProperty("入库日期")
75 50 private LocalDateTime receiptDate;
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemreceipt/vo/detail/MesWmItemReceiptDetailSaveReqVO.java
1 1 package cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.detail;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.DecimalMin;
4 5 import jakarta.validation.constraints.NotNull;
5 6 import lombok.Data;
6 7  
... ... @@ -27,6 +28,7 @@ public class MesWmItemReceiptDetailSaveReqVO {
27 28  
28 29 @Schema(description = "上架数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "300.00")
29 30 @NotNull(message = "上架数量不能为空")
  31 + @DecimalMin(value = "0", inclusive = false, 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/controller/admin/wm/itemreceipt/vo/line/MesWmItemReceiptLineSaveReqVO.java
1 1 package cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.line;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.DecimalMin;
4 5 import jakarta.validation.constraints.NotNull;
5 6 import lombok.Data;
6 7  
... ... @@ -27,6 +28,7 @@ public class MesWmItemReceiptLineSaveReqVO {
27 28  
28 29 @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "500.00")
29 30 @NotNull(message = "入库数量不能为空")
  31 + @DecimalMin(value = "0", inclusive = false, message = "入库数量必须大于 0")
30 32 private BigDecimal receivedQuantity;
31 33  
32 34 // TODO DONE @芋艿:已添加 batchCode 字段,前端可以使用 batchCode 输入批次编码
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -397,6 +397,7 @@ public interface ErrorCodeConstants {
397 397 ErrorCode WM_ITEM_RECEIPT_LINE_ARRIVAL_NOTICE_LINE_NOT_ALLOWED = new ErrorCode(1_040_705_102,
398 398 "入库单未关联到货通知单,不能选择到货通知单行");
399 399 ErrorCode WM_ITEM_RECEIPT_DETAIL_NOT_EXISTS = new ErrorCode(1_040_705_200, "采购入库明细不存在");
  400 + ErrorCode WM_ITEM_RECEIPT_DETAIL_QUANTITY_EXCEED = new ErrorCode(1_040_705_202, "上架明细总数量不能超过行入库数量");
400 401  
401 402 // ========== MES 仓库管理-领料申请单(1-040-706-000) ==========
402 403 ErrorCode WM_MATERIAL_REQUEST_NOT_EXISTS = new ErrorCode(1_040_706_000, "领料申请单不存在");
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeService.java
... ... @@ -93,6 +93,14 @@ public interface MesWmArrivalNoticeService {
93 93 }
94 94  
95 95 /**
  96 + * 校验到货通知单存在
  97 + *
  98 + * @param id 编号
  99 + * @return 到货通知单
  100 + */
  101 + MesWmArrivalNoticeDO validateArrivalNoticeExists(Long id);
  102 +
  103 + /**
96 104 * 校验到货通知单和行存在,且行属于该通知单
97 105 *
98 106 * @param noticeId 到货通知单 ID
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
... ... @@ -184,7 +184,8 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService
184 184 }
185 185 }
186 186  
187   - private MesWmArrivalNoticeDO validateArrivalNoticeExists(Long id) {
  187 + @Override
  188 + public MesWmArrivalNoticeDO validateArrivalNoticeExists(Long id) {
188 189 MesWmArrivalNoticeDO notice = arrivalNoticeMapper.selectById(id);
189 190 if (notice == null) {
190 191 throw exception(WM_ARRIVAL_NOTICE_NOT_EXISTS);
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptDetailServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.itemreceipt;
2 2  
  3 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
3 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.detail.MesWmItemReceiptDetailSaveReqVO;
5 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDetailDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO;
6 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptDetailMapper;
  9 +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
7 10 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
8 11 import jakarta.annotation.Resource;
9 12 import org.springframework.context.annotation.Lazy;
10 13 import org.springframework.stereotype.Service;
11 14 import org.springframework.validation.annotation.Validated;
12 15  
  16 +import java.math.BigDecimal;
13 17 import java.util.List;
14 18  
15 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -30,40 +34,53 @@ public class MesWmItemReceiptDetailServiceImpl implements MesWmItemReceiptDetail
30 34 private MesWmItemReceiptService itemReceiptService;
31 35  
32 36 @Resource
  37 + @Lazy
  38 + private MesWmItemReceiptLineService itemReceiptLineService;
  39 +
  40 + @Resource
33 41 private MesWmWarehouseAreaService warehouseAreaService;
  42 + @Resource
  43 + @Lazy
  44 + private MesWmMaterialStockService materialStockService;
34 45  
35 46 @Override
36 47 public Long createItemReceiptDetail(MesWmItemReceiptDetailSaveReqVO createReqVO) {
37   - // 校验父单据存在且为可编辑状态
  48 + // 1. 校验数据
38 49 itemReceiptService.validateItemReceiptEditable(createReqVO.getReceiptId());
39   - // 校验库区关系
40   - warehouseAreaService.validateWarehouseAreaExists(
41   - createReqVO.getWarehouseId(), createReqVO.getLocationId(), createReqVO.getAreaId());
42   - // TODO @芋艿:【后续搞】超过数量验证;
43   -
44   - // TODO @芋艿:【后续搞】不允许物资混放
  50 + validateItemReceiptDetailSaveData(createReqVO);
45 51  
  52 + // 2. 插入
46 53 MesWmItemReceiptDetailDO detail = BeanUtils.toBean(createReqVO, MesWmItemReceiptDetailDO.class);
47 54 itemReceiptDetailMapper.insert(detail);
  55 +
  56 + // 3. 插入后校验数量是否超出
  57 + validateDetailQuantityNotExceed(createReqVO.getLineId());
48 58 return detail.getId();
49 59 }
50 60  
51 61 @Override
52 62 public void updateItemReceiptDetail(MesWmItemReceiptDetailSaveReqVO updateReqVO) {
53   - // 校验存在
  63 + // 1.1 校验存在
54 64 MesWmItemReceiptDetailDO detail = validateItemReceiptDetailExists(updateReqVO.getId());
55   - // 校验父单据存在且为可编辑状态
56   - itemReceiptService.validateItemReceiptEditable(detail.getReceiptId());
57   - // 校验库区关系
58   - warehouseAreaService.validateWarehouseAreaExists(
59   - updateReqVO.getWarehouseId(), updateReqVO.getLocationId(), updateReqVO.getAreaId());
60   - // TODO @芋艿:【后续搞】超过数量验证;
61   -
62   - // TODO @芋艿:【后续搞】不允许物资混放
  65 + // 1.2 校验数据
  66 + updateReqVO.setReceiptId(detail.getReceiptId());
  67 + validateItemReceiptDetailSaveData(updateReqVO);
63 68  
64   - // 更新
  69 + // 2. 更新
65 70 MesWmItemReceiptDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmItemReceiptDetailDO.class);
66 71 itemReceiptDetailMapper.updateById(updateObj);
  72 +
  73 + // 3. 更新后校验数量是否超出
  74 + validateDetailQuantityNotExceed(detail.getLineId());
  75 + }
  76 +
  77 + private void validateItemReceiptDetailSaveData(MesWmItemReceiptDetailSaveReqVO reqVO) {
  78 + // 校验父单据存在且为可编辑状态
  79 + itemReceiptService.validateItemReceiptEditable(reqVO.getReceiptId());
  80 + // 校验库区关系
  81 + warehouseAreaService.validateWarehouseAreaExists(reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
  82 + // 校验库位物料/批次混放规则
  83 + materialStockService.checkAreaMixingRule(reqVO.getAreaId(), reqVO.getItemId(), reqVO.getBatchId());
67 84 }
68 85  
69 86 @Override
... ... @@ -107,4 +124,22 @@ public class MesWmItemReceiptDetailServiceImpl implements MesWmItemReceiptDetail
107 124 return detail;
108 125 }
109 126  
  127 + /**
  128 + * 校验行下所有明细的数量之和不超过行的入库数量
  129 + *
  130 + * @param lineId 行编号
  131 + */
  132 + private void validateDetailQuantityNotExceed(Long lineId) {
  133 + MesWmItemReceiptLineDO line = itemReceiptLineService.getItemReceiptLine(lineId);
  134 + if (line == null || line.getReceivedQuantity() == null) {
  135 + return;
  136 + }
  137 + List<MesWmItemReceiptDetailDO> details = itemReceiptDetailMapper.selectListByLineId(lineId);
  138 + BigDecimal totalDetailQty = CollectionUtils.getSumValue(details,
  139 + MesWmItemReceiptDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
  140 + if (totalDetailQty.compareTo(line.getReceivedQuantity()) > 0) {
  141 + throw exception(WM_ITEM_RECEIPT_DETAIL_QUANTITY_EXCEED);
  142 + }
  143 + }
  144 +
110 145 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptLineServiceImpl.java
... ... @@ -52,20 +52,20 @@ public class MesWmItemReceiptLineServiceImpl implements MesWmItemReceiptLineServ
52 52  
53 53 @Override
54 54 public Long createItemReceiptLine(MesWmItemReceiptLineSaveReqVO createReqVO) {
55   - // 1.1 校验父单据存在且为可编辑状态
  55 + // 1. 校验父单据存在且为可编辑状态
56 56 MesWmItemReceiptDO receipt = itemReceiptService.validateItemReceiptEditable(createReqVO.getReceiptId());
57   - // 1.2 校验关联到货通知单行存在
58   - validateArrivalNoticeLine(receipt, createReqVO.getArrivalNoticeLineId());
  57 + // 2. 校验数据
  58 + validateItemReceiptLineSaveData(receipt, createReqVO);
59 59  
60   - // 2.1 创建入库单行
  60 + // 3.1 创建入库单行
61 61 MesWmItemReceiptLineDO line = BeanUtils.toBean(createReqVO, MesWmItemReceiptLineDO.class);
62   - // 2.2 生成或获取批次
  62 + // 3.2 生成或获取批次
63 63 MesWmBatchDO batch = generateOrGetBatch(receipt, createReqVO);
64 64 if (batch != null) {
65 65 line.setBatchId(batch.getId());
66 66 line.setBatchCode(batch.getCode());
67 67 }
68   - // 2.3 插入数据库
  68 + // 3.3 插入数据库
69 69 itemReceiptLineMapper.insert(line);
70 70 return line.getId();
71 71 }
... ... @@ -76,8 +76,8 @@ public class MesWmItemReceiptLineServiceImpl implements MesWmItemReceiptLineServ
76 76 MesWmItemReceiptLineDO line = validateItemReceiptLineExists(updateReqVO.getId());
77 77 // 1.2 校验父单据存在且为可编辑状态
78 78 MesWmItemReceiptDO receipt = itemReceiptService.validateItemReceiptEditable(line.getReceiptId());
79   - // 1.3 校验关联到货通知单行存在
80   - validateArrivalNoticeLine(receipt, updateReqVO.getArrivalNoticeLineId());
  79 + // 1.3 校验数据
  80 + validateItemReceiptLineSaveData(receipt, updateReqVO);
81 81  
82 82 // 2.1 更新
83 83 MesWmItemReceiptLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmItemReceiptLineDO.class);
... ... @@ -90,6 +90,11 @@ public class MesWmItemReceiptLineServiceImpl implements MesWmItemReceiptLineServ
90 90 itemReceiptLineMapper.updateById(updateObj);
91 91 }
92 92  
  93 + private void validateItemReceiptLineSaveData(MesWmItemReceiptDO receipt, MesWmItemReceiptLineSaveReqVO reqVO) {
  94 + // 校验关联到货通知单行
  95 + validateArrivalNoticeLine(receipt, reqVO.getArrivalNoticeLineId());
  96 + }
  97 +
93 98 @Override
94 99 @Transactional(rollbackFor = Exception.class)
95 100 public void deleteItemReceiptLine(Long id) {
... ... @@ -162,6 +167,8 @@ public class MesWmItemReceiptLineServiceImpl implements MesWmItemReceiptLineServ
162 167 return line;
163 168 }
164 169  
  170 +
  171 +
165 172 /**
166 173 * 生成或获取批次
167 174 *
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
... ... @@ -15,6 +15,8 @@ import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptMapp
15 15 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
16 16 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemReceiptStatusEnum;
17 17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
  18 +import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
  19 +import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
18 20 import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
19 21 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
20 22 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
... ... @@ -48,14 +50,19 @@ public class MesWmItemReceiptServiceImpl implements MesWmItemReceiptService {
48 50 @Resource
49 51 @Lazy
50 52 private MesWmArrivalNoticeService arrivalNoticeService;
  53 + @Resource
  54 + private MesMdVendorService vendorService;
  55 + @Resource
  56 + @Lazy
  57 + private MesQcIqcService iqcService;
51 58  
52 59 @Resource
53 60 private MesWmTransactionService wmTransactionService;
54 61  
55 62 @Override
56 63 public Long createItemReceipt(MesWmItemReceiptSaveReqVO createReqVO) {
57   - // 校验编码唯一
58   - validateCodeUnique(null, createReqVO.getCode());
  64 + // 校验数据
  65 + validateItemReceiptSaveData(createReqVO);
59 66  
60 67 // 插入
61 68 MesWmItemReceiptDO receipt = BeanUtils.toBean(createReqVO, MesWmItemReceiptDO.class);
... ... @@ -68,14 +75,29 @@ public class MesWmItemReceiptServiceImpl implements MesWmItemReceiptService {
68 75 public void updateItemReceipt(MesWmItemReceiptSaveReqVO updateReqVO) {
69 76 // 校验存在 + 草稿状态
70 77 validateItemReceiptExistsAndDraft(updateReqVO.getId());
71   - // 校验编码唯一
72   - validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
  78 + // 校验数据
  79 + validateItemReceiptSaveData(updateReqVO);
73 80  
74 81 // 更新
75 82 MesWmItemReceiptDO updateObj = BeanUtils.toBean(updateReqVO, MesWmItemReceiptDO.class);
76 83 itemReceiptMapper.updateById(updateObj);
77 84 }
78 85  
  86 + private void validateItemReceiptSaveData(MesWmItemReceiptSaveReqVO reqVO) {
  87 + // 校验编码唯一
  88 + validateCodeUnique(reqVO.getId(), reqVO.getCode());
  89 + // 校验供应商存在
  90 + vendorService.validateVendorExists(reqVO.getVendorId());
  91 + // 校验到货通知单存在
  92 + if (reqVO.getNoticeId() != null) {
  93 + arrivalNoticeService.validateArrivalNoticeExists(reqVO.getNoticeId());
  94 + }
  95 + // 校验来料检验单存在
  96 + if (reqVO.getIqcId() != null) {
  97 + iqcService.validateIqcExists(reqVO.getIqcId());
  98 + }
  99 + }
  100 +
79 101 @Override
80 102 @Transactional(rollbackFor = Exception.class)
81 103 public void deleteItemReceipt(Long id) {
... ...