| src/main/java/com/ruoyi/account/bean/vo/purchase/PurchaseInboundVo.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/purchase/dto/PurchaseStockInDto.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/stock/pojo/StockInRecord.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/resources/mapper/stock/StockInRecordMapper.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/main/java/com/ruoyi/account/bean/vo/purchase/PurchaseInboundVo.java
@@ -42,6 +42,18 @@ @Excel(name = "金额") private BigDecimal InboundAmount; @Schema(description = "理论入库数量") @Excel(name = "理论入库数量") private BigDecimal theoryStockInNum; @Schema(description = "实际入库数量") @Excel(name = "实际入库数量") private BigDecimal stockInNum; @Schema(description = "差额") @Excel(name = "差额") private BigDecimal differenceNum; @Schema(description = "采购订单号") @Excel(name = "采购订单号") private String purchaseContractNumber; src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -156,6 +156,9 @@ stockInventoryDto.setIsContainsWater(isContainsWater); stockInventoryDto.setWaterContent(waterContent); stockInventoryDto.setTheoryStockInNum(theoryStockInNum); if (theoryStockInNum != null && actualStockInNum != null) { stockInventoryDto.setDifferenceNum(theoryStockInNum.subtract(actualStockInNum)); } stockInventoryService.addStockInRecordOnly(stockInventoryDto); } src/main/java/com/ruoyi/purchase/dto/PurchaseStockInDto.java
@@ -35,7 +35,7 @@ @Schema(description = "含水量(%)") private BigDecimal waterContent; @Schema(description = "实际入库数量") @Schema(description = "实际入库数量,后端会按理论数量和含水量重新计算") private BigDecimal actualInboundQuantity; } } src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -22,7 +22,6 @@ import com.ruoyi.basic.pojo.ProductModel; import com.ruoyi.basic.pojo.SupplierManage; import com.ruoyi.basic.utils.FileUtil; import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.enums.ApprovalStatusEnum; import com.ruoyi.common.enums.ReviewStatusEnum; @@ -1093,35 +1092,44 @@ if (salesLedgerProduct == null) { throw new BaseException("采购产品不存在"); } if (!purchaseLedger.getId().equals(salesLedgerProduct.getSalesLedgerId()) || !Integer.valueOf(2).equals(salesLedgerProduct.getType())) { throw new BaseException("采购产品与采购台账不匹配"); } // 获取理论入库数量(前端传入的inboundQuantity) ProductModel productModel = productModelMapper.selectById(salesLedgerProduct.getProductModelId()); if (Boolean.TRUE.equals(salesLedgerProduct.getIsChecked())) { throw new BaseException("产品【" + productModel.getModel() + "】需要质检,请走质检入库流程"); } BigDecimal theoryStockInNum = item.getInboundQuantity(); if (theoryStockInNum == null) { theoryStockInNum = salesLedgerProduct.getQuantity(); } // 空值和非正数校验 if (theoryStockInNum == null || theoryStockInNum.compareTo(BigDecimal.ZERO) <= 0) { throw new BaseException("理论入库数量必须大于0"); } // 获取实际入库数量(前端传入的actualInboundQuantity) BigDecimal actualStockInNum = item.getActualInboundQuantity(); if (actualStockInNum == null) { actualStockInNum = theoryStockInNum; BigDecimal approvedStockInNum = getApprovedPurchaseStockInNum( purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), salesLedgerProduct.getId(), salesLedgerProduct.getProductModelId() ); BigDecimal remainingStockInNum = salesLedgerProduct.getQuantity().subtract(approvedStockInNum); if (remainingStockInNum.compareTo(BigDecimal.ZERO) <= 0) { throw new BaseException("产品【" + productModel.getModel() + "】已完成入库"); } // 非正数校验 if (theoryStockInNum.compareTo(remainingStockInNum) > 0) { throw new BaseException("产品【" + productModel.getModel() + "】理论入库数量不能大于待入库数量" + remainingStockInNum.stripTrailingZeros().toPlainString()); } Boolean isContainsWater = Boolean.TRUE.equals(item.getIsContainsWater()); BigDecimal waterContent = normalizeWaterContent(isContainsWater, item.getWaterContent()); BigDecimal actualStockInNum = calculateActualStockInNum(theoryStockInNum, isContainsWater, waterContent); if (actualStockInNum.compareTo(BigDecimal.ZERO) <= 0) { throw new BaseException("实际入库数量必须大于0"); } // 获取是否含水和含水量 Boolean isContainsWater = item.getIsContainsWater(); BigDecimal waterContent = item.getWaterContent(); if (waterContent == null) { waterContent = BigDecimal.ZERO; } // 调用StockUtils进行入库(带含水量信息) stockUtils.addStockWithBatchNo( salesLedgerProduct.getProductModelId(), actualStockInNum, @@ -1139,6 +1147,41 @@ return count; } private BigDecimal getApprovedPurchaseStockInNum(Long purchaseLedgerId, String purchaseContractNumber, Long productId, Long productModelId) { if (purchaseLedgerId == null || productId == null || productModelId == null || !StringUtils.hasText(purchaseContractNumber)) { return BigDecimal.ZERO; } List<StockInRecord> stockRecords = findDirectStockRecords(purchaseLedgerId, purchaseContractNumber, productModelId, productId); return stockRecords.stream() .filter(Objects::nonNull) .filter(item -> ReviewStatusEnum.APPROVED.getCode().equals(item.getApprovalStatus())) .map(StockInRecord::getStockInNum) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); } private BigDecimal normalizeWaterContent(Boolean isContainsWater, BigDecimal waterContent) { if (!Boolean.TRUE.equals(isContainsWater)) { return BigDecimal.ZERO; } if (waterContent == null) { throw new BaseException("含水时必须填写含水量"); } if (waterContent.compareTo(BigDecimal.ZERO) < 0 || waterContent.compareTo(new BigDecimal("100")) >= 0) { throw new BaseException("含水量必须在0到100之间"); } return waterContent; } private BigDecimal calculateActualStockInNum(BigDecimal theoryStockInNum, Boolean isContainsWater, BigDecimal waterContent) { if (!Boolean.TRUE.equals(isContainsWater)) { return theoryStockInNum; } BigDecimal percent = waterContent.divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP); BigDecimal actualStockInNum = theoryStockInNum.multiply(BigDecimal.ONE.subtract(percent)); return actualStockInNum.setScale(4, RoundingMode.HALF_UP); } /** * 下划线命名转驼峰命名 */ src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
@@ -101,4 +101,7 @@ @Schema(description = "理论入库数量") private BigDecimal theoryStockInNum; @Schema(description = "差额(理论入库数量-实际入库数量)") private BigDecimal differenceNum; } src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
@@ -88,4 +88,8 @@ @Schema(description = "理论入库数量") private BigDecimal theoryStockInNum; @TableField(exist = false) @Schema(description = "差额(理论入库数量-实际入库数量)") private BigDecimal differenceNum; } src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -36,6 +36,7 @@ import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; @Service @@ -231,6 +232,7 @@ final BigDecimal finalStockInNum; if (item.getStockInNum() != null && item.getStockInNum().compareTo(BigDecimal.ZERO) > 0) { finalStockInNum = item.getStockInNum(); adjustPurchaseInboundAuditFields(stockInRecord, finalStockInNum); // 更新入库记录的入库数量 stockInRecord.setStockInNum(finalStockInNum); } else { @@ -296,6 +298,28 @@ return items.size(); } private void adjustPurchaseInboundAuditFields(StockInRecord stockInRecord, BigDecimal finalStockInNum) { if (stockInRecord == null || finalStockInNum == null) { return; } if (!StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode().equals(stockInRecord.getRecordType())) { return; } BigDecimal theoryStockInNum = stockInRecord.getTheoryStockInNum(); if (theoryStockInNum == null || theoryStockInNum.compareTo(BigDecimal.ZERO) <= 0) { return; } if (finalStockInNum.compareTo(theoryStockInNum) > 0) { throw new BaseException("采购入库审核时,实际入库数量不能大于理论入库数量"); } if (Boolean.TRUE.equals(stockInRecord.getIsContainsWater())) { BigDecimal waterContent = theoryStockInNum.subtract(finalStockInNum) .multiply(new BigDecimal("100")) .divide(theoryStockInNum, 4, RoundingMode.HALF_UP); stockInRecord.setWaterContent(waterContent); } } private static @NonNull StockInventoryDto getStockInventoryDto(StockInRecord stockInRecord) { StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setProductModelId(stockInRecord.getProductModelId()); src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -194,6 +194,7 @@ stockInRecordDto.setIsContainsWater(stockInventoryDto.getIsContainsWater()); stockInRecordDto.setWaterContent(stockInventoryDto.getWaterContent()); stockInRecordDto.setTheoryStockInNum(stockInventoryDto.getTheoryStockInNum()); stockInRecordDto.setDifferenceNum(stockInventoryDto.getDifferenceNum()); stockInRecordService.add(stockInRecordDto); return true; } src/main/resources/mapper/stock/StockInRecordMapper.xml
@@ -16,6 +16,10 @@ ) SELECT sir.*, CASE WHEN sir.theory_stock_in_num IS NULL THEN NULL ELSE sir.theory_stock_in_num - sir.stock_in_num END AS differenceNum, p.product_name as product_name, pm.model, pm.unit, @@ -137,6 +141,10 @@ <select id="listStockInRecordExportData" resultType="com.ruoyi.stock.execl.StockInRecordExportData"> SELECT sir.*, CASE WHEN sir.theory_stock_in_num IS NULL THEN NULL ELSE sir.theory_stock_in_num - sir.stock_in_num END AS differenceNum, p.product_name as product_name, pm.model, pm.unit, @@ -169,6 +177,12 @@ DATE(sir.create_time) AS inboundDate, p.product_name, pm.model as specification_model, sir.theory_stock_in_num, sir.stock_in_num, CASE WHEN sir.theory_stock_in_num IS NULL THEN NULL ELSE sir.theory_stock_in_num - sir.stock_in_num END AS differenceNum, sir.stock_in_num * slp.tax_inclusive_unit_price AS InboundAmount, pl.purchase_contract_number FROM stock_in_record sir @@ -177,12 +191,26 @@ -- 动态关联采购(自动适配 7 和 10) LEFT JOIN purchase_ledger pl ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id) -- 产品关联不动 LEFT JOIN sales_ledger_product slp ON pl.id = slp.sales_ledger_id LEFT JOIN sales_ledger_product slp ON slp.type = 2 AND ( (sir.record_type = 7 AND slp.sales_ledger_id = pl.id AND ( (sir.batch_no IS NOT NULL AND sir.batch_no LIKE CONCAT('%-', slp.id)) OR (sir.batch_no IS NULL AND slp.product_model_id = sir.product_model_id) ) ) OR (sir.record_type = 10 AND slp.sales_ledger_id = pl.id AND slp.product_model_id = sir.product_model_id ) ) LEFT JOIN product_model pm ON sir.product_model_id = pm.id LEFT JOIN product p ON pm.product_id = p.id -- 条件 WHERE sir.approval_status = 1 AND slp.type = 2 WHERE sir.approval_status = 1 AND sir.record_type IN ('7','10') <if test="req.inboundBatches != null and req.inboundBatches != ''"> AND sir.inbound_batches LIKE CONCAT('%',#{req.inboundBatches},'%')