src/main/java/com/ruoyi/stock/controller/StockInRecordController.java
@@ -7,7 +7,6 @@ import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.stock.dto.StockInRecordDto; import com.ruoyi.stock.service.StockInRecordService; import com.ruoyi.stock.word.WeighbridgeDocGenerator; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; @@ -24,9 +23,6 @@ public class StockInRecordController { @Autowired private StockInRecordService stockInRecordService; @Autowired private WeighbridgeDocGenerator weighbridgeDocGenerator; @GetMapping("/listPage") @Log(title = "生产入库-入库管理-列表", businessType = BusinessType.OTHER) @@ -40,13 +36,7 @@ @PreAuthorize("@ss.hasPermi('receipt_edit')") @ApiOperation("编辑入库记录") public AjaxResult editStockInStock(@RequestBody StockInRecordDto stockInRecordDto) { if (stockInRecordDto.getId() == null) { return AjaxResult.error("入库记录ID不能为空"); } stockInRecordDto.setStockInNum(stockInRecordDto.getNetWeight()); String absoluteDocPath = weighbridgeDocGenerator.generateWeighbridgeDoc(stockInRecordDto); stockInRecordDto.setWeighbridgeDocPath(absoluteDocPath); return AjaxResult.success(stockInRecordService.updateById(stockInRecordDto)); return AjaxResult.success(stockInRecordService.editStockInStock(stockInRecordDto)); } @DeleteMapping("") src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java
@@ -2,11 +2,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.stock.dto.StockInRecordDto; import com.ruoyi.stock.dto.StockOutRecordDto; import com.ruoyi.stock.service.StockOutRecordService; import com.ruoyi.stock.word.WeighbridgeDocGenerator; @@ -54,17 +52,9 @@ @PostMapping("/editStockOut") @PreAuthorize("@ss.hasPermi('dispatch_edit')") @ApiOperation("编辑出库记录") public AjaxResult update(@RequestBody StockOutRecordDto stockOutRecordDto) { if (stockOutRecordDto.getId() == null) { return AjaxResult.error("出库记录ID不能为空"); } StockInRecordDto stockInRecordDto = new StockInRecordDto(); BeanUtils.copyProperties(stockOutRecordDto, stockInRecordDto); stockInRecordDto.setStockInNum(stockOutRecordDto.getStockOutNum()); String absoluteDocPath = weighbridgeDocGenerator.generateWeighbridgeDoc(stockInRecordDto); stockOutRecordDto.setWeighbridgeDocPath(absoluteDocPath); stockOutRecordDto.setStockOutNum(stockOutRecordDto.getStockOutNum()); return AjaxResult.success(stockOutRecordService.updateById(stockOutRecordDto)); public AjaxResult editStockOut(@RequestBody StockOutRecordDto stockOutRecordDto) { return AjaxResult.success(stockOutRecordService.editStockOut(stockOutRecordDto)); } @DeleteMapping("") src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java
@@ -16,10 +16,6 @@ * 产品规格 */ private String model; /** * 产品单位 */ private String unit; private String timeStr; @@ -31,6 +27,9 @@ //现净重 private BigDecimal currentWeight; //产品类型 private String productType; //产品类型(0-原材料) private Integer productType; //产品大类 private String parentName; } src/main/java/com/ruoyi/stock/dto/StockOutRecordDto.java
@@ -17,12 +17,13 @@ * 产品规格 */ private String model; /** * 产品单位 */ private String unit; private String timeStr; private String createBy; private String parentName; //产品类型(0-原材料) private Integer productType; } src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
@@ -99,4 +99,7 @@ @ApiModelProperty("产品id") private Long productId; @ApiModelProperty(value = "单位") private String unit; } src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java
@@ -97,4 +97,9 @@ @ApiModelProperty("磅单文件路径") private String weighbridgeDocPath; /** * 产品单位 */ private String unit; } src/main/java/com/ruoyi/stock/service/StockInRecordService.java
@@ -17,4 +17,6 @@ int batchDelete(List<Long> ids); void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto); int editStockInStock(StockInRecordDto stockInRecordDto); } src/main/java/com/ruoyi/stock/service/StockOutRecordService.java
@@ -27,4 +27,6 @@ int batchDelete(List<Long> ids); void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto); int editStockOut(StockOutRecordDto stockOutRecordDto); } src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -1,9 +1,12 @@ 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; @@ -22,20 +25,29 @@ import com.ruoyi.stock.pojo.StockInventory; import com.ruoyi.stock.pojo.StockUninventory; import com.ruoyi.stock.service.StockInRecordService; import lombok.AllArgsConstructor; 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; @Service @AllArgsConstructor public class StockInRecordServiceImpl extends ServiceImpl<StockInRecordMapper, StockInRecord> implements StockInRecordService { @Autowired private StockInRecordMapper stockInRecordMapper; @Autowired private StockInventoryMapper stockInventoryMapper; @Autowired private StockUninventoryMapper stockUninventoryMapper; @Autowired private WeighbridgeDocGenerator weighbridgeDocGenerator; @Autowired private ProductModelMapper productModelMapper; @Override public IPage<StockInRecordDto> listPage(Page page, StockInRecordDto stockInRecordDto) { @@ -58,28 +70,71 @@ public int batchDelete(List<Long> ids) { for (Long id : ids) { StockInRecord stockInRecord = stockInRecordMapper.selectById(id); if (stockInRecord.getType().equals("0")) { StockInventory stockInventory = stockInventoryMapper.selectOne(new LambdaQueryWrapper<StockInventory>().eq(StockInventory::getProductModelId, stockInRecord.getProductModelId())); if (stockInRecord == null) { throw new BaseException("入库记录不存在,ID:" + id); } // 查询产品型号(用于单位转换) ProductModel productModel = productModelMapper.selectById(stockInRecord.getProductModelId()); if (productModel == null) { throw new BaseException("产品型号不存在,无法删除"); } // 获取入库数量及其单位,转换为库存单位 BigDecimal stockInNum = stockInRecord.getStockInNum(); // 入库记录中存储的数量 String sourceUnit = stockInRecord.getUnit(); // 入库记录的单位 String stockUnit = productModel.getUnit(); // 库存单位 // 将入库数量转换为库存单位 BigDecimal convertedQuantity = convertQuantityToStockUnit(stockInNum, sourceUnit, stockUnit); // 根据入库类型处理不同的库存表 if ("0".equals(stockInRecord.getType())) { // 正常入库,删除时扣减库存 StockInventory stockInventory = stockInventoryMapper.selectOne( new LambdaQueryWrapper<StockInventory>() .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId()) ); if (stockInventory == null) { throw new BaseException("库存记录中没有对应的产品,无法删除!!!"); }else { StockInventoryDto stockInRecordDto = new StockInventoryDto(); stockInRecordDto.setProductModelId(stockInventory.getProductModelId()); stockInRecordDto.setQualitity(stockInRecord.getStockInNum()); stockInventoryMapper.updateSubtractStockInventory(stockInRecordDto); throw new BaseException("库存记录中没有对应的产品,无法删除"); } }else if (stockInRecord.getType().equals("1")) { StockUninventory stockUninventory = stockUninventoryMapper.selectOne(new LambdaQueryWrapper<StockUninventory>().eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId())); StockInventoryDto inventoryDto = new StockInventoryDto(); inventoryDto.setProductModelId(stockInventory.getProductModelId()); inventoryDto.setQualitity(convertedQuantity); // 扣减库存(删除入库记录相当于减少库存) int affectedRows = stockInventoryMapper.updateSubtractStockInventory(inventoryDto); if (affectedRows == 0) { throw new BaseException("扣减库存失败,库存可能不足"); } } else if ("1".equals(stockInRecord.getType())) { // 非正常入库,删除时扣减非正常库存 StockUninventory stockUninventory = stockUninventoryMapper.selectOne( new LambdaQueryWrapper<StockUninventory>() .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId()) ); if (stockUninventory == null) { throw new BaseException("库存记录中没有对应的产品,无法删除!!!"); }else { StockUninventoryDto stockUninventoryDto = new StockUninventoryDto(); stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId()); stockUninventoryDto.setQualitity(stockInRecord.getStockInNum()); stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto); throw new BaseException("非正常库存记录中没有对应的产品,无法删除"); } // 创建非正常库存更新DTO StockUninventoryDto uninventoryDto = new StockUninventoryDto(); uninventoryDto.setProductModelId(stockUninventory.getProductModelId()); uninventoryDto.setQualitity(convertedQuantity); // 扣减非正常库存 int affectedRows = stockUninventoryMapper.updateSubtractStockUnInventory(uninventoryDto); if (affectedRows == 0) { throw new BaseException("扣减非正常库存失败"); } } } // 5. 批量删除入库记录 return stockInRecordMapper.deleteBatchIds(ids); } @@ -96,4 +151,150 @@ ExcelUtil<StockInRecordExportData> util = new ExcelUtil<>(StockInRecordExportData.class); util.exportExcel(response,list, "入库记录信息"); } @Override public int editStockInStock(StockInRecordDto stockInRecordDto) { if (stockInRecordDto.getId() == null) { throw new BaseException("入库记录ID不能为空"); } // 1. 查询原始入库记录 StockInRecord originalRecord = stockInRecordMapper.selectById(stockInRecordDto.getId()); if (originalRecord == null) { throw new BaseException("入库记录不存在"); } // 2. 查询产品型号信息(用于单位转换) ProductModel productModel = productModelMapper.selectById(originalRecord.getProductModelId()); if (productModel == null) { throw new BaseException("产品型号不存在"); } // 3. 获取原始入库数量及其单位 BigDecimal originalNetWeight = originalRecord.getStockInNum(); // 原始记录中存储的数量 String originalUnit = originalRecord.getUnit(); // 原始记录的单位 String stockUnit = productModel.getUnit(); // 库存单位 // 将原始入库数量转换为库存单位 BigDecimal originalNetWeightInStockUnit = convertQuantityToStockUnit(originalNetWeight, originalUnit, stockUnit); // 4. 获取新入库数量及其单位 BigDecimal newNetWeight = stockInRecordDto.getNetWeight(); // 新输入的数量 if (newNetWeight == null) { throw new BaseException("净重不能为空"); } String newUnit = stockInRecordDto.getUnit(); // 新输入的单位 // 将新入库数量转换为库存单位 BigDecimal newNetWeightInStockUnit = convertQuantityToStockUnit(newNetWeight, newUnit, stockUnit); // 5. 计算库存变化量(使用统一转换后的库存单位值) // 入库数量变化量(正数表示入库变多,需要增加库存;负数表示入库变少,需要减少库存) BigDecimal diffQuantity = newNetWeightInStockUnit.subtract(originalNetWeightInStockUnit); // 6. 更新入库记录(保存用户输入的原始数值和单位) stockInRecordDto.setStockInNum(stockInRecordDto.getNetWeight()); stockInRecordDto.setUnit(stockInRecordDto.getUnit()); // 7. 生成磅单 String absoluteDocPath = weighbridgeDocGenerator.generateWeighbridgeDoc(stockInRecordDto); stockInRecordDto.setWeighbridgeDocPath(absoluteDocPath); int updateResult = stockInRecordMapper.updateById(stockInRecordDto); // 8. 更新库存(仅在数量发生变化时) if (diffQuantity.compareTo(BigDecimal.ZERO) != 0) { updateInventory(stockInRecordDto.getProductId(), diffQuantity); } 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 diffQuantity 库存变化量(正数为增加,负数为减少) */ private void updateInventory(Long productId, BigDecimal diffQuantity) { // 查询当前库存 StockInventory stockInventory = stockInventoryMapper.selectOne( new QueryWrapper<StockInventory>().lambda() .eq(StockInventory::getProductId, productId) ); if (stockInventory == null) { throw new BaseException("库存记录不存在"); } StockInventoryDto inventoryDto = new StockInventoryDto(); BeanUtils.copyProperties(stockInventory, inventoryDto); inventoryDto.setQualitity(diffQuantity.abs()); // 根据变化量正负调用不同的更新方法 if (diffQuantity.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("库存不足,无法扣减"); } } } } src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -11,6 +11,7 @@ import com.ruoyi.basic.pojo.Product; import com.ruoyi.basic.pojo.ProductModel; import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.equipmentenergyconsumption.mapper.ElectricityConsumptionAreaMapper; import com.ruoyi.framework.web.domain.R; @@ -37,6 +38,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; import java.util.Optional; /** * <p> @@ -73,6 +75,7 @@ stockInRecordDto.setRecordId(stockInventoryDto.getRecordId()); stockInRecordDto.setRecordType(stockInventoryDto.getRecordType()); stockInRecordDto.setWeighingOperator(stockInventoryDto.getWeighingOperator()); stockInRecordDto.setUnit(stockInventoryDto.getUnit()); Long modelId; if (stockInventoryDto.getProductId() != null) { stockInRecordDto.setProductId(stockInventoryDto.getProductId()); @@ -139,6 +142,7 @@ stockInRecordDto.setGrossWeight(stockInventoryDto.getGrossWeight()); stockInRecordDto.setTareWeight(stockInventoryDto.getTareWeight()); stockInRecordDto.setLicensePlateNo(stockInventoryDto.getLicensePlateNo()); stockInRecordDto.setUnit(stockInventoryDto.getUnit()); // 生成磅单 String absoluteDocPath = weighbridgeDocGenerator.generateWeighbridgeDoc(stockInRecordDto); stockInRecordDto.setWeighbridgeDocPath(absoluteDocPath); @@ -181,68 +185,111 @@ @Transactional(rollbackFor = Exception.class) public Boolean subtractStockInventory(StockInventoryDto stockInventoryDto) { // 1. 查询产品型号和库存信息 ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId()); BigDecimal weight = compareUnit(stockInventoryDto, productModel); // 新增出库记录 StockOutRecordDto stockOutRecordDto = new StockOutRecordDto(); stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId()); stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType()); stockOutRecordDto.setWeighingDate(stockInventoryDto.getWeighingDate()); stockOutRecordDto.setStockOutNum(stockInventoryDto.getNetWeight()); stockOutRecordDto.setNetWeight(stockInventoryDto.getNetWeight()); stockOutRecordDto.setGrossWeight(stockInventoryDto.getGrossWeight()); stockOutRecordDto.setTareWeight(stockInventoryDto.getTareWeight()); stockOutRecordDto.setWeighingOperator(stockInventoryDto.getWeighingOperator()); stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId()); stockOutRecordDto.setLicensePlateNo(stockInventoryDto.getLicensePlateNo()); stockOutRecordDto.setProductId(stockInventoryDto.getProductId()); stockOutRecordDto.setRemark(stockInventoryDto.getRemark()); stockOutRecordDto.setType("0"); //生成磅单 StockInRecordDto stockInRecordDto = new StockInRecordDto(); BeanUtils.copyProperties(stockOutRecordDto, stockInRecordDto); String absoluteDocPath = weighbridgeDocGenerator.generateWeighbridgeDoc(stockInRecordDto); stockOutRecordDto.setWeighbridgeDocPath(absoluteDocPath); stockOutRecordService.add(stockOutRecordDto); StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId())); if (ObjectUtils.isEmpty(oldStockInventory)) { throw new RuntimeException("产品库存不存在"); } BigDecimal lockedQty = oldStockInventory.getLockedQuantity(); if (lockedQty == null) { lockedQty = BigDecimal.ZERO; } stockInventoryDto.setQualitity(weight); if (stockInventoryDto.getQualitity().compareTo(oldStockInventory.getQualitity().subtract(lockedQty)) > 0) { throw new RuntimeException("库存不足无法出库"); if (productModel == null) { throw new BaseException("产品型号不存在"); } stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); StockInventory oldStockInventory = stockInventoryMapper.selectOne( new QueryWrapper<StockInventory>().lambda() .eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()) ); if (oldStockInventory == null) { throw new BaseException("产品库存不存在"); } // 2. 计算并转换出库数量 BigDecimal outQuantity = compareUnit(stockInventoryDto, productModel); // 3. 检查库存是否充足(考虑锁定库存) BigDecimal availableQuantity = oldStockInventory.getQualitity() .subtract(Optional.ofNullable(oldStockInventory.getLockedQuantity()).orElse(BigDecimal.ZERO)); if (outQuantity.compareTo(availableQuantity) > 0) { throw new BaseException("库存不足,当前可用库存:" + availableQuantity); } // 4. 扣减库存 stockInventoryDto.setQualitity(outQuantity); int affectedRows = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); if (affectedRows == 0) { throw new BaseException("扣减库存失败,可能库存不足或数据已被修改"); } // 5. 创建并保存出库记录 createAndSaveStockOutRecord(stockInventoryDto, productModel); return true; } /** * 计算并转换出库数量(统一转换为库存单位) */ private BigDecimal compareUnit(StockInventoryDto stockInventoryDto, ProductModel productModel) { String unit = ""; if (productModel != null) { unit = productModel.getUnit(); // 获取原始出库数量(根据产品类型选择净重或数量) BigDecimal originalQuantity = (stockInventoryDto.getProductType() != null && stockInventoryDto.getProductType() == 0) ? stockInventoryDto.getNetWeight() : stockInventoryDto.getQualitity(); String sourceUnit = stockInventoryDto.getUnit(); String targetUnit = productModel.getUnit(); // 单位相同,直接返回 if (sourceUnit.equals(targetUnit)) { return originalQuantity; } BigDecimal weight; if (stockInventoryDto.getUnit().equals(unit)) { weight = stockInventoryDto.getProductType() != null && stockInventoryDto.getProductType() == 0 ? stockInventoryDto.getNetWeight() : stockInventoryDto.getQualitity(); // 单位转换 if ("吨".equals(targetUnit)) { // 转换为吨 return originalQuantity.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP); } else if ("公斤".equals(targetUnit)) { // 转换为公斤 return originalQuantity.multiply(BigDecimal.valueOf(1000)); } else { if ("吨".equals(unit)) { weight =stockInventoryDto.getProductType() != null && stockInventoryDto.getProductType() == 0 ? stockInventoryDto.getNetWeight().divide(BigDecimal.valueOf(1000),2, RoundingMode.HALF_UP) : stockInventoryDto.getQualitity().divide(BigDecimal.valueOf(1000),2,RoundingMode.HALF_UP); } else if ("公斤".equals(unit)) { weight = stockInventoryDto.getProductType() != null && stockInventoryDto.getProductType() == 0 ? stockInventoryDto.getNetWeight().multiply(BigDecimal.valueOf(1000)) : stockInventoryDto.getQualitity().multiply(BigDecimal.valueOf(1000)); }else { weight =stockInventoryDto.getProductType() != null && stockInventoryDto.getProductType() == 0 ? stockInventoryDto.getNetWeight() : stockInventoryDto.getQualitity(); } // 单位不匹配时,返回原值 return originalQuantity; } return weight; } /** * 创建并保存出库记录 */ private void createAndSaveStockOutRecord(StockInventoryDto stockInventoryDto, ProductModel productModel) { // 创建出库记录DTO StockOutRecordDto stockOutRecordDto = new StockOutRecordDto(); if (stockInventoryDto.getProductType() != null && stockInventoryDto.getProductType() == 0) { stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId()); stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType()); stockOutRecordDto.setWeighingDate(stockInventoryDto.getWeighingDate()); stockOutRecordDto.setStockOutNum(stockInventoryDto.getNetWeight()); stockOutRecordDto.setNetWeight(stockInventoryDto.getNetWeight()); stockOutRecordDto.setGrossWeight(stockInventoryDto.getGrossWeight()); stockOutRecordDto.setTareWeight(stockInventoryDto.getTareWeight()); stockOutRecordDto.setWeighingOperator(stockInventoryDto.getWeighingOperator()); stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId()); stockOutRecordDto.setLicensePlateNo(stockInventoryDto.getLicensePlateNo()); stockOutRecordDto.setProductId(stockInventoryDto.getProductId()); stockOutRecordDto.setRemark(stockInventoryDto.getRemark()); stockOutRecordDto.setUnit(stockInventoryDto.getUnit()); stockOutRecordDto.setType("0"); // 生成磅单 StockInRecordDto stockInRecordDto = new StockInRecordDto(); BeanUtils.copyProperties(stockOutRecordDto, stockInRecordDto); String absoluteDocPath = weighbridgeDocGenerator.generateWeighbridgeDoc(stockInRecordDto); stockOutRecordDto.setWeighbridgeDocPath(absoluteDocPath); } else { stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId()); stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType()); stockOutRecordDto.setStockOutNum(stockInventoryDto.getQualitity()); stockOutRecordDto.setNetWeight(stockInventoryDto.getQualitity()); stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId()); stockOutRecordDto.setProductId(stockInventoryDto.getProductId()); stockOutRecordDto.setRemark(stockInventoryDto.getRemark()); stockOutRecordDto.setUnit(stockInventoryDto.getUnit()); stockOutRecordDto.setType("0"); } stockOutRecordService.add(stockOutRecordDto); } @Override src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
@@ -1,9 +1,12 @@ 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; @@ -11,6 +14,7 @@ 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; @@ -22,10 +26,14 @@ import com.ruoyi.stock.pojo.StockOutRecord; import com.ruoyi.stock.pojo.StockUninventory; import com.ruoyi.stock.service.StockOutRecordService; import lombok.AllArgsConstructor; 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; /** @@ -37,11 +45,17 @@ * @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; @Autowired private StockUninventoryMapper stockUninventoryMapper; @Autowired private WeighbridgeDocGenerator weighbridgeDocGenerator; @Autowired private ProductModelMapper productModelMapper; @Override public IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto) { @@ -59,7 +73,7 @@ public int update(Long id, StockOutRecordDto stockOutRecordDto) { // 判断对象是否存在 StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id); if (stockOutRecord == null){ if (stockOutRecord == null) { throw new BaseException("该出库记录不存在,无法更新!!!"); } @@ -69,31 +83,77 @@ } @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); } @@ -103,11 +163,168 @@ for (StockOutRecordExportData stockInRecordExportData : list) { if (stockInRecordExportData.getType().equals("0")) { stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockOutQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue()); }else { } else { stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockInUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue()); } } ExcelUtil<StockOutRecordExportData> util = new ExcelUtil<>(StockOutRecordExportData.class); util.exportExcel(response,list, "出库记录信息"); 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("库存不足,无法扣减"); } } } } src/main/java/com/ruoyi/stock/word/WeighbridgeDocGenerator.java
@@ -204,7 +204,7 @@ } XWPFRun unitRun = unitPara.createRun(); unitRun.setText("计量单位:(吨)"); unitRun.setText("计量单位:" + dto.getUnit()); unitRun.setFontSize(12); unitRun.setFontFamily("宋体"); @@ -303,9 +303,9 @@ String[] data = { dto.getLicensePlateNo() != null ? dto.getLicensePlateNo() : "", getProductModelName(dto.getProductId()), grossWeight + "t", tareWeight + "t", netWeight + "t", grossWeight + dto.getUnit(), tareWeight + dto.getUnit(), netWeight + dto.getUnit(), dto.getRemark() != null ? dto.getRemark() : "" }; @@ -362,7 +362,7 @@ ); XWPFTableCell startCell = totalRow.getCell(1); startCell.setText(netWeightChinese + "吨"); startCell.setText(netWeightChinese + dto.getUnit()); startCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); while (startCell.getParagraphs().size() > 1) { src/main/resources/mapper/stock/StockInRecordMapper.xml
@@ -4,9 +4,9 @@ <select id="listPage" resultType="com.ruoyi.stock.dto.StockInRecordDto"> SELECT p1.product_name AS product_type, p1.product_name AS parentName, sir.*, p.product_name AS product_name, p.product_name AS productName, pm.model, pm.unit, sir.weighbridge_doc_path, src/main/resources/mapper/stock/StockOutRecordMapper.xml
@@ -20,6 +20,7 @@ <select id="listPage" resultType="com.ruoyi.stock.dto.StockOutRecordDto"> SELECT p1.product_name AS parentName, sor.*, p.product_name as productName, pm.model, @@ -29,6 +30,7 @@ FROM stock_out_record as sor LEFT JOIN product_model as pm on sor.product_model_id = pm.id LEFT JOIN product as p on pm.product_id = p.id INNER JOIN product p1 ON p1.id = p.parent_id AND p1.product_name != '半成品' LEFT JOIN sys_user as u on sor.create_user = u.user_id <where> 1=1