package com.ruoyi.stock.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.account.mapper.sales.AccountInvoiceApplicationMapper; import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper; import com.ruoyi.basic.pojo.Vehicle; import com.ruoyi.basic.service.IVehicleService; import com.ruoyi.common.enums.ReviewStatusEnum; import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; 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.StringUtils; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.outsourcing.enums.OutsourcingOrderStatusEnum; import com.ruoyi.outsourcing.mapper.OutsourcingOrderMapper; import com.ruoyi.outsourcing.mapper.OutsourcingOrderProductMapper; import com.ruoyi.outsourcing.pojo.OutsourcingOrder; import com.ruoyi.outsourcing.pojo.OutsourcingOrderProduct; import java.time.LocalDateTime; import com.ruoyi.stock.dto.OilOutAvailableDto; 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 jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.util.Collections; import java.util.List; import java.util.Objects; /** *

* 出库记录表 服务实现类 *

* * @author 芯导软件(江苏)有限公司 * @since 2026-01-21 05:27:04 */ @Service @RequiredArgsConstructor public class StockOutRecordServiceImpl extends ServiceImpl implements StockOutRecordService { /** * 出库类别:销售。销售类油品出库在审批通过时按批次扣减库存 */ private static final String OUT_CATEGORY_SALE = "销售"; private final StockOutRecordMapper stockOutRecordMapper; private final IVehicleService vehicleService; private final StockInventoryMapper stockInventoryMapper; private final StockUninventoryMapper stockUninventoryMapper; private final AccountSalesCollectionMapper accountSalesCollectionMapper; private final AccountInvoiceApplicationMapper accountInvoiceApplicationMapper; private final OutsourcingOrderProductMapper outsourcingOrderProductMapper; private final OutsourcingOrderMapper outsourcingOrderMapper; @Override public IPage listPage(Page page, StockOutRecordDto stockOutRecordDto) { return stockOutRecordMapper.listPage(page, stockOutRecordDto); } @Override public int add(StockOutRecordDto stockOutRecordDto) { if (OUT_CATEGORY_SALE.equals(stockOutRecordDto.getOutCategory())) { // 销售类油品出库审批通过时要按批次扣减库存,批次不能为空 if (StringUtils.isEmpty(stockOutRecordDto.getBatchNo())) { throw new BaseException("销售类出库必须选择出库批次"); } resolveVehicle(stockOutRecordDto); checkSaleOutAvailable(stockOutRecordDto); } LocalDateTime createTime = stockOutRecordDto.getCreateTime(); if (createTime == null) { createTime = LocalDateTime.now(); } String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK","outbound_batches", createTime); stockOutRecordDto.setOutboundBatches(no); stockOutRecordDto.setCreateTime(createTime); if (StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode().equals(stockOutRecordDto.getRecordType())){ stockOutRecordDto.setApprovalStatus(3); } 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) { //todo 如果出库与开票收款有关联则无法删除 if (accountSalesCollectionMapper.existsByStockOutRecordId(ids) || accountInvoiceApplicationMapper.existsByStockOutRecordId(ids)) { throw new BaseException("出库记录存在开票收款关联数据,无法删除!!!"); } for (Long id : ids) { StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id); if (stockOutRecord.getType().equals("0")) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(StockInventory::getProductModelId, stockOutRecord.getProductModelId()); if (StringUtils.isEmpty(stockOutRecord.getBatchNo())) { wrapper.isNull(StockInventory::getBatchNo); } else { wrapper.eq(StockInventory::getBatchNo, stockOutRecord.getBatchNo()); } StockInventory stockInventory = stockInventoryMapper.selectOne(wrapper); if (stockInventory == null) { throw new BaseException("库存记录中没有对应的产品,无法删除!!!"); }else { StockInventoryDto stockInRecordDto = new StockInventoryDto(); stockInRecordDto.setProductModelId(stockInventory.getProductModelId()); stockInRecordDto.setQualitity(stockOutRecord.getStockOutNum()); stockInRecordDto.setBatchNo(stockInventory.getBatchNo()); stockInventoryMapper.updateAddStockInventory(stockInRecordDto); } } else if (stockOutRecord.getType().equals("1")) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(StockUninventory::getProductModelId, stockOutRecord.getProductModelId()); if (StringUtils.isEmpty(stockOutRecord.getBatchNo())) { wrapper.isNull(StockUninventory::getBatchNo); } else { wrapper.eq(StockUninventory::getBatchNo, stockOutRecord.getBatchNo()); } StockUninventory stockUninventory = stockUninventoryMapper.selectOne(wrapper); if (stockUninventory == null) { throw new BaseException("库存记录中没有对应的产品,无法删除!!!"); }else { StockUninventoryDto stockUninventoryDto = new StockUninventoryDto(); stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId()); stockUninventoryDto.setQualitity(stockOutRecord.getStockOutNum()); stockUninventoryDto.setBatchNo(stockUninventory.getBatchNo()); stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto); } } else if (OUT_CATEGORY_SALE.equals(stockOutRecord.getOutCategory())) { // 销售类油品出库只有审批通过时才扣过库存,删除已通过的记录需按同批次归还 returnSaleOilOutStock(stockOutRecord); } } return stockOutRecordMapper.deleteBatchIds(ids); } /** * 销售类油品出库归还库存,仅对审批通过(即扣过库存)的记录生效。 * 归还量取 deduct_quantity(实际扣减量);改造前审批通过的记录没有该值,按全额归还 */ private void returnSaleOilOutStock(StockOutRecord stockOutRecord) { if (!ReviewStatusEnum.APPROVED.getCode().equals(stockOutRecord.getApprovalStatus()) || StringUtils.isEmpty(stockOutRecord.getBatchNo()) || stockOutRecord.getStockOutNum() == null) { return; } BigDecimal returnQuantity = stockOutRecord.getDeductQuantity() != null ? stockOutRecord.getDeductQuantity() : stockOutRecord.getStockOutNum(); if (returnQuantity.compareTo(BigDecimal.ZERO) <= 0) { return; } StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setProductModelId(stockOutRecord.getProductModelId()); stockInventoryDto.setBatchNo(stockOutRecord.getBatchNo()); stockInventoryDto.setQualitity(returnQuantity); stockInventoryMapper.updateAddStockInventory(stockInventoryDto); } @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(StockInQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue()); } } ExcelUtil util = new ExcelUtil<>(StockOutRecordExportData.class); util.exportExcel(response,list, "出库记录信息"); } @Override @Transactional(rollbackFor = Exception.class) public int batchDeletePending(List ids) { for (Long id : ids) { StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id); if (stockOutRecord == null) { throw new BaseException("出库记录不存在,无法删除!!!"); } if (stockOutRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockOutRecord.getApprovalStatus())) { throw new BaseException("只有待审批状态的记录才能删除,出库批次:" + stockOutRecord.getOutboundBatches()); } } return stockOutRecordMapper.deleteBatchIds(ids); } @Override @Transactional(rollbackFor = Exception.class) public int batchApprove(List ids, Integer approvalStatus) { if (CollectionUtils.isEmpty(ids)) { throw new BaseException("请选择至少一条数据"); } if (approvalStatus == null || (!ReviewStatusEnum.APPROVED.getCode().equals(approvalStatus) && !ReviewStatusEnum.REJECTED.getCode().equals(approvalStatus))) { throw new BaseException("审批状态值无效"); } for (Long id : ids) { StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id); if (stockOutRecord == null) { throw new BaseException("出库记录不存在,无法审批!!!"); } if (stockOutRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockOutRecord.getApprovalStatus())) { throw new BaseException("只有待审批状态的记录才能审批,出库批次:" + stockOutRecord.getOutboundBatches()); } // 审批通过时先扣减库存,扣减量(deduct_quantity)要随审批状态一起落库,所以放在更新之前 if (ReviewStatusEnum.APPROVED.getCode().equals(approvalStatus)) { if ("0".equals(stockOutRecord.getType())) { // 合格出库 -> 先查库存是否存在,存在才扣减 StockInventory stockInventory = getStockInventory(stockOutRecord.getProductModelId(), stockOutRecord.getBatchNo()); if (stockInventory == null) { throw new BaseException("合格库存记录不存在,出库批次:" + stockOutRecord.getOutboundBatches()); } StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setProductModelId(stockOutRecord.getProductModelId()); stockInventoryDto.setBatchNo(stockOutRecord.getBatchNo()); stockInventoryDto.setQualitity(stockOutRecord.getStockOutNum()); stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); } else if ("1".equals(stockOutRecord.getType())) { // 不合格出库 -> 先查库存是否存在,存在才扣减 StockUninventory stockUninventory = getStockUninventory(stockOutRecord.getProductModelId(), stockOutRecord.getBatchNo()); if (stockUninventory == null) { throw new BaseException("不合格库存记录不存在,出库批次:" + stockOutRecord.getOutboundBatches()); } StockUninventoryDto stockUninventoryDto = new StockUninventoryDto(); stockUninventoryDto.setProductModelId(stockOutRecord.getProductModelId()); stockUninventoryDto.setBatchNo(stockOutRecord.getBatchNo()); stockUninventoryDto.setQualitity(stockOutRecord.getStockOutNum()); stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto); } else if (OUT_CATEGORY_SALE.equals(stockOutRecord.getOutCategory())) { // 销售类油品出库(type 为空)审批通过时按批次扣减库存 deductSaleOilOutStock(stockOutRecord); } } stockOutRecord.setApprovalStatus(approvalStatus); stockOutRecordMapper.updateById(stockOutRecord); if (ReviewStatusEnum.APPROVED.getCode().equals(approvalStatus) && StockOutQualifiedRecordTypeEnum.OUTSOURCING_SCRAP_OUT.getCode() .equals(stockOutRecord.getRecordType())) { tryCompleteOutsourcingOrder(stockOutRecord); } } return ids.size(); } /** * 委外报废品出库审批通过后,若该委外订单的报废品选料行已全部出库审批通过, * 自动把订单从「已确认」推到「已完成」,省掉人工再点一次完成。 * *

直接用 mapper 而不是 {@code IOutsourcingOrderService#complete}:后者依赖本服务, * 两边都走构造器注入会形成循环依赖。驳回/反审不回退订单状态(处理错误审批走人工重新审批)。

*/ private void tryCompleteOutsourcingOrder(StockOutRecord record) { if (record.getRecordId() == null) { return; } OutsourcingOrderProduct line = outsourcingOrderProductMapper.selectById(record.getRecordId()); if (line == null || line.getOutsourcingOrderId() == null) { return; } Long orderId = line.getOutsourcingOrderId(); // isScrap 在 Java 侧过滤,与委外模块自己的 scrapLineIds 一致,不依赖 Boolean 字段的列名推断 List lineIds = outsourcingOrderProductMapper.selectList( Wrappers.lambdaQuery() .eq(OutsourcingOrderProduct::getOutsourcingOrderId, orderId)) .stream() .filter(item -> Boolean.TRUE.equals(item.getIsScrap())) .map(OutsourcingOrderProduct::getId) .toList(); if (lineIds.isEmpty()) { return; } // 出库记录一条对应一行明细;缺记录(被反审删除等)就不完成,与人工完成的校验口径一致 long approvedCount = listByRecordIds(lineIds, record.getRecordType()).stream() .filter(item -> ReviewStatusEnum.APPROVED.getCode().equals(item.getApprovalStatus())) .count(); if (approvedCount < lineIds.size()) { return; } // 条件更新,只有「已确认」能推进;已作废/已完成的订单不动 outsourcingOrderMapper.update(null, Wrappers.lambdaUpdate() .eq(OutsourcingOrder::getId, orderId) .eq(OutsourcingOrder::getStatus, OutsourcingOrderStatusEnum.CONFIRMED.getCode()) .set(OutsourcingOrder::getStatus, OutsourcingOrderStatusEnum.COMPLETED.getCode())); } /** * 销售类油品出库扣减库存。出库数量是本次装车总量,车上上次剩的油本次继续用, * 所以只从库存补足「出库数量 − 上次剩余油量」的差额。 * updateSubtractStockInventory 自带 qualitity >= 数量 的守卫,影响行数为 0 即库存不足, * 抛错由外层事务回滚 */ private void deductSaleOilOutStock(StockOutRecord stockOutRecord) { String suffix = ",出库批次:" + stockOutRecord.getOutboundBatches(); if (StringUtils.isEmpty(stockOutRecord.getBatchNo())) { throw new BaseException("销售类出库必须选择出库批次" + suffix); } BigDecimal quantity = stockOutRecord.getStockOutNum(); if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) { throw new BaseException("销售类出库数量必须大于0" + suffix); } BigDecimal lastResidual = getLastResidualOil(stockOutRecord.getVehicleId(), stockOutRecord.getProductModelId(), stockOutRecord.getId()); if (lastResidual.compareTo(quantity) > 0) { throw new BaseException("出库数量不能小于该车上次剩余油量" + toPlain(lastResidual) + suffix); } BigDecimal deduct = quantity.subtract(lastResidual); if (deduct.compareTo(BigDecimal.ZERO) > 0) { StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setProductModelId(stockOutRecord.getProductModelId()); stockInventoryDto.setBatchNo(stockOutRecord.getBatchNo()); stockInventoryDto.setQualitity(deduct); if (stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto) == 0) { throw new BaseException("库存不足无法出库" + suffix); } } // 车上剩油刚好够(deduct=0)时不涉及库存更新,同样落库便于删除时按实际扣减量归还 stockOutRecord.setDeductQuantity(deduct); } @Override @Transactional(rollbackFor = Exception.class) public int batchReAudit(List ids) { if (CollectionUtils.isEmpty(ids)) { throw new BaseException("请选择至少一条数据"); } for (Long id : ids) { StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id); if (stockOutRecord == null) { throw new BaseException("出库记录不存在,无法重新审核!!!"); } if (!ReviewStatusEnum.REJECTED.getCode().equals(stockOutRecord.getApprovalStatus())) { throw new BaseException("只有驳回状态的记录才能重新审核,出库批次:" + stockOutRecord.getOutboundBatches()); } stockOutRecord.setApprovalStatus(ReviewStatusEnum.PENDING_REVIEW.getCode()); stockOutRecordMapper.updateById(stockOutRecord); } return ids.size(); } @Override public int updateResidualOil(Long id, BigDecimal residualOil) { StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id); if (stockOutRecord == null) { throw new BaseException("该出库记录不存在,无法更新!!!"); } if (residualOil == null || residualOil.compareTo(BigDecimal.ZERO) < 0) { throw new BaseException("剩余油量不能为空且不能小于0"); } if (stockOutRecord.getStockOutNum() != null && residualOil.compareTo(stockOutRecord.getStockOutNum()) > 0) { throw new BaseException("剩余油量不能大于出库数量" + toPlain(stockOutRecord.getStockOutNum())); } StockOutRecord update = new StockOutRecord(); update.setId(id); update.setResidualOil(residualOil); return stockOutRecordMapper.updateById(update); } private StockInventory getStockInventory(Long productModelId, String batchNo) { LambdaQueryWrapper eq = new LambdaQueryWrapper<>(); eq.eq(StockInventory::getProductModelId, productModelId); if (StringUtils.isEmpty(batchNo)) { eq.isNull(StockInventory::getBatchNo); } else { eq.eq(StockInventory::getBatchNo, batchNo); } return stockInventoryMapper.selectOne(eq); } private StockUninventory getStockUninventory(Long productModelId, String batchNo) { LambdaQueryWrapper eq = new LambdaQueryWrapper<>(); eq.eq(StockUninventory::getProductModelId, productModelId); if (StringUtils.isEmpty(batchNo)) { eq.isNull(StockUninventory::getBatchNo); } else { eq.eq(StockUninventory::getBatchNo, batchNo); } return stockUninventoryMapper.selectOne(eq); } /** * 车辆回填:前端可以直接传 vehicleId,也可以只传车牌号, * 后者按车辆主数据反查回填。两者都拿不到就无法定位「上次剩余油量」,直接报错 */ private void resolveVehicle(StockOutRecordDto stockOutRecordDto) { if (stockOutRecordDto.getVehicleId() != null) { return; } if (StringUtils.isEmpty(stockOutRecordDto.getTruckPlateNo())) { throw new BaseException("销售类出库必须选择车辆"); } LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(Vehicle::getPlateNumber, stockOutRecordDto.getTruckPlateNo()); Vehicle vehicle = vehicleService.getOne(wrapper, false); if (vehicle == null) { throw new BaseException("车牌号 " + stockOutRecordDto.getTruckPlateNo() + " 不在车辆管理中,请先到车辆管理添加"); } stockOutRecordDto.setVehicleId(vehicle.getId()); } /** * 出库数量是本次装车总量,必须落在「上次剩余油量 ~ 上次剩余油量 + 库存」区间内 */ private void checkSaleOutAvailable(StockOutRecordDto stockOutRecordDto) { String suffix = ",出库批次:" + stockOutRecordDto.getOutboundBatches(); BigDecimal quantity = stockOutRecordDto.getStockOutNum(); if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) { throw new BaseException("销售类出库数量必须大于0" + suffix); } BigDecimal lastResidual = getLastResidualOil(stockOutRecordDto.getVehicleId(), stockOutRecordDto.getProductModelId(), null); if (lastResidual.compareTo(quantity) > 0) { throw new BaseException("出库数量不能小于该车上次剩余油量" + toPlain(lastResidual) + suffix); } BigDecimal stockQuantity = getStockQuantity(stockOutRecordDto.getProductModelId(), stockOutRecordDto.getBatchNo()); BigDecimal availableQty = lastResidual.add(stockQuantity); if (quantity.compareTo(availableQty) > 0) { throw new BaseException("出库数量超出可用量(上次剩余" + toPlain(lastResidual) + ",库存" + toPlain(stockQuantity) + ",可用" + toPlain(availableQty) + ")" + suffix); } } /** * 该车该规格上次送完货后剩余的油量,取最近一条填过残油且已审批的记录。 * excludeId 用于排除「本次要审批的这条自己」——它自己的 residual_oil 是送完之后的值,不能用来算本次扣减 */ private BigDecimal getLastResidualOil(Long vehicleId, Long productModelId, Long excludeId) { if (vehicleId == null || productModelId == null) { return BigDecimal.ZERO; } LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(StockOutRecord::getVehicleId, vehicleId) .eq(StockOutRecord::getProductModelId, productModelId) .isNotNull(StockOutRecord::getResidualOil) .eq(StockOutRecord::getApprovalStatus, ReviewStatusEnum.APPROVED.getCode()); if (excludeId != null) { wrapper.lt(StockOutRecord::getId, excludeId); } wrapper.orderByDesc(StockOutRecord::getId).last("limit 1"); StockOutRecord last = stockOutRecordMapper.selectOne(wrapper); return last == null || last.getResidualOil() == null ? BigDecimal.ZERO : last.getResidualOil(); } /** * 当前库存量,按规格合计;传了批次号再叠加批次条件 */ private BigDecimal getStockQuantity(Long productModelId, String batchNo) { if (productModelId == null) { return BigDecimal.ZERO; } LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(StockInventory::getProductModelId, productModelId); if (!StringUtils.isEmpty(batchNo)) { wrapper.eq(StockInventory::getBatchNo, batchNo); } return stockInventoryMapper.selectList(wrapper).stream() .map(StockInventory::getQualitity) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); } @Override public OilOutAvailableDto getOilOutAvailableQty(StockOutRecordDto query) { if (query == null || query.getVehicleId() == null || query.getProductModelId() == null) { throw new BaseException("请先选择车辆和产品规格"); } BigDecimal lastResidualOil = getLastResidualOil(query.getVehicleId(), query.getProductModelId(), query.getId()); BigDecimal stockQuantity = getStockQuantity(query.getProductModelId(), query.getBatchNo()); OilOutAvailableDto result = new OilOutAvailableDto(); result.setLastResidualOil(lastResidualOil); result.setStockQuantity(stockQuantity); result.setAvailableQty(lastResidualOil.add(stockQuantity)); return result; } private String toPlain(BigDecimal value) { return value == null ? "0" : value.stripTrailingZeros().toPlainString(); } @Override public List listByRecordIds(List recordIds, String recordType) { if (CollectionUtils.isEmpty(recordIds) || StringUtils.isEmpty(recordType)) { return Collections.emptyList(); } return stockOutRecordMapper.selectByRecordIds(recordIds, recordType); } @Override @Transactional(rollbackFor = Exception.class) public int releaseByRecordIds(List recordIds, String recordType) { List records = listByRecordIds(recordIds, recordType); if (records.isEmpty()) { return 0; } for (StockOutRecord record : records) { if (!ReviewStatusEnum.APPROVED.getCode().equals(record.getApprovalStatus())) { continue; } // type=0 归还合格库存(stock_inventory),type=1 归还不合格库存(stock_uninventory) if ("0".equals(record.getType())) { StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setProductModelId(record.getProductModelId()); stockInventoryDto.setBatchNo(record.getBatchNo()); stockInventoryDto.setQualitity(record.getStockOutNum()); if (stockInventoryMapper.updateAddStockInventory(stockInventoryDto) == 0) { throw new BaseException("库存记录中没有对应的产品,无法归还,出库批次:" + record.getOutboundBatches()); } } else if ("1".equals(record.getType())) { StockUninventoryDto stockUninventoryDto = new StockUninventoryDto(); stockUninventoryDto.setProductModelId(record.getProductModelId()); stockUninventoryDto.setBatchNo(record.getBatchNo()); stockUninventoryDto.setQualitity(record.getStockOutNum()); if (stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto) == 0) { throw new BaseException("库存记录中没有对应的产品,无法归还,出库批次:" + record.getOutboundBatches()); } } else { throw new BaseException("出库记录类型异常,无法归还库存,出库批次:" + record.getOutboundBatches()); } } return stockOutRecordMapper.deleteBatchIds(records.stream().map(StockOutRecord::getId).toList()); } }