src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
@@ -1,26 +1,39 @@
package com.ruoyi.stock.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.EnumUtil;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.stock.dto.StockInRecordDto;
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 com.ruoyi.stock.word.WeighbridgeDocGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
@@ -39,6 +52,10 @@
    private StockInventoryMapper stockInventoryMapper;
    @Autowired
    private StockUninventoryMapper stockUninventoryMapper;
    @Autowired
    private WeighbridgeDocGenerator weighbridgeDocGenerator;
    @Autowired
    private ProductModelMapper productModelMapper;
    @Override
    public IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto) {
@@ -49,8 +66,6 @@
    public int add(StockOutRecordDto stockOutRecordDto) {
        String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK");
        stockOutRecordDto.setOutboundBatches(no);
        StockInRecord stockInRecord = new StockInRecord();
        BeanUtils.copyProperties(stockOutRecordDto, stockInRecord);
        return stockOutRecordMapper.insert(stockOutRecordDto);
    }
@@ -58,7 +73,7 @@
    public int update(Long id, StockOutRecordDto stockOutRecordDto) {
        // 判断对象是否存在
        StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
        if (stockOutRecord == null){
        if (stockOutRecord == null) {
            throw new BaseException("该出库记录不存在,无法更新!!!");
        }
@@ -68,31 +83,248 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchDelete(List<Long> ids) {
        for (Long id : ids) {
            // 1. 查询出库记录
            StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
            if (stockOutRecord.getType().equals("0")) {
                StockInventory stockInventory = stockInventoryMapper.selectOne(new LambdaQueryWrapper<StockInventory>().eq(StockInventory::getProductModelId, stockOutRecord.getProductModelId()));
            if (stockOutRecord == null) {
                throw new BaseException("出库记录不存在,ID:" + id);
            }
            // 2. 查询产品型号(用于单位转换)
            ProductModel productModel = productModelMapper.selectById(stockOutRecord.getProductModelId());
            if (productModel == null) {
                throw new BaseException("产品型号不存在,无法删除");
            }
            // 3. 获取出库数量并进行单位转换
            BigDecimal stockOutNum = stockOutRecord.getStockOutNum();
            String sourceUnit = stockOutRecord.getUnit(); // 出库记录中保存的单位
            String targetUnit = productModel.getUnit();
            // 转换出库数量为库存单位(删除时需要加回库存,所以需要转换)
            BigDecimal convertedQuantity = convertQuantityToStockUnit(stockOutNum, sourceUnit, targetUnit);
            // 4. 根据出库类型处理不同的库存表
            if ("0".equals(stockOutRecord.getType())) {
                // 正常出库,恢复库存
                StockInventory stockInventory = stockInventoryMapper.selectOne(
                        new LambdaQueryWrapper<StockInventory>()
                                .eq(StockInventory::getProductModelId, stockOutRecord.getProductModelId())
                );
                if (stockInventory == null) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }else {
                    StockInventoryDto stockInRecordDto = new StockInventoryDto();
                    stockInRecordDto.setProductModelId(stockInventory.getProductModelId());
                    stockInRecordDto.setQualitity(stockOutRecord.getStockOutNum());
                    stockInventoryMapper.updateAddStockInventory(stockInRecordDto);
                    throw new BaseException("库存记录中没有对应的产品,无法删除");
                }
            }else if (stockOutRecord.getType().equals("1")) {
                StockUninventory stockUninventory = stockUninventoryMapper.selectOne(new LambdaQueryWrapper<StockUninventory>().eq(StockUninventory::getProductModelId, stockOutRecord.getProductModelId()));
                // 创建库存更新DTO
                StockInventoryDto inventoryDto = new StockInventoryDto();
                inventoryDto.setProductModelId(stockInventory.getProductModelId());
                inventoryDto.setQualitity(convertedQuantity);
                // 加回库存
                int affectedRows = stockInventoryMapper.updateAddStockInventory(inventoryDto);
                if (affectedRows == 0) {
                    throw new BaseException("恢复库存失败");
                }
            } else if ("1".equals(stockOutRecord.getType())) {
                // 非正常出库,恢复非正常库存
                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);
                    throw new BaseException("非正常库存记录中没有对应的产品,无法删除");
                }
                // 创建非正常库存更新DTO
                StockUninventoryDto uninventoryDto = new StockUninventoryDto();
                uninventoryDto.setProductModelId(stockUninventory.getProductModelId());
                uninventoryDto.setQualitity(convertedQuantity);
                // 加回非正常库存
                int affectedRows = stockUninventoryMapper.updateAddStockUnInventory(uninventoryDto);
                if (affectedRows == 0) {
                    throw new BaseException("恢复非正常库存失败");
                }
            }
        }
        // 5. 批量删除出库记录
        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(EnumUtil.fromCode(StockOutQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            } else {
                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockInUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            }
        }
        ExcelUtil<StockOutRecordExportData> util = new ExcelUtil<>(StockOutRecordExportData.class);
        util.exportExcel(response, list, "出库记录信息");
    }
    @Override
    public int editStockOut(StockOutRecordDto stockOutRecordDto) {
        if (stockOutRecordDto.getId() == null) {
            throw new BaseException("出库记录ID不能为空");
        }
        // 1. 查询原始出库记录
        StockOutRecord originalRecord = stockOutRecordMapper.selectById(stockOutRecordDto.getId());
        if (originalRecord == null) {
            throw new BaseException("出库记录不存在");
        }
        // 2. 查询产品型号信息(用于单位转换)
        ProductModel productModel = productModelMapper.selectById(originalRecord.getProductModelId());
        if (productModel == null) {
            throw new BaseException("产品型号不存在");
        }
        // 3. 获取原始出库数量及其单位
        BigDecimal originalOutNum = originalRecord.getStockOutNum();  // 原始记录中存储的数量(可能是吨或公斤)
        String originalUnit = originalRecord.getUnit();  // 原始记录的单位(吨、公斤等)
        String stockUnit = productModel.getUnit();  // 库存单位
        // 将原始出库数量转换为库存单位
        BigDecimal originalOutNumInStockUnit = convertQuantityToStockUnit(originalOutNum, originalUnit, stockUnit);
        // 4. 获取新出库数量及其单位
        BigDecimal newOutNum = stockOutRecordDto.getNetWeight();  // 新输入的数量
        if (newOutNum == null) {
            throw new BaseException("出库数量不能为空");
        }
        String newUnit = stockOutRecordDto.getUnit();  // 新输入的单位
        // 将新出库数量转换为库存单位
        BigDecimal newOutNumInStockUnit = convertQuantityToStockUnit(newOutNum, newUnit, stockUnit);
        // 5. 计算库存变化量(使用统一转换后的库存单位值)
        // 出库数量变化量(正数表示出库变多,需要多扣库存;负数表示出库变少,需要加回库存)
        BigDecimal diffQuantity = newOutNumInStockUnit.subtract(originalOutNumInStockUnit);
        // 转换为库存变化量:出库变多(diffQuantity > 0)时,库存减少;出库变少(diffQuantity < 0)时,库存增加
        BigDecimal inventoryChange = diffQuantity.negate();
        // 6. 更新出库记录(保存用户输入的原始数值和单位)
        stockOutRecordDto.setStockOutNum(stockOutRecordDto.getNetWeight());  // 保存用户输入的值
        stockOutRecordDto.setUnit(stockOutRecordDto.getUnit());  // 保存用户输入的单位
        // 7. 生成磅单
        StockInRecordDto stockInRecordDto = new StockInRecordDto();
        BeanUtils.copyProperties(stockOutRecordDto, stockInRecordDto);
        stockInRecordDto.setStockInNum(stockOutRecordDto.getStockOutNum());
        String absoluteDocPath = weighbridgeDocGenerator.generateWeighbridgeDoc(stockInRecordDto);
        stockOutRecordDto.setWeighbridgeDocPath(absoluteDocPath);
        int updateResult = stockOutRecordMapper.updateById(stockOutRecordDto);
        // 8. 更新库存(仅在数量发生变化时)
        if (inventoryChange.compareTo(BigDecimal.ZERO) != 0) {
            updateInventoryForStockOut(stockOutRecordDto.getProductId(), inventoryChange);
        }
        return updateResult;
    }
    /**
     * 将数量转换为库存单位
     *
     * @param quantity   原始数量
     * @param sourceUnit 原始单位
     * @param targetUnit 目标单位(库存单位)
     * @return 转换后的数量
     */
    private BigDecimal convertQuantityToStockUnit(BigDecimal quantity, String sourceUnit, String targetUnit) {
        if (quantity == null) {
            throw new BaseException("数量不能为空");
        }
        // 如果源单位为空,直接返回原值
        if (sourceUnit == null || sourceUnit.isEmpty()) {
            return quantity;
        }
        // 如果源单位和目标单位相同,直接返回
        if (sourceUnit.equals(targetUnit)) {
            return quantity;
        }
        // 单位转换
        if ("吨".equals(targetUnit)) {
            // 目标单位是吨,需要将源单位转换为吨
            if ("公斤".equals(sourceUnit)) {
                // 公斤转吨:除以1000
                return quantity.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
            } else if ("克".equals(sourceUnit)) {
                // 克转吨:除以1000000
                return quantity.divide(BigDecimal.valueOf(1000000), 2, RoundingMode.HALF_UP);
            }
        } else if ("公斤".equals(targetUnit)) {
            // 目标单位是公斤
            if ("吨".equals(sourceUnit)) {
                // 吨转公斤:乘以1000
                return quantity.multiply(BigDecimal.valueOf(1000));
            } else if ("克".equals(sourceUnit)) {
                // 克转公斤:除以1000
                return quantity.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
            }
        } else if ("克".equals(targetUnit)) {
            // 目标单位是克
            if ("吨".equals(sourceUnit)) {
                // 吨转克:乘以1000000
                return quantity.multiply(BigDecimal.valueOf(1000000));
            } else if ("公斤".equals(sourceUnit)) {
                // 公斤转克:乘以1000
                return quantity.multiply(BigDecimal.valueOf(1000));
            }
        }
        // 无法转换时返回原值
        return quantity;
    }
    /**
     * 更新库存(出库场景)
     *
     * @param productId       产品ID
     * @param inventoryChange 库存变化量(正数为增加库存,负数为减少库存)
     */
    private void updateInventoryForStockOut(Long productId, BigDecimal inventoryChange) {
        // 查询当前库存
        StockInventory stockInventory = stockInventoryMapper.selectOne(
                new QueryWrapper<StockInventory>().lambda()
                        .eq(StockInventory::getProductId, productId)
        );
        if (stockInventory == null) {
            throw new BaseException("库存记录不存在");
        }
        // 创建库存更新DTO
        StockInventoryDto inventoryDto = new StockInventoryDto();
        BeanUtils.copyProperties(stockInventory, inventoryDto);
        inventoryDto.setQualitity(inventoryChange.abs());
        // 根据库存变化量调用不同的更新方法
        if (inventoryChange.compareTo(BigDecimal.ZERO) > 0) {
            // 库存增加(出库数量变少,需要加回库存)
            int affectedRows = stockInventoryMapper.updateAddStockInventory(inventoryDto);
            if (affectedRows == 0) {
                throw new BaseException("更新库存失败");
            }
        } else {
            // 库存减少(出库数量变多,需要多扣库存)
            int affectedRows = stockInventoryMapper.updateSubtractStockInventory(inventoryDto);
            if (affectedRows == 0) {
                throw new BaseException("库存不足,无法扣减");
            }
        }
    }
}