From 1c518e10a50050d383e714b581c94dea58ec4d67 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期五, 22 五月 2026 18:01:34 +0800
Subject: [PATCH] fix(approve): 修复审批实例查询条件及流程控制问题

---
 src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java |  376 ++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 297 insertions(+), 79 deletions(-)

diff --git a/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java b/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
index f9b09de..0f7e586 100644
--- a/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
+++ b/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
@@ -3,8 +3,8 @@
 import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
-import com.ruoyi.account.mapper.SalesReceiptReturnMapper;
-import com.ruoyi.account.pojo.SalesReceiptReturn;
+import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper;
+import com.ruoyi.account.pojo.sales.AccountSalesCollection;
 import com.ruoyi.ai.context.AiSessionUserContext;
 import com.ruoyi.basic.dto.CustomerDto;
 import com.ruoyi.basic.mapper.CustomerMapper;
@@ -17,6 +17,8 @@
 import com.ruoyi.sales.pojo.SalesLedger;
 import com.ruoyi.sales.pojo.SalesQuotation;
 import com.ruoyi.sales.pojo.ShippingInfo;
+import com.ruoyi.stock.mapper.StockOutRecordMapper;
+import com.ruoyi.stock.pojo.StockOutRecord;
 import dev.langchain4j.agent.tool.P;
 import dev.langchain4j.agent.tool.Tool;
 import dev.langchain4j.agent.tool.ToolMemoryId;
@@ -49,20 +51,23 @@
     private final SalesLedgerMapper salesLedgerMapper;
     private final SalesQuotationMapper salesQuotationMapper;
     private final ShippingInfoMapper shippingInfoMapper;
-    private final SalesReceiptReturnMapper salesReceiptReturnMapper;
+    private final AccountSalesCollectionMapper accountSalesCollectionMapper;
+    private final StockOutRecordMapper stockOutRecordMapper;
     private final AiSessionUserContext aiSessionUserContext;
 
     public SalesAgentTools(CustomerMapper customerMapper,
                            SalesLedgerMapper salesLedgerMapper,
                            SalesQuotationMapper salesQuotationMapper,
                            ShippingInfoMapper shippingInfoMapper,
-                           SalesReceiptReturnMapper salesReceiptReturnMapper,
+                           AccountSalesCollectionMapper accountSalesCollectionMapper,
+                           StockOutRecordMapper stockOutRecordMapper,
                            AiSessionUserContext aiSessionUserContext) {
         this.customerMapper = customerMapper;
         this.salesLedgerMapper = salesLedgerMapper;
         this.salesQuotationMapper = salesQuotationMapper;
         this.shippingInfoMapper = shippingInfoMapper;
-        this.salesReceiptReturnMapper = salesReceiptReturnMapper;
+        this.accountSalesCollectionMapper = accountSalesCollectionMapper;
+        this.stockOutRecordMapper = stockOutRecordMapper;
         this.aiSessionUserContext = aiSessionUserContext;
     }
 
@@ -242,36 +247,35 @@
                                    @P(value = "杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�30", required = false) Integer limit) {
         LoginUser loginUser = currentLoginUser(memoryId);
         DateRange range = resolveDateRange(startDate, endDate, null);
-        LambdaQueryWrapper<SalesReceiptReturn> wrapper = new LambdaQueryWrapper<>();
-        applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), SalesReceiptReturn::getDeptId);
+        LambdaQueryWrapper<AccountSalesCollection> wrapper = new LambdaQueryWrapper<>();
+        applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountSalesCollection::getDeptId);
         if (StringUtils.hasText(keyword)) {
-            wrapper.and(w -> w.like(SalesReceiptReturn::getRefundId, keyword)
-                    .or().like(SalesReceiptReturn::getTransactionNo, keyword)
-                    .or().like(SalesReceiptReturn::getPaymentAccountName, keyword));
+            wrapper.and(w -> w.like(AccountSalesCollection::getCollectionNumber, keyword)
+                    .or().like(AccountSalesCollection::getCollectionMethod, keyword)
+                    .or().like(AccountSalesCollection::getRemark, keyword));
         }
-        wrapper.ge(SalesReceiptReturn::getCreateTime, range.start().atStartOfDay())
-                .le(SalesReceiptReturn::getCreateTime, range.end().atTime(23, 59, 59))
-                .orderByDesc(SalesReceiptReturn::getCreateTime, SalesReceiptReturn::getId)
+        wrapper.ge(AccountSalesCollection::getCollectionDate, range.start())
+                .le(AccountSalesCollection::getCollectionDate, range.end())
+                .orderByDesc(AccountSalesCollection::getCollectionDate, AccountSalesCollection::getId)
                 .last("limit " + normalizeLimit(limit));
-        List<SalesReceiptReturn> rows = defaultList(salesReceiptReturnMapper.selectList(wrapper));
+        List<AccountSalesCollection> rows = defaultList(accountSalesCollectionMapper.selectList(wrapper));
 
         BigDecimal returnAmount = rows.stream()
-                .map(SalesReceiptReturn::getActualAmount)
+                .map(AccountSalesCollection::getCollectionAmount)
                 .filter(Objects::nonNull)
                 .reduce(BigDecimal.ZERO, BigDecimal::add);
 
         List<Map<String, Object>> items = rows.stream().map(item -> {
             Map<String, Object> map = new LinkedHashMap<>();
             map.put("id", item.getId());
-            map.put("refundId", safe(item.getRefundId()));
-            map.put("paymentAccount", safe(item.getPaymentAccount()));
-            map.put("paymentAccountName", safe(item.getPaymentAccountName()));
-            map.put("paymentMethod", item.getPaymentMethod());
-            map.put("actualAmount", item.getActualAmount());
-            map.put("fee", item.getFee());
-            map.put("discountAmount", item.getDiscountAmount());
-            map.put("transactionNo", safe(item.getTransactionNo()));
-            map.put("createTime", formatDateTime(item.getCreateTime()));
+            map.put("refundId", safe(item.getCollectionNumber()));
+            map.put("collectionNumber", safe(item.getCollectionNumber()));
+            map.put("paymentMethod", safe(item.getCollectionMethod()));
+            map.put("actualAmount", item.getCollectionAmount());
+            map.put("collectionAmount", item.getCollectionAmount());
+            map.put("customerId", item.getCustomerId());
+            map.put("remark", safe(item.getRemark()));
+            map.put("createTime", formatDate(item.getCollectionDate()));
             return map;
         }).collect(Collectors.toList());
 
@@ -288,56 +292,59 @@
                                            @P(value = "杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�30", required = false) Integer limit) {
         LoginUser loginUser = currentLoginUser(memoryId);
         DateRange range = resolveDateRange(startDate, endDate, null);
-//        LambdaQueryWrapper<ReceiptPayment> wrapper = new LambdaQueryWrapper<>();
-//        applyTenantFilter(wrapper, loginUser.getTenantId(), ReceiptPayment::getTenantId);
-//        applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ReceiptPayment::getDeptId);
-//        wrapper.ge(ReceiptPayment::getReceiptPaymentDate, range.start())
-//                .le(ReceiptPayment::getReceiptPaymentDate, range.end())
-//                .orderByDesc(ReceiptPayment::getReceiptPaymentDate, ReceiptPayment::getId);
-//        List<ReceiptPayment> payments = defaultList(receiptPaymentMapper.selectList(wrapper));
-//        if (payments.isEmpty()) {
-//            return jsonResponse(true, "sales_customer_interaction_list", "鏈煡璇㈠埌瀹㈡埛寰�鏉ヨ褰�", rangeSummary(range, 0, keyword), Map.of("items", List.of()), Map.of());
-//        }
-//
-//        List<Long> ledgerIds = payments.stream()
-//                .map(ReceiptPayment::getSalesLedgerId)
-//                .filter(Objects::nonNull)
-//                .distinct()
-//                .collect(Collectors.toList());
-//        Map<Long, SalesLedger> ledgerMap = defaultList(salesLedgerMapper.selectBatchIds(ledgerIds)).stream()
-//                .filter(ledger -> tenantMatched(ledger.getTenantId(), loginUser.getTenantId()))
-//                .collect(Collectors.toMap(SalesLedger::getId, item -> item, (a, b) -> a, LinkedHashMap::new));
-//
-//        List<ReceiptPayment> filtered = payments.stream()
-//                .filter(item -> matchInteractionKeyword(item, ledgerMap.get(item.getSalesLedgerId()), keyword))
-//                .limit(normalizeLimit(limit))
-//                .collect(Collectors.toList());
-//
-//        BigDecimal totalReceiptAmount = filtered.stream()
-//                .map(ReceiptPayment::getReceiptPaymentAmount)
-//                .filter(Objects::nonNull)
-//                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        List<AccountSalesCollection> collections = queryCollections(loginUser, range);
+        if (collections.isEmpty()) {
+            return jsonResponse(true, "sales_customer_interaction_list", "no_customer_interactions", rangeSummary(range, 0, keyword), Map.of("items", List.of()), Map.of());
+        }
 
-//        List<Map<String, Object>> items = filtered.stream().map(item -> {
-//            SalesLedger ledger = ledgerMap.get(item.getSalesLedgerId());
-//            Map<String, Object> map = new LinkedHashMap<>();
-//            map.put("id", item.getId());
-//            map.put("salesLedgerId", item.getSalesLedgerId());
-//            map.put("salesContractNo", ledger == null ? "" : safe(ledger.getSalesContractNo()));
-//            map.put("customerName", ledger == null ? "" : safe(ledger.getCustomerName()));
-//            map.put("projectName", ledger == null ? "" : safe(ledger.getProjectName()));
-//            map.put("receiptPaymentDate", formatDate(item.getReceiptPaymentDate()));
-//            map.put("receiptPaymentAmount", item.getReceiptPaymentAmount());
-//            map.put("receiptPaymentType", safe(item.getReceiptPaymentType()));
-//            map.put("registrant", safe(item.getRegistrant()));
-//            return map;
-//        }).collect(Collectors.toList());
+        Map<Integer, Set<Long>> ledgerIdsByCollectionId = mapCollectionLedgerIds(loginUser, collections);
+        Set<Long> ledgerIds = ledgerIdsByCollectionId.values().stream()
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+        Map<Long, SalesLedger> ledgerMap = defaultList(salesLedgerMapper.selectBatchIds(ledgerIds)).stream()
+                .filter(ledger -> tenantMatched(ledger.getTenantId(), loginUser.getTenantId()))
+                .collect(Collectors.toMap(SalesLedger::getId, item -> item, (a, b) -> a, LinkedHashMap::new));
 
-//        Map<String, Object> summary = rangeSummary(range, items.size(), keyword);
-//        summary.put("totalReceiptAmount", totalReceiptAmount);
-//        summary.put("customerCount", items.stream().map(item -> String.valueOf(item.get("customerName"))).filter(StringUtils::hasText).distinct().count());
-//        return jsonResponse(true, "sales_customer_interaction_list", "宸茶繑鍥炲鎴峰線鏉ユ槑缁�", summary, Map.of("items", items), Map.of());
-        return jsonResponse(true, "sales_customer_interaction_list", "宸茶繑鍥炲鎴峰線鏉ユ槑缁�", null, Map.of("items", List.of()), Map.of());
+        int finalLimit = normalizeLimit(limit);
+        List<Map<String, Object>> items = new ArrayList<>();
+        for (AccountSalesCollection collection : collections) {
+            Set<Long> relatedLedgerIds = ledgerIdsByCollectionId.get(collection.getId());
+            if (relatedLedgerIds == null || relatedLedgerIds.isEmpty()) {
+                if (!matchInteractionKeyword(collection, null, keyword)) {
+                    continue;
+                }
+                items.add(toInteractionItem(collection, null));
+                if (items.size() >= finalLimit) {
+                    break;
+                }
+                continue;
+            }
+            for (Long ledgerId : relatedLedgerIds) {
+                SalesLedger ledger = ledgerMap.get(ledgerId);
+                if (ledger == null || !matchInteractionKeyword(collection, ledger, keyword)) {
+                    continue;
+                }
+                items.add(toInteractionItem(collection, ledger));
+                if (items.size() >= finalLimit) {
+                    break;
+                }
+            }
+            if (items.size() >= finalLimit) {
+                break;
+            }
+        }
+
+        BigDecimal totalReceiptAmount = items.stream()
+                .map(item -> asBigDecimal(item.get("receiptPaymentAmount")))
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        Map<String, Object> summary = rangeSummary(range, items.size(), keyword);
+        summary.put("totalReceiptAmount", totalReceiptAmount);
+        summary.put("customerCount", items.stream()
+                .map(item -> String.valueOf(item.get("customerName")))
+                .filter(StringUtils::hasText)
+                .distinct()
+                .count());
+        return jsonResponse(true, "sales_customer_interaction_list", "ok", summary, Map.of("items", items), Map.of());
     }
 
     @Tool(name = "鏌ヨ鍙戣揣鍙拌处", value = "鎸夊叧閿瘝鍜屾椂闂磋寖鍥存煡璇㈠彂璐у彴璐�")
@@ -852,19 +859,213 @@
             return Map.of();
         }
         Map<Long, BigDecimal> result = new HashMap<>();
-//        for (InvoiceLedgerDto item : defaultList(invoiceLedgerMapper.invoicedTotal(ledgerIds))) {
-//            if (item.getSalesLedgerId() == null) {
-//                continue;
-//            }
-//            result.merge(item.getSalesLedgerId().longValue(), defaultDecimal(item.getInvoiceTotal()), BigDecimal::add);
-//        }
         return result;
     }
 
     private Map<Long, BigDecimal> sumReceiptAmounts(LoginUser loginUser, List<Long> ledgerIds) {
-        Map<Long, BigDecimal> result = new HashMap<>();
+        if (ledgerIds == null || ledgerIds.isEmpty()) {
+            return Map.of();
+        }
+        List<SalesLedger> ledgers = defaultList(salesLedgerMapper.selectBatchIds(ledgerIds)).stream()
+                .filter(ledger -> tenantMatched(ledger.getTenantId(), loginUser.getTenantId()))
+                .collect(Collectors.toList());
+        if (ledgers.isEmpty()) {
+            return Map.of();
+        }
 
+        Set<Integer> customerIds = ledgers.stream()
+                .map(SalesLedger::getCustomerId)
+                .filter(Objects::nonNull)
+                .map(Long::intValue)
+                .collect(Collectors.toSet());
+        if (customerIds.isEmpty()) {
+            return Map.of();
+        }
+
+        LambdaQueryWrapper<AccountSalesCollection> wrapper = new LambdaQueryWrapper<>();
+        applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountSalesCollection::getDeptId);
+        wrapper.in(AccountSalesCollection::getCustomerId, customerIds);
+        List<AccountSalesCollection> collections = defaultList(accountSalesCollectionMapper.selectList(wrapper));
+        if (collections.isEmpty()) {
+            return Map.of();
+        }
+
+        Map<Integer, Set<Long>> ledgerIdsByCollectionId = mapCollectionLedgerIds(loginUser, collections);
+        Map<Long, List<Long>> ledgerIdsByCustomerId = ledgers.stream()
+                .filter(item -> item.getId() != null && item.getCustomerId() != null)
+                .collect(Collectors.groupingBy(item -> item.getCustomerId().longValue(),
+                        Collectors.mapping(SalesLedger::getId, Collectors.toList())));
+        Set<Long> targetLedgerIdSet = new HashSet<>(ledgerIds);
+
+        Map<Long, BigDecimal> result = new HashMap<>();
+        for (AccountSalesCollection collection : collections) {
+            BigDecimal amount = defaultDecimal(collection.getCollectionAmount());
+            if (amount.compareTo(BigDecimal.ZERO) == 0) {
+                continue;
+            }
+            Set<Long> relatedLedgerIds = ledgerIdsByCollectionId.getOrDefault(collection.getId(), Set.of());
+            if (!relatedLedgerIds.isEmpty()) {
+                for (Long ledgerId : relatedLedgerIds) {
+                    if (targetLedgerIdSet.contains(ledgerId)) {
+                        result.merge(ledgerId, amount, BigDecimal::add);
+                    }
+                }
+                continue;
+            }
+            if (collection.getCustomerId() == null) {
+                continue;
+            }
+            List<Long> customerLedgerIds = ledgerIdsByCustomerId.get(collection.getCustomerId().longValue());
+            if (customerLedgerIds == null || customerLedgerIds.isEmpty()) {
+                continue;
+            }
+            for (Long ledgerId : customerLedgerIds) {
+                if (targetLedgerIdSet.contains(ledgerId)) {
+                    result.merge(ledgerId, amount, BigDecimal::add);
+                }
+            }
+        }
         return result;
+    }
+
+    private List<AccountSalesCollection> queryCollections(LoginUser loginUser, DateRange range) {
+        LambdaQueryWrapper<AccountSalesCollection> wrapper = new LambdaQueryWrapper<>();
+        applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountSalesCollection::getDeptId);
+        if (range != null) {
+            wrapper.ge(AccountSalesCollection::getCollectionDate, range.start())
+                    .le(AccountSalesCollection::getCollectionDate, range.end());
+        }
+        wrapper.orderByDesc(AccountSalesCollection::getCollectionDate, AccountSalesCollection::getId);
+        return defaultList(accountSalesCollectionMapper.selectList(wrapper));
+    }
+
+    private Map<Integer, Set<Long>> mapCollectionLedgerIds(LoginUser loginUser, List<AccountSalesCollection> collections) {
+        Map<Integer, Set<Long>> result = new HashMap<>();
+        if (collections == null || collections.isEmpty()) {
+            return result;
+        }
+
+        Map<Integer, List<Long>> stockOutRecordIdsByCollection = new HashMap<>();
+        Set<Long> allStockOutRecordIds = new HashSet<>();
+        for (AccountSalesCollection collection : collections) {
+            if (collection.getId() == null) {
+                continue;
+            }
+            List<Long> stockOutRecordIds = parseLongIds(collection.getStockOutRecordIds());
+            if (stockOutRecordIds.isEmpty()) {
+                continue;
+            }
+            stockOutRecordIdsByCollection.put(collection.getId(), stockOutRecordIds);
+            allStockOutRecordIds.addAll(stockOutRecordIds);
+        }
+        if (allStockOutRecordIds.isEmpty()) {
+            return result;
+        }
+
+        List<StockOutRecord> stockOutRecords = defaultList(stockOutRecordMapper.selectList(new LambdaQueryWrapper<StockOutRecord>()
+                .in(StockOutRecord::getId, allStockOutRecordIds)));
+        if (stockOutRecords.isEmpty()) {
+            return result;
+        }
+        Map<Long, StockOutRecord> stockOutRecordMap = stockOutRecords.stream()
+                .filter(item -> item.getId() != null)
+                .collect(Collectors.toMap(StockOutRecord::getId, item -> item, (a, b) -> a));
+
+        Set<Long> shippingIds = stockOutRecords.stream()
+                .filter(this::isSalesOutboundRecord)
+                .map(StockOutRecord::getRecordId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        if (shippingIds.isEmpty()) {
+            return result;
+        }
+
+        LambdaQueryWrapper<ShippingInfo> shippingWrapper = new LambdaQueryWrapper<>();
+        applyTenantFilter(shippingWrapper, loginUser.getTenantId(), ShippingInfo::getTenantId);
+        applyDeptFilter(shippingWrapper, loginUser.getCurrentDeptId(), ShippingInfo::getDeptId);
+        shippingWrapper.in(ShippingInfo::getId, shippingIds);
+        Map<Long, Long> ledgerIdByShippingId = defaultList(shippingInfoMapper.selectList(shippingWrapper)).stream()
+                .filter(item -> item.getId() != null && item.getSalesLedgerId() != null)
+                .collect(Collectors.toMap(ShippingInfo::getId, ShippingInfo::getSalesLedgerId, (a, b) -> a));
+
+        for (Map.Entry<Integer, List<Long>> entry : stockOutRecordIdsByCollection.entrySet()) {
+            Set<Long> ledgerIds = new LinkedHashSet<>();
+            for (Long stockOutRecordId : entry.getValue()) {
+                StockOutRecord stockOutRecord = stockOutRecordMap.get(stockOutRecordId);
+                if (!isSalesOutboundRecord(stockOutRecord)) {
+                    continue;
+                }
+                Long ledgerId = ledgerIdByShippingId.get(stockOutRecord.getRecordId());
+                if (ledgerId != null) {
+                    ledgerIds.add(ledgerId);
+                }
+            }
+            if (!ledgerIds.isEmpty()) {
+                result.put(entry.getKey(), ledgerIds);
+            }
+        }
+        return result;
+    }
+
+    private boolean isSalesOutboundRecord(StockOutRecord stockOutRecord) {
+        if (stockOutRecord == null || !StringUtils.hasText(stockOutRecord.getRecordType())) {
+            return false;
+        }
+        if (stockOutRecord.getApprovalStatus() != null && stockOutRecord.getApprovalStatus() != 1) {
+            return false;
+        }
+        return "13".equals(stockOutRecord.getRecordType().trim());
+    }
+
+    private List<Long> parseLongIds(String raw) {
+        if (!StringUtils.hasText(raw)) {
+            return List.of();
+        }
+        List<Long> result = new ArrayList<>();
+        for (String part : raw.split(",")) {
+            if (!StringUtils.hasText(part)) {
+                continue;
+            }
+            try {
+                result.add(Long.parseLong(part.trim()));
+            } catch (Exception ignored) {
+            }
+        }
+        return result;
+    }
+
+    private boolean matchInteractionKeyword(AccountSalesCollection collection, SalesLedger ledger, String keyword) {
+        if (!StringUtils.hasText(keyword)) {
+            return true;
+        }
+        String text = keyword.trim();
+        if (safe(collection.getCollectionNumber()).contains(text)
+                || safe(collection.getCollectionMethod()).contains(text)
+                || safe(collection.getRemark()).contains(text)) {
+            return true;
+        }
+        if (ledger == null) {
+            return false;
+        }
+        return safe(ledger.getSalesContractNo()).contains(text)
+                || safe(ledger.getCustomerName()).contains(text)
+                || safe(ledger.getProjectName()).contains(text);
+    }
+
+    private Map<String, Object> toInteractionItem(AccountSalesCollection collection, SalesLedger ledger) {
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("id", collection.getId());
+        map.put("salesLedgerId", ledger == null ? null : ledger.getId());
+        map.put("salesContractNo", ledger == null ? "" : safe(ledger.getSalesContractNo()));
+        map.put("customerName", ledger == null ? "" : safe(ledger.getCustomerName()));
+        map.put("projectName", ledger == null ? "" : safe(ledger.getProjectName()));
+        map.put("receiptPaymentDate", formatDate(collection.getCollectionDate()));
+        map.put("receiptPaymentAmount", collection.getCollectionAmount());
+        map.put("receiptPaymentType", safe(collection.getCollectionMethod()));
+        map.put("collectionNumber", safe(collection.getCollectionNumber()));
+        map.put("registrant", collection.getCreateUser());
+        map.put("remark", safe(collection.getRemark()));
+        return map;
     }
 
     private boolean isLedgerFullyShipped(Long ledgerId, Map<Long, List<ShippingInfo>> shippingByLedgerId) {
@@ -1090,6 +1291,23 @@
         return value == null ? BigDecimal.ZERO : value;
     }
 
+    private BigDecimal asBigDecimal(Object value) {
+        if (value == null) {
+            return BigDecimal.ZERO;
+        }
+        if (value instanceof BigDecimal decimal) {
+            return decimal;
+        }
+        if (value instanceof Number number) {
+            return new BigDecimal(String.valueOf(number));
+        }
+        try {
+            return new BigDecimal(String.valueOf(value));
+        } catch (Exception ignored) {
+            return BigDecimal.ZERO;
+        }
+    }
+
     private BigDecimal maxZero(BigDecimal value) {
         return value == null || value.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : value;
     }

--
Gitblit v1.9.3