gaoluyang
2026-10-30 cbadfd8e691ab335965202a984db16e55ac8e42c
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
@@ -18,6 +18,7 @@
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
import cn.iocoder.yudao.module.mes.service.wm.stockwarning.MesWmStockWarningService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
@@ -30,6 +31,8 @@
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -56,6 +59,8 @@
    private MesWmWarehouseLocationService locationService;
    @Resource
    private MesWmWarehouseAreaService areaService;
    @Resource
    private MesWmStockWarningService stockWarningService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -81,24 +86,34 @@
        MesWmMaterialStockDO materialStock;
        MesWmTransactionTypeEnum transactionType = MesWmTransactionTypeEnum.valueOf(reqDTO.getType());
        boolean isInbound = transactionType != null && transactionType.isInbound();
        boolean forceCreateNewStock = Boolean.TRUE.equals(reqDTO.getForceCreateNewStock());
        if (reqDTO.getMaterialStockId() != null) {
            // 传入了 materialStockId,直接使用
            materialStock = materialStockService.getMaterialStock(reqDTO.getMaterialStockId());
            if (materialStock == null) {
                throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
            }
            // 校验物料、批次、仓库等维度一致(防止传入错误的 materialStockId)
            // 校验物料、批次、仓库等维度一致(防止传入错误的 materialStockId)。
            // 若此处不一致,通常是该库存已被移库/合并/拆分,需退回重新拣货,故给出针对性提示
            if (ObjUtil.notEqual(materialStock.getItemId(), reqDTO.getItemId())
                    || ObjUtil.notEqual(materialStock.getWarehouseId(), reqDTO.getWarehouseId())
                    || ObjUtil.notEqual(materialStock.getLocationId(), reqDTO.getLocationId())
                    || ObjUtil.notEqual(materialStock.getAreaId(), reqDTO.getAreaId())) {
                throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
                throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(),
                        "该库存记录已变动(可能被移库/合并/拆分),与出库明细不一致,无法扣减,请退回重新拣货");
            }
        } else if (isInbound) {
            // 入库事务:找不到则自动创建库存记录
            materialStock = materialStockService.getOrCreateMaterialStock(
                    reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
                    reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
            // 入库事务:根据 forceCreateNewStock 标志决定是创建新记录还是查找/创建
            if (forceCreateNewStock) {
                materialStock = materialStockService.createMaterialStock(
                        reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
                        reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
            } else {
                materialStock = materialStockService.getOrCreateMaterialStock(
                        reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
                        reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
            }
        } else {
            // 出库事务:库存记录必须已存在,不允许自动创建(除非 checkFlag=false 允许负库存)
            materialStock = materialStockService.getMaterialStockByCompositeKey(
@@ -124,6 +139,8 @@
        // 2.3 更新库存数量
        boolean checkFlag = ObjUtil.defaultIfNull(reqDTO.getCheckFlag(), true);
        materialStockService.updateMaterialStockQuantity(materialStock.getId(), reqDTO.getQuantity(), checkFlag);
        // 2.4 库存预警校验:本次变动导致库存跨越上下限时,自动发送站内通知(需求 5.1②)
        stockWarningService.checkAfterStockChange(reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getQuantity());
        // 3. 插入事务流水
        MesWmTransactionDO transaction = MesWmTransactionDO.builder()