src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -22,12 +22,12 @@
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;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.FreightUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -679,6 +679,7 @@
        // 执行更新操作
        if (!updateList.isEmpty()) {
            for (SalesLedgerProduct product : updateList) {
                FreightUtils.fillFreightAmount(product);
                product.setType(ledgerType);
                salesLedgerProductMapper.updateById(product);
            }
@@ -686,6 +687,7 @@
        // 执行插入操作
        if (!insertList.isEmpty()) {
            for (SalesLedgerProduct salesLedgerProduct : insertList) {
                FreightUtils.fillFreightAmount(salesLedgerProduct);
                salesLedgerProduct.setType(ledgerType);
                Date entryDate = purchaseLedger.getEntryDate();
@@ -975,6 +977,7 @@
                for (PurchaseLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) {
                    SalesLedgerProduct salesLedgerProduct = new SalesLedgerProduct();
                    BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct);
                    FreightUtils.fillFreightAmount(salesLedgerProduct);
                    salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
                    salesLedgerProduct.setType(2);
                    // 计算不含税总价
@@ -1093,35 +1096,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 +1151,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);
    }
    /**
     * 下划线命名转驼峰命名
     */