FINERACT-1981: Remove hardcoded MoneyHelper dependency during progressive loan
This commit is contained in:
committed by
Adam Saghy
parent
806ee69798
commit
a9ecdf97a4
+17
@@ -359,6 +359,11 @@ public final class MathUtil {
|
||||
return value == null || isGreaterThanZero(value) ? value : Money.zero(value.getCurrency());
|
||||
}
|
||||
|
||||
/** @return parameter value or ZERO if it is negative */
|
||||
public static Money negativeToZero(Money value, MathContext mc) {
|
||||
return value == null || isGreaterThanZero(value, mc) ? value : Money.zero(value.getCurrency(), mc);
|
||||
}
|
||||
|
||||
public static boolean isEmpty(Money value) {
|
||||
return value == null || value.isZero();
|
||||
}
|
||||
@@ -367,6 +372,10 @@ public final class MathUtil {
|
||||
return value != null && value.isGreaterThanZero();
|
||||
}
|
||||
|
||||
public static boolean isGreaterThanZero(Money value, MathContext mc) {
|
||||
return value != null && value.isGreaterThanZero(mc);
|
||||
}
|
||||
|
||||
public static boolean isLessThanZero(Money value) {
|
||||
return value != null && value.isLessThanZero();
|
||||
}
|
||||
@@ -387,10 +396,18 @@ public final class MathUtil {
|
||||
return first == null ? second : second == null ? first : first.plus(second);
|
||||
}
|
||||
|
||||
public static Money plus(Money first, Money second, MathContext mc) {
|
||||
return first == null ? second : second == null ? first : first.plus(second, mc);
|
||||
}
|
||||
|
||||
public static Money plus(Money first, Money second, Money third) {
|
||||
return plus(plus(first, second), third);
|
||||
}
|
||||
|
||||
public static Money plus(Money first, Money second, Money third, MathContext mc) {
|
||||
return plus(plus(first, second), third, mc);
|
||||
}
|
||||
|
||||
public static Money plus(Money first, Money second, Money third, Money fourth) {
|
||||
return plus(plus(plus(first, second), third), fourth);
|
||||
}
|
||||
|
||||
+149
-30
@@ -21,11 +21,14 @@ package org.apache.fineract.organisation.monetary.domain;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Iterator;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
|
||||
@Embeddable
|
||||
@Getter
|
||||
public class Money implements Comparable<Money> {
|
||||
|
||||
@Column(name = "currency_code", length = 3)
|
||||
@@ -40,6 +43,16 @@ public class Money implements Comparable<Money> {
|
||||
@Column(name = "amount", scale = 6, precision = 19)
|
||||
private BigDecimal amount;
|
||||
|
||||
private final transient MathContext mc;
|
||||
|
||||
protected Money() {
|
||||
this.currencyCode = null;
|
||||
this.currencyDigitsAfterDecimal = 0;
|
||||
this.inMultiplesOf = 0;
|
||||
this.amount = null;
|
||||
this.mc = getMc();
|
||||
}
|
||||
|
||||
public static Money total(final Money... monies) {
|
||||
if (monies.length == 0) {
|
||||
throw new IllegalArgumentException("Money array must not be empty");
|
||||
@@ -53,7 +66,7 @@ public class Money implements Comparable<Money> {
|
||||
|
||||
public static Money total(final Iterable<? extends Money> monies) {
|
||||
final Iterator<? extends Money> it = monies.iterator();
|
||||
if (it.hasNext() == false) {
|
||||
if (!it.hasNext()) {
|
||||
throw new IllegalArgumentException("Money iterator must not be empty");
|
||||
}
|
||||
Money total = it.next();
|
||||
@@ -63,34 +76,47 @@ public class Money implements Comparable<Money> {
|
||||
return total;
|
||||
}
|
||||
|
||||
public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount, final MathContext mc) {
|
||||
return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), defaultToZeroIfNull(newAmount),
|
||||
currency.getCurrencyInMultiplesOf(), mc);
|
||||
}
|
||||
|
||||
public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount) {
|
||||
return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), defaultToZeroIfNull(newAmount),
|
||||
currency.getCurrencyInMultiplesOf());
|
||||
currency.getCurrencyInMultiplesOf(), null);
|
||||
}
|
||||
|
||||
public static Money of(final CurrencyData currency, final BigDecimal newAmount) {
|
||||
return new Money(currency.getCode(), currency.getDecimalPlaces(), defaultToZeroIfNull(newAmount), currency.getInMultiplesOf());
|
||||
return new Money(currency.getCode(), currency.getDecimalPlaces(), defaultToZeroIfNull(newAmount), currency.getInMultiplesOf(),
|
||||
null);
|
||||
}
|
||||
|
||||
public static Money of(final CurrencyData currency, final BigDecimal newAmount, final MathContext mc) {
|
||||
return new Money(currency.getCode(), currency.getDecimalPlaces(), defaultToZeroIfNull(newAmount), currency.getInMultiplesOf(), mc);
|
||||
}
|
||||
|
||||
public static Money zero(final MonetaryCurrency currency) {
|
||||
return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO, currency.getCurrencyInMultiplesOf());
|
||||
return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO, currency.getCurrencyInMultiplesOf(), null);
|
||||
}
|
||||
|
||||
public static Money zero(final MonetaryCurrency currency, MathContext mc) {
|
||||
return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO, currency.getCurrencyInMultiplesOf(), mc);
|
||||
}
|
||||
|
||||
public static Money zero(final CurrencyData currency, MathContext mc) {
|
||||
return new Money(currency.getCode(), currency.getDecimalPlaces(), BigDecimal.ZERO, currency.getInMultiplesOf(), mc);
|
||||
}
|
||||
|
||||
public static Money zero(final CurrencyData currency) {
|
||||
return new Money(currency.getCode(), currency.getDecimalPlaces(), BigDecimal.ZERO, currency.getInMultiplesOf());
|
||||
return new Money(currency.getCode(), currency.getDecimalPlaces(), BigDecimal.ZERO, currency.getInMultiplesOf(), null);
|
||||
}
|
||||
|
||||
protected Money() {
|
||||
this.currencyCode = null;
|
||||
this.currencyDigitsAfterDecimal = 0;
|
||||
this.inMultiplesOf = 0;
|
||||
this.amount = null;
|
||||
}
|
||||
|
||||
private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount, final Integer inMultiplesOf) {
|
||||
private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount, final Integer inMultiplesOf,
|
||||
final MathContext mc) {
|
||||
this.currencyCode = currencyCode;
|
||||
this.currencyDigitsAfterDecimal = digitsAfterDecimal;
|
||||
this.inMultiplesOf = inMultiplesOf;
|
||||
this.mc = mc;
|
||||
|
||||
final BigDecimal amountZeroed = defaultToZeroIfNull(amount);
|
||||
final BigDecimal amountStripped = amountZeroed.stripTrailingZeros();
|
||||
@@ -101,7 +127,7 @@ public class Money implements Comparable<Money> {
|
||||
final double existingVal = amountScaled.doubleValue();
|
||||
amountScaled = BigDecimal.valueOf(roundToMultiplesOf(existingVal, inMultiplesOf));
|
||||
}
|
||||
this.amount = amountScaled.setScale(this.currencyDigitsAfterDecimal, MoneyHelper.getRoundingMode());
|
||||
this.amount = amountScaled.setScale(this.currencyDigitsAfterDecimal, getMc().getRoundingMode());
|
||||
}
|
||||
|
||||
public static double roundToMultiplesOf(final double existingVal, final Integer inMultiplesOf) {
|
||||
@@ -130,10 +156,14 @@ public class Money implements Comparable<Money> {
|
||||
}
|
||||
|
||||
public static Money roundToMultiplesOf(final Money existingVal, final Integer inMultiplesOf) {
|
||||
return roundToMultiplesOf(existingVal, inMultiplesOf, MoneyHelper.getMathContext());
|
||||
}
|
||||
|
||||
public static Money roundToMultiplesOf(final Money existingVal, final Integer inMultiplesOf, final MathContext mc) {
|
||||
BigDecimal amountScaled = existingVal.getAmount();
|
||||
BigDecimal inMultiplesOfValue = BigDecimal.valueOf(inMultiplesOf.intValue());
|
||||
if (inMultiplesOfValue.compareTo(BigDecimal.ZERO) > 0) {
|
||||
amountScaled = amountScaled.divide(inMultiplesOfValue, 0, MoneyHelper.getRoundingMode()).multiply(inMultiplesOfValue);
|
||||
amountScaled = amountScaled.divide(inMultiplesOfValue, 0, mc.getRoundingMode()).multiply(inMultiplesOfValue);
|
||||
}
|
||||
return Money.of(existingVal.getCurrency(), amountScaled);
|
||||
}
|
||||
@@ -171,7 +201,7 @@ public class Money implements Comparable<Money> {
|
||||
}
|
||||
|
||||
public Money copy() {
|
||||
return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, this.amount.stripTrailingZeros(), this.inMultiplesOf);
|
||||
return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, this.amount.stripTrailingZeros(), this.inMultiplesOf, this.mc);
|
||||
}
|
||||
|
||||
public Money plus(final Iterable<? extends Money> moniesToAdd) {
|
||||
@@ -188,6 +218,11 @@ public class Money implements Comparable<Money> {
|
||||
return this.plus(toAdd.getAmount());
|
||||
}
|
||||
|
||||
public Money plus(final Money moneyToAdd, final MathContext mc) {
|
||||
final Money toAdd = checkCurrencyEqual(moneyToAdd);
|
||||
return this.plus(toAdd.getAmount(), mc);
|
||||
}
|
||||
|
||||
public Money plus(final BigDecimal amountToAdd) {
|
||||
if (amountToAdd == null || amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return this;
|
||||
@@ -196,6 +231,14 @@ public class Money implements Comparable<Money> {
|
||||
return Money.of(monetaryCurrency(), newAmount);
|
||||
}
|
||||
|
||||
public Money plus(final BigDecimal amountToAdd, MathContext mc) {
|
||||
if (amountToAdd == null || amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return this;
|
||||
}
|
||||
final BigDecimal newAmount = this.amount.add(amountToAdd);
|
||||
return Money.of(monetaryCurrency(), newAmount, mc);
|
||||
}
|
||||
|
||||
public Money plus(final double amountToAdd) {
|
||||
if (amountToAdd == 0) {
|
||||
return this;
|
||||
@@ -209,11 +252,21 @@ public class Money implements Comparable<Money> {
|
||||
return this.minus(toSubtract.getAmount());
|
||||
}
|
||||
|
||||
public Money minus(final Money moneyToSubtract, final MathContext mc) {
|
||||
final Money toSubtract = checkCurrencyEqual(moneyToSubtract);
|
||||
return this.minus(toSubtract.getAmount(), mc);
|
||||
}
|
||||
|
||||
public Money add(final Money moneyToAdd) {
|
||||
final Money toAdd = checkCurrencyEqual(moneyToAdd);
|
||||
return this.add(toAdd.getAmount());
|
||||
}
|
||||
|
||||
public Money add(final Money moneyToAdd, final MathContext mc) {
|
||||
final Money toAdd = checkCurrencyEqual(moneyToAdd);
|
||||
return this.add(toAdd.getAmount(), mc);
|
||||
}
|
||||
|
||||
public Money add(final BigDecimal amountToAdd) {
|
||||
if (amountToAdd == null || amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return this;
|
||||
@@ -222,6 +275,14 @@ public class Money implements Comparable<Money> {
|
||||
return Money.of(monetaryCurrency(), newAmount);
|
||||
}
|
||||
|
||||
public Money add(final BigDecimal amountToAdd, final MathContext mc) {
|
||||
if (amountToAdd == null || amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return this;
|
||||
}
|
||||
final BigDecimal newAmount = this.amount.add(amountToAdd);
|
||||
return Money.of(monetaryCurrency(), newAmount, mc);
|
||||
}
|
||||
|
||||
public Money minus(final BigDecimal amountToSubtract) {
|
||||
if (amountToSubtract == null || amountToSubtract.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return this;
|
||||
@@ -230,8 +291,16 @@ public class Money implements Comparable<Money> {
|
||||
return Money.of(monetaryCurrency(), newAmount);
|
||||
}
|
||||
|
||||
public Money minus(final BigDecimal amountToSubtract, final MathContext mc) {
|
||||
if (amountToSubtract == null || amountToSubtract.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return this;
|
||||
}
|
||||
final BigDecimal newAmount = this.amount.subtract(amountToSubtract);
|
||||
return Money.of(monetaryCurrency(), newAmount, mc);
|
||||
}
|
||||
|
||||
private Money checkCurrencyEqual(final Money money) {
|
||||
if (isSameCurrency(money) == false) {
|
||||
if (!isSameCurrency(money)) {
|
||||
throw new UnsupportedOperationException("currencies are different.");
|
||||
}
|
||||
return money;
|
||||
@@ -249,6 +318,14 @@ public class Money implements Comparable<Money> {
|
||||
return Money.of(monetaryCurrency(), newAmount);
|
||||
}
|
||||
|
||||
public Money dividedBy(final BigDecimal valueToDivideBy, final RoundingMode roundingMode, final MathContext mc) {
|
||||
if (valueToDivideBy.compareTo(BigDecimal.ONE) == 0) {
|
||||
return this;
|
||||
}
|
||||
final BigDecimal newAmount = this.amount.divide(valueToDivideBy, roundingMode);
|
||||
return Money.of(monetaryCurrency(), newAmount, mc);
|
||||
}
|
||||
|
||||
public Money dividedBy(final double valueToDivideBy, final RoundingMode roundingMode) {
|
||||
if (valueToDivideBy == 1) {
|
||||
return this;
|
||||
@@ -265,6 +342,14 @@ public class Money implements Comparable<Money> {
|
||||
return Money.of(monetaryCurrency(), newAmount);
|
||||
}
|
||||
|
||||
public Money dividedBy(final long valueToDivideBy, final RoundingMode roundingMode, final MathContext mc) {
|
||||
if (valueToDivideBy == 1) {
|
||||
return this;
|
||||
}
|
||||
final BigDecimal newAmount = this.amount.divide(BigDecimal.valueOf(valueToDivideBy), roundingMode);
|
||||
return Money.of(monetaryCurrency(), newAmount, mc);
|
||||
}
|
||||
|
||||
public Money multipliedBy(final BigDecimal valueToMultiplyBy) {
|
||||
if (valueToMultiplyBy.compareTo(BigDecimal.ONE) == 0) {
|
||||
return this;
|
||||
@@ -273,6 +358,14 @@ public class Money implements Comparable<Money> {
|
||||
return Money.of(monetaryCurrency(), newAmount);
|
||||
}
|
||||
|
||||
public Money multipliedBy(final BigDecimal valueToMultiplyBy, final MathContext mc) {
|
||||
if (valueToMultiplyBy.compareTo(BigDecimal.ONE) == 0) {
|
||||
return this;
|
||||
}
|
||||
final BigDecimal newAmount = this.amount.multiply(valueToMultiplyBy);
|
||||
return Money.of(monetaryCurrency(), newAmount, mc);
|
||||
}
|
||||
|
||||
public Money multipliedBy(final double valueToMultiplyBy) {
|
||||
if (valueToMultiplyBy == 1) {
|
||||
return this;
|
||||
@@ -289,6 +382,14 @@ public class Money implements Comparable<Money> {
|
||||
return Money.of(monetaryCurrency(), newAmount);
|
||||
}
|
||||
|
||||
public Money multipliedBy(final long valueToMultiplyBy, final MathContext mc) {
|
||||
if (valueToMultiplyBy == 1) {
|
||||
return this;
|
||||
}
|
||||
final BigDecimal newAmount = this.amount.multiply(BigDecimal.valueOf(valueToMultiplyBy));
|
||||
return Money.of(monetaryCurrency(), newAmount, mc);
|
||||
}
|
||||
|
||||
public Money multiplyRetainScale(final BigDecimal valueToMultiplyBy, final RoundingMode roundingMode) {
|
||||
if (valueToMultiplyBy.compareTo(BigDecimal.ONE) == 0) {
|
||||
return this;
|
||||
@@ -309,17 +410,20 @@ public class Money implements Comparable<Money> {
|
||||
|
||||
@Override
|
||||
public int compareTo(final Money other) {
|
||||
final Money otherMoney = other;
|
||||
if (this.currencyCode.equals(otherMoney.currencyCode) == false) {
|
||||
if (!this.currencyCode.equals(other.currencyCode)) {
|
||||
throw new UnsupportedOperationException("currencies arent different");
|
||||
}
|
||||
return this.amount.compareTo(otherMoney.amount);
|
||||
return this.amount.compareTo(other.amount);
|
||||
}
|
||||
|
||||
public boolean isZero() {
|
||||
return isEqualTo(Money.zero(getCurrency()));
|
||||
}
|
||||
|
||||
public boolean isZero(final MathContext mc) {
|
||||
return isEqualTo(Money.zero(getCurrency(), mc));
|
||||
}
|
||||
|
||||
public boolean isEqualTo(final Money other) {
|
||||
return compareTo(other) == 0;
|
||||
}
|
||||
@@ -340,6 +444,10 @@ public class Money implements Comparable<Money> {
|
||||
return isGreaterThan(Money.zero(getCurrency()));
|
||||
}
|
||||
|
||||
public boolean isGreaterThanZero(MathContext mc) {
|
||||
return isGreaterThan(Money.zero(getCurrency(), mc));
|
||||
}
|
||||
|
||||
public boolean isLessThan(final Money other) {
|
||||
return compareTo(other) < 0;
|
||||
}
|
||||
@@ -348,22 +456,14 @@ public class Money implements Comparable<Money> {
|
||||
return isLessThan(Money.zero(getCurrency()));
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return this.currencyCode;
|
||||
}
|
||||
|
||||
public int getCurrencyDigitsAfterDecimal() {
|
||||
return this.currencyDigitsAfterDecimal;
|
||||
public boolean isLessThanZero(final MathContext mc) {
|
||||
return isLessThan(Money.zero(getCurrency(), mc));
|
||||
}
|
||||
|
||||
public Integer getCurrencyInMultiplesOf() {
|
||||
return this.inMultiplesOf;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public BigDecimal getAmountDefaultedToNullIfZero() {
|
||||
return defaultToNullIfZero(this.amount);
|
||||
}
|
||||
@@ -388,10 +488,21 @@ public class Money implements Comparable<Money> {
|
||||
return Money.of(monetaryCurrency(), this.amount.negate());
|
||||
}
|
||||
|
||||
public Money negated(final MathContext mc) {
|
||||
if (isZero(mc)) {
|
||||
return this;
|
||||
}
|
||||
return Money.of(monetaryCurrency(), this.amount.negate(), mc);
|
||||
}
|
||||
|
||||
public Money abs() {
|
||||
return isLessThanZero() ? negated() : this;
|
||||
}
|
||||
|
||||
public Money abs(MathContext mc) {
|
||||
return isLessThanZero(mc) ? negated(mc) : this;
|
||||
}
|
||||
|
||||
public MonetaryCurrency getCurrency() {
|
||||
return monetaryCurrency();
|
||||
}
|
||||
@@ -403,4 +514,12 @@ public class Money implements Comparable<Money> {
|
||||
public Money zero() {
|
||||
return Money.zero(getCurrency());
|
||||
}
|
||||
|
||||
public Money zero(MathContext mc) {
|
||||
return Money.zero(getCurrency(), mc);
|
||||
}
|
||||
|
||||
public MathContext getMc() {
|
||||
return mc != null ? mc : MoneyHelper.getMathContext();
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -65,6 +65,7 @@ public class MoneyHelper {
|
||||
public static void fetchRoundingModeFromGlobalConfig() {
|
||||
roundingMode = RoundingMode.valueOf(staticConfigurationDomainService.getRoundingMode());
|
||||
log.info("Fetch Rounding Mode from Global Config {}", roundingMode.name());
|
||||
mathContext = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ dependencies {
|
||||
'org.apache.commons:commons-collections4'
|
||||
|
||||
)
|
||||
implementation ('org.mnode.ical4j:ical4j') {
|
||||
exclude group: 'commons-logging'
|
||||
exclude group: 'javax.activation'
|
||||
exclude group: 'com.sun.mail', module: 'javax.mail'
|
||||
}
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
annotationProcessor 'org.projectlombok:lombok'
|
||||
annotationProcessor 'org.mapstruct:mapstruct-processor'
|
||||
|
||||
+38
-34
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -98,6 +99,7 @@ public final class LoanScheduleParams {
|
||||
private int loanTermInDays;
|
||||
private final MonetaryCurrency currency;
|
||||
private final boolean applyInterestRecalculation;
|
||||
private final MathContext mc;
|
||||
|
||||
private LoanScheduleParams(final int periodNumber, final int instalmentNumber, int loanTermInDays, LocalDate periodStartDate,
|
||||
final LocalDate actualRepaymentDate, final Money totalCumulativePrincipal, final Money totalCumulativeInterest,
|
||||
@@ -108,7 +110,7 @@ public final class LoanScheduleParams {
|
||||
final Money outstandingBalanceAsPerRest, final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||
final boolean partialUpdate, final MonetaryCurrency currency, final boolean applyInterestRecalculation) {
|
||||
final boolean partialUpdate, final MonetaryCurrency currency, final boolean applyInterestRecalculation, final MathContext mc) {
|
||||
this.periodNumber = periodNumber;
|
||||
this.instalmentNumber = instalmentNumber;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
@@ -136,8 +138,9 @@ public final class LoanScheduleParams {
|
||||
this.partialUpdate = partialUpdate;
|
||||
this.currency = currency;
|
||||
this.applyInterestRecalculation = applyInterestRecalculation;
|
||||
this.mc = mc;
|
||||
if (this.currency != null) {
|
||||
this.compoundedInLastInstallment = Money.zero(this.currency);
|
||||
this.compoundedInLastInstallment = Money.zero(this.currency, mc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,19 +153,19 @@ public final class LoanScheduleParams {
|
||||
final Money principalToBeScheduled, final Money outstandingBalance, final Money outstandingBalanceAsPerRest,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||
final MonetaryCurrency currency, final boolean applyInterestRecalculation) {
|
||||
final MonetaryCurrency currency, final boolean applyInterestRecalculation, final MathContext mc) {
|
||||
final boolean partialUpdate = true;
|
||||
return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap,
|
||||
compoundingMap, unCompoundedAmount, disburseDetailMap, principalToBeScheduled, outstandingBalance,
|
||||
outstandingBalanceAsPerRest, installments, recalculationDetails, loanRepaymentScheduleTransactionProcessor,
|
||||
scheduleTillDate, partialUpdate, currency, applyInterestRecalculation);
|
||||
scheduleTillDate, partialUpdate, currency, applyInterestRecalculation, mc);
|
||||
}
|
||||
|
||||
public static LoanScheduleParams createLoanScheduleParamsForCompleteUpdate(final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||
final boolean applyInterestRecalculation) {
|
||||
final boolean applyInterestRecalculation, MathContext mc) {
|
||||
final int periodNumber = 1;
|
||||
final int instalmentNumber = 1;
|
||||
final LocalDate periodStartDate = null;
|
||||
@@ -191,22 +194,22 @@ public final class LoanScheduleParams {
|
||||
totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap,
|
||||
compoundingMap, unCompoundedAmount, disburseDetailMap, principalToBeScheduled, outstandingBalance,
|
||||
outstandingBalanceAsPerRest, installments, recalculationDetails, loanRepaymentScheduleTransactionProcessor,
|
||||
scheduleTillDate, partialUpdate, currency, applyInterestRecalculation);
|
||||
scheduleTillDate, partialUpdate, currency, applyInterestRecalculation, mc);
|
||||
}
|
||||
|
||||
public static LoanScheduleParams createLoanScheduleParams(final MonetaryCurrency currency, final Money chargesDueAtTimeOfDisbursement,
|
||||
final LocalDate periodStartDate, final Money principalToBeScheduled) {
|
||||
final LocalDate periodStartDate, final Money principalToBeScheduled, final MathContext mc) {
|
||||
final int loanTermInDays = 0;
|
||||
final int periodNumber = 1;
|
||||
final int instalmentNumber = 1;
|
||||
final Money totalCumulativePrincipal = Money.zero(currency);
|
||||
final Money totalCumulativeInterest = Money.zero(currency);
|
||||
final Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
|
||||
final Money totalCumulativePrincipal = Money.zero(currency, mc);
|
||||
final Money totalCumulativeInterest = Money.zero(currency, mc);
|
||||
final Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency, mc);
|
||||
final LocalDate actualRepaymentDate = periodStartDate;
|
||||
final Money totalFeeChargesCharged = chargesDueAtTimeOfDisbursement;
|
||||
final Money totalPenaltyChargesCharged = Money.zero(currency);
|
||||
final Money totalPenaltyChargesCharged = Money.zero(currency, mc);
|
||||
final Money totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
|
||||
final Money reducePrincipal = Money.zero(currency);
|
||||
final Money reducePrincipal = Money.zero(currency, mc);
|
||||
final Map<LocalDate, Money> principalPortionMap = new HashMap<>();
|
||||
final Map<LocalDate, Money> latePaymentMap = new HashMap<>();
|
||||
final Map<LocalDate, Money> compoundingMap = new TreeMap<>();
|
||||
@@ -218,27 +221,28 @@ public final class LoanScheduleParams {
|
||||
final Collection<RecalculationDetail> recalculationDetails = null;
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = null;
|
||||
final LocalDate scheduleTillDate = null;
|
||||
final Money unCompoundedAmount = Money.zero(currency);
|
||||
final Money unCompoundedAmount = Money.zero(currency, mc);
|
||||
final boolean applyInterestRecalculation = false;
|
||||
return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap,
|
||||
compoundingMap, unCompoundedAmount, disburseDetailMap, principalToBeScheduled, outstandingBalance,
|
||||
outstandingBalanceAsPerRest, installments, recalculationDetails, loanRepaymentScheduleTransactionProcessor,
|
||||
scheduleTillDate, partialUpdate, currency, applyInterestRecalculation);
|
||||
scheduleTillDate, partialUpdate, currency, applyInterestRecalculation, mc);
|
||||
}
|
||||
|
||||
public static LoanScheduleParams createLoanScheduleParams(final MonetaryCurrency currency, final Money chargesDueAtTimeOfDisbursement,
|
||||
final LocalDate periodStartDate, final Money principalToBeScheduled, final LoanScheduleParams loanScheduleParams) {
|
||||
final LocalDate periodStartDate, final Money principalToBeScheduled, final LoanScheduleParams loanScheduleParams,
|
||||
MathContext mc) {
|
||||
final int loanTermInDays = 0;
|
||||
final int periodNumber = 1;
|
||||
final int instalmentNumber = 1;
|
||||
final Money totalCumulativePrincipal = Money.zero(currency);
|
||||
final Money totalCumulativeInterest = Money.zero(currency);
|
||||
final Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
|
||||
final Money totalCumulativePrincipal = Money.zero(currency, mc);
|
||||
final Money totalCumulativeInterest = Money.zero(currency, mc);
|
||||
final Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency, mc);
|
||||
final LocalDate actualRepaymentDate = periodStartDate;
|
||||
final Money totalFeeChargesCharged = chargesDueAtTimeOfDisbursement;
|
||||
final Money totalPenaltyChargesCharged = Money.zero(currency);
|
||||
final Money totalPenaltyChargesCharged = Money.zero(currency, mc);
|
||||
final Money totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
|
||||
final Money reducePrincipal = Money.zero(currency);
|
||||
final Map<LocalDate, Money> principalPortionMap = new HashMap<>();
|
||||
@@ -253,13 +257,13 @@ public final class LoanScheduleParams {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanScheduleParams.loanRepaymentScheduleTransactionProcessor;
|
||||
final LocalDate scheduleTillDate = loanScheduleParams.scheduleTillDate;
|
||||
final boolean applyInterestRecalculation = loanScheduleParams.applyInterestRecalculation;
|
||||
final Money unCompoundedAmount = Money.zero(currency);
|
||||
final Money unCompoundedAmount = Money.zero(currency, mc);
|
||||
return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap,
|
||||
compoundingMap, unCompoundedAmount, disburseDetailMap, principalToBeScheduled, outstandingBalance,
|
||||
outstandingBalanceAsPerRest, installments, recalculationDetails, loanRepaymentScheduleTransactionProcessor,
|
||||
scheduleTillDate, partialUpdate, currency, applyInterestRecalculation);
|
||||
scheduleTillDate, partialUpdate, currency, applyInterestRecalculation, mc);
|
||||
}
|
||||
|
||||
public int getPeriodNumber() {
|
||||
@@ -288,7 +292,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void addTotalCumulativePrincipal(final Money totalCumulativePrincipal) {
|
||||
if (this.totalCumulativePrincipal != null) {
|
||||
this.totalCumulativePrincipal = this.totalCumulativePrincipal.plus(totalCumulativePrincipal);
|
||||
this.totalCumulativePrincipal = this.totalCumulativePrincipal.plus(totalCumulativePrincipal, mc);
|
||||
} else {
|
||||
this.totalCumulativePrincipal = totalCumulativePrincipal;
|
||||
}
|
||||
@@ -300,7 +304,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void addTotalCumulativeInterest(final Money totalCumulativeInterest) {
|
||||
if (this.totalCumulativeInterest != null) {
|
||||
this.totalCumulativeInterest = this.totalCumulativeInterest.plus(totalCumulativeInterest);
|
||||
this.totalCumulativeInterest = this.totalCumulativeInterest.plus(totalCumulativeInterest, mc);
|
||||
} else {
|
||||
this.totalCumulativeInterest = totalCumulativeInterest;
|
||||
}
|
||||
@@ -312,7 +316,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void addTotalFeeChargesCharged(final Money totalFeeChargesCharged) {
|
||||
if (this.totalFeeChargesCharged != null) {
|
||||
this.totalFeeChargesCharged = this.totalFeeChargesCharged.plus(totalFeeChargesCharged);
|
||||
this.totalFeeChargesCharged = this.totalFeeChargesCharged.plus(totalFeeChargesCharged, mc);
|
||||
} else {
|
||||
this.totalFeeChargesCharged = totalFeeChargesCharged;
|
||||
}
|
||||
@@ -324,7 +328,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void addTotalPenaltyChargesCharged(final Money totalPenaltyChargesCharged) {
|
||||
if (this.totalPenaltyChargesCharged != null) {
|
||||
this.totalPenaltyChargesCharged = this.totalPenaltyChargesCharged.plus(totalPenaltyChargesCharged);
|
||||
this.totalPenaltyChargesCharged = this.totalPenaltyChargesCharged.plus(totalPenaltyChargesCharged, mc);
|
||||
} else {
|
||||
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
|
||||
}
|
||||
@@ -336,7 +340,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void addTotalRepaymentExpected(final Money totalRepaymentExpected) {
|
||||
if (this.totalRepaymentExpected != null) {
|
||||
this.totalRepaymentExpected = this.totalRepaymentExpected.plus(totalRepaymentExpected);
|
||||
this.totalRepaymentExpected = this.totalRepaymentExpected.plus(totalRepaymentExpected, mc);
|
||||
} else {
|
||||
this.totalRepaymentExpected = totalRepaymentExpected;
|
||||
}
|
||||
@@ -428,7 +432,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void addReducePrincipal(Money reducePrincipal) {
|
||||
if (this.reducePrincipal != null) {
|
||||
this.reducePrincipal = this.reducePrincipal.plus(reducePrincipal);
|
||||
this.reducePrincipal = this.reducePrincipal.plus(reducePrincipal, mc);
|
||||
} else {
|
||||
this.reducePrincipal = reducePrincipal;
|
||||
}
|
||||
@@ -436,13 +440,13 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void reduceReducePrincipal(Money reducePrincipal) {
|
||||
if (this.reducePrincipal != null) {
|
||||
this.reducePrincipal = this.reducePrincipal.minus(reducePrincipal);
|
||||
this.reducePrincipal = this.reducePrincipal.minus(reducePrincipal, mc);
|
||||
}
|
||||
}
|
||||
|
||||
public void addPrincipalToBeScheduled(Money principalToBeScheduled) {
|
||||
if (this.principalToBeScheduled != null) {
|
||||
this.principalToBeScheduled = this.principalToBeScheduled.plus(principalToBeScheduled);
|
||||
this.principalToBeScheduled = this.principalToBeScheduled.plus(principalToBeScheduled, mc);
|
||||
} else {
|
||||
this.principalToBeScheduled = principalToBeScheduled;
|
||||
}
|
||||
@@ -450,7 +454,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void addOutstandingBalance(Money outstandingBalance) {
|
||||
if (this.outstandingBalance != null) {
|
||||
this.outstandingBalance = this.outstandingBalance.plus(outstandingBalance);
|
||||
this.outstandingBalance = this.outstandingBalance.plus(outstandingBalance, mc);
|
||||
} else {
|
||||
this.outstandingBalance = outstandingBalance;
|
||||
}
|
||||
@@ -458,13 +462,13 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void reduceOutstandingBalance(Money outstandingBalance) {
|
||||
if (this.outstandingBalance != null) {
|
||||
this.outstandingBalance = this.outstandingBalance.minus(outstandingBalance);
|
||||
this.outstandingBalance = this.outstandingBalance.minus(outstandingBalance, mc);
|
||||
}
|
||||
}
|
||||
|
||||
public void addOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
|
||||
if (this.outstandingBalanceAsPerRest != null) {
|
||||
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.plus(outstandingBalanceAsPerRest);
|
||||
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.plus(outstandingBalanceAsPerRest, mc);
|
||||
} else {
|
||||
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
|
||||
}
|
||||
@@ -472,7 +476,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void reduceOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
|
||||
if (this.outstandingBalanceAsPerRest != null) {
|
||||
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.minus(outstandingBalanceAsPerRest);
|
||||
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.minus(outstandingBalanceAsPerRest, mc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,7 +526,7 @@ public final class LoanScheduleParams {
|
||||
|
||||
public void minusUnCompoundedAmount(Money unCompoundedAmount) {
|
||||
if (this.unCompoundedAmount != null) {
|
||||
this.unCompoundedAmount = this.unCompoundedAmount.minus(unCompoundedAmount);
|
||||
this.unCompoundedAmount = this.unCompoundedAmount.minus(unCompoundedAmount, mc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -94,10 +94,10 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
: loanApplicationTerms.getSubmittedOnDate();
|
||||
if (loanScheduleParams == null) {
|
||||
scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency, Money.of(currency, chargesDueAtTimeOfDisbursement),
|
||||
periodStartDate, getPrincipalToBeScheduled(loanApplicationTerms));
|
||||
periodStartDate, getPrincipalToBeScheduled(loanApplicationTerms), mc);
|
||||
} else if (!loanScheduleParams.isPartialUpdate()) {
|
||||
scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency, Money.of(currency, chargesDueAtTimeOfDisbursement),
|
||||
periodStartDate, getPrincipalToBeScheduled(loanApplicationTerms), loanScheduleParams);
|
||||
periodStartDate, getPrincipalToBeScheduled(loanApplicationTerms), loanScheduleParams, mc);
|
||||
} else {
|
||||
scheduleParams = loanScheduleParams;
|
||||
}
|
||||
@@ -345,7 +345,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
scheduleParams.getPeriodStartDate(), scheduledDueDate, currentPeriodParams.getPrincipalForThisPeriod(),
|
||||
scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(),
|
||||
currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(),
|
||||
totalInstallmentDue, !isCompletePeriod);
|
||||
totalInstallmentDue, !isCompletePeriod, mc);
|
||||
if (principalInterestForThisPeriod.getRescheduleInterestPortion() != null) {
|
||||
installment.setRescheduleInterestPortion(principalInterestForThisPeriod.getRescheduleInterestPortion().getAmount());
|
||||
}
|
||||
@@ -651,7 +651,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
scheduleParams.getPeriodStartDate(), transactionDate, currentPeriodParams.getPrincipalForThisPeriod(),
|
||||
scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(),
|
||||
currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(),
|
||||
currentPeriodParams.fetchTotalAmountForPeriod(), false);
|
||||
currentPeriodParams.fetchTotalAmountForPeriod(), false, mc);
|
||||
scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(interestTillDate.interestPaymentDueToGrace());
|
||||
}
|
||||
}
|
||||
@@ -789,7 +789,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
installment = LoanScheduleModelRepaymentPeriod.repayment(scheduleParams.getInstalmentNumber(),
|
||||
scheduleParams.getPeriodStartDate(), transactionDate, principalForThisPeriod,
|
||||
scheduleParams.getOutstandingBalance(), interestForCurrentInstallment, feeChargesForInstallment,
|
||||
penaltyChargesForInstallment, totalInstallmentDue, true);
|
||||
penaltyChargesForInstallment, totalInstallmentDue, true, mc);
|
||||
periods.add(installment);
|
||||
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment);
|
||||
updateCompoundingMap(loanApplicationTerms, holidayDetailDTO, scheduleParams, lastRestDate, scheduledDueDate);
|
||||
@@ -1416,7 +1416,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
|
||||
LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(params.getInstalmentNumber(),
|
||||
startDate, transactionDate, totalInterest.zero(), totalInterest.zero(), totalInterest, totalInterest.zero(),
|
||||
totalInterest.zero(), totalInterest, true);
|
||||
totalInterest.zero(), totalInterest, true, mc);
|
||||
params.incrementInstalmentNumber();
|
||||
periods.add(installment);
|
||||
totalCumulativeInterest = totalCumulativeInterest.plus(totalInterest);
|
||||
@@ -1489,7 +1489,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
if (totalInterest.isGreaterThanZero()) {
|
||||
LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(params.getInstalmentNumber(),
|
||||
startDate, params.getActualRepaymentDate(), totalInterest.zero(), totalInterest.zero(), totalInterest,
|
||||
totalInterest.zero(), totalInterest.zero(), totalInterest, true);
|
||||
totalInterest.zero(), totalInterest.zero(), totalInterest, true, mc);
|
||||
params.incrementInstalmentNumber();
|
||||
periods.add(installment);
|
||||
params.getCompoundingDateVariations().put(startDate, new TreeMap<>(params.getCompoundingMap()));
|
||||
@@ -2189,7 +2189,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
|
||||
// for complete schedule generation
|
||||
LoanScheduleParams loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForCompleteUpdate(recalculationDetails,
|
||||
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation);
|
||||
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation, mc);
|
||||
|
||||
List<LoanScheduleModelPeriod> periods = new ArrayList<>();
|
||||
final List<LoanRepaymentScheduleInstallment> retainedInstallments = new ArrayList<>();
|
||||
@@ -2402,7 +2402,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
installment.resetDerivedComponents();
|
||||
newRepaymentScheduleInstallments.add(installment);
|
||||
outstandingBalance = outstandingBalance.minus(installment.getPrincipal(currency));
|
||||
final LoanScheduleModelPeriod loanScheduleModelPeriod = createLoanScheduleModelPeriod(installment, outstandingBalance);
|
||||
final LoanScheduleModelPeriod loanScheduleModelPeriod = createLoanScheduleModelPeriod(installment, outstandingBalance, mc);
|
||||
periods.add(loanScheduleModelPeriod);
|
||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(installment.getPrincipal(currency));
|
||||
totalCumulativeInterest = totalCumulativeInterest.plus(installment.getInterestCharged(currency));
|
||||
@@ -2493,7 +2493,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap,
|
||||
uncompoundedAmount, disburseDetailMap, principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest,
|
||||
newRepaymentScheduleInstallments, recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate,
|
||||
currency, applyInterestRecalculation);
|
||||
currency, applyInterestRecalculation, mc);
|
||||
retainedInstallments.addAll(newRepaymentScheduleInstallments);
|
||||
loanScheduleParams.getCompoundingDateVariations().putAll(compoundingDateVariations);
|
||||
loanApplicationTerms.updateTotalInterestDue(Money.of(currency, loan.getSummary().getTotalInterestCharged()));
|
||||
@@ -2729,13 +2729,13 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
||||
}
|
||||
|
||||
private LoanScheduleModelPeriod createLoanScheduleModelPeriod(final LoanRepaymentScheduleInstallment installment,
|
||||
final Money outstandingPrincipal) {
|
||||
final Money outstandingPrincipal, final MathContext mc) {
|
||||
final MonetaryCurrency currency = outstandingPrincipal.getCurrency();
|
||||
return LoanScheduleModelRepaymentPeriod.repayment(installment.getInstallmentNumber(), installment.getFromDate(),
|
||||
installment.getDueDate(), installment.getPrincipal(currency), outstandingPrincipal,
|
||||
installment.getInterestCharged(currency), installment.getFeeChargesCharged(currency),
|
||||
installment.getPenaltyChargesCharged(currency), installment.getDue(currency),
|
||||
installment.isRecalculatedInterestComponent());
|
||||
installment.getPenaltyChargesCharged(currency), installment.getDue(currency), installment.isRecalculatedInterestComponent(),
|
||||
mc);
|
||||
}
|
||||
|
||||
private LocalDate getNextRestScheduleDate(LocalDate startDate, LoanApplicationTerms loanApplicationTerms,
|
||||
|
||||
+32
-27
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
@@ -45,9 +46,9 @@ import org.springframework.stereotype.Component;
|
||||
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
|
||||
@Override
|
||||
public List<LoanScheduleModelRepaymentPeriod> generateRepaymentPeriods(final LocalDate scheduleStartDate,
|
||||
public List<LoanScheduleModelRepaymentPeriod> generateRepaymentPeriods(final MathContext mc, final LocalDate scheduleStartDate,
|
||||
final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
|
||||
final Money zeroAmount = Money.zero(loanApplicationTerms.getCurrency());
|
||||
final Money zeroAmount = Money.zero(loanApplicationTerms.getCurrency(), mc);
|
||||
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
|
||||
final ArrayList<LoanScheduleModelRepaymentPeriod> repaymentPeriods = new ArrayList<>(numberOfRepayments);
|
||||
LocalDate lastRepaymentDate = scheduleStartDate;
|
||||
@@ -65,7 +66,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
}
|
||||
nextRepaymentDate = applyLoanTermVariations(loanApplicationTerms, nextRepaymentDate);
|
||||
repaymentPeriods.add(LoanScheduleModelRepaymentPeriod.repayment(repaymentPeriodNumber, lastRepaymentDate, nextRepaymentDate,
|
||||
zeroAmount, zeroAmount, zeroAmount, zeroAmount, zeroAmount, zeroAmount, false));
|
||||
zeroAmount, zeroAmount, zeroAmount, zeroAmount, zeroAmount, zeroAmount, false, mc));
|
||||
lastRepaymentDate = nextRepaymentDate;
|
||||
isFirstRepayment = false;
|
||||
}
|
||||
@@ -75,11 +76,13 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
private LocalDate applyLoanTermVariations(final LoanApplicationTerms loanApplicationTerms, final LocalDate scheduledDueDate) {
|
||||
LocalDate modifiedScheduledDueDate = scheduledDueDate;
|
||||
// due date changes should be applied only for that dueDate
|
||||
if (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(scheduledDueDate)) {
|
||||
LoanTermVariationsData loanTermVariationsData = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
|
||||
if (DateUtils.isEqual(modifiedScheduledDueDate, loanTermVariationsData.getTermVariationApplicableFrom())) {
|
||||
modifiedScheduledDueDate = loanTermVariationsData.getDateValue();
|
||||
loanApplicationTerms.updateVariationDays(DateUtils.getDifferenceInDays(scheduledDueDate, modifiedScheduledDueDate));
|
||||
if (loanApplicationTerms.getLoanTermVariations() != null) {
|
||||
if (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(scheduledDueDate)) {
|
||||
LoanTermVariationsData loanTermVariationsData = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
|
||||
if (DateUtils.isEqual(modifiedScheduledDueDate, loanTermVariationsData.getTermVariationApplicableFrom())) {
|
||||
modifiedScheduledDueDate = loanTermVariationsData.getDateValue();
|
||||
loanApplicationTerms.updateVariationDays(DateUtils.getDifferenceInDays(scheduledDueDate, modifiedScheduledDueDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
return modifiedScheduledDueDate;
|
||||
@@ -197,28 +200,30 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
private AdjustedDateDetailsDTO recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(
|
||||
final AdjustedDateDetailsDTO adjustedDateDetailsDTO, final LoanApplicationTerms loanApplicationTerms,
|
||||
final HolidayDetailDTO holidayDetailDTO, final boolean isFirstRepayment) {
|
||||
final Recur recur = CalendarUtils.getICalRecur(holidayDetailDTO.getWorkingDays().getRecurrence());
|
||||
final boolean isSevenDaysWeek = (recur.getDayList().size() == 7); // 7 Seven days in the week
|
||||
// If Workings days are not seven day week
|
||||
if (!isSevenDaysWeek) {
|
||||
checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
|
||||
if (holidayDetailDTO != null) {
|
||||
final Recur recur = CalendarUtils.getICalRecur(holidayDetailDTO.getWorkingDays().getRecurrence());
|
||||
final boolean isSevenDaysWeek = (recur.getDayList().size() == 7); // 7 Seven days in the week
|
||||
// If Workings days are not seven day week
|
||||
if (!isSevenDaysWeek) {
|
||||
checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
|
||||
isFirstRepayment);
|
||||
}
|
||||
// Check Holidays If applied
|
||||
checkAndUpdateWorkingDayIfRepaymentDateIsHolidayDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
|
||||
isFirstRepayment);
|
||||
}
|
||||
// Check Holidays If applied
|
||||
checkAndUpdateWorkingDayIfRepaymentDateIsHolidayDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
|
||||
isFirstRepayment);
|
||||
|
||||
/**
|
||||
* Check Changed Schedule Date is holiday or is not a working day Then re-call this method to get the non
|
||||
* holiday and working day
|
||||
*/
|
||||
if ((holidayDetailDTO.isHolidayEnabled() && HolidayUtil.getApplicableHoliday(adjustedDateDetailsDTO.getChangedScheduleDate(),
|
||||
holidayDetailDTO.getHolidays()) != null)
|
||||
|| WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDateDetailsDTO.getChangedScheduleDate())) {
|
||||
recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(adjustedDateDetailsDTO,
|
||||
loanApplicationTerms, holidayDetailDTO, isFirstRepayment);
|
||||
/**
|
||||
* Check Changed Schedule Date is holiday or is not a working day Then re-call this method to get the non
|
||||
* holiday and working day
|
||||
*/
|
||||
if ((holidayDetailDTO.isHolidayEnabled() && HolidayUtil.getApplicableHoliday(adjustedDateDetailsDTO.getChangedScheduleDate(),
|
||||
holidayDetailDTO.getHolidays()) != null)
|
||||
|| WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(),
|
||||
adjustedDateDetailsDTO.getChangedScheduleDate())) {
|
||||
recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(adjustedDateDetailsDTO,
|
||||
loanApplicationTerms, holidayDetailDTO, isFirstRepayment);
|
||||
}
|
||||
}
|
||||
|
||||
return adjustedDateDetailsDTO;
|
||||
}
|
||||
|
||||
+29
-12
@@ -249,6 +249,8 @@ public final class LoanApplicationTerms {
|
||||
this.inArrearsTolerance = builder.inArrearsTolerance;
|
||||
this.disbursementDatas = builder.disbursementDatas;
|
||||
this.downPaymentAmount = builder.downPaymentAmount;
|
||||
this.submittedOnDate = builder.submittedOnDate;
|
||||
this.seedDate = builder.seedDate;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
@@ -272,6 +274,8 @@ public final class LoanApplicationTerms {
|
||||
private Money inArrearsTolerance;
|
||||
private List<DisbursementData> disbursementDatas;
|
||||
private Money downPaymentAmount;
|
||||
private LocalDate submittedOnDate;
|
||||
private LocalDate seedDate;
|
||||
|
||||
public Builder currency(ApplicationCurrency currency) {
|
||||
this.currency = currency;
|
||||
@@ -343,11 +347,6 @@ public final class LoanApplicationTerms {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder variationsDataWrapper(LoanTermVariationsDataWrapper variationsDataWrapper) {
|
||||
this.variationsDataWrapper = variationsDataWrapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fixedLength(Integer fixedLength) {
|
||||
this.fixedLength = fixedLength;
|
||||
return this;
|
||||
@@ -368,23 +367,41 @@ public final class LoanApplicationTerms {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder submittedOnDate(LocalDate submittedOnDate) {
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder seedDate(LocalDate seedDate) {
|
||||
this.seedDate = seedDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanApplicationTerms build() {
|
||||
return new LoanApplicationTerms(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(LoanRepaymentScheduleModelData modelData) {
|
||||
Money principal = Money.of(modelData.currency().toData(), modelData.disbursementAmount());
|
||||
Money downPaymentAmount = Money.zero(modelData.currency().toData());
|
||||
public static LoanApplicationTerms assembleFrom(LoanRepaymentScheduleModelData modelData, MathContext mc) {
|
||||
Money principal = Money.of(modelData.currency().toData(), modelData.disbursementAmount(), mc);
|
||||
Money downPaymentAmount = Money.zero(modelData.currency().toData(), mc);
|
||||
|
||||
if (modelData.downPaymentEnabled()) {
|
||||
downPaymentAmount = Money.of(modelData.currency().toData(),
|
||||
MathUtil.percentageOf(principal.getAmount(), modelData.disbursementAmount(), 19));
|
||||
MathUtil.percentageOf(principal.getAmount(), modelData.disbursementAmount(), mc), mc);
|
||||
if (modelData.installmentAmountInMultiplesOf() != null) {
|
||||
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount, modelData.installmentAmountInMultiplesOf());
|
||||
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount, modelData.installmentAmountInMultiplesOf(), mc);
|
||||
}
|
||||
}
|
||||
|
||||
LocalDate seedDate;
|
||||
|
||||
if (modelData.disbursementDate() != null) {
|
||||
seedDate = modelData.disbursementDate();
|
||||
} else {
|
||||
seedDate = modelData.scheduleGenerationStartDate();
|
||||
}
|
||||
|
||||
return new Builder().currency(modelData.currency()).loanTermFrequency(modelData.numberOfRepayments())
|
||||
.loanTermPeriodFrequencyType(PeriodFrequencyType.valueOf(modelData.repaymentFrequencyType()))
|
||||
.numberOfRepayments(modelData.numberOfRepayments()).repaymentEvery(modelData.repaymentFrequency())
|
||||
@@ -394,8 +411,8 @@ public final class LoanApplicationTerms {
|
||||
.annualNominalInterestRate(modelData.annualNominalInterestRate()).principal(principal)
|
||||
.expectedDisbursementDate(modelData.disbursementDate()).repaymentsStartingFromDate(modelData.scheduleGenerationStartDate())
|
||||
.daysInMonthType(modelData.daysInMonth()).daysInYearType(modelData.daysInYear()).fixedLength(modelData.fixedLength())
|
||||
.inArrearsTolerance(Money.zero(modelData.currency().toData())).disbursementDatas(new ArrayList<>())
|
||||
.downPaymentAmount(downPaymentAmount).build();
|
||||
.inArrearsTolerance(Money.zero(modelData.currency().toData(), mc)).disbursementDatas(new ArrayList<>())
|
||||
.downPaymentAmount(downPaymentAmount).submittedOnDate(modelData.scheduleGenerationStartDate()).seedDate(seedDate).build();
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
|
||||
+14
-10
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -46,18 +47,20 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
||||
private final Set<LoanInterestRecalcualtionAdditionalDetails> loanCompoundingDetails = new HashSet<>();
|
||||
private boolean isEMIFixedSpecificToInstallment = false;
|
||||
BigDecimal rescheduleInterestPortion;
|
||||
private final MathContext mc;
|
||||
|
||||
public static LoanScheduleModelRepaymentPeriod repayment(final int periodNumber, final LocalDate startDate,
|
||||
final LocalDate scheduledDueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue,
|
||||
final Money feeChargesDue, final Money penaltyChargesDue, final Money totalDue, boolean recalculatedInterestComponent) {
|
||||
final Money feeChargesDue, final Money penaltyChargesDue, final Money totalDue, boolean recalculatedInterestComponent,
|
||||
final MathContext mc) {
|
||||
|
||||
return new LoanScheduleModelRepaymentPeriod(periodNumber, startDate, scheduledDueDate, principalDue, outstandingLoanBalance,
|
||||
interestDue, feeChargesDue, penaltyChargesDue, totalDue, recalculatedInterestComponent);
|
||||
interestDue, feeChargesDue, penaltyChargesDue, totalDue, recalculatedInterestComponent, mc);
|
||||
}
|
||||
|
||||
public LoanScheduleModelRepaymentPeriod(final int periodNumber, final LocalDate fromDate, final LocalDate dueDate,
|
||||
final Money principalDue, final Money outstandingLoanBalance, final Money interestDue, final Money feeChargesDue,
|
||||
final Money penaltyChargesDue, final Money totalDue, final boolean recalculatedInterestComponent) {
|
||||
final Money penaltyChargesDue, final Money totalDue, final boolean recalculatedInterestComponent, final MathContext mc) {
|
||||
this.periodNumber = periodNumber;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
@@ -68,6 +71,7 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
||||
this.penaltyChargesDue = penaltyChargesDue;
|
||||
this.totalDue = totalDue;
|
||||
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,15 +148,15 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
||||
|
||||
@Override
|
||||
public void addLoanCharges(BigDecimal feeCharge, BigDecimal penaltyCharge) {
|
||||
this.feeChargesDue = this.feeChargesDue.plus(feeCharge);
|
||||
this.penaltyChargesDue = this.penaltyChargesDue.plus(penaltyCharge);
|
||||
this.totalDue = this.totalDue.plus(feeCharge).plus(penaltyCharge);
|
||||
this.feeChargesDue = this.feeChargesDue.plus(feeCharge, mc);
|
||||
this.penaltyChargesDue = this.penaltyChargesDue.plus(penaltyCharge, mc);
|
||||
this.totalDue = this.totalDue.plus(feeCharge, mc).plus(penaltyCharge, mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPrincipalAmount(final Money principalDue) {
|
||||
this.principalDue = this.principalDue.plus(principalDue);
|
||||
this.totalDue = this.totalDue.plus(principalDue);
|
||||
this.principalDue = this.principalDue.plus(principalDue, mc);
|
||||
this.totalDue = this.totalDue.plus(principalDue, mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,8 +166,8 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
||||
|
||||
@Override
|
||||
public void addInterestAmount(Money interestDue) {
|
||||
this.interestDue = this.interestDue.plus(interestDue);
|
||||
this.totalDue = this.totalDue.plus(interestDue);
|
||||
this.interestDue = this.interestDue.plus(interestDue, mc);
|
||||
this.totalDue = this.totalDue.plus(interestDue, mc);
|
||||
}
|
||||
|
||||
public void setPeriodNumber(int periodNumber) {
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO;
|
||||
@@ -27,8 +28,8 @@ import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
|
||||
public interface ScheduledDateGenerator {
|
||||
|
||||
List<LoanScheduleModelRepaymentPeriod> generateRepaymentPeriods(LocalDate scheduledDueDate, LoanApplicationTerms loanApplicationTerms,
|
||||
HolidayDetailDTO holidayDetailDTO);
|
||||
List<LoanScheduleModelRepaymentPeriod> generateRepaymentPeriods(MathContext mc, LocalDate scheduledDueDate,
|
||||
LoanApplicationTerms loanApplicationTerms, HolidayDetailDTO holidayDetailDTO);
|
||||
|
||||
LocalDate getLastRepaymentDate(LoanApplicationTerms loanApplicationTerms, HolidayDetailDTO holidayDetailDTO);
|
||||
|
||||
|
||||
+5
-2
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
@@ -64,6 +65,7 @@ public class SingleLoanChargeRepaymentScheduleProcessingWrapperTest {
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
|
||||
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,11 +144,12 @@ public class SingleLoanChargeRepaymentScheduleProcessingWrapperTest {
|
||||
@NotNull
|
||||
private LoanRepaymentScheduleInstallment createPeriod(int periodId, LocalDate start, LocalDate end) {
|
||||
LoanRepaymentScheduleInstallment period = Mockito.mock(LoanRepaymentScheduleInstallment.class);
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN);
|
||||
Mockito.when(period.getInstallmentNumber()).thenReturn(periodId);
|
||||
Mockito.when(period.getFromDate()).thenReturn(start);
|
||||
Mockito.when(period.getDueDate()).thenReturn(end);
|
||||
Money principal = Money.of(currency, new BigDecimal("1000.0"));
|
||||
Money interest = Money.of(currency, BigDecimal.ZERO);
|
||||
Money principal = Money.of(currency, new BigDecimal("1000.0"), mc);
|
||||
Money interest = Money.of(currency, BigDecimal.ZERO, mc);
|
||||
|
||||
Mockito.when(period.getPrincipal(eq(currency))).thenReturn(principal);
|
||||
Mockito.when(period.getInterestCharged(eq(currency))).thenReturn(interest);
|
||||
|
||||
+1
-1
@@ -176,7 +176,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
final Integer installmentAmountInMultiplesOf = loan.getLoanProduct().getInstallmentAmountInMultiplesOf();
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
ProgressiveLoanInterestScheduleModel scheduleModel = emiCalculator.generateModel(loanProductRelatedDetail,
|
||||
installmentAmountInMultiplesOf, installments);
|
||||
installmentAmountInMultiplesOf, installments, overpaymentHolder.getMoneyObject().getMc());
|
||||
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(currency, installments, charges, overpaymentHolder,
|
||||
changedTransactionDetail, scheduleModel);
|
||||
|
||||
|
||||
+14
-11
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Optional;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -44,9 +45,10 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
private Money disbursementAmount;
|
||||
private Money balanceCorrectionAmount;
|
||||
private Money outstandingLoanBalance;
|
||||
private final MathContext mc;
|
||||
|
||||
public InterestPeriod(RepaymentPeriod repaymentPeriod, LocalDate fromDate, LocalDate dueDate, BigDecimal rateFactor,
|
||||
Money disbursementAmount, Money balanceCorrectionAmount, Money outstandingLoanBalance) {
|
||||
Money disbursementAmount, Money balanceCorrectionAmount, Money outstandingLoanBalance, MathContext mc) {
|
||||
this.repaymentPeriod = repaymentPeriod;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
@@ -54,12 +56,13 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
this.disbursementAmount = disbursementAmount;
|
||||
this.balanceCorrectionAmount = balanceCorrectionAmount;
|
||||
this.outstandingLoanBalance = outstandingLoanBalance;
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod interestPeriod) {
|
||||
public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod interestPeriod, MathContext mc) {
|
||||
this(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(), interestPeriod.getRateFactor(),
|
||||
interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
||||
interestPeriod.getOutstandingLoanBalance());
|
||||
interestPeriod.getOutstandingLoanBalance(), mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,11 +75,11 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
}
|
||||
|
||||
public void addDisbursementAmount(final Money disbursementAmount) {
|
||||
this.disbursementAmount = MathUtil.plus(this.disbursementAmount, disbursementAmount);
|
||||
this.disbursementAmount = MathUtil.plus(this.disbursementAmount, disbursementAmount, mc);
|
||||
}
|
||||
|
||||
public Money getCalculatedDueInterest() {
|
||||
return getOutstandingLoanBalance().multipliedBy(getRateFactor());
|
||||
return getOutstandingLoanBalance().multipliedBy(getRateFactor(), mc);
|
||||
}
|
||||
|
||||
public void updateOutstandingLoanBalance() {
|
||||
@@ -86,17 +89,17 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
InterestPeriod previousInterestPeriod = previousRepaymentPeriod.get().getInterestPeriods()
|
||||
.get(previousRepaymentPeriod.get().getInterestPeriods().size() - 1);
|
||||
this.outstandingLoanBalance = previousInterestPeriod.getOutstandingLoanBalance()//
|
||||
.plus(previousInterestPeriod.getDisbursementAmount())//
|
||||
.plus(previousInterestPeriod.getBalanceCorrectionAmount())//
|
||||
.minus(previousRepaymentPeriod.get().getDuePrincipal())//
|
||||
.plus(previousRepaymentPeriod.get().getPaidPrincipal());//
|
||||
.plus(previousInterestPeriod.getDisbursementAmount(), mc)//
|
||||
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc)//
|
||||
.minus(previousRepaymentPeriod.get().getDuePrincipal(), mc)//
|
||||
.plus(previousRepaymentPeriod.get().getPaidPrincipal(), mc);//
|
||||
}
|
||||
} else {
|
||||
int index = getRepaymentPeriod().getInterestPeriods().indexOf(this);
|
||||
InterestPeriod previousInterestPeriod = getRepaymentPeriod().getInterestPeriods().get(index - 1);
|
||||
this.outstandingLoanBalance = previousInterestPeriod.getOutstandingLoanBalance() //
|
||||
.plus(previousInterestPeriod.getBalanceCorrectionAmount()) //
|
||||
.plus(previousInterestPeriod.getDisbursementAmount()); //
|
||||
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc) //
|
||||
.plus(previousInterestPeriod.getDisbursementAmount(), mc); //
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-8
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
@@ -38,33 +39,36 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
private final List<InterestRate> interestRates;
|
||||
private final LoanProductRelatedDetail loanProductRelatedDetail;
|
||||
private final Integer installmentAmountInMultiplesOf;
|
||||
private MathContext mc;
|
||||
|
||||
public ProgressiveLoanInterestScheduleModel(List<RepaymentPeriod> repaymentPeriods, LoanProductRelatedDetail loanProductRelatedDetail,
|
||||
Integer installmentAmountInMultiplesOf) {
|
||||
Integer installmentAmountInMultiplesOf, MathContext mc) {
|
||||
this.repaymentPeriods = repaymentPeriods;
|
||||
this.interestRates = new ArrayList<>();
|
||||
this.loanProductRelatedDetail = loanProductRelatedDetail;
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
private ProgressiveLoanInterestScheduleModel(List<RepaymentPeriod> repaymentPeriods, final List<InterestRate> interestRates,
|
||||
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf) {
|
||||
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc) {
|
||||
this.mc = mc;
|
||||
this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods);
|
||||
this.interestRates = new ArrayList<>(interestRates);
|
||||
this.loanProductRelatedDetail = loanProductRelatedDetail;
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
}
|
||||
|
||||
public ProgressiveLoanInterestScheduleModel deepCopy() {
|
||||
public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) {
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentPeriods, interestRates, loanProductRelatedDetail,
|
||||
installmentAmountInMultiplesOf);
|
||||
installmentAmountInMultiplesOf, mc);
|
||||
}
|
||||
|
||||
private List<RepaymentPeriod> copyRepaymentPeriods(final List<RepaymentPeriod> repaymentPeriods) {
|
||||
final List<RepaymentPeriod> repaymentCopies = new ArrayList<>(repaymentPeriods.size());
|
||||
RepaymentPeriod previousPeriod = null;
|
||||
for (RepaymentPeriod repaymentPeriod : repaymentPeriods) {
|
||||
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, repaymentPeriod);
|
||||
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, repaymentPeriod, mc);
|
||||
previousPeriod = currentPeriod;
|
||||
repaymentCopies.add(currentPeriod);
|
||||
}
|
||||
@@ -175,11 +179,11 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
previousInterestPeriod.addDisbursementAmount(disbursedAmount);
|
||||
previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount);
|
||||
final InterestPeriod interestPeriod = new InterestPeriod(repaymentPeriod, previousInterestPeriod.getDueDate(), originalDueDate,
|
||||
BigDecimal.ZERO, getZero(), getZero(), getZero());
|
||||
BigDecimal.ZERO, getZero(mc), getZero(mc), getZero(mc), mc);
|
||||
repaymentPeriod.getInterestPeriods().add(interestPeriod);
|
||||
}
|
||||
|
||||
private Money getZero() {
|
||||
return Money.zero(loanProductRelatedDetail.getCurrency());
|
||||
private Money getZero(MathContext mc) {
|
||||
return Money.zero(loanProductRelatedDetail.getCurrency(), mc);
|
||||
}
|
||||
}
|
||||
|
||||
+30
-25
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -54,20 +55,23 @@ public class RepaymentPeriod {
|
||||
private Memo<Money> calculatedDueInterestCalculation;
|
||||
private Memo<Money> dueInterestCalculation;
|
||||
private Memo<Money> outstandingBalanceCalculation;
|
||||
private final MathContext mc;
|
||||
|
||||
public RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, Money emi) {
|
||||
public RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, Money emi, MathContext mc) {
|
||||
this.previous = previous;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
this.emi = emi;
|
||||
this.mc = mc;
|
||||
this.interestPeriods = new ArrayList<>();
|
||||
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
||||
getInterestPeriods().add(new InterestPeriod(this, getFromDate(), getDueDate(), BigDecimal.ZERO, getZero(), getZero(), getZero()));
|
||||
this.paidInterest = getZero();
|
||||
this.paidPrincipal = getZero();
|
||||
getInterestPeriods()
|
||||
.add(new InterestPeriod(this, getFromDate(), getDueDate(), BigDecimal.ZERO, getZero(mc), getZero(mc), getZero(mc), mc));
|
||||
this.paidInterest = getZero(mc);
|
||||
this.paidPrincipal = getZero(mc);
|
||||
}
|
||||
|
||||
public RepaymentPeriod(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod) {
|
||||
public RepaymentPeriod(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod, MathContext mc) {
|
||||
this.previous = previous;
|
||||
this.fromDate = repaymentPeriod.fromDate;
|
||||
this.dueDate = repaymentPeriod.dueDate;
|
||||
@@ -75,9 +79,10 @@ public class RepaymentPeriod {
|
||||
this.interestPeriods = new ArrayList<>();
|
||||
this.paidPrincipal = repaymentPeriod.paidPrincipal;
|
||||
this.paidInterest = repaymentPeriod.paidInterest;
|
||||
this.mc = mc;
|
||||
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
||||
for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
|
||||
interestPeriods.add(new InterestPeriod(this, interestPeriod));
|
||||
interestPeriods.add(new InterestPeriod(this, interestPeriod, mc));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,21 +110,21 @@ public class RepaymentPeriod {
|
||||
}
|
||||
|
||||
private Money calculateCalculatedDueInterest() {
|
||||
Money calculatedDueInterest = getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest).reduce(getZero(),
|
||||
Money::plus);
|
||||
Money calculatedDueInterest = getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest).reduce(getZero(mc),
|
||||
(m1, m2) -> m1.plus(m2, mc));
|
||||
if (getPrevious().isPresent()) {
|
||||
calculatedDueInterest = calculatedDueInterest.add(getPrevious().get().getUnrecognizedInterest());
|
||||
calculatedDueInterest = calculatedDueInterest.add(getPrevious().get().getUnrecognizedInterest(), mc);
|
||||
}
|
||||
return calculatedDueInterest;
|
||||
}
|
||||
|
||||
private Money getZero() {
|
||||
private Money getZero(MathContext mc) {
|
||||
// EMI is always initiated
|
||||
return this.emi.zero();
|
||||
return this.emi.zero(mc);
|
||||
}
|
||||
|
||||
public Money getCalculatedDuePrincipal() {
|
||||
return getEmi().minus(getCalculatedDueInterest());
|
||||
return getEmi().minus(getCalculatedDueInterest(), mc);
|
||||
}
|
||||
|
||||
public boolean isFullyPaid() {
|
||||
@@ -138,11 +143,11 @@ public class RepaymentPeriod {
|
||||
|
||||
public Money getDuePrincipal() {
|
||||
// Due principal might be the maximum paid if there is pay-off or early repayment
|
||||
return MathUtil.max(getEmi().minus(getDueInterest()), getPaidPrincipal(), false);
|
||||
return MathUtil.max(getEmi().minus(getDueInterest(), mc), getPaidPrincipal(), false);
|
||||
}
|
||||
|
||||
public Money getUnrecognizedInterest() {
|
||||
return getCalculatedDueInterest().minus(getDueInterest());
|
||||
return getCalculatedDueInterest().minus(getDueInterest(), mc);
|
||||
}
|
||||
|
||||
public Money getOutstandingLoanBalance() {
|
||||
@@ -150,22 +155,22 @@ public class RepaymentPeriod {
|
||||
outstandingBalanceCalculation = Memo.of(() -> {
|
||||
InterestPeriod lastInstallmentPeriod = getInterestPeriods().get(getInterestPeriods().size() - 1);
|
||||
Money calculatedOutStandingLoanBalance = lastInstallmentPeriod.getOutstandingLoanBalance() //
|
||||
.plus(lastInstallmentPeriod.getBalanceCorrectionAmount()) //
|
||||
.plus(lastInstallmentPeriod.getDisbursementAmount()) //
|
||||
.minus(getDuePrincipal())//
|
||||
.plus(getPaidPrincipal());//
|
||||
return MathUtil.negativeToZero(calculatedOutStandingLoanBalance);
|
||||
.plus(lastInstallmentPeriod.getBalanceCorrectionAmount(), mc) //
|
||||
.plus(lastInstallmentPeriod.getDisbursementAmount(), mc) //
|
||||
.minus(getDuePrincipal(), mc)//
|
||||
.plus(getPaidPrincipal(), mc);//
|
||||
return MathUtil.negativeToZero(calculatedOutStandingLoanBalance, mc);
|
||||
}, () -> new Object[] { paidPrincipal, paidInterest, interestPeriods });
|
||||
}
|
||||
return outstandingBalanceCalculation.get();
|
||||
}
|
||||
|
||||
public void addPaidPrincipalAmount(Money paidPrincipal) {
|
||||
this.paidPrincipal = MathUtil.plus(this.paidPrincipal, paidPrincipal);
|
||||
this.paidPrincipal = MathUtil.plus(this.paidPrincipal, paidPrincipal, mc);
|
||||
}
|
||||
|
||||
public void addPaidInterestAmount(Money paidInterest) {
|
||||
this.paidInterest = MathUtil.plus(this.paidInterest, paidInterest);
|
||||
this.paidInterest = MathUtil.plus(this.paidInterest, paidInterest, mc);
|
||||
}
|
||||
|
||||
public Money getInitialBalanceForEmiRecalculation() {
|
||||
@@ -173,10 +178,10 @@ public class RepaymentPeriod {
|
||||
if (getPrevious().isPresent()) {
|
||||
initialBalance = getPrevious().get().getOutstandingLoanBalance();
|
||||
} else {
|
||||
initialBalance = getZero();
|
||||
initialBalance = getZero(mc);
|
||||
}
|
||||
Money totalDisbursedAmount = getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount).reduce(getZero(),
|
||||
Money::plus);
|
||||
return initialBalance.add(totalDisbursedAmount);
|
||||
Money totalDisbursedAmount = getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount).reduce(getZero(mc),
|
||||
(m1, m2) -> m1.plus(m2, mc));
|
||||
return initialBalance.add(totalDisbursedAmount, mc);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -87,17 +87,17 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
: loanApplicationTerms.getSubmittedOnDate();
|
||||
|
||||
final LoanScheduleParams scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency,
|
||||
Money.of(currency, chargesDueAtTimeOfDisbursement), periodStartDate, Money.zero(currency));
|
||||
Money.of(currency, chargesDueAtTimeOfDisbursement, mc), periodStartDate, Money.zero(currency, mc), mc);
|
||||
|
||||
// charges which depends on total loan interest will be added to this
|
||||
// set and handled separately after all installments generated
|
||||
final Set<LoanCharge> nonCompoundingCharges = separateTotalCompoundingPercentageCharges(loanCharges);
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator
|
||||
.generateRepaymentPeriods(periodStartDate, loanApplicationTerms, holidayDetailDTO);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator.generateRepaymentPeriods(mc,
|
||||
periodStartDate, loanApplicationTerms, holidayDetailDTO);
|
||||
final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator.generateInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanApplicationTerms.toLoanProductRelatedDetail(),
|
||||
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
|
||||
loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc);
|
||||
final List<LoanScheduleModelPeriod> periods = new ArrayList<>(expectedRepaymentPeriods.size());
|
||||
|
||||
prepareDisbursementsOnLoanApplicationTerms(loanApplicationTerms);
|
||||
@@ -110,7 +110,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
scheduleParams.setActualRepaymentDate(repaymentPeriod.getDueDate());
|
||||
|
||||
processDisbursements(loanApplicationTerms, disbursementDataList, scheduleParams, interestScheduleModel, periods,
|
||||
chargesDueAtTimeOfDisbursement, false);
|
||||
chargesDueAtTimeOfDisbursement, false, mc);
|
||||
repaymentPeriod.setPeriodNumber(scheduleParams.getInstalmentNumber());
|
||||
|
||||
if (loanApplicationTerms.getLoanTermVariations() != null) {
|
||||
@@ -135,7 +135,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
scheduleParams.addTotalCumulativePrincipal(principalDue);
|
||||
scheduleParams.addTotalCumulativeInterest(interestDue);
|
||||
// add everything
|
||||
scheduleParams.addTotalRepaymentExpected(principalDue.plus(interestDue));
|
||||
scheduleParams.addTotalRepaymentExpected(principalDue.plus(interestDue, mc));
|
||||
});
|
||||
|
||||
applyChargesForCurrentPeriod(repaymentPeriod, loanCharges, scheduleParams, currency, mc);
|
||||
@@ -147,7 +147,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
|
||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||
processDisbursements(loanApplicationTerms, disbursementDataList, scheduleParams, interestScheduleModel, periods,
|
||||
chargesDueAtTimeOfDisbursement, true);
|
||||
chargesDueAtTimeOfDisbursement, true, mc);
|
||||
}
|
||||
|
||||
// determine fees and penalties for charges which depends on total
|
||||
@@ -158,8 +158,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
final BigDecimal totalOutstanding = BigDecimal.ZERO;
|
||||
|
||||
return LoanScheduleModel.from(periods, applicationCurrency, interestScheduleModel.getLoanTermInDays(),
|
||||
scheduleParams.getPrincipalToBeScheduled().plus(loanApplicationTerms.getDownPaymentAmount()),
|
||||
scheduleParams.getTotalCumulativePrincipal().plus(loanApplicationTerms.getDownPaymentAmount()).getAmount(),
|
||||
scheduleParams.getPrincipalToBeScheduled().plus(loanApplicationTerms.getDownPaymentAmount(), mc),
|
||||
scheduleParams.getTotalCumulativePrincipal().plus(loanApplicationTerms.getDownPaymentAmount(), mc).getAmount(),
|
||||
totalPrincipalPaid, scheduleParams.getTotalCumulativeInterest().getAmount(),
|
||||
scheduleParams.getTotalFeeChargesCharged().getAmount(), scheduleParams.getTotalPenaltyChargesCharged().getAmount(),
|
||||
scheduleParams.getTotalRepaymentExpected().getAmount(), totalOutstanding);
|
||||
@@ -167,7 +167,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
|
||||
public LoanScheduleModel generate(final MathContext mc, final LoanRepaymentScheduleModelData modelData) {
|
||||
|
||||
LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(modelData);
|
||||
LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(modelData, mc);
|
||||
|
||||
return generate(mc, loanApplicationTerms, null, null);
|
||||
}
|
||||
@@ -184,7 +184,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
private void processDisbursements(final LoanApplicationTerms loanApplicationTerms,
|
||||
final ArrayList<DisbursementData> disbursementDataList, final LoanScheduleParams scheduleParams,
|
||||
final ProgressiveLoanInterestScheduleModel interestScheduleModel, final List<LoanScheduleModelPeriod> periods,
|
||||
final BigDecimal chargesDueAtTimeOfDisbursement, final boolean includeDisbursementsAfterMaturityDate) {
|
||||
final BigDecimal chargesDueAtTimeOfDisbursement, final boolean includeDisbursementsAfterMaturityDate, final MathContext mc) {
|
||||
|
||||
for (DisbursementData disbursementData : disbursementDataList) {
|
||||
final LocalDate disbursementDate = disbursementData.disbursementDate();
|
||||
@@ -202,7 +202,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
|
||||
Money outstandingBalance = emiCalculator.getOutstandingLoanBalance(interestScheduleModel, periodDueDate, disbursementDate);
|
||||
|
||||
final Money disbursedAmount = Money.of(loanApplicationTerms.getCurrency(), disbursementData.getPrincipal());
|
||||
final Money disbursedAmount = Money.of(loanApplicationTerms.getCurrency(), disbursementData.getPrincipal(), mc);
|
||||
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod
|
||||
.disbursement(disbursementData.disbursementDate(), disbursedAmount, chargesDueAtTimeOfDisbursement);
|
||||
periods.add(disbursementPeriod);
|
||||
@@ -218,7 +218,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
Money downPaymentAmount = Money.zero(loanApplicationTerms.getCurrency());
|
||||
Money downPaymentAmount = Money.zero(loanApplicationTerms.getCurrency(), mc);
|
||||
if (loanApplicationTerms.isDownPaymentEnabled()) {
|
||||
downPaymentAmount = Money.of(loanApplicationTerms.getCurrency(), MathUtil.percentageOf(disbursedAmount.getAmount(),
|
||||
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19));
|
||||
@@ -321,7 +321,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
|
||||
final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, final boolean isFirstPeriod,
|
||||
final MathContext mc) {
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
Money cumulative = Money.zero(monetaryCurrency, mc);
|
||||
if (loanCharges != null) {
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (!loanCharge.isDueAtDisbursement() && loanCharge.isFeeCharge()) {
|
||||
@@ -356,7 +356,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
|
||||
final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, final boolean isFirstPeriod,
|
||||
final MathContext mc) {
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
Money cumulative = Money.zero(monetaryCurrency, mc);
|
||||
if (loanCharges != null) {
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isPenaltyCharge()) {
|
||||
@@ -407,8 +407,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
final Collection<LoanScheduleModelPeriod> periods, final Set<LoanCharge> nonCompoundingCharges, MathContext mc) {
|
||||
for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) {
|
||||
if (loanScheduleModelPeriod.isRepaymentPeriod()) {
|
||||
PrincipalInterest principalInterest = new PrincipalInterest(Money.of(currency, loanScheduleModelPeriod.principalDue()),
|
||||
Money.of(currency, loanScheduleModelPeriod.interestDue()), null);
|
||||
PrincipalInterest principalInterest = new PrincipalInterest(Money.of(currency, loanScheduleModelPeriod.principalDue(), mc),
|
||||
Money.of(currency, loanScheduleModelPeriod.interestDue(), mc), null);
|
||||
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||
loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest,
|
||||
scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativeInterest(),
|
||||
|
||||
+3
-2
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanproduct.calc;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -33,10 +34,10 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
|
||||
public interface EMICalculator {
|
||||
|
||||
ProgressiveLoanInterestScheduleModel generateInterestScheduleModel(List<LoanScheduleModelRepaymentPeriod> periods,
|
||||
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf);
|
||||
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
|
||||
ProgressiveLoanInterestScheduleModel generateModel(LoanProductRelatedDetail loanProductRelatedDetail,
|
||||
Integer installmentAmountInMultiplesOf, List<LoanRepaymentScheduleInstallment> repaymentPeriods);
|
||||
Integer installmentAmountInMultiplesOf, List<LoanRepaymentScheduleInstallment> repaymentPeriods, MathContext mc);
|
||||
|
||||
Optional<RepaymentPeriod> findRepaymentPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate dueDate);
|
||||
|
||||
|
||||
+82
-74
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanproduct.calc;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Year;
|
||||
import java.util.ArrayList;
|
||||
@@ -29,7 +30,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
@@ -40,12 +40,10 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLo
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.RepaymentPeriod;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@DependsOn("moneyHelper")
|
||||
public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
|
||||
private static final BigDecimal DIVISOR_100 = new BigDecimal("100");
|
||||
@@ -53,19 +51,19 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
|
||||
@Override
|
||||
public ProgressiveLoanInterestScheduleModel generateInterestScheduleModel(final List<LoanScheduleModelRepaymentPeriod> periods,
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf) {
|
||||
final Money zeroAmount = Money.zero(loanProductRelatedDetail.getCurrency());
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
final Money zeroAmount = Money.zero(loanProductRelatedDetail.getCurrency(), mc);
|
||||
final ArrayList<RepaymentPeriod> interestRepaymentModelList = new ArrayList<>(periods.size());
|
||||
RepaymentPeriod previousPeriod = null;
|
||||
for (final LoanScheduleModelRepaymentPeriod period : periods) {
|
||||
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, period.periodFromDate(), period.periodDueDate(),
|
||||
zeroAmount);
|
||||
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, period.periodFromDate(), period.periodDueDate(), zeroAmount,
|
||||
mc);
|
||||
previousPeriod = currentPeriod;
|
||||
interestRepaymentModelList.add(currentPeriod);
|
||||
|
||||
}
|
||||
return new ProgressiveLoanInterestScheduleModel(interestRepaymentModelList, loanProductRelatedDetail,
|
||||
installmentAmountInMultiplesOf);
|
||||
installmentAmountInMultiplesOf, mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,7 +83,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
final Money disbursedAmount) {
|
||||
scheduleModel
|
||||
.changeOutstandingBalanceAndUpdateInterestPeriods(disbursementDueDate, disbursedAmount,
|
||||
Money.zero(disbursedAmount.getCurrency()))
|
||||
Money.zero(disbursedAmount.getCurrency(), scheduleModel.mc()))
|
||||
.ifPresent((repaymentPeriod) -> calculateEMIValueAndRateFactors(repaymentPeriod.getDueDate(), scheduleModel));
|
||||
}
|
||||
|
||||
@@ -130,7 +128,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
@Override
|
||||
public PayableDetails getPayableDetails(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final LocalDate repaymentPeriodDueDate, final LocalDate targetDate) {
|
||||
RepaymentPeriod repaymentPeriod = scheduleModel.deepCopy().repaymentPeriods().stream()
|
||||
MathContext mc = scheduleModel.mc();
|
||||
RepaymentPeriod repaymentPeriod = scheduleModel.deepCopy(mc).repaymentPeriods().stream()
|
||||
.filter(rp -> rp.getDueDate().equals(repaymentPeriodDueDate)).findFirst().orElseThrow();
|
||||
LocalDate adjustedTargetDate = targetDate;
|
||||
InterestPeriod interestPeriod;
|
||||
@@ -151,19 +150,19 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
|
||||
// TODO: gather all the unrecognized interest from previous periods based on target date
|
||||
Money payableInterest = targetDate.isBefore(repaymentPeriod.getFromDate())
|
||||
? Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency())
|
||||
? Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc)
|
||||
: repaymentPeriod.getDueInterest();
|
||||
Money outstandingLoanBalance = interestPeriod.getOutstandingLoanBalance().add(interestPeriod.getDisbursementAmount());
|
||||
|
||||
Money calculatedEmi = outstandingLoanBalance.plus(payableInterest);
|
||||
Money calculatedEmi = outstandingLoanBalance.plus(payableInterest, mc);
|
||||
if (calculatedEmi.isLessThan(repaymentPeriod.getEmi())) {
|
||||
// Review this logic
|
||||
repaymentPeriod.setEmi(outstandingLoanBalance.plus(payableInterest).plus(repaymentPeriod.getPaidInterest())
|
||||
.plus(repaymentPeriod.getPaidPrincipal()));
|
||||
repaymentPeriod.setEmi(outstandingLoanBalance.plus(payableInterest).plus(repaymentPeriod.getPaidInterest(), mc)
|
||||
.plus(repaymentPeriod.getPaidPrincipal(), mc));
|
||||
}
|
||||
Money payablePrincipal = repaymentPeriod.getEmi().minus(payableInterest);
|
||||
Money payablePrincipal = repaymentPeriod.getEmi().minus(payableInterest, mc);
|
||||
return new PayableDetails(repaymentPeriod.getEmi(), payablePrincipal, payableInterest,
|
||||
interestPeriod.getOutstandingLoanBalance().add(interestPeriod.getDisbursementAmount()));
|
||||
interestPeriod.getOutstandingLoanBalance().add(interestPeriod.getDisbursementAmount(), mc));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -189,18 +188,19 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
}
|
||||
|
||||
private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
MathContext mc = scheduleModel.mc();
|
||||
Money totalDueInterest = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest)
|
||||
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency()), Money::plus); // 1.46
|
||||
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 1.46
|
||||
Money totalEMI = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getEmi)
|
||||
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency()), Money::plus); // 101.48
|
||||
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 101.48
|
||||
Money totalDisbursedAmount = scheduleModel.repaymentPeriods().stream()
|
||||
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount))
|
||||
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency()), Money::plus); // 100
|
||||
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 100
|
||||
|
||||
Money diff = totalDisbursedAmount.plus(totalDueInterest).minus(totalEMI);
|
||||
Money diff = totalDisbursedAmount.plus(totalDueInterest, mc).minus(totalEMI, mc);
|
||||
Optional<RepaymentPeriod> findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid())
|
||||
.reduce((first, second) -> second);
|
||||
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff)));
|
||||
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff, mc)));
|
||||
}
|
||||
|
||||
private void calculateOutstandingBalance(ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
@@ -209,24 +209,26 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
|
||||
private void checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final List<RepaymentPeriod> relatedRepaymentPeriods) {
|
||||
MathContext mc = scheduleModel.mc();
|
||||
final Money emiDifference = getDifferenceBetweenLastTwoPeriod(relatedRepaymentPeriods, scheduleModel);
|
||||
final int numberOfRelatedPeriods = relatedRepaymentPeriods.size();
|
||||
double lowerHalfOfRelatedPeriods = Math.floor(numberOfRelatedPeriods / 2.0);
|
||||
if (emiDifference.isZero() || lowerHalfOfRelatedPeriods == 0.0) {
|
||||
if (emiDifference.isZero(mc) || lowerHalfOfRelatedPeriods == 0.0) {
|
||||
return;
|
||||
}
|
||||
final Money originalEmi = relatedRepaymentPeriods.get(numberOfRelatedPeriods - 2).getEmi();
|
||||
boolean shouldBeAdjusted = emiDifference.abs().multipliedBy(100)
|
||||
.isGreaterThan(Money.of(originalEmi.getCurrency(), BigDecimal.valueOf(lowerHalfOfRelatedPeriods)));
|
||||
boolean shouldBeAdjusted = emiDifference.abs(mc).multipliedBy(100, mc)
|
||||
.isGreaterThan(Money.of(originalEmi.getCurrency(), BigDecimal.valueOf(lowerHalfOfRelatedPeriods), mc));
|
||||
|
||||
if (shouldBeAdjusted) {
|
||||
Money adjustment = emiDifference.dividedBy(numberOfRelatedPeriods, MoneyHelper.getMathContext().getRoundingMode());
|
||||
Money adjustedEqualMonthlyInstallmentValue = applyInstallmentAmountInMultiplesOf(scheduleModel, originalEmi.plus(adjustment));
|
||||
Money adjustment = emiDifference.dividedBy(numberOfRelatedPeriods, mc.getRoundingMode(), mc);
|
||||
Money adjustedEqualMonthlyInstallmentValue = applyInstallmentAmountInMultiplesOf(scheduleModel,
|
||||
originalEmi.plus(adjustment, mc));
|
||||
if (adjustedEqualMonthlyInstallmentValue.isEqualTo(originalEmi)) {
|
||||
return;
|
||||
}
|
||||
final LocalDate relatedPeriodsFirstDueDate = relatedRepaymentPeriods.get(0).getDueDate();
|
||||
final ProgressiveLoanInterestScheduleModel newScheduleModel = scheduleModel.deepCopy();
|
||||
final ProgressiveLoanInterestScheduleModel newScheduleModel = scheduleModel.deepCopy(mc);
|
||||
newScheduleModel.repaymentPeriods().forEach(period -> {
|
||||
if (!period.getDueDate().isBefore(relatedPeriodsFirstDueDate)) {
|
||||
period.setEmi(adjustedEqualMonthlyInstallmentValue);
|
||||
@@ -235,7 +237,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
calculateOutstandingBalance(newScheduleModel);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(newScheduleModel);
|
||||
final Money newEmiDifference = getDifferenceBetweenLastTwoPeriod(newScheduleModel.repaymentPeriods(), scheduleModel);
|
||||
final boolean newEmiHasLessDifference = newEmiDifference.abs().isLessThan(emiDifference.abs());
|
||||
final boolean newEmiHasLessDifference = newEmiDifference.abs(mc).isLessThan(emiDifference.abs(mc));
|
||||
if (!newEmiHasLessDifference) {
|
||||
return;
|
||||
}
|
||||
@@ -263,8 +265,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
*
|
||||
* @return Rate Interest Rate in fraction format
|
||||
*/
|
||||
BigDecimal calcNominalInterestRatePercentage(final BigDecimal interestRate) {
|
||||
return MathUtil.nullToZero(interestRate).divide(DIVISOR_100, MoneyHelper.getMathContext());
|
||||
BigDecimal calcNominalInterestRatePercentage(final BigDecimal interestRate, MathContext mc) {
|
||||
return MathUtil.nullToZero(interestRate).divide(DIVISOR_100, mc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,8 +288,10 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
*/
|
||||
BigDecimal calculateRateFactorPerPeriod(final RepaymentPeriod repaymentPeriod, final InterestPeriod interestPeriod,
|
||||
final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
final MathContext mc = scheduleModel.mc();
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
|
||||
final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriod.getFromDate()));
|
||||
final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriod.getFromDate()),
|
||||
scheduleModel.mc());
|
||||
final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType());
|
||||
final DaysInMonthType daysInMonthType = DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType());
|
||||
final PeriodFrequencyType repaymentFrequency = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
|
||||
@@ -306,13 +310,13 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
// TODO review: (repayment frequency: days, weeks, years; validation day is month fix 30)
|
||||
// TODO refactor this logic to represent in interest period
|
||||
if (partialPeriodCalculationNeeded) {
|
||||
final BigDecimal cumulatedPeriodFractions = calculatePeriodFractions(interestPeriod);
|
||||
final BigDecimal cumulatedPeriodFractions = calculatePeriodFractions(interestPeriod, mc);
|
||||
return rateFactorByRepaymentPartialPeriod(interestRate, repaymentEvery, cumulatedPeriodFractions, BigDecimal.ONE,
|
||||
BigDecimal.ONE);
|
||||
BigDecimal.ONE, mc);
|
||||
}
|
||||
|
||||
return calculateRateFactorPerPeriodBasedOnRepaymentFrequency(interestRate, repaymentFrequency, repaymentEvery, daysInMonth,
|
||||
daysInYear, actualDaysInPeriod, calculatedDaysInPeriod);
|
||||
daysInYear, actualDaysInPeriod, calculatedDaysInPeriod, mc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,7 +325,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
* @param interestPeriod
|
||||
* @return
|
||||
*/
|
||||
BigDecimal calculatePeriodFractions(InterestPeriod interestPeriod) {
|
||||
BigDecimal calculatePeriodFractions(InterestPeriod interestPeriod, MathContext mc) {
|
||||
BigDecimal cumulatedRateFactor = BigDecimal.ZERO;
|
||||
int actualYear = interestPeriod.getFromDate().getYear();
|
||||
int endYear = interestPeriod.getDueDate().getYear();
|
||||
@@ -332,8 +336,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
endOfActualYear = actualYear == endYear ? interestPeriod.getDueDate() : LocalDate.of(actualYear, 12, 31);
|
||||
BigDecimal numberOfDaysInYear = BigDecimal.valueOf(Year.of(actualYear).length());
|
||||
BigDecimal calculatedDaysInActualYear = BigDecimal.valueOf(DateUtils.getDifferenceInDays(actualDate, endOfActualYear));
|
||||
cumulatedRateFactor = cumulatedRateFactor
|
||||
.add(calculatedDaysInActualYear.divide(numberOfDaysInYear, MoneyHelper.getMathContext()), MoneyHelper.getMathContext());
|
||||
cumulatedRateFactor = cumulatedRateFactor.add(calculatedDaysInActualYear.divide(numberOfDaysInYear, mc), mc);
|
||||
actualDate = endOfActualYear;
|
||||
actualYear++;
|
||||
}
|
||||
@@ -354,14 +357,15 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
*/
|
||||
BigDecimal calculateRateFactorPerPeriodBasedOnRepaymentFrequency(final BigDecimal interestRate,
|
||||
final PeriodFrequencyType repaymentFrequency, final BigDecimal repaymentEvery, final BigDecimal daysInMonth,
|
||||
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
|
||||
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
|
||||
final MathContext mc) {
|
||||
return switch (repaymentFrequency) {
|
||||
case DAYS ->
|
||||
rateFactorByRepaymentEveryDay(interestRate, repaymentEvery, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod);
|
||||
rateFactorByRepaymentEveryDay(interestRate, repaymentEvery, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod, mc);
|
||||
case WEEKS ->
|
||||
rateFactorByRepaymentEveryWeek(interestRate, repaymentEvery, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod);
|
||||
rateFactorByRepaymentEveryWeek(interestRate, repaymentEvery, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod, mc);
|
||||
case MONTHS -> rateFactorByRepaymentEveryMonth(interestRate, repaymentEvery, daysInMonth, daysInYear, actualDaysInPeriod,
|
||||
calculatedDaysInPeriod);
|
||||
calculatedDaysInPeriod, mc);
|
||||
default -> throw new UnsupportedOperationException("Invalid repayment frequency"); // not supported yet
|
||||
};
|
||||
}
|
||||
@@ -370,14 +374,15 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
if (repaymentPeriods.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final BigDecimal rateFactorN = MathUtil.stripTrailingZeros(calculateRateFactorPlus1N(repaymentPeriods));
|
||||
final BigDecimal fnResult = MathUtil.stripTrailingZeros(calculateFnResult(repaymentPeriods));
|
||||
final MathContext mc = scheduleModel.mc();
|
||||
final BigDecimal rateFactorN = MathUtil.stripTrailingZeros(calculateRateFactorPlus1N(repaymentPeriods, mc));
|
||||
final BigDecimal fnResult = MathUtil.stripTrailingZeros(calculateFnResult(repaymentPeriods, mc));
|
||||
final RepaymentPeriod startPeriod = repaymentPeriods.get(0);
|
||||
// TODO: double check
|
||||
final Money outstandingBalance = startPeriod.getInitialBalanceForEmiRecalculation();
|
||||
|
||||
final Money equalMonthlyInstallment = Money.of(outstandingBalance.getCurrency(),
|
||||
calculateEMIValue(rateFactorN, outstandingBalance.getAmount(), fnResult));
|
||||
calculateEMIValue(rateFactorN, outstandingBalance.getAmount(), fnResult, mc), mc);
|
||||
final Money finalEqualMonthlyInstallment = applyInstallmentAmountInMultiplesOf(scheduleModel, equalMonthlyInstallment);
|
||||
|
||||
repaymentPeriods.forEach(period -> period.setEmi(finalEqualMonthlyInstallment));
|
||||
@@ -392,39 +397,40 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
|
||||
Money getDifferenceBetweenLastTwoPeriod(final List<RepaymentPeriod> repaymentPeriods,
|
||||
final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
MathContext mc = scheduleModel.mc();
|
||||
int numberOfUpcomingPeriods = repaymentPeriods.size();
|
||||
if (numberOfUpcomingPeriods < 2) {
|
||||
return Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency());
|
||||
return Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc);
|
||||
}
|
||||
final RepaymentPeriod lastPeriod = repaymentPeriods.get(numberOfUpcomingPeriods - 1);
|
||||
final RepaymentPeriod penultimatePeriod = repaymentPeriods.get(numberOfUpcomingPeriods - 2);
|
||||
return lastPeriod.getEmi().minus(penultimatePeriod.getEmi());
|
||||
return lastPeriod.getEmi().minus(penultimatePeriod.getEmi(), mc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Rate Factor Product from rate factors
|
||||
*/
|
||||
BigDecimal calculateRateFactorPlus1N(final List<RepaymentPeriod> periods) {
|
||||
BigDecimal calculateRateFactorPlus1N(final List<RepaymentPeriod> periods, MathContext mc) {
|
||||
return periods.stream().map(RepaymentPeriod::getRateFactorPlus1).reduce(BigDecimal.ONE,
|
||||
(BigDecimal acc, BigDecimal value) -> acc.multiply(value, MoneyHelper.getMathContext()));
|
||||
(BigDecimal acc, BigDecimal value) -> acc.multiply(value, mc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize Fn values
|
||||
*/
|
||||
BigDecimal calculateFnResult(final List<RepaymentPeriod> periods) {
|
||||
BigDecimal calculateFnResult(final List<RepaymentPeriod> periods, final MathContext mc) {
|
||||
return periods.stream()//
|
||||
.skip(1)//
|
||||
.map(RepaymentPeriod::getRateFactorPlus1)//
|
||||
.reduce(BigDecimal.ONE, this::fnValue);//
|
||||
.reduce(BigDecimal.ONE, (previousFnValue, currentRateFactor) -> fnValue(previousFnValue, currentRateFactor, mc));//
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the EMI (Equal Monthly Installment) value
|
||||
*/
|
||||
BigDecimal calculateEMIValue(final BigDecimal rateFactorPlus1N, final BigDecimal outstandingBalanceForRest, final BigDecimal fnResult) {
|
||||
return rateFactorPlus1N.multiply(outstandingBalanceForRest, MoneyHelper.getMathContext()).divide(fnResult,
|
||||
MoneyHelper.getMathContext());
|
||||
BigDecimal calculateEMIValue(final BigDecimal rateFactorPlus1N, final BigDecimal outstandingBalanceForRest, final BigDecimal fnResult,
|
||||
MathContext mc) {
|
||||
return rateFactorPlus1N.multiply(outstandingBalanceForRest, mc).divide(fnResult, mc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,9 +458,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
* @return Rate Factor for period
|
||||
*/
|
||||
BigDecimal rateFactorByRepaymentEveryDay(final BigDecimal interestRate, final BigDecimal repaymentEvery, final BigDecimal daysInYear,
|
||||
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
|
||||
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod, MathContext mc) {
|
||||
return rateFactorByRepaymentPeriod(interestRate, BigDecimal.ONE, repaymentEvery, daysInYear, actualDaysInPeriod,
|
||||
calculatedDaysInPeriod);
|
||||
calculatedDaysInPeriod, mc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,9 +488,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
* @return Rate Factor for period
|
||||
*/
|
||||
BigDecimal rateFactorByRepaymentEveryWeek(final BigDecimal interestRate, final BigDecimal repaymentEvery, final BigDecimal daysInYear,
|
||||
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
|
||||
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod, MathContext mc) {
|
||||
return rateFactorByRepaymentPeriod(interestRate, ONE_WEEK_IN_DAYS, repaymentEvery, daysInYear, actualDaysInPeriod,
|
||||
calculatedDaysInPeriod);
|
||||
calculatedDaysInPeriod, mc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -515,9 +521,10 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
* @return Rate Factor for period
|
||||
*/
|
||||
BigDecimal rateFactorByRepaymentEveryMonth(final BigDecimal interestRate, final BigDecimal repaymentEvery, final BigDecimal daysInMonth,
|
||||
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
|
||||
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
|
||||
final MathContext mc) {
|
||||
return rateFactorByRepaymentPeriod(interestRate, daysInMonth, repaymentEvery, daysInYear, actualDaysInPeriod,
|
||||
calculatedDaysInPeriod);
|
||||
calculatedDaysInPeriod, mc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,14 +556,14 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
*/
|
||||
BigDecimal rateFactorByRepaymentPeriod(final BigDecimal interestRate, final BigDecimal repaymentPeriodMultiplierInDays,
|
||||
final BigDecimal repaymentEvery, final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod,
|
||||
final BigDecimal calculatedDaysInPeriod) {
|
||||
final BigDecimal calculatedDaysInPeriod, final MathContext mc) {
|
||||
final BigDecimal interestFractionPerPeriod = repaymentPeriodMultiplierInDays//
|
||||
.multiply(repaymentEvery, MoneyHelper.getMathContext())//
|
||||
.divide(daysInYear, MoneyHelper.getMathContext());//
|
||||
.multiply(repaymentEvery, mc)//
|
||||
.divide(daysInYear, mc);//
|
||||
return interestRate//
|
||||
.multiply(interestFractionPerPeriod, MoneyHelper.getMathContext())//
|
||||
.multiply(actualDaysInPeriod, MoneyHelper.getMathContext())//
|
||||
.divide(calculatedDaysInPeriod, MoneyHelper.getMathContext());//
|
||||
.multiply(interestFractionPerPeriod, mc)//
|
||||
.multiply(actualDaysInPeriod, mc)//
|
||||
.divide(calculatedDaysInPeriod, mc);//
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -564,12 +571,13 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
*
|
||||
*/
|
||||
BigDecimal rateFactorByRepaymentPartialPeriod(final BigDecimal interestRate, final BigDecimal repaymentEvery,
|
||||
final BigDecimal cumulatedPeriodRatio, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
|
||||
final BigDecimal cumulatedPeriodRatio, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
|
||||
final MathContext mc) {
|
||||
final BigDecimal interestFractionPerPeriod = repaymentEvery.multiply(cumulatedPeriodRatio);
|
||||
return interestRate//
|
||||
.multiply(interestFractionPerPeriod, MoneyHelper.getMathContext())//
|
||||
.multiply(actualDaysInPeriod, MoneyHelper.getMathContext())//
|
||||
.divide(calculatedDaysInPeriod, MoneyHelper.getMathContext());//
|
||||
.multiply(interestFractionPerPeriod, mc)//
|
||||
.multiply(actualDaysInPeriod, mc)//
|
||||
.divide(calculatedDaysInPeriod, mc);//
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -582,13 +590,13 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
* @param currentRateFactor
|
||||
*
|
||||
*/
|
||||
BigDecimal fnValue(final BigDecimal previousFnValue, final BigDecimal currentRateFactor) {
|
||||
return BigDecimal.ONE.add(previousFnValue.multiply(currentRateFactor, MoneyHelper.getMathContext()), MoneyHelper.getMathContext());
|
||||
BigDecimal fnValue(final BigDecimal previousFnValue, final BigDecimal currentRateFactor, final MathContext mc) {
|
||||
return BigDecimal.ONE.add(previousFnValue.multiply(currentRateFactor, mc), mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgressiveLoanInterestScheduleModel generateModel(LoanProductRelatedDetail loanProductRelatedDetail,
|
||||
Integer installmentAmountInMultiplesOf, List<LoanRepaymentScheduleInstallment> repaymentPeriods) {
|
||||
Integer installmentAmountInMultiplesOf, List<LoanRepaymentScheduleInstallment> repaymentPeriods, MathContext mc) {
|
||||
List<LoanRepaymentScheduleInstallment> repaymentModelsWithoutDownPayment = repaymentPeriods.stream()
|
||||
.filter(period -> !period.isDownPayment() && !period.isAdditional()).toList();
|
||||
|
||||
@@ -596,11 +604,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
RepaymentPeriod previousPeriod = null;
|
||||
for (LoanRepaymentScheduleInstallment repaymentModel : repaymentModelsWithoutDownPayment) {
|
||||
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, repaymentModel.getFromDate(), repaymentModel.getDueDate(),
|
||||
Money.zero(repaymentModel.getLoan().getCurrency()));
|
||||
Money.zero(repaymentModel.getLoan().getCurrency(), mc), mc);
|
||||
previousPeriod = currentPeriod;
|
||||
repaymentModels.add(currentPeriod);
|
||||
}
|
||||
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentModels, loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentModels, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
@@ -90,11 +91,12 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
||||
private static final MonetaryCurrency MONETARY_CURRENCY = new MonetaryCurrency("USD", 2, 1);
|
||||
private static final MockedStatic<MoneyHelper> MONEY_HELPER = mockStatic(MoneyHelper.class);
|
||||
private AdvancedPaymentScheduleTransactionProcessor underTest;
|
||||
private final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class);
|
||||
private static final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class);
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
|
||||
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
|
||||
+14
-49
@@ -20,77 +20,36 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
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.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
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 LoanScheduleGeneratorTest {
|
||||
|
||||
private static final ProgressiveEMICalculator emiCalculator = new ProgressiveEMICalculator();
|
||||
private static MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
|
||||
private static final ApplicationCurrency APPLICATION_CURRENCY = new ApplicationCurrency("USD", "USD", 2, 1, "USD", "$");
|
||||
private static final MonetaryCurrency MONETARY_CURRENCY = MonetaryCurrency.fromApplicationCurrency(APPLICATION_CURRENCY);
|
||||
private static final BigDecimal DISBURSEMENT_AMOUNT = BigDecimal.valueOf(192.22);
|
||||
private static final BigDecimal NOMINAL_INTEREST_RATE = BigDecimal.valueOf(9.99);
|
||||
private static final int NUMBER_OF_REPAYMENTS = 6;
|
||||
private static final int REPAYMENT_FREQUENCY = 1;
|
||||
private static final String REPAYMENT_FREQUENCY_TYPE = "MONTHS";
|
||||
private static final LocalDate DISBURSEMENT_DATE = LocalDate.of(2024, 1, 15);
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void destruct() {
|
||||
moneyHelper.close();
|
||||
}
|
||||
private static final MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN);
|
||||
|
||||
@Test
|
||||
void testGenerateLoanSchedule() {
|
||||
LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), APPLICATION_CURRENCY,
|
||||
DISBURSEMENT_AMOUNT, DISBURSEMENT_DATE, NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE,
|
||||
NOMINAL_INTEREST_RATE, true, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, null, null, null);
|
||||
|
||||
final MathContext mc = MoneyHelper.getMathContext();
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = new ArrayList<>();
|
||||
|
||||
expectedRepaymentPeriods.add(repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1)));
|
||||
expectedRepaymentPeriods.add(repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1)));
|
||||
expectedRepaymentPeriods.add(repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1)));
|
||||
expectedRepaymentPeriods.add(repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)));
|
||||
expectedRepaymentPeriods.add(repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1)));
|
||||
expectedRepaymentPeriods.add(repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1)));
|
||||
|
||||
ScheduledDateGenerator mockScheduledDateGenerator = Mockito.mock(ScheduledDateGenerator.class);
|
||||
ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(mockScheduledDateGenerator, emiCalculator);
|
||||
when(mockScheduledDateGenerator.generateRepaymentPeriods(any(), any(), any())).thenReturn(expectedRepaymentPeriods);
|
||||
|
||||
LoanScheduleModel loanSchedule = generator.generate(mc, modelData);
|
||||
List<LoanScheduleModelPeriod> periods = loanSchedule.getPeriods();
|
||||
List<LoanScheduleModelPeriod> periods = getLoanScheduleModelPeriods();
|
||||
|
||||
assertEquals(7, periods.size(), "Expected 7 periods including the downpayment period.");
|
||||
|
||||
@@ -111,6 +70,18 @@ class LoanScheduleGeneratorTest {
|
||||
BigDecimal.valueOf(0.27), BigDecimal.valueOf(32.87), BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
private static List<LoanScheduleModelPeriod> getLoanScheduleModelPeriods() {
|
||||
LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), APPLICATION_CURRENCY,
|
||||
DISBURSEMENT_AMOUNT, DISBURSEMENT_DATE, NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE,
|
||||
NOMINAL_INTEREST_RATE, true, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, null, null, null);
|
||||
|
||||
ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator);
|
||||
|
||||
LoanScheduleModel loanSchedule = generator.generate(mc, modelData);
|
||||
return loanSchedule.getPeriods();
|
||||
}
|
||||
|
||||
private void checkPeriod(LoanScheduleModelPeriod period, int expectedPeriodNumber, LocalDate expectedFromDate,
|
||||
LocalDate expectedDueDate, BigDecimal expectedPrincipalDue, BigDecimal expectedInterestDue, BigDecimal expectedTotalDue,
|
||||
BigDecimal expectedOutstandingLoanBalance) {
|
||||
@@ -123,10 +94,4 @@ class LoanScheduleGeneratorTest {
|
||||
assertEquals(0, expectedTotalDue.compareTo(repaymentPeriod.getTotalDue().getAmount()));
|
||||
assertEquals(0, expectedOutstandingLoanBalance.compareTo(repaymentPeriod.getOutstandingLoanBalance().getAmount()));
|
||||
}
|
||||
|
||||
private static LoanScheduleModelRepaymentPeriod repayment(int periodNumber, LocalDate fromDate, LocalDate dueDate) {
|
||||
final Money zeroAmount = Money.zero(MONETARY_CURRENCY);
|
||||
return LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate, dueDate, zeroAmount, zeroAmount, zeroAmount, zeroAmount,
|
||||
zeroAmount, zeroAmount, false);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-21
@@ -59,6 +59,7 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
private static MockedStatic<ThreadLocalContextUtil> threadLocalContextUtil = Mockito.mockStatic(ThreadLocalContextUtil.class);
|
||||
private static MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
|
||||
private static MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN);
|
||||
private static LoanProductRelatedDetail loanProductRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
|
||||
|
||||
private static final MonetaryCurrency monetaryCurrency = MonetaryCurrency
|
||||
@@ -95,7 +96,7 @@ class ProgressiveEMICalculatorTest {
|
||||
final BigDecimal daysInYear = BigDecimal.valueOf(daysInYearType.getNumberOfDays(period.getFromDate()));
|
||||
final BigDecimal daysInMonth = BigDecimal.valueOf(daysInMonthType.getNumberOfDays(period.getFromDate()));
|
||||
final BigDecimal rateFactor = emiCalculator.rateFactorByRepaymentEveryMonth(interestRate, BigDecimal.ONE, daysInMonth, daysInYear,
|
||||
daysInPeriod, daysInPeriod);
|
||||
daysInPeriod, daysInPeriod, mc);
|
||||
return rateFactor.setScale(12, MoneyHelper.getRoundingMode());
|
||||
}
|
||||
|
||||
@@ -145,7 +146,7 @@ class ProgressiveEMICalculatorTest {
|
||||
BigDecimal rateFactorPlus1 = getRateFactorsByMonth(daysInYearType, daysInMonthType, interestRate, period).add(BigDecimal.ONE,
|
||||
MoneyHelper.getMathContext());
|
||||
|
||||
final BigDecimal currentFnValue = emiCalculator.fnValue(previousFnValue, rateFactorPlus1);
|
||||
final BigDecimal currentFnValue = emiCalculator.fnValue(previousFnValue, rateFactorPlus1, mc);
|
||||
fnValuesCalculated.add(currentFnValue);
|
||||
|
||||
previousFnValue = currentFnValue;
|
||||
@@ -171,7 +172,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator
|
||||
.generateInterestScheduleModel(expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
.generateInterestScheduleModel(expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
Assertions.assertTrue(interestScheduleModel != null);
|
||||
Assertions.assertTrue(interestScheduleModel.loanProductRelatedDetail() != null);
|
||||
@@ -211,7 +212,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -256,7 +257,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -293,7 +294,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -330,7 +331,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -370,7 +371,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -414,7 +415,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -460,7 +461,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -577,7 +578,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -661,7 +662,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -739,7 +740,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -788,7 +789,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -837,7 +838,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 5), disbursedAmount);
|
||||
@@ -881,7 +882,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2023, 12, 12), disbursedAmount);
|
||||
@@ -915,7 +916,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(1000));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -948,7 +949,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -981,7 +982,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1014,7 +1015,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf);
|
||||
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1031,7 +1032,7 @@ class ProgressiveEMICalculatorTest {
|
||||
private static LoanScheduleModelRepaymentPeriod repayment(int periodNumber, LocalDate fromDate, LocalDate dueDate) {
|
||||
final Money zeroAmount = Money.zero(monetaryCurrency);
|
||||
return LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate, dueDate, zeroAmount, zeroAmount, zeroAmount, zeroAmount,
|
||||
zeroAmount, zeroAmount, false);
|
||||
zeroAmount, zeroAmount, false, mc);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+2
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
@@ -88,6 +89,7 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
|
||||
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
|
||||
+6
-9
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
@@ -116,8 +117,12 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
@Mock
|
||||
private AvroDateTimeMapper mapper;
|
||||
|
||||
private MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
|
||||
ThreadLocalContextUtil
|
||||
@@ -127,6 +132,7 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
moneyHelper.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -141,7 +147,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
LoanAccountData loanAccountData = mock(LoanAccountData.class);
|
||||
CollectionData delinquentData = mock(CollectionData.class);
|
||||
MonetaryCurrency loanCurrency = new MonetaryCurrency("CODE", 1, 1);
|
||||
MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
|
||||
String delinquentDateAsStr = "2022-12-01";
|
||||
LocalDate delinquentDate = LocalDate.parse(delinquentDateAsStr);
|
||||
|
||||
@@ -168,8 +173,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
when(loanChargeReadPlatformService.retrieveLoanCharges(anyLong())).thenAnswer(a -> repaymentScheduleInstallments.get(0)
|
||||
.getInstallmentCharges().stream().map(c -> c.getLoanCharge().toData()).collect(Collectors.toList()));
|
||||
|
||||
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
|
||||
|
||||
// when
|
||||
LoanAccountDelinquencyRangeDataV1 data = (LoanAccountDelinquencyRangeDataV1) serializer.toAvroDTO(event);
|
||||
|
||||
@@ -190,8 +193,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
assertEquals(0, data.getAmount().getFeeAmount().compareTo(new BigDecimal("5.0")));
|
||||
assertEquals(0, data.getAmount().getPenaltyAmount().compareTo(new BigDecimal("50.0")));
|
||||
assertEquals(delinquentDateAsStr, data.getDelinquentDate());
|
||||
|
||||
moneyHelper.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -206,7 +207,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
LoanAccountData loanAccountData = mock(LoanAccountData.class);
|
||||
CollectionData delinquentData = mock(CollectionData.class);
|
||||
MonetaryCurrency loanCurrency = new MonetaryCurrency("CODE", 1, 1);
|
||||
MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
|
||||
String delinquentDateAsStr = "2022-12-01";
|
||||
LocalDate delinquentDate = LocalDate.parse(delinquentDateAsStr);
|
||||
when(loanForProcessing.getId()).thenReturn(1L);
|
||||
@@ -263,8 +263,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
when(loanForProcessing.getLoanCharges()).thenAnswer(a -> repaymentScheduleInstallments.get(0).getInstallmentCharges().stream()
|
||||
.map(c -> c.getLoanCharge()).collect(Collectors.toList()));
|
||||
|
||||
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
|
||||
|
||||
// when
|
||||
LoanAccountDelinquencyRangeDataV1 data = (LoanAccountDelinquencyRangeDataV1) serializer.toAvroDTO(event);
|
||||
|
||||
@@ -314,7 +312,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
assertEquals(0, installmentDelinquencyBucketDataV1_2.getAmount().getFeeAmount().compareTo(new BigDecimal("0.0")));
|
||||
assertEquals(0, installmentDelinquencyBucketDataV1_2.getAmount().getPenaltyAmount().compareTo(new BigDecimal("0.0")));
|
||||
assertEquals(0, installmentDelinquencyBucketDataV1_2.getCharges().size());
|
||||
moneyHelper.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-1
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
@@ -85,7 +86,8 @@ public class LoanRepaymentBusinessEventSerializerTest {
|
||||
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
|
||||
ThreadLocalContextUtil
|
||||
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
|
||||
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
+3
-1
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
@@ -104,7 +105,8 @@ public class LoanDelinquencyDomainServiceTest {
|
||||
currency = new MonetaryCurrency("USD", 2, null);
|
||||
|
||||
moneyHelperStatic = Mockito.mockStatic(MoneyHelper.class);
|
||||
moneyHelperStatic.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
|
||||
moneyHelperStatic.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelperStatic.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
+3
-1
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanStatusChangedBusinessEvent;
|
||||
@@ -55,7 +56,8 @@ class DefaultLoanLifecycleStateMachineTest {
|
||||
public void setUp() {
|
||||
|
||||
moneyHelperStatic = Mockito.mockStatic(MoneyHelper.class);
|
||||
moneyHelperStatic.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
|
||||
moneyHelperStatic.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelperStatic.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
underTest = new DefaultLoanLifecycleStateMachine(businessEventNotifierService);
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.refEq;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
@@ -87,6 +88,7 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
|
||||
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.refEq;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
@@ -88,6 +89,7 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
|
||||
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -37,6 +37,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
@@ -82,6 +84,7 @@ public class DefaultScheduledDateGeneratorTest {
|
||||
public void test_generateRepaymentPeriods() {
|
||||
// given
|
||||
HolidayDetailDTO holidayDetailDTO = createHolidayDTO();
|
||||
MathContext mathContext = new MathContext(12, RoundingMode.HALF_EVEN);
|
||||
|
||||
ApplicationCurrency dollarCurrency = new ApplicationCurrency("USD", "US Dollar", 2, 0, "currency.USD", "$");
|
||||
Money principalAmount = Money.of(fromApplicationCurrency(dollarCurrency), BigDecimal.valueOf(100));
|
||||
@@ -98,8 +101,8 @@ public class DefaultScheduledDateGeneratorTest {
|
||||
submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null);
|
||||
|
||||
// when
|
||||
List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(expectedDisbursementDate, loanApplicationTerms,
|
||||
holidayDetailDTO);
|
||||
List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(mathContext, expectedDisbursementDate,
|
||||
loanApplicationTerms, holidayDetailDTO);
|
||||
|
||||
// then
|
||||
assertThat(result.size()).isEqualTo(4);
|
||||
|
||||
+3
-1
@@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
@@ -64,7 +65,8 @@ public class LoanCalculateRepaymentPastDueServiceTest {
|
||||
ThreadLocalContextUtil
|
||||
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
|
||||
underTest = new LoanCalculateRepaymentPastDueService();
|
||||
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
+3
-1
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
|
||||
@@ -75,7 +76,8 @@ public class LoanDownPaymentHandlerServiceImplTest {
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService);
|
||||
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
Reference in New Issue
Block a user