3 小时以前 6d910029b6c81db0a2b66caa66952781397b153a
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stocktaking/task/MesWmStockTakingTaskServiceImpl.java
@@ -2,16 +2,33 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.MesWmStockTakingTaskPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.MesWmStockTakingTaskSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.MesWmStockTakingTaskScanReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.result.MesWmStockTakingTaskResultSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.stocktaking.task.MesWmStockTakingTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.stocktaking.task.MesWmStockTakingTaskLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.stocktaking.task.MesWmStockTakingTaskResultDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.stocktaking.task.MesWmStockTakingTaskLineMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.stocktaking.task.MesWmStockTakingTaskMapper;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockTakingTaskStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService;
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.stocktaking.plan.MesWmStockTakingPlanService;
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.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
@@ -19,6 +36,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -28,7 +46,7 @@
/**
 * MES 盘点任务 Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -47,6 +65,20 @@
    @Lazy
    @Resource
    private MesWmStockTakingTaskResultService stockTakingTaskResultService;
    @Resource
    private MesWmTransactionService wmTransactionService;
    @Resource
    private MesWmStockTakingTaskLineMapper stockTakingTaskLineMapper;
    @Resource
    private MesWmBatchService mesWmBatchService;
    @Resource
    private MesProBatchService proBatchService;
    @Resource
    private MesProBagCodeMapper bagCodeMapper;
    @Resource
    private MesProPalletMapper palletMapper;
    @Resource
    private AdminUserApi adminUserApi;
@@ -220,4 +252,126 @@
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long scanStockTakingTask(MesWmStockTakingTaskScanReqVO scanReqVO) {
        // 1. 校验任务存在且处于盘点中状态
        validateStockTakingTaskExistsAndApproving(scanReqVO.getTaskId());
        // 2. 解析追溯码定位仓库批次(支持批次号/袋码/托盘码,走生产批次桥接)
        MesWmBatchDO wmBatch = resolveWmBatchByScanCode(scanReqVO.getScanCode());
        if (wmBatch == null) {
            throw exception(WM_STOCK_TAKING_TASK_SCAN_BATCH_NOT_FOUND);
        }
        // 3. 在任务范围内定位任务行
        MesWmStockTakingTaskLineDO line = stockTakingTaskLineMapper
                .selectByTaskIdAndBatchId(scanReqVO.getTaskId(), wmBatch.getId());
        if (line == null) {
            throw exception(WM_STOCK_TAKING_TASK_SCAN_BATCH_NOT_IN_TASK);
        }
        // 4. 写入盘点结果(复用 createStockTakingTaskResult:含重复盘点校验,联动更新任务行盘点数量与盈亏状态)
        MesWmStockTakingTaskResultSaveReqVO resultReqVO = new MesWmStockTakingTaskResultSaveReqVO()
                .setTaskId(line.getTaskId()).setLineId(line.getId())
                .setMaterialStockId(line.getMaterialStockId())
                .setItemId(line.getItemId()).setBatchId(line.getBatchId()).setBatchCode(line.getBatchCode())
                .setWarehouseId(line.getWarehouseId()).setLocationId(line.getLocationId()).setAreaId(line.getAreaId())
                .setTakingQuantity(scanReqVO.getTakingQuantity())
                .setRemark(scanReqVO.getRemark());
        return stockTakingTaskResultService.createStockTakingTaskResult(resultReqVO);
    }
    /**
     * 解析追溯码定位仓库批次:依次匹配批次号、袋码、托盘码,生产批次经桥接返回仓库批次
     */
    private MesWmBatchDO resolveWmBatchByScanCode(String scanCode) {
        if (StrUtil.isBlank(scanCode)) {
            return null;
        }
        // 1. 直接命中仓库批次号
        MesWmBatchDO wmBatch = mesWmBatchService.getBatchByCode(scanCode);
        if (wmBatch != null) {
            return wmBatch;
        }
        // 2. 命中袋码
        MesProBagCodeDO bag = bagCodeMapper.selectByCode(scanCode);
        if (bag != null && bag.getBatchId() != null) {
            return bridgeProBatch(bag.getBatchId());
        }
        // 3. 命中托盘码
        MesProPalletDO pallet = palletMapper.selectByCode(scanCode);
        if (pallet != null && pallet.getBatchId() != null) {
            return bridgeProBatch(pallet.getBatchId());
        }
        return null;
    }
    private MesWmBatchDO bridgeProBatch(Long proBatchId) {
        MesProBatchDO proBatch = proBatchService.getBatch(proBatchId);
        if (proBatch == null) {
            return null;
        }
        return mesWmBatchService.getOrCreateByProBatch(proBatch);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processStockTakingDifference(Long id) {
        // 1. 校验任务存在且已完成
        MesWmStockTakingTaskDO task = validateStockTakingTaskExists(id);
        if (!MesWmStockTakingTaskStatusEnum.FINISHED.getStatus().equals(task.getStatus())) {
            throw exception(WM_STOCK_TAKING_TASK_NOT_FINISHED);
        }
        // 2. 获取盘点结果列表
        List<MesWmStockTakingTaskResultDO> results = stockTakingTaskResultService.getStockTakingTaskResultListByTaskId(id);
        if (CollUtil.isEmpty(results)) {
            return; // 无盘点结果,无需处理
        }
        // 3. 遍历结果,处理差异
        for (MesWmStockTakingTaskResultDO result : results) {
            BigDecimal bookQuantity = result.getQuantity();          // 账面数量
            BigDecimal takingQuantity = result.getTakingQuantity(); // 盘点数量
            if (takingQuantity == null || bookQuantity == null) {
                continue;
            }
            // 计算差异:盘点数量 - 账面数量
            BigDecimal difference = takingQuantity.subtract(bookQuantity);
            if (difference.compareTo(BigDecimal.ZERO) == 0) {
                continue; // 无差异,跳过
            }
            // 4. 创建库存调整事务
            MesWmTransactionSaveReqDTO transaction = new MesWmTransactionSaveReqDTO()
                    .setItemId(result.getItemId())
                    .setBatchId(result.getBatchId())
                    .setBatchCode(result.getBatchCode())
                    .setWarehouseId(result.getWarehouseId())
                    .setLocationId(result.getLocationId())
                    .setAreaId(result.getAreaId())
                    .setBizId(task.getId())
                    .setBizCode(task.getCode())
                    .setBizLineId(result.getLineId());
            if (difference.compareTo(BigDecimal.ZERO) > 0) {
                // 盘盈:库存增加
                transaction.setType(MesWmTransactionTypeEnum.IN.getType())
                        .setQuantity(difference)
                        .setBizType(MesBizTypeConstants.WM_STOCK_TAKING);
            } else {
                // 盘亏:库存减少
                transaction.setType(MesWmTransactionTypeEnum.OUT.getType())
                        .setQuantity(difference.negate()) // 出库数量传正数,但内部会转为负数
                        .setBizType(MesBizTypeConstants.WM_STOCK_TAKING);
            }
            wmTransactionService.createTransaction(transaction);
        }
    }
}