src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
@@ -4,20 +4,33 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutUnQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.EnumUtil;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockOutRecordExportData;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
import com.ruoyi.stock.mapper.StockUninventoryMapper;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.stock.pojo.StockOutRecord;
import com.ruoyi.stock.pojo.StockUninventory;
import com.ruoyi.stock.service.StockOutRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -29,15 +42,19 @@
 * @since 2026-01-21 05:27:04
 */
@Service
@AllArgsConstructor
public class StockOutRecordServiceImpl extends ServiceImpl<StockOutRecordMapper, StockOutRecord> implements StockOutRecordService {
    @Autowired
    private StockOutRecordMapper stockOutRecordMapper;
    @Autowired
    private StockInventoryMapper stockInventoryMapper;
    private StockUninventoryMapper stockUninventoryMapper;
    @Override
    public IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto) {
        return stockOutRecordMapper.listPage(page, stockOutRecordDto);
        IPage<StockOutRecordDto> result = stockOutRecordMapper.listPage(page, stockOutRecordDto);
        for (StockOutRecordDto record : result.getRecords()) {
            record.setRecordTypeName(resolveRecordTypeName(record.getType(), record.getRecordType(), record.getProcessName()));
        }
        return result;
    }
    @Override
@@ -66,16 +83,87 @@
    public int batchDelete(List<Long> ids) {
        for (Long id : ids) {
            StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
            StockInventory stockInventory = stockInventoryMapper.selectOne(new LambdaQueryWrapper<StockInventory>().eq(StockInventory::getProductModelId, id));
            if (stockInventory == null) {
                throw new BaseException("该入库记录下没有对应的产品,无法删除!!!");
            }else {
                StockInventoryDto stockInRecordDto = new StockInventoryDto();
                stockInRecordDto.setProductModelId(stockInventory.getProductModelId());
                stockInRecordDto.setQualitity(stockOutRecord.getStockOutNum());
                stockInventoryMapper.updateAddStockInventory(stockInRecordDto);
            if (stockOutRecord.getType().equals("0")) {
                // 查询该产品规格的所有库存记录,按ID升序(与出库扣减顺序一致)
                List<StockInventory> stockInventoryList = stockInventoryMapper.selectList(
                    new LambdaQueryWrapper<StockInventory>()
                        .eq(StockInventory::getProductModelId, stockOutRecord.getProductModelId())
                        .orderByAsc(StockInventory::getId)
                );
                if (stockInventoryList.isEmpty()) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }
                // 按照出库扣减逻辑反向回滚
                BigDecimal remainingQty = stockOutRecord.getStockOutNum();
                for (StockInventory stockInventory : stockInventoryList) {
                    if (remainingQty.compareTo(BigDecimal.ZERO) <= 0) {
                        break;
                    }
                    BigDecimal lockedQty = stockInventory.getLockedQuantity() == null ? BigDecimal.ZERO : stockInventory.getLockedQuantity();
                    BigDecimal availableQty = stockInventory.getQualitity().subtract(lockedQty);
                    if (availableQty.compareTo(BigDecimal.ZERO) <= 0) {
                        continue;
                    }
                    // 增加数量 = min(剩余需返还数量, 当前批次可用库存)
                    BigDecimal addQty = remainingQty.min(availableQty);
                    stockInventory.setQualitity(stockInventory.getQualitity().add(addQty));
                    stockInventory.setVersion(stockInventory.getVersion() == null ? 1 : stockInventory.getVersion() + 1);
                    stockInventory.setUpdateTime(LocalDateTime.now());
                    stockInventoryMapper.updateById(stockInventory);
                    remainingQty = remainingQty.subtract(addQty);
                }
            }else if (stockOutRecord.getType().equals("1")) {
                StockUninventory stockUninventory = stockUninventoryMapper.selectOne(new LambdaQueryWrapper<StockUninventory>().eq(StockUninventory::getProductModelId, stockOutRecord.getProductModelId()));
                if (stockUninventory == null) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }else {
                    StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
                    stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId());
                    stockUninventoryDto.setQualitity(stockOutRecord.getStockOutNum());
                    stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
                }
            }
        }
        return stockOutRecordMapper.deleteBatchIds(ids);
    }
    @Override
    public void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto) {
        List<StockOutRecordExportData> list = stockOutRecordMapper.listStockOutRecordExportData(stockOutRecordDto);
        for (StockOutRecordExportData stockInRecordExportData : list) {
            if (stockInRecordExportData.getType().equals("0")) {
                stockInRecordExportData.setRecordType(resolveQualifiedRecordType(stockInRecordExportData.getRecordType(), stockInRecordExportData.getProcessName()));
            }else {
                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockOutUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            }
        }
        ExcelUtil<StockOutRecordExportData> util = new ExcelUtil<>(StockOutRecordExportData.class);
        util.exportExcel(response,list, "出库记录信息");
    }
    private String resolveRecordTypeName(String type, String recordType, String processName) {
        if ("0".equals(type)) {
            return resolveQualifiedRecordType(recordType, processName);
        }
        return EnumUtil.fromCode(StockOutUnQualifiedRecordTypeEnum.class, Integer.parseInt(recordType)).getValue();
    }
    private String resolveQualifiedRecordType(String recordType, String processName) {
        if (StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode().equals(recordType)
                && StringUtils.isNotEmpty(processName)) {
            return "生产报工-" + processName + "-出库";
        }
        if (recordType == null) {
            return null;
        }
        try {
            return EnumUtil.fromCode(StockOutQualifiedRecordTypeEnum.class, Integer.parseInt(recordType)).getValue();
        } catch (NumberFormatException ex) {
            return recordType;
        }
    }
}