Skip to content

Commit

Permalink
FINERACT-1981: Fix interest calculation when applies multi disbursement
Browse files Browse the repository at this point in the history
  • Loading branch information
janez89 authored and adamsaghy committed Jan 20, 2025
1 parent 6e6ac8f commit 8ab9c9e
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 107 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.TreeSet;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import lombok.Data;
import lombok.experimental.Accessors;
Expand Down Expand Up @@ -62,7 +65,8 @@ private ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> repayme
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf,
final MathContext mc) {
this.mc = mc;
this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods);
this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods,
(previousPeriod, repaymentPeriod) -> new RepaymentPeriod(previousPeriod, repaymentPeriod, mc));
this.interestRates = new TreeSet<>(interestRates);
this.loanProductRelatedDetail = loanProductRelatedDetail;
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
Expand All @@ -74,11 +78,20 @@ public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) {
installmentAmountInMultiplesOf, mc);
}

private List<RepaymentPeriod> copyRepaymentPeriods(final List<RepaymentPeriod> repaymentPeriods) {
public ProgressiveLoanInterestScheduleModel emptyCopy() {
final List<RepaymentPeriod> repaymentPeriodCopies = copyRepaymentPeriods(repaymentPeriods,
(previousPeriod, repaymentPeriod) -> new RepaymentPeriod(previousPeriod, repaymentPeriod.getFromDate(),
repaymentPeriod.getDueDate(), repaymentPeriod.getEmi().zero(), mc));
return new ProgressiveLoanInterestScheduleModel(repaymentPeriodCopies, interestRates, loanProductRelatedDetail,
installmentAmountInMultiplesOf, mc);
}

private List<RepaymentPeriod> copyRepaymentPeriods(final List<RepaymentPeriod> repaymentPeriods,
final BiFunction<RepaymentPeriod, RepaymentPeriod, RepaymentPeriod> repaymentCopyFunction) {
final List<RepaymentPeriod> repaymentCopies = new ArrayList<>(repaymentPeriods.size());
RepaymentPeriod previousPeriod = null;
for (RepaymentPeriod repaymentPeriod : repaymentPeriods) {
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, repaymentPeriod, mc);
RepaymentPeriod currentPeriod = repaymentCopyFunction.apply(previousPeriod, repaymentPeriod);
previousPeriod = currentPeriod;
repaymentCopies.add(currentPeriod);
}
Expand Down Expand Up @@ -229,4 +242,40 @@ public Optional<RepaymentPeriod> findRepaymentPeriod(@NotNull LocalDate transact
.filter(period -> isInPeriod(transactionDate, period.getFromDate(), period.getDueDate(), period.isFirstRepaymentPeriod()))//
.findFirst();
}

public boolean isEmpty() {
return repaymentPeriods.stream() //
.filter(rp -> !rp.getEmi().isZero()) //
.findFirst() //
.isEmpty(); //
}

/**
* This method gives you repayment pairs to copy attributes.
*
* @param periodFromDueDate
* Copy from this due periods.
* @param copyFromPeriods
* Copy source
* @param copyConsumer
* Consumer to copy attributes. Params: (from, to)
*/
public void copyPeriodsFrom(final LocalDate periodFromDueDate, List<RepaymentPeriod> copyFromPeriods,
BiConsumer<RepaymentPeriod, RepaymentPeriod> copyConsumer) {
if (copyFromPeriods.isEmpty()) {
return;
}
final Iterator<RepaymentPeriod> actualIterator = repaymentPeriods.iterator();
final Iterator<RepaymentPeriod> copyFromIterator = copyFromPeriods.iterator();
while (actualIterator.hasNext()) {
final RepaymentPeriod copyFromPeriod = copyFromIterator.next();
RepaymentPeriod actualPeriod = actualIterator.next();
while (actualIterator.hasNext() && !copyFromPeriod.getDueDate().isEqual(actualPeriod.getDueDate())) {
actualPeriod = actualIterator.next();
}
if (!actualPeriod.getDueDate().isBefore(periodFromDueDate)) {
copyConsumer.accept(copyFromPeriod, actualPeriod);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* 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.calc;

import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.fineract.organisation.monetary.domain.Money;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class EmiChangeOperation {

public enum Action {
DISBURSEMENT, INTEREST_RATE_CHANGE
}

private final Action action;
private final LocalDate submittedOnDate;

private final Money amount;
private final BigDecimal interestRate;

public static EmiChangeOperation disburse(final LocalDate disbursementDueDate, final Money disbursedAmount) {
return new EmiChangeOperation(EmiChangeOperation.Action.DISBURSEMENT, disbursementDueDate, disbursedAmount, null);
}

public static EmiChangeOperation changeInterestRate(final LocalDate newInterestSubmittedOnDate, final BigDecimal newInterestRate) {
return new EmiChangeOperation(EmiChangeOperation.Action.INTEREST_RATE_CHANGE, newInterestSubmittedOnDate, null, newInterestRate);
}
}
Loading

0 comments on commit 8ab9c9e

Please sign in to comment.