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