FINERACT-2492: AprCalculator produces incorrect annual interest rate when DaysInYearType is ACTUAL with WHOLE_TERM frequency

This commit is contained in:
Ralph Hopman
2026-02-19 11:30:28 +00:00
committed by Adam Saghy
parent 41da36b1f4
commit 17c2febcac
2 changed files with 360 additions and 2 deletions
@@ -19,21 +19,25 @@
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import java.math.BigDecimal;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class AprCalculator {
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator;
public BigDecimal calculateFrom(final PeriodFrequencyType interestPeriodFrequencyType, final BigDecimal interestRatePerPeriod,
final Integer numberOfRepayments, final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
final DaysInYearType daysInYearType) {
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.ZERO;
switch (interestPeriodFrequencyType) {
case DAYS:
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(daysInYearType.getValue()));
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(getDaysInYear(daysInYearType)));
break;
case WEEKS:
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(52));
@@ -50,7 +54,7 @@ public class AprCalculator {
switch (repaymentPeriodFrequencyType) {
case DAYS:
defaultAnnualNominalInterestRate = ratePerPeriod.multiply(BigDecimal.valueOf(daysInYearType.getValue()));
defaultAnnualNominalInterestRate = ratePerPeriod.multiply(BigDecimal.valueOf(getDaysInYear(daysInYearType)));
break;
case WEEKS:
defaultAnnualNominalInterestRate = ratePerPeriod.multiply(BigDecimal.valueOf(52));
@@ -74,4 +78,24 @@ public class AprCalculator {
return defaultAnnualNominalInterestRate;
}
/**
* Helper method to get the number of days in a year, handling ACTUAL appropriately.
*
* When daysInYearType is ACTUAL, this delegates to the PaymentPeriodsInOneYearCalculator (consistent with how
* Fineract handles ACTUAL elsewhere). For other types (DAYS_360, DAYS_364, DAYS_365), it returns the configured
* value.
*
* @param daysInYearType
* the days in year type configuration
* @return the number of days in a year
*/
private int getDaysInYear(final DaysInYearType daysInYearType) {
// When ACTUAL, delegate to calculator (consistent with LoanApplicationTerms.calculatePeriodsInOneYear)
if (daysInYearType == DaysInYearType.ACTUAL) {
return paymentPeriodsInOneYearCalculator.calculate(PeriodFrequencyType.DAYS);
}
// For DAYS_360, DAYS_364, DAYS_365: use configured value
return daysInYearType.getValue();
}
}