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 cfdfa6cd5 FINERACT-1981: EMI Calculation, Daily, Weekly, Monthly 
support plug to Repayment Schedule
cfdfa6cd5 is described below

commit cfdfa6cd57bb7da6ade67cf0700578989a3f8e9c
Author: Janos Meszaros <[email protected]>
AuthorDate: Fri Jun 28 14:34:17 2024 +0200

    FINERACT-1981: EMI Calculation, Daily, Weekly, Monthly support plug to 
Repayment Schedule
---
 .../loanschedule/data/LoanSchedulePeriodData.java  |  2 +-
 .../domain/ScheduledDateGenerator.java             |  2 +-
 .../AbstractProgressiveLoanScheduleGenerator.java  | 25 ++++++++++++++--------
 .../domain/ProgressiveLoanScheduleGenerator.java   |  7 ++++--
 .../loanproduct/calc/ProgressiveEMICalculator.java |  5 +----
 .../calc/ProgressiveEMICalculatorTest.java         |  6 ++----
 .../domain/DefaultScheduledDateGenerator.java      |  7 +++---
 .../domain/DefaultScheduledDateGeneratorTest.java  |  3 ++-
 8 files changed, 31 insertions(+), 26 deletions(-)

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 fc6b5c468..fb50ee5f8 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
@@ -275,7 +275,7 @@ public final class LoanSchedulePeriodData {
         this.dueDate = dueDate;
         this.obligationsMetOnDate = null;
         this.complete = null;
-        this.daysInPeriod = 1;
+        this.daysInPeriod = 1; // TODO: check this may should be 0 or 
somewhere else should be 1?
         this.principalDisbursed = null;
         this.principalOriginalDue = principalDue;
         this.principalDue = principalOriginalDue;
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java
index 1fd14d85f..88271f95c 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java
@@ -27,7 +27,7 @@ import 
org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
 
 public interface ScheduledDateGenerator {
 
-    List<PreGeneratedLoanSchedulePeriod> 
generateRepaymentPeriods(LoanApplicationTerms loanApplicationTerms,
+    List<PreGeneratedLoanSchedulePeriod> generateRepaymentPeriods(LocalDate 
scheduledDueDate, LoanApplicationTerms loanApplicationTerms,
             HolidayDetailDTO holidayDetailDTO);
 
     LocalDate getLastRepaymentDate(LoanApplicationTerms loanApplicationTerms, 
HolidayDetailDTO holidayDetailDTO);
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java
index 960e79dd7..c98935cbc 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java
@@ -85,11 +85,7 @@ public abstract class 
AbstractProgressiveLoanScheduleGenerator implements LoanSc
         List<LoanScheduleModelPeriod> periods = 
createNewLoanScheduleListWithDisbursementDetails(loanApplicationTerms, 
scheduleParams,
                 chargesDueAtTimeOfDisbursement);
 
-        List<PreGeneratedLoanSchedulePeriod> expectedRepaymentPeriods = 
getScheduledDateGenerator()
-                .generateRepaymentPeriods(loanApplicationTerms, 
holidayDetailDTO);
-        EMICalculationResult emiCalculationResult = 
getEMICalculator().calculateEMIValueAndRateFactors(scheduleParams,
-                loanApplicationTerms.toLoanProductRelatedDetail(), 
expectedRepaymentPeriods, mc);
-
+        EMICalculationResult emiCalculationResult = null;
         boolean isFirstRepayment = true;
 
         // charges which depends on total loan interest will be added to this
@@ -105,7 +101,6 @@ public abstract class 
AbstractProgressiveLoanScheduleGenerator implements LoanSc
             AdjustedDateDetailsDTO adjustedDateDetailsDTO = 
getScheduledDateGenerator()
                     
.adjustRepaymentDate(scheduleParams.getActualRepaymentDate(), 
loanApplicationTerms, holidayDetailDTO);
             
scheduleParams.setActualRepaymentDate(adjustedDateDetailsDTO.getChangedActualRepaymentDate());
-            isFirstRepayment = false;
             LocalDate scheduledDueDate = 
adjustedDateDetailsDTO.getChangedScheduleDate();
 
             // Loan Schedule Exceptions that need to be applied for Loan 
Account
@@ -117,8 +112,16 @@ public abstract class 
AbstractProgressiveLoanScheduleGenerator implements LoanSc
                     
.addLoanTermInDays(Math.toIntExact(ChronoUnit.DAYS.between(scheduleParams.getPeriodStartDate(),
 scheduledDueDate)));
 
             ScheduleCurrentPeriodParams currentPeriodParams = new 
ScheduleCurrentPeriodParams(currency, BigDecimal.ZERO);
-
-            processDisbursements(loanApplicationTerms, 
chargesDueAtTimeOfDisbursement, scheduleParams, periods, scheduledDueDate);
+            final boolean hasAnyProcessedDisbursement = 
processDisbursements(loanApplicationTerms, chargesDueAtTimeOfDisbursement,
+                    scheduleParams, periods, scheduledDueDate);
+            if (isFirstRepayment || hasAnyProcessedDisbursement) {
+                final LocalDate startDate = isFirstRepayment ? 
loanApplicationTerms.getRepaymentStartDate()
+                        : scheduleParams.getPeriodStartDate();
+                List<PreGeneratedLoanSchedulePeriod> expectedRepaymentPeriods 
= getScheduledDateGenerator()
+                        .generateRepaymentPeriods(startDate, 
loanApplicationTerms, holidayDetailDTO);
+                emiCalculationResult = 
getEMICalculator().calculateEMIValueAndRateFactors(scheduleParams,
+                        loanApplicationTerms.toLoanProductRelatedDetail(), 
expectedRepaymentPeriods, mc);
+            }
 
             // 5 determine principal,interest of repayment period
             PrincipalInterest principalInterestForThisPeriod = 
calculatePrincipalInterestComponentsForPeriod(loanApplicationTerms,
@@ -189,6 +192,7 @@ public abstract class 
AbstractProgressiveLoanScheduleGenerator implements LoanSc
             // }
             thereIsDisbursementBeforeOrOnLoanEndDate = 
scheduleParams.getDisburseDetailMap().entrySet().stream()
                     .anyMatch(d -> 
!d.getKey().isAfter(loanApplicationTerms.getLoanEndDate()));
+            isFirstRepayment = false;
         }
 
         // If the disbursement happened after maturity date
@@ -327,9 +331,10 @@ public abstract class 
AbstractProgressiveLoanScheduleGenerator implements LoanSc
     /**
      * Method add extra disbursement periods (if applicable) and update the 
schedule params
      */
-    private void processDisbursements(final LoanApplicationTerms 
loanApplicationTerms, final BigDecimal chargesDueAtTimeOfDisbursement,
+    private boolean processDisbursements(final LoanApplicationTerms 
loanApplicationTerms, final BigDecimal chargesDueAtTimeOfDisbursement,
             LoanScheduleParams scheduleParams, final 
Collection<LoanScheduleModelPeriod> periods, final LocalDate scheduledDueDate) {
         Iterator<Map.Entry<LocalDate, Money>> iter = 
scheduleParams.getDisburseDetailMap().entrySet().iterator();
+        boolean hasProcessedDisbursement = false;
         while (iter.hasNext()) {
             Map.Entry<LocalDate, Money> disburseDetail = iter.next();
             if 
((disburseDetail.getKey().isEqual(scheduleParams.getPeriodStartDate())
@@ -373,8 +378,10 @@ public abstract class 
AbstractProgressiveLoanScheduleGenerator implements LoanSc
                     }
                 }
                 iter.remove();
+                hasProcessedDisbursement = true;
             }
         }
+        return hasProcessedDisbursement;
     }
 
     private void applyChargesForCurrentPeriod(final Set<LoanCharge> 
loanCharges, final MonetaryCurrency currency,
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
index 10bb88987..3f51b727d 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
@@ -54,8 +54,11 @@ public class ProgressiveLoanScheduleGenerator extends 
AbstractProgressiveLoanSch
     public PrincipalInterest 
calculatePrincipalInterestComponentsForPeriod(final LoanApplicationTerms 
loanApplicationTerms,
             final LoanScheduleParams loanScheduleParams, final 
EMICalculationResult emiCalculationResult, final MathContext mc) {
 
-        final Money equalMonthlyInstallmentValue = 
Money.of(loanApplicationTerms.getCurrency(),
-                emiCalculationResult.getEqualMonthlyInstallmentValue());
+        final Money equalMonthlyInstallmentValue = 
loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null
+                ? Money.of(loanApplicationTerms.getCurrency(),
+                        
Money.roundToMultiplesOf(emiCalculationResult.getEqualMonthlyInstallmentValue(),
+                                
loanApplicationTerms.getInstallmentAmountInMultiplesOf()))
+                : Money.of(loanApplicationTerms.getCurrency(), 
emiCalculationResult.getEqualMonthlyInstallmentValue());
         final BigDecimal rateFactorMinus1 = 
emiCalculationResult.getNextRepaymentPeriodRateFactorMinus1();
         final Money calculatedInterest = 
loanScheduleParams.getOutstandingBalance().multipliedBy(rateFactorMinus1);
         final Money calculatedPrincipal = 
equalMonthlyInstallmentValue.minus(calculatedInterest);
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java
index 477ff9377..f0812a6ed 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java
@@ -58,12 +58,9 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
     public EMICalculationResult calculateEMIValueAndRateFactors(final 
LoanScheduleParams scheduleParams,
             final LoanProductRelatedDetail loanProductRelatedDetail, final 
List<? extends LoanScheduleModelPeriod> expectedRepaymentPeriods,
             final MathContext mc) {
-        // TODO: decide which outstanding balance method needed in the future
         final BigDecimal nominalInterestRatePerPeriod = 
calcNominalInterestRatePerPeriod(
                 loanProductRelatedDetail.getNominalInterestRatePerPeriod(), 
mc);
-        final BigDecimal outstandingBalance = 
scheduleParams.getOutstandingBalanceAsPerRest().getAmount().compareTo(BigDecimal.ZERO)
 > 0
-                ? scheduleParams.getOutstandingBalanceAsPerRest().getAmount()
-                : loanProductRelatedDetail.getPrincipal().getAmount();
+        final BigDecimal outstandingBalance = 
scheduleParams.getOutstandingBalanceAsPerRest().getAmount();
         final DaysInYearType daysInYearType = 
DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType());
         final DaysInMonthType daysInMonthType = 
DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType());
         final PeriodFrequencyType repaymentFrequency = 
loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
diff --git 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java
 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java
index 447948e8b..94cb18a26 100644
--- 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java
+++ 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java
@@ -66,7 +66,7 @@ class ProgressiveEMICalculatorTest {
     @BeforeAll
     public static void init() {
         periods = new ArrayList<>();
-        LocalDate startDate = LocalDate.of(2024, 01, 1);
+        LocalDate startDate = LocalDate.of(2024, 1, 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)));
@@ -495,11 +495,9 @@ class ProgressiveEMICalculatorTest {
         final MathContext mc = MoneyHelper.getMathContext();
 
         final BigDecimal interestRate = BigDecimal.valueOf(0);
-        final Money principal = Money.of(monetaryCurrency, 
BigDecimal.valueOf(1000));
-        final Money outstandingBalance = Money.of(monetaryCurrency, 
BigDecimal.ZERO);
+        final Money outstandingBalance = Money.of(monetaryCurrency, 
BigDecimal.valueOf(1000));
 
         
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
-        
Mockito.when(loanProductRelatedDetail.getPrincipal()).thenReturn(principal);
         
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
         
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
         
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
index 1d2e9f487..83245dfbb 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
@@ -43,12 +43,11 @@ import org.springframework.stereotype.Component;
 public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
 
     @Override
-    public List<PreGeneratedLoanSchedulePeriod> generateRepaymentPeriods(final 
LoanApplicationTerms loanApplicationTerms,
-            final HolidayDetailDTO holidayDetailDTO) {
+    public List<PreGeneratedLoanSchedulePeriod> generateRepaymentPeriods(final 
LocalDate scheduledDueDate,
+            final LoanApplicationTerms loanApplicationTerms, final 
HolidayDetailDTO holidayDetailDTO) {
         final int numberOfRepayments = 
loanApplicationTerms.getNumberOfRepayments();
         final ArrayList<PreGeneratedLoanSchedulePeriod> repaymentPeriods = new 
ArrayList<>(numberOfRepayments);
-        // TODO: check periodStartDate
-        LocalDate lastRepaymentDate = 
loanApplicationTerms.getRepaymentStartDate();
+        LocalDate lastRepaymentDate = scheduledDueDate;
         LocalDate nextRepaymentDate;
         boolean isFirstRepayment = true;
         for (int repaymentPeriod = 1; repaymentPeriod <= numberOfRepayments; 
repaymentPeriod++) {
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java
index 5b6987bea..666c1b6e8 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java
@@ -98,7 +98,8 @@ public class DefaultScheduledDateGeneratorTest {
                 submittedOnDate, CUMULATIVE, 
LoanScheduleProcessingType.HORIZONTAL, null);
 
         // when
-        List<PreGeneratedLoanSchedulePeriod> result = 
underTest.generateRepaymentPeriods(loanApplicationTerms, holidayDetailDTO);
+        List<PreGeneratedLoanSchedulePeriod> result = 
underTest.generateRepaymentPeriods(expectedDisbursementDate, 
loanApplicationTerms,
+                holidayDetailDTO);
 
         // then
         assertThat(result.size()).isEqualTo(4);

Reply via email to