liu
8 天以前 38d39b8c11096668d1a49eea84bbec298489d23e
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
@@ -278,14 +278,13 @@
            throw exception(WM_MATERIAL_STOCK_REQUIRED);
        }
        MesWmMaterialStockDO stock = validateMaterialStockExists(materialStockId);
        // 2. 校验库存记录的物料、批次、仓库、库区、库位等信息与前端选择的一致,避免串单或越权提交
        if (ObjUtil.notEqual(stock.getItemId(), itemId)
                || ObjUtil.notEqual(stock.getBatchId(), batchId)
                || (batchCode != null && ObjUtil.notEqual(stock.getBatchCode(), batchCode))
                || ObjUtil.notEqual(stock.getWarehouseId(), warehouseId)
                || ObjUtil.notEqual(stock.getLocationId(), locationId)
                || ObjUtil.notEqual(stock.getAreaId(), areaId)) {
            throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
        // 2. 校验库存记录的物料、批次、仓库、库区、库位等信息与单据一致,避免串单或越权提交。
        //    逐个维度比对,命中时提示具体差异(库存侧 vs 单据侧)并给出处理办法,避免笼统报"信息不一致"
        String mismatch = buildSelectedStockMismatch(
                stock, itemId, batchId, batchCode, warehouseId, locationId, areaId);
        if (mismatch != null) {
            throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(),
                    "所选库存记录与单据不一致:" + mismatch + "。请重新选择该物料正确的库存;若仓库/库区/库位/批次被禁用无法修改,请删除本条明细后重新添加。");
        }
        // 3. 校验库存数量充足(如果前端传了 quantity,则必须保证库存数量 >= quantity)
        if (quantity != null && stock.getQuantity() != null && stock.getQuantity().compareTo(quantity) < 0) {
@@ -294,6 +293,74 @@
        return stock;
    }
    /**
     * 逐维度比对待选库存与单据传入的信息,返回第一个不一致维度的可读描述;全部一致返回 null
     */
    private String buildSelectedStockMismatch(MesWmMaterialStockDO stock, Long itemId, Long batchId, String batchCode,
                                              Long warehouseId, Long locationId, Long areaId) {
        if (ObjUtil.notEqual(stock.getItemId(), itemId)) {
            return "所选库存归属物料[" + itemIdText(itemId) + "],单据物料为[" + itemIdText(stock.getItemId()) + "]";
        }
        if (ObjUtil.notEqual(stock.getBatchId(), batchId)) {
            return "所选库存批次[" + batchText(stock.getBatchId(), stock.getBatchCode())
                    + "],单据批次为[" + batchText(batchId, batchCode) + "]";
        }
        if (batchCode != null && ObjUtil.notEqual(stock.getBatchCode(), batchCode)) {
            return "所选库存批次号[" + stock.getBatchCode() + "],单据批次号为[" + batchCode + "]";
        }
        if (ObjUtil.notEqual(stock.getWarehouseId(), warehouseId)) {
            return "所选库存仓库[" + warehouseText(stock.getWarehouseId())
                    + "],单据仓库为[" + warehouseText(warehouseId) + "]";
        }
        if (ObjUtil.notEqual(stock.getLocationId(), locationId)) {
            return "所选库存库区[" + locationText(stock.getLocationId())
                    + "],单据库区为[" + locationText(locationId) + "]";
        }
        if (ObjUtil.notEqual(stock.getAreaId(), areaId)) {
            return "所选库存库位[" + areaText(stock.getAreaId())
                    + "],单据库位为[" + areaText(areaId) + "]";
        }
        return null;
    }
    private String itemIdText(Long itemId) {
        return itemId == null ? "未选择" : ("物料ID=" + itemId);
    }
    private String batchText(Long batchId, String batchCode) {
        if (StrUtil.isNotBlank(batchCode)) {
            return batchCode;
        }
        return batchId == null ? "未选择" : ("批次ID=" + batchId);
    }
    private String warehouseText(Long warehouseId) {
        if (warehouseId == null) {
            return "未选择";
        }
        MesWmWarehouseDO warehouse = warehouseService.getWarehouse(warehouseId);
        return warehouse != null && StrUtil.isNotBlank(warehouse.getName())
                ? warehouse.getName() : ("仓库ID=" + warehouseId);
    }
    private String locationText(Long locationId) {
        if (locationId == null) {
            return "未选择";
        }
        MesWmWarehouseLocationDO location = locationService.getWarehouseLocation(locationId);
        return location != null && StrUtil.isNotBlank(location.getName())
                ? location.getName() : ("库区ID=" + locationId);
    }
    private String areaText(Long areaId) {
        if (areaId == null) {
            return "未选择";
        }
        MesWmWarehouseAreaDO area = areaService.getWarehouseArea(areaId);
        return area != null && StrUtil.isNotBlank(area.getName())
                ? area.getName() : ("库位ID=" + areaId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long adjustMaterialStockIn(MesWmMaterialStockAdjustReqVO reqVO) {