2 天以前 1ba0dbfde5634c8bf2ec20891d6b226b996f6b59
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
@@ -29,6 +29,9 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
import cn.iocoder.yudao.module.mes.service.wm.materialstockbag.MesWmMaterialStockBagService;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag.MesWmMaterialStockBagMapper;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
import jakarta.annotation.Resource;
@@ -44,6 +47,7 @@
import java.util.function.Function;
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.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -74,6 +78,11 @@
    @Resource
    @Lazy
    private MesWmBatchService batchService;
    @Resource
    private MesWmMaterialStockBagMapper materialStockBagMapper;
    @Resource
    private MesWmMaterialStockBagService materialStockBagService;
    @Override
    public MesWmMaterialStockDO getMaterialStock(Long id) {
@@ -121,9 +130,27 @@
            Assert.notNull(virtualWarehouse, "虚拟仓库(WIP_VIRTUAL_WAREHOUSE)不存在");
            virtualWarehouseId = virtualWarehouse.getId();
        }
        // 1.4 解析 warehouseType:转换为该类型仓库 warehouseIds(与 warehouseId / virtualFilter 并存,保留兼容)
        List<Long> warehouseIds = resolveWarehouseIdsByType(pageReqVO.getWarehouseType());
        if (warehouseIds != null && warehouseIds.isEmpty()) {
            return PageResult.empty();
        }
        // 2. 分页查询
        return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId);
        return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId, warehouseIds);
    }
    /**
     * 将仓库类型解析为仓库 id 集合(warehouseType 为 null 时不加过滤,返回 null)
     *
     * @param warehouseType 仓库类型,见 {@link cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum}
     * @return 该类型仓库 id 集合;warehouseType 为 null 返回 null;该类型无仓库返回空集合
     */
    private List<Long> resolveWarehouseIdsByType(Integer warehouseType) {
        if (warehouseType == null) {
            return null;
        }
        return convertList(warehouseService.getWarehouseListByType(warehouseType), MesWmWarehouseDO::getId);
    }
    @Override
@@ -169,7 +196,11 @@
    @Override
    public List<MesWmMaterialStockDO> getMaterialStockList(MesWmMaterialStockListReqVO reqVO) {
        return materialStockMapper.selectList(reqVO);
        List<Long> warehouseIds = resolveWarehouseIdsByType(reqVO.getWarehouseType());
        if (warehouseIds != null && warehouseIds.isEmpty()) {
            return Collections.emptyList();
        }
        return materialStockMapper.selectList(reqVO, warehouseIds);
    }
    @Override
@@ -282,7 +313,17 @@
    @Override
    public MesWmMaterialStockDO getMaterialStockByCompositeKey(Long itemId, Long warehouseId, Long locationId, Long areaId,
                                                                Long batchId, Long vendorId) {
        return materialStockMapper.selectByCompositeKey(itemId, warehouseId, locationId, areaId, batchId, vendorId);
        // 出库/占用只从合格库匹配,避免扣到同批次的暂存库(种子质检按袋拆数量后同批次双状态并存)
        return materialStockMapper.selectQualifiedByCompositeKey(itemId, warehouseId, locationId, areaId, batchId, vendorId);
    }
    @Override
    public List<MesWmMaterialStockDO> getQualifiedMaterialStockListByItemAndBatch(Long itemId, Long batchId) {
        List<Long> qualifiedWarehouseIds = warehouseService.getQualifiedWarehouseIds();
        if (CollUtil.isEmpty(qualifiedWarehouseIds)) {
            return Collections.emptyList();
        }
        return materialStockMapper.selectQualifiedListByItemAndBatch(itemId, batchId, qualifiedWarehouseIds);
    }
    @Override
@@ -302,11 +343,6 @@
    @Override
    public void updateMaterialStockState(Long id, Integer stockState) {
        materialStockMapper.updateById(new MesWmMaterialStockDO().setId(id).setStockState(stockState));
    }
    @Override
    public int updateStockStateByBatchId(Long batchId, Integer toState, Integer fromState) {
        return materialStockMapper.updateStockStateByBatchId(batchId, toState, fromState);
    }
    @Override
@@ -356,7 +392,11 @@
                || ObjUtil.notEqual(stock.getAreaId(), areaId)) {
            throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
        }
        // 3. 校验库存数量充足(如果前端传了 quantity,则必须保证库存数量 >= quantity)
        // 3. 校验库存记录必须是合格库(暂存库存未质检,不可用于出库/占用)
        if (!MesWmStockStateEnum.QUALIFIED.getState().equals(stock.getStockState())) {
            throw exception(WM_MATERIAL_STOCK_NOT_QUALIFIED);
        }
        // 4. 校验库存数量充足(如果前端传了 quantity,则必须保证库存数量 >= quantity)
        if (quantity != null && stock.getQuantity() != null && stock.getQuantity().compareTo(quantity) < 0) {
            throw exception(WM_MATERIAL_STOCK_INSUFFICIENT);
        }
@@ -592,6 +632,32 @@
                .setRelatedTransactionId(outTransactionId)
                .setCheckFlag(false);
        wmTransactionService.createTransaction(inTransaction);
        // 9. 整位置移库时,逐袋迁移袋码明细位置(数量等于源库位活动袋数且为整数才视为整位移库)
        MesWmMaterialStockDO finalTargetStock = materialStockMapper.selectByCompositeKeyIgnoreVendor(
                sourceStock.getItemId(), reqVO.getToWarehouseId(), reqVO.getToLocationId(),
                reqVO.getToAreaId(), sourceStock.getBatchId());
        relocateBagsForMove(sourceStock, reqVO.getQuantity(), inTransaction, finalTargetStock);
    }
    /**
     * 整位移库:将源库位的活动袋码明细迁移到目标库位。
     * <p>
     * 仅当移库数量等于源库位活动袋数且为整数时执行(整位置搬移),将每袋明细的库存汇总、仓库、库区、库位
     * 更新为目标位置;部分移库/无袋码明细的旧库存不做袋码迁移,保持汇总库存变动即可。
     */
    private void relocateBagsForMove(MesWmMaterialStockDO sourceStock, BigDecimal quantity,
                                     MesWmTransactionSaveReqDTO moveInReq, MesWmMaterialStockDO targetStock) {
        List<MesWmMaterialStockBagDO> bags = materialStockBagMapper.selectActiveListByItemAndBatch(
                sourceStock.getItemId(), sourceStock.getBatchId(), sourceStock.getStockState());
        if (CollUtil.isEmpty(bags) || targetStock == null || quantity == null
                || quantity.compareTo(BigDecimal.valueOf(bags.size())) != 0) {
            return;
        }
        for (MesWmMaterialStockBagDO bag : bags) {
            moveInReq.setBagCodeId(bag.getBagCodeId()).setBagCode(bag.getBagCode());
            materialStockBagService.moveIn(moveInReq, targetStock, null);
        }
    }
}