diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java index a60b7e736..abfc246fb 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java @@ -269,9 +269,13 @@ public final class MathUtil { /** @return sum the values considering null values */ public static BigDecimal add(BigDecimal... amounts) { + return add(MoneyHelper.getMathContext(), amounts); + } + + public static BigDecimal add(MathContext mc, BigDecimal... amounts) { BigDecimal result = null; for (BigDecimal amount : amounts) { - result = add(result, amount, MoneyHelper.getMathContext()); + result = add(result, amount, mc); } return result; } diff --git a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/MoneyHelper.java b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/MoneyHelper.java index 1a99e7311..8300a463b 100644 --- a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/MoneyHelper.java +++ b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/MoneyHelper.java @@ -33,7 +33,7 @@ public class MoneyHelper { private static RoundingMode roundingMode = null; private static MathContext mathContext; - private static final int PRECISION = 12; + public static final int PRECISION = 19; private static ConfigurationDomainService staticConfigurationDomainService; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java index e5fdb22ba..65aac943c 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java @@ -19,11 +19,13 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.data; import java.math.BigDecimal; +import java.math.MathContext; import java.time.LocalDate; import lombok.Builder; import lombok.Getter; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; +import org.apache.fineract.organisation.monetary.domain.MoneyHelper; /** * Immutable data object that represents a period of a loan schedule. @@ -156,11 +158,13 @@ public final class LoanSchedulePeriodData { final BigDecimal totalPaidLateForPeriod, final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalCredits, final boolean isDownPayment, final BigDecimal totalAccruedInterest) { - BigDecimal totalDue = MathUtil.add(principalOriginalDue, interestDue, feeChargesDue, penaltyChargesDue); - BigDecimal totalOutstanding = MathUtil.add(principalOutstanding, interestOutstanding, feeChargesOutstanding, + final MathContext mc = MoneyHelper.getMathContext(); + + BigDecimal totalDue = MathUtil.add(mc, principalOriginalDue, interestDue, feeChargesDue, penaltyChargesDue); + BigDecimal totalOutstanding = MathUtil.add(mc, principalOutstanding, interestOutstanding, feeChargesOutstanding, penaltyChargesOutstanding); - BigDecimal totalActualCostOfLoanForPeriod = MathUtil.add(interestDue, feeChargesDue, penaltyChargesDue); - BigDecimal totalInstallmentAmount = MathUtil.add(principalOriginalDue, interestDue); + BigDecimal totalActualCostOfLoanForPeriod = MathUtil.add(mc, interestDue, feeChargesDue, penaltyChargesDue); + BigDecimal totalInstallmentAmount = MathUtil.add(mc, principalOriginalDue, interestDue); return builder().period(periodNumber) // .fromDate(fromDate) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index c9b7a22af..264b4f1e8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -1226,7 +1226,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa Set disbursementPeriodIds = new HashSet<>(); while (rs.next()) { - final Long loanId = rs.getLong("loanId"); final Integer period = JdbcSupport.getInteger(rs, "period"); LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate"); final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate"); @@ -1307,8 +1306,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding) .add(penaltyChargesOutstanding); - final BigDecimal totalActualCostOfLoanForPeriod = interestActualDue.add(feeChargesActualDue).add(penaltyChargesActualDue); - totalRepaymentExpected = totalRepaymentExpected.plus(totalDueForPeriod); totalRepayment = totalRepayment.plus(totalPaidForPeriod); totalPaidInAdvance = totalPaidInAdvance.plus(totalPaidInAdvanceForPeriod); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentScheduleForChargesAfterMaturityTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentScheduleForChargesAfterMaturityTest.java index 43abd6637..0df407dfd 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentScheduleForChargesAfterMaturityTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentScheduleForChargesAfterMaturityTest.java @@ -296,6 +296,41 @@ public class LoanRepaymentScheduleForChargesAfterMaturityTest extends BaseLoanIn }); } + @Test + public void incorrectValueAfterCharge() { + runAt("20 December 2024", () -> { + // Create Client + Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + // Create Loan Product + PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation() + .minPrincipal(100.0); + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product); + Long loanProductId = loanProductResponse.getResourceId(); + + // Apply and Approve Loan + Long loanId = applyAndApproveLoan(clientId, loanProductId, "20 December 2024", 800.0, 4, req -> { + req.setRepaymentEvery(30); + req.setLoanTermFrequency(120); + req.setTransactionProcessingStrategyCode("advanced-payment-allocation-strategy"); + }); + + // Disburse Loan + disburseLoan(loanId, BigDecimal.valueOf(800.00), "20 December 2024"); + + // add charge with a huge amount + addCharge(loanId, false, 123456789012.12, "23 December 2024"); + + // verify repayment schedule + verifyRepaymentSchedule(loanId, // + installment(800.0, null, "20 December 2024"), // + installment(200.0, 0.0, 123456789212.12, false, "19 January 2025"), // + installment(200.0, 0.0, 200.0, false, "18 February 2025"), // + installment(200.0, 0.0, 200.0, false, "20 March 2025"), // + installment(200.0, 0.0, 200.0, false, "19 April 2025")// + ); + }); + } + private Long createLoanProductWithMultiDisbursalAndRepayments() { boolean multiDisburseEnabled = true; PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/MultiActivityAccrualsTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/MultiActivityAccrualsTest.java index 954a1e054..3b4dfc8fe 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/MultiActivityAccrualsTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/MultiActivityAccrualsTest.java @@ -96,12 +96,12 @@ public class MultiActivityAccrualsTest extends BaseLoanIntegrationTest { verifyTransactions(loanId, // transaction(600.0, "Disbursement", "09 August 2024", 600.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), - transaction(20.00, "Accrual", "09 December 2024", 0, 0, 20.00, 0, 0, 0.0, 0.0), - transaction(5.0, "Accrual Activity", "09 September 2024", 0, 0, 5.0, 0, 0, 0.0, 0.0), + transaction(19.98, "Accrual", "09 December 2024", 0, 0, 19.98, 0, 0, 0.0, 0.0), + transaction(4.99, "Accrual Activity", "09 September 2024", 0, 0, 4.99, 0, 0, 0.0, 0.0), transaction(5.0, "Accrual Activity", "09 October 2024", 0, 0, 5.0, 0, 0, 0.0, 0.0), - transaction(5.0, "Accrual Activity", "09 November 2024", 0, 0, 5.0, 0, 0, 0.0, 0.0), + transaction(4.99, "Accrual Activity", "09 November 2024", 0, 0, 4.99, 0, 0, 0.0, 0.0), transaction(5.0, "Accrual Activity", "09 December 2024", 0, 0, 5.0, 0, 0, 0.0, 0.0), - transaction(700.00, "Repayment", "09 December 2024", 0, 600.00, 20.0, 0, 0, 0.0, 80.0)); + transaction(700.00, "Repayment", "09 December 2024", 0, 600.00, 19.98, 0, 0, 0.0, 80.02)); }); } }