From a46f2b1f1aa2469a49210800f48f079f8edd73b8 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 31 Mar 2026 12:18:16 +0800 Subject: [PATCH] ✨ feat(mes): 新增单据状态字段,更新库存冻结状态批量处理 --- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/MesWmTransferPageReqVO.java | 7 ++----- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java | 5 +++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transfer/MesWmTransferMapper.java | 1 + yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java | 1 + yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java | 3 ++- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductProduceStatusEnum.java | 6 +++++- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmTransferTypeEnum.java | 1 - yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java | 19 ++++++++++--------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java | 12 ++++++++++-- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java | 26 ++++++++++++++------------ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailService.java | 7 +++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailServiceImpl.java | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferLineServiceImpl.java | 51 +++++++++++++++++++++++++++++++++++---------------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferServiceImpl.java | 27 +++++++++++++-------------- 14 files changed, 188 insertions(+), 78 deletions(-) diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/MesWmTransferPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/MesWmTransferPageReqVO.java index 33fe65a..b16dad6 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/MesWmTransferPageReqVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transfer/vo/MesWmTransferPageReqVO.java @@ -5,11 +5,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @Schema(description = "管理后台 - MES 转移单分页 Request VO") @Data @@ -26,5 +21,7 @@ public class MesWmTransferPageReqVO extends PageParam { @Schema(description = "转移单类型", example = "1") private Integer type; + @Schema(description = "单据状态", example = "0") + private Integer status; } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java index a85023c..87707d5 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java @@ -49,6 +49,11 @@ public interface MesWmMaterialStockMapper extends BaseMapperX ids, MesWmMaterialStockDO updateObj) { + update(updateObj, new LambdaUpdateWrapper() + .in(MesWmMaterialStockDO::getId, ids)); + } + /** * 增量更新库存数量 * diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transfer/MesWmTransferMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transfer/MesWmTransferMapper.java index 8fd10c7..a556a8a 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transfer/MesWmTransferMapper.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transfer/MesWmTransferMapper.java @@ -18,6 +18,7 @@ public interface MesWmTransferMapper extends BaseMapperX { .likeIfPresent(MesWmTransferDO::getCode, reqVO.getCode()) .likeIfPresent(MesWmTransferDO::getName, reqVO.getName()) .eqIfPresent(MesWmTransferDO::getType, reqVO.getType()) + .eqIfPresent(MesWmTransferDO::getStatus, reqVO.getStatus()) .orderByDesc(MesWmTransferDO::getId)); } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java index 620bee0..e96b842 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java @@ -453,6 +453,7 @@ public interface ErrorCodeConstants { ErrorCode WM_TRANSFER_LINE_NOT_EXISTS = new ErrorCode(1_040_710_100, "转移单行不存在"); ErrorCode WM_TRANSFER_DETAIL_NOT_EXISTS = new ErrorCode(1_040_710_200, "调拨明细不存在"); ErrorCode WM_TRANSFER_DETAIL_QUANTITY_EXCEED = new ErrorCode(1_040_710_201, "调拨明细总数量不能超过调拨单行数量"); + ErrorCode WM_TRANSFER_DETAIL_MIXED_GOODS = new ErrorCode(1_040_710_202, "同一目标仓位下已存在其他物料的明细,不允许混货"); // ========== MES 仓库管理-生产退料单(1-040-711-000) ========== ErrorCode WM_RETURN_ISSUE_NOT_EXISTS = new ErrorCode(1_040_710_000, "生产退料单不存在"); diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java index c62a37e..3524997 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java @@ -33,7 +33,8 @@ public enum MesMdAutoCodeRuleCodeEnum { WM_LOCATION_CODE("WM_LOCATION_CODE", "库区编码"), WM_AREA_CODE("WM_AREA_CODE", "库位编码"), WM_PRODUCT_SALES_CODE("WM_PRODUCT_SALES_CODE", "销售出库单编码"), - WM_MISC_RECEIPT_CODE("WM_MISC_RECEIPT_CODE", "杂项入库单编码"); + WM_MISC_RECEIPT_CODE("WM_MISC_RECEIPT_CODE", "杂项入库单编码"), + WM_TRANSFER_CODE("WM_TRANSFER_CODE", "转移调拨单编码"); /** * 规则代码 diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductProduceStatusEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductProduceStatusEnum.java index c9a278d..0ace9d2 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductProduceStatusEnum.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductProduceStatusEnum.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.enums.wm; import cn.iocoder.yudao.framework.common.core.ArrayValuable; import cn.iocoder.yudao.module.mes.enums.MesOrderStatusConstants; +import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceService; import lombok.AllArgsConstructor; import lombok.Getter; @@ -16,13 +17,16 @@ import java.util.Arrays; @AllArgsConstructor public enum MesWmProductProduceStatusEnum implements ArrayValuable { - // TODO @AI:对应的方法;参考别的 MesWmProductIssueStatusEnum /** * 草稿 + * + * 对应方法:{@link MesWmProductProduceService#generateProductProduce} */ PREPARE(MesOrderStatusConstants.PREPARE, "草稿"), /** * 已完成 + * + * 对应方法:{@link MesWmProductProduceService#finishProductProduce(Long)} */ FINISHED(MesOrderStatusConstants.FINISHED, "已完成"), /** diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmTransferTypeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmTransferTypeEnum.java index c016113..7b93560 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmTransferTypeEnum.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmTransferTypeEnum.java @@ -6,7 +6,6 @@ import lombok.Getter; import java.util.Arrays; -// TODO @AI:没有 IntArrayValuable,你检查下;应该是另外个 /** * MES 转移单类型枚举 * diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java index ca0868f..02b8f84 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java @@ -11,9 +11,6 @@ import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Collection; import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * MES 库存台账 Service 接口 @@ -52,6 +49,14 @@ public interface MesWmMaterialStockService { void updateMaterialStockFrozen(@Valid MesWmMaterialStockFreezeReqVO updateReqVO); /** + * 批量更新库存冻结状态 + * + * @param ids 库存记录编号列表 + * @param frozen 是否冻结 + */ + void updateMaterialStockFrozen(Collection ids, boolean frozen); + + /** * 获得仓库下库存记录数量 * * @param warehouseId 仓库编号 @@ -91,10 +96,6 @@ public interface MesWmMaterialStockService { */ List getMaterialStockList(MesWmMaterialStockListReqVO reqVO); - default Map getMaterialStockMap(Collection ids) { - return convertMap(getMaterialStockList(ids), MesWmMaterialStockDO::getId); - } - /** * 获得库存精简列表(用于前端下拉选择) * @@ -122,11 +123,11 @@ public interface MesWmMaterialStockService { /** * 更新库存数量 * - * @param materialStockId 库存记录编号 + * @param id 库存记录编号 * @param quantity 变动数量(正数=增加,负数=扣减) * @param checkFlag 是否校验库存充足(为 true 且扣减后为负则报错) */ - void updateMaterialStockQuantity(Long materialStockId, BigDecimal quantity, boolean checkFlag); + void updateMaterialStockQuantity(Long id, BigDecimal quantity, boolean checkFlag); /** * 库位混放规则校验 diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java index ce53953..443a259 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java @@ -93,6 +93,14 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService } @Override + public void updateMaterialStockFrozen(Collection ids, boolean frozen) { + if (CollUtil.isEmpty(ids)) { + return; + } + materialStockMapper.updateByIds(ids, new MesWmMaterialStockDO().setFrozen(frozen)); + } + + @Override public Long getMaterialStockCountByWarehouseId(Long warehouseId) { return materialStockMapper.selectCountByWarehouseId(warehouseId); } @@ -153,9 +161,9 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService } @Override - public void updateMaterialStockQuantity(Long materialStockId, BigDecimal quantity, boolean checkFlag) { + public void updateMaterialStockQuantity(Long id, BigDecimal quantity, boolean checkFlag) { // 更新库存数量(Mapper 层 CAS 防负库存;checkFlag=false 时允许负数) - int updatedRows = materialStockMapper.updateQuantity(materialStockId, quantity, checkFlag); + int updatedRows = materialStockMapper.updateQuantity(id, quantity, checkFlag); if (updatedRows == 0) { throw exception(WM_MATERIAL_STOCK_INSUFFICIENT); } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java index 3d4eccf..d71bffb 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java @@ -45,11 +45,8 @@ public class MesWmSalesNoticeLineServiceImpl implements MesWmSalesNoticeLineServ @Override public Long createSalesNoticeLine(MesWmSalesNoticeLineSaveReqVO createReqVO) { - // TODO @AI:增加 validateXXXSaveData 方法 - // 校验父单据存在且为草稿状态 - validateNoticeStatusDraft(createReqVO.getNoticeId()); - // 校验物料存在 - itemService.validateItemExists(createReqVO.getItemId()); + // 校验数据 + validateLineSaveData(createReqVO); // 插入 MesWmSalesNoticeLineDO line = BeanUtils.toBean(createReqVO, MesWmSalesNoticeLineDO.class); @@ -62,13 +59,9 @@ public class MesWmSalesNoticeLineServiceImpl implements MesWmSalesNoticeLineServ public void updateSalesNoticeLine(MesWmSalesNoticeLineSaveReqVO updateReqVO) { // 校验存在 MesWmSalesNoticeLineDO line = validateSalesNoticeLineExists(updateReqVO.getId()); - // TODO @AI:增加 validateXXXSaveData 方法 - // 校验父单据存在且为草稿状态 - validateNoticeStatusDraft(line.getNoticeId()); - // 校验物料存在 - if (updateReqVO.getItemId() != null) { - itemService.validateItemExists(updateReqVO.getItemId()); - } + // 校验数据 + updateReqVO.setNoticeId(line.getNoticeId()); + validateLineSaveData(updateReqVO); // 更新 MesWmSalesNoticeLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmSalesNoticeLineDO.class); @@ -115,6 +108,15 @@ public class MesWmSalesNoticeLineServiceImpl implements MesWmSalesNoticeLineServ return line; } + private void validateLineSaveData(MesWmSalesNoticeLineSaveReqVO reqVO) { + // 校验父单据存在且为草稿状态 + validateNoticeStatusDraft(reqVO.getNoticeId()); + // 校验物料存在 + if (reqVO.getItemId() != null) { + itemService.validateItemExists(reqVO.getItemId()); + } + } + /** * 校验父发货通知单存在且为草稿状态 */ diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailService.java index e5e00bc..6506bdb 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailService.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailService.java @@ -64,4 +64,11 @@ public interface MesWmTransferDetailService { */ void deleteTransferDetailByTransferId(Long transferId); + /** + * 根据行 ID 删除明细 + * + * @param lineId 行 ID + */ + void deleteTransferDetailByLineId(Long lineId); + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailServiceImpl.java index 549d7d4..f38366c 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferDetailServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.mes.service.wm.transfer; +import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.wm.transfer.vo.detail.MesWmTransferDetailSaveReqVO; @@ -18,6 +19,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_TRANSFER_DETAIL_NOT_EXISTS; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_TRANSFER_DETAIL_QUANTITY_EXCEED; +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_TRANSFER_DETAIL_MIXED_GOODS; /** * MES 调拨明细 Service 实现类 @@ -31,6 +33,9 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic @Resource @Lazy + private MesWmTransferService transferService; + @Resource + @Lazy private MesWmTransferLineService transferLineService; @Resource @@ -38,14 +43,8 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic @Override public Long createTransferDetail(MesWmTransferDetailSaveReqVO createReqVO) { - // 校验父数据存在 - MesWmTransferLineDO line = transferLineService.validateTransferLineExists(createReqVO.getLineId()); - // 校验仓库、库区、库位的关联关系 - warehouseAreaService.validateWarehouseAreaExists( - createReqVO.getToWarehouseId(), createReqVO.getToLocationId(), createReqVO.getToAreaId()); - // 校验明细总数量不超过行数量 - validateDetailQuantityNotExceed(createReqVO.getLineId(), createReqVO.getQuantity(), null, line); - // TODO @芋艿:不能混货 + // 校验 + validateDetailSaveData(createReqVO, null); // 插入 MesWmTransferDetailDO detail = BeanUtils.toBean(createReqVO, MesWmTransferDetailDO.class); @@ -57,14 +56,8 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic public void updateTransferDetail(MesWmTransferDetailSaveReqVO updateReqVO) { // 校验存在 validateTransferDetailExists(updateReqVO.getId()); - // 校验父数据存在 - MesWmTransferLineDO line = transferLineService.validateTransferLineExists(updateReqVO.getLineId()); - // 校验仓库、库区、库位的关联关系 - warehouseAreaService.validateWarehouseAreaExists( - updateReqVO.getToWarehouseId(), updateReqVO.getToLocationId(), updateReqVO.getToAreaId()); - // 校验明细总数量不超过行数量(排除自身) - validateDetailQuantityNotExceed(updateReqVO.getLineId(), updateReqVO.getQuantity(), updateReqVO.getId(), line); - // TODO @芋艿:不能混货 + // 校验 + validateDetailSaveData(updateReqVO, updateReqVO.getId()); // 更新 MesWmTransferDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmTransferDetailDO.class); @@ -74,7 +67,13 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic @Override public void deleteTransferDetail(Long id) { // 校验存在 - validateTransferDetailExists(id); + MesWmTransferDetailDO detail = transferDetailMapper.selectById(id); + if (detail == null) { + throw exception(WM_TRANSFER_DETAIL_NOT_EXISTS); + } + // 校验父转移单可编辑(通过 line 找到 transfer) + MesWmTransferLineDO line = transferLineService.validateTransferLineExists(detail.getLineId()); + transferService.validateTransferEditable(line.getTransferId()); // 删除 transferDetailMapper.deleteById(id); } @@ -100,6 +99,13 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic .forEach(detail -> transferDetailMapper.deleteById(detail.getId())); } + @Override + public void deleteTransferDetailByLineId(Long lineId) { + // TODO @AI:不要 get;直接去 deletebylineId; + getTransferDetailListByLineId(lineId) + .forEach(detail -> transferDetailMapper.deleteById(detail.getId())); + } + private void validateTransferDetailExists(Long id) { if (transferDetailMapper.selectById(id) == null) { throw exception(WM_TRANSFER_DETAIL_NOT_EXISTS); @@ -107,6 +113,25 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic } /** + * 校验明细保存数据 + * + * @param reqVO 保存请求 + * @param excludeDetailId 排除的明细 ID(更新时排除自身,新增时传 null) + */ + private void validateDetailSaveData(MesWmTransferDetailSaveReqVO reqVO, Long excludeDetailId) { + // 校验父数据存在 + MesWmTransferLineDO line = transferLineService.validateTransferLineExists(reqVO.getLineId()); + // 校验仓库、库区、库位的关联关系 + warehouseAreaService.validateWarehouseAreaExists( + reqVO.getToWarehouseId(), reqVO.getToLocationId(), reqVO.getToAreaId()); + // 校验明细总数量不超过行数量 + validateDetailQuantityNotExceed(reqVO.getLineId(), reqVO.getQuantity(), excludeDetailId, line); + // 校验不能混货:同一个目标仓位下,不能存在不同物料的明细 + validateNoMixedGoods(reqVO.getLineId(), reqVO.getToWarehouseId(), + reqVO.getToLocationId(), reqVO.getToAreaId(), line.getItemId(), excludeDetailId); + } + + /** * 校验明细总数量不超过行数量 * * @param lineId 行 ID @@ -128,4 +153,45 @@ public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailServic } } + /** + * 校验不能混货:同一转移单下,同一个目标仓位(仓库+库区+库位)只能放同一个物料 + * + * @param lineId 当前行 ID(用于排除同行明细,因为同行物料相同) + * @param toWarehouseId 目标仓库 ID + * @param toLocationId 目标库区 ID + * @param toAreaId 目标库位 ID + * @param itemId 当前物料 ID + * @param excludeDetailId 排除的明细 ID(更新时排除自身) + */ + private void validateNoMixedGoods(Long lineId, Long toWarehouseId, Long toLocationId, + Long toAreaId, Long itemId, Long excludeDetailId) { + // 1. 获取当前转移单下的所有明细 + // 说明:因为无法直接从参数拿到 transferId,所以先通过 lineId 查询当前行,进而获取 transferId 和所有明细 + MesWmTransferLineDO currentLine = transferLineService.validateTransferLineExists(lineId); + List allDetails = transferDetailMapper.selectListByTransferId(currentLine.getTransferId()); + + // 2. 遍历所有明细,校验同一个目标仓位不能有不同的物料 + for (MesWmTransferDetailDO detail : allDetails) { + // 2.1 排除当前正在编辑的明细(更新时) + if (excludeDetailId != null && excludeDetailId.equals(detail.getId())) { + continue; + } + // 2.2 排除同行的明细(同行属于同一个转移单行,物料必定相同,无需校验) + if (detail.getLineId().equals(lineId)) { + continue; + } + // 2.3 目标仓位不同,则不会混货,直接跳过 + if (ObjUtil.notEqual(detail.getToWarehouseId(), toWarehouseId) + || ObjUtil.notEqual(detail.getToLocationId(), toLocationId) + || ObjUtil.notEqual(detail.getToAreaId(), toAreaId)) { + continue; + } + // 2.4 重点:如果目标仓位相同,则必须保证物料也相同 + MesWmTransferLineDO otherLine = transferLineService.validateTransferLineExists(detail.getLineId()); + if (ObjUtil.notEqual(otherLine.getItemId(), itemId)) { + throw exception(WM_TRANSFER_DETAIL_MIXED_GOODS); + } + } + } + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferLineServiceImpl.java index 6f233b6..14e1606 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferLineServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferLineServiceImpl.java @@ -5,15 +5,18 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.transfer.vo.line.MesWmTra import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer.MesWmTransferLineDO; import cn.iocoder.yudao.module.mes.dal.mysql.wm.transfer.MesWmTransferLineMapper; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_MATERIAL_STOCK_NOT_EXISTS; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_TRANSFER_LINE_NOT_EXISTS; /** @@ -30,19 +33,19 @@ public class MesWmTransferLineServiceImpl implements MesWmTransferLineService { @Lazy private MesWmTransferService transferService; @Resource + @Lazy + private MesWmTransferDetailService transferDetailService; + @Resource private MesMdItemService itemService; @Resource private MesWmWarehouseAreaService warehouseAreaService; + @Resource + private MesWmMaterialStockService materialStockService; @Override public Long createTransferLine(MesWmTransferLineSaveReqVO createReqVO) { - // 校验父数据可编辑 - transferService.validateTransferEditable(createReqVO.getTransferId()); - // 校验产品存在 - itemService.validateItemExists(createReqVO.getItemId()); - // 校验来源仓库、库区、库位的关联关系 - warehouseAreaService.validateWarehouseAreaExists(createReqVO.getFromWarehouseId(), - createReqVO.getFromLocationId(), createReqVO.getFromAreaId()); + // 校验 + validateLineSaveData(createReqVO); // 插入 MesWmTransferLineDO line = BeanUtils.toBean(createReqVO, MesWmTransferLineDO.class); @@ -54,13 +57,8 @@ public class MesWmTransferLineServiceImpl implements MesWmTransferLineService { public void updateTransferLine(MesWmTransferLineSaveReqVO updateReqVO) { // 校验存在 validateTransferLineExists(updateReqVO.getId()); - // 校验父数据可编辑 - transferService.validateTransferEditable(updateReqVO.getTransferId()); - // 校验产品存在 - itemService.validateItemExists(updateReqVO.getItemId()); - // 校验来源仓库、库区、库位的关联关系 - warehouseAreaService.validateWarehouseAreaExists(updateReqVO.getFromWarehouseId(), - updateReqVO.getFromLocationId(), updateReqVO.getFromAreaId()); + // 校验 + validateLineSaveData(updateReqVO); // 更新 MesWmTransferLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmTransferLineDO.class); @@ -68,10 +66,16 @@ public class MesWmTransferLineServiceImpl implements MesWmTransferLineService { } @Override + @Transactional(rollbackFor = Exception.class) public void deleteTransferLine(Long id) { // 校验存在 - validateTransferLineExists(id); - // 删除 + MesWmTransferLineDO line = validateTransferLineExists(id); + // 校验父数据可编辑 + transferService.validateTransferEditable(line.getTransferId()); + + // 级联删除该行下所有明细 + transferDetailService.deleteTransferDetailByLineId(id); + // 删除行 transferLineMapper.deleteById(id); } @@ -100,4 +104,19 @@ public class MesWmTransferLineServiceImpl implements MesWmTransferLineService { return line; } + private void validateLineSaveData(MesWmTransferLineSaveReqVO reqVO) { + // 校验父数据可编辑 + transferService.validateTransferEditable(reqVO.getTransferId()); + // 校验产品存在 + itemService.validateItemExists(reqVO.getItemId()); + // 校验来源仓库、库区、库位的关联关系 + warehouseAreaService.validateWarehouseAreaExists(reqVO.getFromWarehouseId(), + reqVO.getFromLocationId(), reqVO.getFromAreaId()); + // 校验库存记录存在 + if (reqVO.getMaterialStockId() != null + && materialStockService.getMaterialStock(reqVO.getMaterialStockId()) == null) { + throw exception(WM_MATERIAL_STOCK_NOT_EXISTS); + } + } + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferServiceImpl.java index fae427d..9edc490 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transfer/MesWmTransferServiceImpl.java @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockFreezeReqVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.transfer.vo.MesWmTransferPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.wm.transfer.vo.MesWmTransferSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer.MesWmTransferDO; @@ -27,6 +26,7 @@ import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** @@ -109,7 +109,7 @@ public class MesWmTransferServiceImpl implements MesWmTransferService { if (Boolean.TRUE.equals(transfer.getDeliveryFlag()) && !Boolean.TRUE.equals(transfer.getConfirmFlag())) { transfer.setStatus(MesWmTransferStatusEnum.UNCONFIRMED.getStatus()).setConfirmFlag(false); // DONE @AI:冻结库存属于主单状态流转的一部分,保留在主单 service 统一处理 - freezeTransferLineStocks(lines, true); + freezeTransferLineStocks(id, true); } else { transfer.setStatus(MesWmTransferStatusEnum.APPROVING.getStatus()); } @@ -171,7 +171,7 @@ public class MesWmTransferServiceImpl implements MesWmTransferService { // 4. 配送模式下,执行完成后解除来源库存冻结 if (Boolean.TRUE.equals(transfer.getDeliveryFlag())) { - freezeTransferLineStocks(lines, false); + freezeTransferLineStocks(id, false); } } @@ -215,6 +215,11 @@ public class MesWmTransferServiceImpl implements MesWmTransferService { // 校验存在 + 非已完成/已取消状态 MesWmTransferDO transfer = validateTransferExistsAndNotFinished(id); + // 配送模式下,取消时需解除冻结 + if (Boolean.TRUE.equals(transfer.getDeliveryFlag())) { + freezeTransferLineStocks(id, false); + } + // 更新状态 -> 已取消 transfer.setStatus(MesWmTransferStatusEnum.CANCELED.getStatus()); transferMapper.updateById(transfer); @@ -305,20 +310,14 @@ public class MesWmTransferServiceImpl implements MesWmTransferService { return transfer; } - // TODO @芋艿:【后续弄】materialStockService 封装一个方法,传入 transferId + frozen 统一处理了;不用判断 line 的 getMaterialStockId - private void freezeTransferLineStocks(List lines, boolean frozen) { + private void freezeTransferLineStocks(Long transferId, boolean frozen) { + List lines = transferLineService.getTransferLineListByTransferId(transferId); if (CollUtil.isEmpty(lines)) { return; } - for (MesWmTransferLineDO line : lines) { - if (line.getMaterialStockId() == null) { - continue; - } - MesWmMaterialStockFreezeReqVO freezeReqVO = new MesWmMaterialStockFreezeReqVO(); - freezeReqVO.setId(line.getMaterialStockId()); - freezeReqVO.setFrozen(frozen); - materialStockService.updateMaterialStockFrozen(freezeReqVO); - } + List materialStockIds = convertList(lines, + MesWmTransferLineDO::getMaterialStockId, line -> line.getMaterialStockId() != null); + materialStockService.updateMaterialStockFrozen(materialStockIds, frozen); } } -- libgit2 0.21.4