3 天以前 1ba0dbfde5634c8bf2ec20891d6b226b996f6b59
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
@@ -6,6 +6,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockListReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockStateEnum;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
@@ -22,13 +23,15 @@
    default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO,
                                                         Collection<Long> itemTypeIds,
                                                         Collection<Long> itemIds,
                                                         Long virtualWarehouseId) {
                                                         Long virtualWarehouseId,
                                                         Collection<Long> warehouseIds) {
        LambdaQueryWrapperX<MesWmMaterialStockDO> wrapper = new LambdaQueryWrapperX<MesWmMaterialStockDO>()
                .inIfPresent(MesWmMaterialStockDO::getItemTypeId, itemTypeIds)
                .inIfPresent(MesWmMaterialStockDO::getItemId, itemIds)
                .likeIfPresent(MesWmMaterialStockDO::getBatchCode, reqVO.getBatchCode())
                .eqIfPresent(MesWmMaterialStockDO::getBatchId, reqVO.getBatchId())
                .eqIfPresent(MesWmMaterialStockDO::getWarehouseId, reqVO.getWarehouseId())
                .inIfPresent(MesWmMaterialStockDO::getWarehouseId, warehouseIds)
                .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId())
                .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId())
                .eqIfPresent(MesWmMaterialStockDO::getVendorId, reqVO.getVendorId())
@@ -149,9 +152,10 @@
    }
    /**
     * 按组合键查询库存记录(忽略供应商维度)
     * 按组合键查询库存记录(忽略供应商维度,仅合格库)
     *
     * 用于移库场景:移库时将库存合并到目标库位已有库存,不区分供应商
     * 用于移库场景:移库时将合格库存合并到目标库位已有合格库存,不区分供应商
     * 只匹配合格库(20),避免将移入数量合并到同批次的暂存库记录
     */
    default MesWmMaterialStockDO selectByCompositeKeyIgnoreVendor(Long itemId, Long warehouseId, Long locationId,
                                                                    Long areaId, Long batchId) {
@@ -159,7 +163,8 @@
                .eq(MesWmMaterialStockDO::getItemId, itemId)
                .eq(MesWmMaterialStockDO::getWarehouseId, warehouseId)
                .eq(MesWmMaterialStockDO::getLocationId, locationId)
                .eq(MesWmMaterialStockDO::getAreaId, areaId);
                .eq(MesWmMaterialStockDO::getAreaId, areaId)
                .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.QUALIFIED.getState());
        // batchId=null 时精确匹配 is null
        if (batchId != null) {
            wrapper.eq(MesWmMaterialStockDO::getBatchId, batchId);
@@ -170,9 +175,41 @@
        return selectOne(wrapper);
    }
    /**
     * 按组合键查询合格库库存记录(用于出库事务匹配)
     *
     * 同批次可能同时存在暂存库(10)与合格库(20)两条记录(种子质检按袋拆数量),
     * 出库必须只从合格库匹配,避免扣到未质检的暂存库存。
     */
    default MesWmMaterialStockDO selectQualifiedByCompositeKey(Long itemId, Long warehouseId, Long locationId,
                                                                 Long areaId, Long batchId, Long vendorId) {
        LambdaQueryWrapperX<MesWmMaterialStockDO> wrapper = new LambdaQueryWrapperX<MesWmMaterialStockDO>()
                .eq(MesWmMaterialStockDO::getItemId, itemId)
                .eq(MesWmMaterialStockDO::getWarehouseId, warehouseId)
                .eq(MesWmMaterialStockDO::getLocationId, locationId)
                .eq(MesWmMaterialStockDO::getAreaId, areaId)
                .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.QUALIFIED.getState());
        // batchId=null 时精确匹配 is null
        if (batchId != null) {
            wrapper.eq(MesWmMaterialStockDO::getBatchId, batchId);
        } else {
            wrapper.isNull(MesWmMaterialStockDO::getBatchId);
        }
        if (vendorId != null) {
            wrapper.eq(MesWmMaterialStockDO::getVendorId, vendorId);
        }
        wrapper.last("ORDER BY quantity DESC LIMIT 1");
        return selectOne(wrapper);
    }
    default List<MesWmMaterialStockDO> selectList(MesWmMaterialStockListReqVO reqVO) {
        return selectList(reqVO, null);
    }
    default List<MesWmMaterialStockDO> selectList(MesWmMaterialStockListReqVO reqVO, Collection<Long> warehouseIds) {
        return selectList(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
                .eqIfPresent(MesWmMaterialStockDO::getWarehouseId, reqVO.getWarehouseId())
                .inIfPresent(MesWmMaterialStockDO::getWarehouseId, warehouseIds)
                .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId())
                .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId())
                .eqIfPresent(MesWmMaterialStockDO::getItemId, reqVO.getItemId())
@@ -197,6 +234,36 @@
                .orderByAsc(MesWmMaterialStockDO::getReceiptTime));
    }
    /**
     * 按物料/批次查询合格库库存记录(仅 stock_state=20,且仅限合格仓库)
     *
     * <p>同批次可能同时存在暂存库(10)与合格库(20)两条记录(种子质检按袋拆数量),
     * 销售出库选库存只能从合格库记录中选,杜绝选中未质检的暂存库存;
     * 同时限定合格仓库(type=20),避免从暂存仓库中误选合格态库存。</p>
     */
    default List<MesWmMaterialStockDO> selectQualifiedListByItemAndBatch(Long itemId, Long batchId,
                                                                          Collection<Long> qualifiedWarehouseIds) {
        return selectList(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
                .eq(MesWmMaterialStockDO::getItemId, itemId)
                .eqIfPresent(MesWmMaterialStockDO::getBatchId, batchId)
                .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.QUALIFIED.getState())
                .in(MesWmMaterialStockDO::getWarehouseId, qualifiedWarehouseIds)
                .gt(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO)
                .orderByAsc(MesWmMaterialStockDO::getReceiptTime));
    }
    /**
     * 按仓库批次查询全部库存记录(含暂存库与合格库,quantity>0)
     *
     * <p>用于种子质检按袋码流转:定位该批次暂存库记录扣减 1 袋,以及定位/新建合格库记录累加 1 袋。</p>
     */
    default List<MesWmMaterialStockDO> selectStockListByBatchId(Long batchId) {
        return selectList(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
                .eq(MesWmMaterialStockDO::getBatchId, batchId)
                .gt(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO)
                .orderByAsc(MesWmMaterialStockDO::getReceiptTime));
    }
    default List<MesWmMaterialStockDO> selectListByItemIds(Collection<Long> itemIds) {
        if (itemIds == null || itemIds.isEmpty()) {
            return java.util.Collections.emptyList();
@@ -207,18 +274,16 @@
    }
    /**
     * 按批次号更新库存状态(用于种子质检合格后,将暂存库整批转为合格库)
     * 统计批次在暂存库中的库存记录数(用于种子质检前校验袋码/批次是否处于待质检状态)
     *
     * @param batchId   仓库批次ID
     * @param toState   目标库存状态
     * @param fromState 原库存状态(限定只更新该状态的记录)
     * @return 影响行数
     * @param batchId 仓库批次ID
     * @return 暂存库库存记录数
     */
    default int updateStockStateByBatchId(Long batchId, Integer toState, Integer fromState) {
        return update(new MesWmMaterialStockDO().setStockState(toState),
                new LambdaUpdateWrapper<MesWmMaterialStockDO>()
                        .eq(MesWmMaterialStockDO::getBatchId, batchId)
                        .eq(MesWmMaterialStockDO::getStockState, fromState));
    default Long selectTempStockCountByBatchId(Long batchId) {
        return selectCount(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
                .eq(MesWmMaterialStockDO::getBatchId, batchId)
                .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.TEMP.getState())
                .gt(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO));
    }
}