Commit deb6cac081fd2e6b2ad459e5b3050b5fa4042144
1 parent
b759c474
✨ feat(mes): 增强产品入库单功能,新增数量校验和明细检查
新增上架数量的正数校验,确保入库单的数量大于0。同时,增加了收货单明细为空的错误提示,提升数据完整性和用户体验。
Showing
5 changed files
with
79 additions
and
43 deletions
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); | ... | ... |