This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 885316690 FINERACT-1981: Rate factor using simple interest for EMI 
calculation
885316690 is described below

commit 885316690e59f24d1fc686147201228c64ca81cc
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Thu May 23 22:58:22 2024 -0600

    FINERACT-1981: Rate factor using simple interest for EMI calculation
---
 .../infrastructure/core/service/DateUtils.java     |  11 +++
 .../ratefactor/RateFactorFunctions.java            |  50 ++++++++++
 .../ratefactor/RateFactorFunctionsTest.java        | 107 +++++++++++++++++++++
 3 files changed, 168 insertions(+)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
index c8a9ccb6f..e972204cd 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
@@ -24,6 +24,7 @@ import jakarta.validation.constraints.NotNull;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
+import java.time.Year;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
@@ -34,6 +35,7 @@ import java.util.Locale;
 import org.apache.fineract.infrastructure.core.data.ApiParameterError;
 import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.portfolio.common.domain.DaysInYearType;
 
 public final class DateUtils {
 
@@ -393,4 +395,13 @@ public final class DateUtils {
         }
         return formatter;
     }
+
+    public static Integer daysInYear(final DaysInYearType daysInYear, final 
LocalDate referenceDate) {
+        return daysInYear.isActual() ? 
DateUtils.getDaysInYear(referenceDate.getYear()) : daysInYear.getValue();
+    }
+
+    public static Integer getDaysInYear(final Integer year) {
+        return Year.isLeap(year) ? 366 : 365;
+    }
+
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/ratefactor/RateFactorFunctions.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/ratefactor/RateFactorFunctions.java
new file mode 100644
index 000000000..c663d4fa7
--- /dev/null
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/ratefactor/RateFactorFunctions.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanproduct.ratefactor;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+
+public class RateFactorFunctions {
+
+    protected RateFactorFunctions() {}
+
+    /**
+     * To calculate the monthly payment, we first need to calculate something 
called the Rate Factor. We're going to be
+     * using simple interest. The Rate Factor for simple interest is 
calculated by the following formula:
+     *
+     *
+     * R = 1 + (r * d / y)
+     *
+     * @param interestRate
+     *            (r)
+     * @param daysInPeriod
+     *            (d)
+     * @param daysInYear
+     *            (y)
+     */
+    public static BigDecimal rateFactor(final BigDecimal interestRate, final 
Long daysInPeriod, final Integer daysInYear,
+            final MathContext mc) {
+        final BigDecimal daysPeriod = BigDecimal.valueOf(daysInPeriod);
+        final BigDecimal daysYear = BigDecimal.valueOf(daysInYear);
+
+        return 
BigDecimal.ONE.add(interestRate.multiply(daysPeriod.divide(daysYear, mc), mc), 
mc);
+    }
+
+}
diff --git 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/ratefactor/RateFactorFunctionsTest.java
 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/ratefactor/RateFactorFunctionsTest.java
new file mode 100644
index 000000000..b535992d7
--- /dev/null
+++ 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/ratefactor/RateFactorFunctionsTest.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanproduct.ratefactor;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
+import org.apache.fineract.portfolio.common.domain.DaysInYearType;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class RateFactorFunctionsTest {
+
+    private static MockedStatic<MoneyHelper> moneyHelper = 
Mockito.mockStatic(MoneyHelper.class);
+
+    private static List<LoanRepaymentScheduleInstallment> periods;
+    private final BigDecimal interestRate = BigDecimal.valueOf(0.09482);
+
+    @BeforeAll
+    public static void init() {
+        periods = new ArrayList<>();
+        LocalDate startDate = LocalDate.of(2024, 01, 1);
+        periods.add(createPeriod(1, startDate, startDate.plusMonths(1)));
+        periods.add(createPeriod(2, startDate.plusMonths(1), 
startDate.plusMonths(2)));
+        periods.add(createPeriod(3, startDate.plusMonths(2), 
startDate.plusMonths(3)));
+        periods.add(createPeriod(4, startDate.plusMonths(3), 
startDate.plusMonths(4)));
+        periods.add(createPeriod(5, startDate.plusMonths(4), 
startDate.plusMonths(5)));
+        periods.add(createPeriod(6, startDate.plusMonths(5), 
startDate.plusMonths(6)));
+
+        // When
+        moneyHelper.when(() -> 
MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.HALF_EVEN);
+        moneyHelper.when(() -> MoneyHelper.getMathContext()).thenReturn(new 
MathContext(8, RoundingMode.HALF_EVEN));
+    }
+
+    @Test
+    public void testRateFactorFunctionDay365() {
+        // Given
+        final DaysInYearType daysInYearType = DaysInYearType.DAYS_365;
+        final String[] expectedValues = new String[] { "1.0080532", 
"1.0075336", "1.0080532", "1.0077934", "1.0080532", "1.0077934" };
+
+        // Then
+        for (LoanRepaymentScheduleInstallment period : periods) {
+            final Long daysInPeriod = 
DateUtils.getDifferenceInDays(period.getFromDate(), period.getDueDate());
+            final Integer daysInYear = DateUtils.daysInYear(daysInYearType, 
period.getFromDate());
+            BigDecimal rateFactor = 
RateFactorFunctions.rateFactor(interestRate, daysInPeriod, daysInYear, 
MoneyHelper.getMathContext());
+
+            
Assertions.assertEquals(expectedValues[period.getInstallmentNumber() - 1], 
rateFactor.toString());
+        }
+    }
+
+    @Test
+    public void testRateFactorFunctionActual() {
+        // Given
+        final DaysInYearType daysInYearType = DaysInYearType.ACTUAL;
+        final String[] expectedValues = new String[] { "1.0080312", 
"1.0075131", "1.0080312", "1.0077721", "1.0080312", "1.0077721" };
+
+        // Then
+        for (LoanRepaymentScheduleInstallment period : periods) {
+            final Long daysInPeriod = 
DateUtils.getDifferenceInDays(period.getFromDate(), period.getDueDate());
+            final Integer daysInYear = DateUtils.daysInYear(daysInYearType, 
period.getFromDate());
+
+            BigDecimal rateFactor = 
RateFactorFunctions.rateFactor(interestRate, daysInPeriod, daysInYear, 
MoneyHelper.getMathContext());
+
+            
Assertions.assertEquals(expectedValues[period.getInstallmentNumber() - 1], 
rateFactor.toString());
+        }
+    }
+
+    @NotNull
+    private static LoanRepaymentScheduleInstallment createPeriod(int periodId, 
LocalDate start, LocalDate end) {
+        LoanRepaymentScheduleInstallment period = 
Mockito.mock(LoanRepaymentScheduleInstallment.class);
+        Mockito.when(period.getInstallmentNumber()).thenReturn(periodId);
+        Mockito.when(period.getFromDate()).thenReturn(start);
+        Mockito.when(period.getDueDate()).thenReturn(end);
+
+        return period;
+    }
+
+}

Reply via email to