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.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;
/**
*
* 出库记录表 服务实现类
*
*
* @author 芯导软件(江苏)有限公司
* @since 2026-01-21 05:27:04
*/
@Service
public class StockOutRecordServiceImpl extends ServiceImpl implements StockOutRecordService {
@Autowired
private StockOutRecordMapper stockOutRecordMapper;
@Autowired
private StockInventoryMapper stockInventoryMapper;
@Autowired
private StockUninventoryMapper stockUninventoryMapper;
@Autowired
private WeighbridgeDocGenerator weighbridgeDocGenerator;
@Autowired
private ProductModelMapper productModelMapper;
@Override
public IPage listPage(Page page, StockOutRecordDto stockOutRecordDto) {
return stockOutRecordMapper.listPage(page, stockOutRecordDto);
}
@Override
public int add(StockOutRecordDto stockOutRecordDto) {
String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK");
stockOutRecordDto.setOutboundBatches(no);
return stockOutRecordMapper.insert(stockOutRecordDto);
}
@Override
public int update(Long id, StockOutRecordDto stockOutRecordDto) {
// 判断对象是否存在
StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
if (stockOutRecord == null) {
throw new BaseException("该出库记录不存在,无法更新!!!");
}
String[] ignoreProperties = {"id", "outbound_batches"};//排除id属性
BeanUtils.copyProperties(stockOutRecordDto, stockOutRecord, ignoreProperties);
return stockOutRecordMapper.updateById(stockOutRecord);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int batchDelete(List ids) {
for (Long id : ids) {
// 1. 查询出库记录
StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
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()
.eq(StockInventory::getProductModelId, stockOutRecord.getProductModelId())
);
if (stockInventory == null) {
throw new BaseException("库存记录中没有对应的产品,无法删除");
}
// 创建库存更新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()
.eq(StockUninventory::getProductModelId, stockOutRecord.getProductModelId())
);
if (stockUninventory == null) {
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 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 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().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("库存不足,无法扣减");
}
}
}
}