package cn.iocoder.yudao.module.mes.service.wm.stockreserve; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.stockreserve.MesWmStockReserveDO; import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; import cn.iocoder.yudao.module.mes.dal.mysql.wm.stockreserve.MesWmStockReserveMapper; import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveStatusEnum; import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; 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; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 库存占用 Service 实现类 */ @Service @Validated @Slf4j public class MesWmStockReserveServiceImpl implements MesWmStockReserveService { @Resource private MesWmStockReserveMapper stockReserveMapper; @Resource private MesWmMaterialStockMapper materialStockMapper; @Resource private MesWmMaterialStockService materialStockService; @Override @Transactional(rollbackFor = Exception.class) public void createReserve(Long materialStockId, Integer bizType, Long bizId, String bizCode, Long bizLineId, Long itemId, BigDecimal quantity) { // 1. 校验库存记录存在 MesWmMaterialStockDO stock = materialStockService.getMaterialStock(materialStockId); if (stock == null) { throw exception(WM_MATERIAL_STOCK_NOT_EXISTS); } // 2. 校验可用量充足(在库数量 - 冻结数量 - 已占用量 >= 本次占用数量) BigDecimal availableQty = stock.getQuantity() .subtract(stock.getFrozenQuantity() != null ? stock.getFrozenQuantity() : BigDecimal.ZERO) .subtract(stock.getReservedQuantity() != null ? stock.getReservedQuantity() : BigDecimal.ZERO); if (availableQty.compareTo(quantity) < 0) { throw exception(WM_MATERIAL_STOCK_INSUFFICIENT); } // 3. 创建占用记录 MesWmStockReserveDO reserve = MesWmStockReserveDO.builder() .materialStockId(materialStockId) .bizType(bizType) .bizId(bizId) .bizCode(bizCode) .bizLineId(bizLineId) .itemId(itemId) .quantity(quantity) .status(MesWmStockReserveStatusEnum.RESERVING.getStatus()) .build(); stockReserveMapper.insert(reserve); // 4. 更新库存占用量字段(累加) int updatedRows = materialStockMapper.updateReservedQuantity(materialStockId, quantity, true); if (updatedRows == 0) { throw exception(WM_MATERIAL_STOCK_INSUFFICIENT); } log.info("[createReserve] 创建库存占用成功,materialStockId={}, bizType={}, bizId={}, quantity={}", materialStockId, bizType, bizId, quantity); } @Override @Transactional(rollbackFor = Exception.class) public void createReserveBatch(List reserves) { if (CollUtil.isEmpty(reserves)) { return; } for (ReserveInfo reserve : reserves) { createReserve(reserve.materialStockId(), reserve.bizType(), reserve.bizId(), reserve.bizCode(), reserve.bizLineId(), reserve.itemId(), reserve.quantity()); } } @Override @Transactional(rollbackFor = Exception.class) public void releaseReserve(Integer bizType, Long bizId) { // 1. 查询占用中的记录 List reserveList = stockReserveMapper.selectReservingListByBiz(bizType, bizId); if (CollUtil.isEmpty(reserveList)) { log.warn("[releaseReserve] 未找到占用记录,bizType={}, bizId={}", bizType, bizId); return; } // 2. 更新占用记录状态为已释放,并扣减库存占用量字段 for (MesWmStockReserveDO reserve : reserveList) { // 2.1 更新占用记录状态 stockReserveMapper.updateById(new MesWmStockReserveDO() .setId(reserve.getId()) .setStatus(MesWmStockReserveStatusEnum.RELEASED.getStatus())); // 2.2 扣减库存占用量(负数表示减少) materialStockMapper.updateReservedQuantity(reserve.getMaterialStockId(), reserve.getQuantity().negate(), false); } log.info("[releaseReserve] 释放库存占用成功,bizType={}, bizId={}, count={}", bizType, bizId, reserveList.size()); } @Override @Transactional(rollbackFor = Exception.class) public void confirmReserveToOutbound(Integer bizType, Long bizId) { // 1. 查询占用中的记录 List reserveList = stockReserveMapper.selectReservingListByBiz(bizType, bizId); if (CollUtil.isEmpty(reserveList)) { log.warn("[confirmReserveToOutbound] 未找到占用记录,bizType={}, bizId={}", bizType, bizId); return; } // 2. 更新占用记录状态为已出库,并扣减库存占用量字段 for (MesWmStockReserveDO reserve : reserveList) { // 2.1 更新占用记录状态 stockReserveMapper.updateById(new MesWmStockReserveDO() .setId(reserve.getId()) .setStatus(MesWmStockReserveStatusEnum.OUTBOUND.getStatus())); // 2.2 扣减库存占用量(负数表示减少) materialStockMapper.updateReservedQuantity(reserve.getMaterialStockId(), reserve.getQuantity().negate(), false); } log.info("[confirmReserveToOutbound] 确认占用转出库成功,bizType={}, bizId={}, count={}", bizType, bizId, reserveList.size()); } @Override public List getReserveListByBiz(Integer bizType, Long bizId) { return stockReserveMapper.selectListByBiz(bizType, bizId); } @Override public BigDecimal sumReservedQuantity(Long itemId, Long warehouseId) { if (warehouseId != null) { return stockReserveMapper.sumReservedQuantityByItemAndWarehouse(itemId, warehouseId); } return stockReserveMapper.sumReservedQuantityByItem(itemId); } }