云
5 天以前 9265065bea5a40bcaced67857cb3ba22324ce06c
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stocktaking/task/MesWmStockTakingTaskServiceImpl.java
@@ -8,10 +8,15 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.MesWmStockTakingTaskSaveReqVO;
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.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.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,7 +24,13 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -28,7 +39,7 @@
/**
 * MES 盘点任务 Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -47,6 +58,9 @@
    @Lazy
    @Resource
    private MesWmStockTakingTaskResultService stockTakingTaskResultService;
    @Resource
    private MesWmTransactionService wmTransactionService;
    @Resource
    private AdminUserApi adminUserApi;
@@ -128,7 +142,7 @@
        // 3. 根据冻结标识,对物资进行冻结
        if (Boolean.TRUE.equals(task.getFrozen())) {
            updateMaterialStockFrozen(lines, true);
            updateMaterialStockFrozen(id, lines, true);
        }
    }
@@ -137,6 +151,7 @@
    public void finishStockTakingTask(Long id) {
        // 1. 校验任务存在且为审批中状态
        MesWmStockTakingTaskDO task = validateStockTakingTaskExistsAndApproving(id);
        validateAllLinesCounted(id);
        // 2. 更新任务状态为已完成
        stockTakingTaskMapper.updateById(new MesWmStockTakingTaskDO().setId(id)
@@ -145,7 +160,41 @@
        // 3. 解冻库存
        if (Boolean.TRUE.equals(task.getFrozen())) {
            List<MesWmStockTakingTaskLineDO> lines = stockTakingTaskLineService.getStockTakingTaskLineListByTaskId(id);
            updateMaterialStockFrozen(lines, false);
            updateMaterialStockFrozen(id, lines, false);
        }
        // 与完成状态、解冻共用事务;差异入账失败时整单回滚。
        processStockTakingDifference(id);
    }
    private void validateAllLinesCounted(Long taskId) {
        List<MesWmStockTakingTaskLineDO> lines = stockTakingTaskLineService.getStockTakingTaskLineListByTaskId(taskId);
        if (CollUtil.isEmpty(lines)) {
            throw exception(WM_STOCK_TAKING_TASK_NO_LINE);
        }
        List<MesWmStockTakingTaskResultDO> results = stockTakingTaskResultService.getStockTakingTaskResultListByTaskId(taskId);
        Map<Long, MesWmStockTakingTaskLineDO> lineMap = new HashMap<>();
        lines.forEach(line -> lineMap.put(line.getId(), line));
        Set<Long> counted = new HashSet<>();
        for (MesWmStockTakingTaskResultDO result : results) {
            MesWmStockTakingTaskLineDO line = lineMap.get(result.getLineId());
            if (line == null || !ObjUtil.equal(result.getTaskId(), taskId)
                    || !ObjUtil.equal(result.getMaterialStockId(), line.getMaterialStockId())
                    || !ObjUtil.equal(result.getItemId(), line.getItemId())
                    || !ObjUtil.equal(result.getBatchId(), line.getBatchId())
                    || !ObjUtil.equal(result.getWarehouseId(), line.getWarehouseId())
                    || !ObjUtil.equal(result.getLocationId(), line.getLocationId())
                    || !ObjUtil.equal(result.getAreaId(), line.getAreaId())
                    || !ObjUtil.equal(result.getQuantity(), line.getQuantity())
                    || result.getTakingQuantity() == null || result.getTakingQuantity().signum() < 0
                    || !counted.add(result.getLineId())) {
                throw exception(WM_STOCK_TAKING_TASK_RESULT_LINE_INVALID);
            }
        }
        List<Long> missing = new ArrayList<>();
        lines.stream().map(MesWmStockTakingTaskLineDO::getId)
                .filter(lineId -> !counted.contains(lineId)).forEach(missing::add);
        if (!missing.isEmpty()) {
            throw exception(WM_STOCK_TAKING_TASK_INCOMPLETE, missing);
        }
    }
@@ -160,7 +209,7 @@
        task.setStatus(MesWmStockTakingTaskStatusEnum.CANCELED.getStatus());
        stockTakingTaskMapper.updateById(task);
        if (Boolean.TRUE.equals(task.getFrozen())) {
            updateMaterialStockFrozen(stockTakingTaskLineService.getStockTakingTaskLineListByTaskId(id), false);
            updateMaterialStockFrozen(id, stockTakingTaskLineService.getStockTakingTaskLineListByTaskId(id), false);
        }
    }
@@ -201,13 +250,13 @@
        return stockTakingTaskMapper.selectPage(pageReqVO);
    }
    private void updateMaterialStockFrozen(List<MesWmStockTakingTaskLineDO> lines, boolean frozen) {
    private void updateMaterialStockFrozen(Long taskId, List<MesWmStockTakingTaskLineDO> lines, boolean frozen) {
        if (CollUtil.isEmpty(lines)) {
            return;
        }
        List<Long> materialStockIds = convertList(lines,
                MesWmStockTakingTaskLineDO::getMaterialStockId, line -> line.getMaterialStockId() != null);
        materialStockService.updateMaterialStockFrozen(materialStockIds, frozen);
        materialStockService.setMaterialStockFreeze(materialStockIds, "STOCK_TAKING", taskId, frozen);
    }
    private void validateStockTakingTaskCodeUnique(Long id, String code) {
@@ -220,4 +269,68 @@
        }
    }
    @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);
        }
        // 原子认领;重复调用不会重复生成盘盈/盘亏流水。
        if (stockTakingTaskMapper.claimDifferenceProcessing(id) == 0) {
            return;
        }
        // 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())
                    .setMaterialStockId(result.getMaterialStockId())
                    .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) // 盘亏为负数,符合出库事务方向
                        .setBizType(MesBizTypeConstants.WM_STOCK_TAKING);
            }
            wmTransactionService.createTransaction(transaction);
        }
    }
}