huminmin
2 天以前 b83811618d78d7a9832b325334b7a35cd6a3b64b
采购台账手动入库,保存差额;入库审核 重新计算含水量
已修改9个文件
158 ■■■■ 文件已修改
src/main/java/com/ruoyi/account/bean/vo/purchase/PurchaseInboundVo.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/PurchaseStockInDto.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 75 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInRecordMapper.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | 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},'%')