From bb8480a03dc18b4b1bfc5abc78bfebcf157b8910 Mon Sep 17 00:00:00 2001 From: Janos Meszaros Date: Tue, 10 Dec 2024 12:21:24 +0100 Subject: [PATCH] FINERACT-1981: Embeddable Progressive Loan Schedule generator --- .../build-embeddable-progressive-loan-jar.yml | 51 +++++ build.gradle | 9 +- .../infrastructure/core/service/MathUtil.java | 4 +- .../monetary/domain/ApplicationCurrency.java | 4 - .../monetary/domain/MonetaryCurrency.java | 18 +- .../organisation/monetary/domain/Money.java | 141 +++++++------ .../monetary/domain/OrganisationCurrency.java | 5 + .../data/ScheduleGeneratorDTO.java | 12 +- .../portfolio/loanaccount/domain/Loan.java | 6 +- .../loanschedule/data/LoanScheduleParams.java | 16 +- ...stractCumulativeLoanScheduleGenerator.java | 59 +++--- .../domain/DefaultScheduledDateGenerator.java | 28 ++- .../domain/LoanApplicationTerms.java | 83 ++++---- .../LoanRepaymentScheduleModelData.java | 4 +- .../domain/LoanScheduleModel.java | 35 ++-- .../domain/ScheduleCurrentPeriodParams.java | 6 +- .../domain/LoanRescheduleModel.java | 118 ----------- .../LoanProductRelatedDetailMinimumData.java | 186 ++++++++++++++++++ ...MinimumRepaymentScheduleRelatedDetail.java | 8 +- .../domain/LoanProductRelatedDetail.java | 14 +- .../README.md | 63 ++++++ .../build.gradle | 41 ++++ .../dependencies.gradle | 26 +++ .../misc/Main.java | 84 ++++++++ ...dableProgressiveLoanScheduleGenerator.java | 42 ++++ .../impl/ProgressiveTransactionCtx.java | 2 +- .../loanschedule/data/InterestPeriod.java | 2 +- .../loanschedule/data/LoanSchedulePlan.java | 2 +- .../ProgressiveLoanInterestScheduleModel.java | 33 ++-- .../ProgressiveLoanScheduleGenerator.java | 28 ++- .../loanproduct/calc/EMICalculator.java | 10 +- .../calc/ProgressiveEMICalculator.java | 38 ++-- ...ymentScheduleTransactionProcessorTest.java | 2 +- .../domain/LoanScheduleGeneratorTest.java | 6 +- .../calc/ProgressiveEMICalculatorTest.java | 63 +++--- .../service/LoanScheduleAssembler.java | 4 +- .../loanaccount/service/LoanUtilService.java | 2 +- ...nRepaymentBusinessEventSerializerTest.java | 10 +- .../DefaultScheduledDateGeneratorTest.java | 17 +- ...nCalculateRepaymentPastDueServiceTest.java | 5 +- settings.gradle | 1 + 41 files changed, 849 insertions(+), 439 deletions(-) create mode 100644 .github/workflows/build-embeddable-progressive-loan-jar.yml delete mode 100644 fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleModel.java create mode 100644 fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductRelatedDetailMinimumData.java create mode 100644 fineract-progressive-loan-embeddable-schedule-generator/README.md create mode 100644 fineract-progressive-loan-embeddable-schedule-generator/build.gradle create mode 100644 fineract-progressive-loan-embeddable-schedule-generator/dependencies.gradle create mode 100644 fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java create mode 100644 fineract-progressive-loan-embeddable-schedule-generator/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGenerator.java diff --git a/.github/workflows/build-embeddable-progressive-loan-jar.yml b/.github/workflows/build-embeddable-progressive-loan-jar.yml new file mode 100644 index 000000000..0cf91e925 --- /dev/null +++ b/.github/workflows/build-embeddable-progressive-loan-jar.yml @@ -0,0 +1,51 @@ +name: Fineract Build Progressive Loan Embeddable Jar & Test with a Sample Application +on: [push, pull_request] + +permissions: + contents: read + + +jobs: + build: + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + - name: Set up JDK 17 + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + with: + java-version: '17' + distribution: 'zulu' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + - name: Build Embeddable Jar + run: ./gradlew --no-daemon --console=plain :fineract-progressive-loan-embeddable-schedule-generator:shadowJar + - name: Pick up the JAR filename + run: | + EMBEDDABLE_JAR_FILE=(`ls fineract-progressive-loan-embeddable-schedule-generator/build/libs/*-all.jar | head -n 1`) + echo "EMBEDDABLE_JAR_FILE=$EMBEDDABLE_JAR_FILE" >> $GITHUB_ENV + - name: Build Sample Application + run: | + mkdir sample-app + javac -cp $EMBEDDABLE_JAR_FILE -d sample-app fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java + env: + EMBEDDABLE_JAR_FILE: ${{ env.EMBEDDABLE_JAR_FILE }} + + - name: Run Schedule Generator Sample Application + run: | + java -cp $EMBEDDABLE_JAR_FILE:sample-app Main + java -cp $EMBEDDABLE_JAR_FILE:sample-app Main 25 + env: + EMBEDDABLE_JAR_FILE: ${{ env.EMBEDDABLE_JAR_FILE }} + + - name: Archive test results + if: always() + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4 + with: + name: test-results + path: | + build/reports/ + fineract-progressive-loan-embeddable-schedule-generator/build/reports/ diff --git a/build.gradle b/build.gradle index 152e9d3d3..b2a216616 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,8 @@ buildscript { 'fineract-avro-schemas', 'fineract-e2e-tests-core', 'fineract-e2e-tests-runner', - 'fineract-progressive-loan' + 'fineract-progressive-loan', + 'fineract-progressive-loan-embeddable-schedule-generator' ].contains(it.name) } fineractPublishProjects = subprojects.findAll{ @@ -66,7 +67,8 @@ buildscript { 'fineract-report', 'fineract-branch', 'fineract-document', - 'fineract-progressive-loan' + 'fineract-progressive-loan', + 'fineract-progressive-loan-embeddable-schedule-generator' ].contains(it.name) } npmRepository = 'https://npm.pkg.github.com' @@ -109,6 +111,7 @@ plugins { id 'se.thinkcode.cucumber-runner' version '0.0.11' apply false id "com.github.davidmc24.gradle.plugin.avro-base" version "1.9.1" apply false id 'org.openapi.generator' version '7.8.0' apply false + id 'com.gradleup.shadow' version '8.3.5' apply false } apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.release.gradle" @@ -169,7 +172,7 @@ allprojects { groovyGradle { target '*.gradle', '**/*.gradle' - targetExclude '**/build/**' + targetExclude '**/build/**', '/**/build.gradle' // TODO: temporary disabled on build.gradle until someone fix this issue: https://github.com/diffplug/spotless/issues/1807 greclipse() indentWithSpaces(4) endWithNewline() diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java index 99365b6c2..a543a3f1d 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java @@ -365,12 +365,12 @@ public final class MathUtil { /** @return parameter value or ZERO if it is negative */ public static Money negativeToZero(Money value) { - return value == null || isGreaterThanZero(value) ? value : Money.zero(value.getCurrency()); + return value == null || isGreaterThanZero(value) ? value : Money.zero(value.getCurrencyData()); } /** @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); + return value == null || isGreaterThanZero(value, mc) ? value : Money.zero(value.getCurrencyData(), mc); } public static boolean isEmpty(Money value) { diff --git a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/ApplicationCurrency.java b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/ApplicationCurrency.java index 4eb1f59af..d79e2562e 100644 --- a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/ApplicationCurrency.java +++ b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/ApplicationCurrency.java @@ -98,10 +98,6 @@ public class ApplicationCurrency extends AbstractPersistableCustom { return new CurrencyData(this.code, this.name, this.decimalPlaces, this.inMultiplesOf, this.displaySymbol, this.nameCode); } - public CurrencyData toData(final int digitsAfterDecimalSupported, final Integer inMultiplesOf) { - return new CurrencyData(this.code, this.name, digitsAfterDecimalSupported, inMultiplesOf, this.displaySymbol, this.nameCode); - } - public OrganisationCurrency toOrganisationCurrency() { return new OrganisationCurrency(this.code, this.name, this.decimalPlaces, this.inMultiplesOf, this.nameCode, this.displaySymbol); } diff --git a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java index cbf4f2f8d..dd50f7bde 100644 --- a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java +++ b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java @@ -34,6 +34,8 @@ public class MonetaryCurrency { @Column(name = "currency_multiplesof") private Integer inMultiplesOf; + private transient CurrencyData currencyData; + protected MonetaryCurrency() { this.code = null; this.digitsAfterDecimal = 0; @@ -46,6 +48,13 @@ public class MonetaryCurrency { this.inMultiplesOf = inMultiplesOf; } + public MonetaryCurrency(final CurrencyData currencyData) { + this.currencyData = currencyData; + this.code = currencyData.getCode(); + this.digitsAfterDecimal = currencyData.getDecimalPlaces(); + this.inMultiplesOf = currencyData.getInMultiplesOf(); + } + public MonetaryCurrency copy() { return new MonetaryCurrency(this.code, this.digitsAfterDecimal, this.inMultiplesOf); } @@ -56,7 +65,14 @@ public class MonetaryCurrency { } public static MonetaryCurrency fromCurrencyData(final CurrencyData currencyData) { - return new MonetaryCurrency(currencyData.getCode(), currencyData.getDecimalPlaces(), currencyData.getInMultiplesOf()); + return new MonetaryCurrency(currencyData); + } + + public CurrencyData toData() { + if (currencyData == null) { + currencyData = new CurrencyData(code, digitsAfterDecimal, inMultiplesOf); + } + return currencyData; } public String getCode() { diff --git a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java index e70974cc6..ecab17b3c 100644 --- a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java +++ b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java @@ -21,43 +21,59 @@ package org.apache.fineract.organisation.monetary.domain; import java.math.BigDecimal; import java.math.MathContext; import java.util.Iterator; -import lombok.Getter; import org.apache.fineract.organisation.monetary.data.CurrencyData; -@Getter public class Money implements Comparable { - private final String currencyCode; - private final int currencyDigitsAfterDecimal; - private final Integer inMultiplesOf; private final BigDecimal amount; + private final CurrencyData currency; private final transient MathContext mc; protected Money() { - this.currencyCode = null; - this.currencyDigitsAfterDecimal = 0; - this.inMultiplesOf = 0; - this.amount = null; + this.currency = CurrencyData.blank(); + this.amount = BigDecimal.ZERO; this.mc = getMc(); } - private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount, final Integer inMultiplesOf, - final MathContext mc) { - this.currencyCode = currencyCode; - this.currencyDigitsAfterDecimal = digitsAfterDecimal; - this.inMultiplesOf = inMultiplesOf; + private Money(final CurrencyData currency, final BigDecimal amount, final MathContext mc) { + this.currency = currency; this.mc = mc; final BigDecimal amountZeroed = defaultToZeroIfNull(amount); BigDecimal amountScaled = amountZeroed.stripTrailingZeros(); // round monetary amounts into multiples of say 20/50. - if (inMultiplesOf != null && this.currencyDigitsAfterDecimal == 0 && inMultiplesOf > 0 && amountScaled.doubleValue() > 0) { + if (currency.getInMultiplesOf() != null && currency.getDecimalPlaces() == 0 && currency.getInMultiplesOf() > 0 + && amountScaled.doubleValue() > 0) { final double existingVal = amountScaled.doubleValue(); - amountScaled = BigDecimal.valueOf(roundToMultiplesOf(existingVal, inMultiplesOf)); + amountScaled = BigDecimal.valueOf(roundToMultiplesOf(existingVal, currency.getInMultiplesOf())); } - this.amount = amountScaled.setScale(this.currencyDigitsAfterDecimal, getMc().getRoundingMode()); + this.amount = amountScaled.setScale(currency.getDecimalPlaces(), getMc().getRoundingMode()); + } + + public MonetaryCurrency getCurrency() { + return MonetaryCurrency.fromCurrencyData(currency); + } + + public CurrencyData getCurrencyData() { + return currency; + } + + public String getCurrencyCode() { + return currency.getCode(); + } + + public Integer getInMultiplesOf() { + return currency.getInMultiplesOf(); + } + + public BigDecimal getAmount() { + return amount; + } + + public BigDecimal getAmountDefaultedToNullIfZero() { + return defaultToNullIfZero(this.amount); } public static Money total(final Money... monies) { @@ -83,21 +99,20 @@ public class Money implements Comparable { return total; } - public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount, final MathContext mc) { - return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), defaultToZeroIfNull(newAmount), - currency.getCurrencyInMultiplesOf(), mc); - } - - public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount) { - return of(currency, newAmount, MoneyHelper.getMathContext()); - } - public static Money of(final CurrencyData currency, final BigDecimal newAmount) { return of(currency, newAmount, MoneyHelper.getMathContext()); } 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); + return new Money(currency, newAmount, mc); + } + + public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount, final MathContext mc) { + return new Money(currency.toData(), newAmount, mc); + } + + public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount) { + return of(currency, newAmount, MoneyHelper.getMathContext()); } public static Money zero(final MonetaryCurrency currency) { @@ -105,11 +120,11 @@ public class Money implements Comparable { } public static Money zero(final MonetaryCurrency currency, MathContext mc) { - return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO, currency.getCurrencyInMultiplesOf(), mc); + return new Money(currency.toData(), BigDecimal.ZERO, mc); } public static Money zero(final CurrencyData currency, MathContext mc) { - return new Money(currency.getCode(), currency.getDecimalPlaces(), BigDecimal.ZERO, currency.getInMultiplesOf(), mc); + return new Money(currency, BigDecimal.ZERO, mc); } public static Money zero(final CurrencyData currency) { @@ -151,7 +166,7 @@ public class Money implements Comparable { if (inMultiplesOfValue.compareTo(BigDecimal.ZERO) > 0) { amountScaled = amountScaled.divide(inMultiplesOfValue, 0, mc.getRoundingMode()).multiply(inMultiplesOfValue); } - return Money.of(existingVal.getCurrency(), amountScaled); + return Money.of(existingVal.getCurrencyData(), amountScaled); } public static double ceiling(final double n, final double s) { @@ -195,11 +210,11 @@ public class Money implements Comparable { } public Money copy() { - return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, this.amount.stripTrailingZeros(), this.inMultiplesOf, this.mc); + return new Money(this.currency, this.amount, this.mc); } public Money copy(final BigDecimal amount) { - return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, amount.stripTrailingZeros(), this.inMultiplesOf, this.mc); + return new Money(this.currency, amount, this.mc); } public Money copy(final double amount) { @@ -212,7 +227,7 @@ public class Money implements Comparable { final Money money = checkCurrencyEqual(moneyProvider); total = total.add(money.amount); } - return Money.of(monetaryCurrency(), total); + return Money.of(getCurrencyData(), total); } public Money plus(final Money moneyToAdd) { @@ -233,7 +248,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.add(amountToAdd); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } public Money plus(final double amountToAdd) { @@ -241,7 +256,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.add(BigDecimal.valueOf(amountToAdd)); - return Money.of(monetaryCurrency(), newAmount); + return Money.of(getCurrencyData(), newAmount); } public Money minus(final Money moneyToSubtract) { @@ -271,7 +286,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.add(amountToAdd); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } public Money minus(final BigDecimal amountToSubtract) { @@ -283,7 +298,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.subtract(amountToSubtract); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } private Money checkCurrencyEqual(final Money money) { @@ -294,7 +309,7 @@ public class Money implements Comparable { } public boolean isSameCurrency(final Money money) { - return this.currencyCode.equals(money.getCurrencyCode()); + return getCurrencyCode().equals(money.getCurrencyCode()); } public Money dividedBy(final BigDecimal valueToDivideBy, final MathContext mc) { @@ -302,7 +317,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.divide(valueToDivideBy, mc); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } public Money dividedBy(final double valueToDivideBy, final MathContext mc) { @@ -310,7 +325,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.divide(BigDecimal.valueOf(valueToDivideBy), mc); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } public Money dividedBy(final long valueToDivideBy, final MathContext mc) { @@ -318,7 +333,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.divide(BigDecimal.valueOf(valueToDivideBy), mc); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } public Money dividedBy(final long valueToDivideBy) { @@ -326,7 +341,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.divide(BigDecimal.valueOf(valueToDivideBy), getMc()); - return Money.of(monetaryCurrency(), newAmount, getMc()); + return Money.of(getCurrencyData(), newAmount, getMc()); } public Money multipliedBy(final BigDecimal valueToMultiplyBy) { @@ -338,7 +353,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.multiply(valueToMultiplyBy, mc); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } public Money multipliedBy(final double valueToMultiplyBy) { @@ -346,7 +361,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.multiply(BigDecimal.valueOf(valueToMultiplyBy)); - return Money.of(monetaryCurrency(), newAmount); + return Money.of(getCurrencyData(), newAmount); } public Money multipliedBy(final long valueToMultiplyBy) { @@ -358,7 +373,7 @@ public class Money implements Comparable { return this; } final BigDecimal newAmount = this.amount.multiply(BigDecimal.valueOf(valueToMultiplyBy), mc); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } public Money multiplyRetainScale(final BigDecimal valueToMultiplyBy, final MathContext mc) { @@ -366,8 +381,8 @@ public class Money implements Comparable { return this; } BigDecimal newAmount = this.amount.multiply(valueToMultiplyBy, mc); - newAmount = newAmount.setScale(this.currencyDigitsAfterDecimal, mc.getRoundingMode()); - return Money.of(monetaryCurrency(), newAmount, mc); + newAmount = newAmount.setScale(this.currency.getDecimalPlaces(), mc.getRoundingMode()); + return Money.of(getCurrencyData(), newAmount, mc); } public Money multiplyRetainScale(final double valueToMultiplyBy, final MathContext mc) { @@ -376,12 +391,12 @@ public class Money implements Comparable { public Money percentageOf(BigDecimal percentage, final MathContext mc) { final BigDecimal newAmount = this.amount.multiply(percentage).divide(BigDecimal.valueOf(100), mc); - return Money.of(monetaryCurrency(), newAmount, mc); + return Money.of(getCurrencyData(), newAmount, mc); } @Override public int compareTo(final Money other) { - if (!this.currencyCode.equals(other.currencyCode)) { + if (!this.getCurrencyCode().equals(other.getCurrencyCode())) { throw new UnsupportedOperationException("currencies arent different"); } return this.amount.compareTo(other.amount); @@ -392,7 +407,7 @@ public class Money implements Comparable { } public boolean isZero(final MathContext mc) { - return isEqualTo(Money.zero(getCurrency(), mc)); + return isEqualTo(Money.zero(getCurrencyData(), mc)); } public boolean isEqualTo(final Money other) { @@ -416,7 +431,7 @@ public class Money implements Comparable { } public boolean isGreaterThanZero(MathContext mc) { - return isGreaterThan(Money.zero(getCurrency(), mc)); + return isGreaterThan(Money.zero(getCurrencyData(), mc)); } public boolean isLessThan(final Money other) { @@ -428,20 +443,12 @@ public class Money implements Comparable { } public boolean isLessThanZero(final MathContext mc) { - return isLessThan(Money.zero(getCurrency(), mc)); - } - - public Integer getCurrencyInMultiplesOf() { - return this.inMultiplesOf; - } - - public BigDecimal getAmountDefaultedToNullIfZero() { - return defaultToNullIfZero(this.amount); + return isLessThan(Money.zero(getCurrencyData(), mc)); } @Override public String toString() { - return new StringBuilder().append(this.currencyCode).append(' ').append(this.amount.toPlainString()).toString(); + return this.getCurrencyCode() + ' ' + this.amount.toPlainString(); } public Money negated() { @@ -452,7 +459,7 @@ public class Money implements Comparable { if (isZero(mc)) { return this; } - return Money.of(monetaryCurrency(), this.amount.negate(), mc); + return Money.of(getCurrencyData(), this.amount.negate(), mc); } public Money abs() { @@ -463,20 +470,12 @@ public class Money implements Comparable { return isLessThanZero(mc) ? negated(mc) : this; } - public MonetaryCurrency getCurrency() { - return monetaryCurrency(); - } - - private MonetaryCurrency monetaryCurrency() { - return new MonetaryCurrency(this.currencyCode, this.currencyDigitsAfterDecimal, this.inMultiplesOf); - } - public Money zero() { return zero(getMc()); } public Money zero(MathContext mc) { - return Money.zero(getCurrency(), mc); + return Money.zero(getCurrencyData(), mc); } public MathContext getMc() { diff --git a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/OrganisationCurrency.java b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/OrganisationCurrency.java index 11bf2e387..c5b750df5 100644 --- a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/OrganisationCurrency.java +++ b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/OrganisationCurrency.java @@ -22,6 +22,7 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.organisation.monetary.data.CurrencyData; /** * Represents currencies allowed for this MFI/organisation. @@ -74,4 +75,8 @@ public class OrganisationCurrency extends AbstractPersistableCustom { public final MonetaryCurrency toMonetaryCurrency() { return new MonetaryCurrency(this.code, this.decimalPlaces, this.inMultiplesOf); } + + public final CurrencyData toData() { + return new CurrencyData(this.code, this.decimalPlaces, this.inMultiplesOf); + } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java index 8fc33719e..6d85063bc 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java @@ -19,7 +19,7 @@ package org.apache.fineract.portfolio.loanaccount.data; import java.time.LocalDate; -import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper; import org.apache.fineract.portfolio.calendar.domain.Calendar; import org.apache.fineract.portfolio.calendar.domain.CalendarInstance; @@ -29,7 +29,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul public class ScheduleGeneratorDTO { final LoanScheduleGeneratorFactory loanScheduleFactory; - final ApplicationCurrency applicationCurrency; + final CurrencyData currency; final LocalDate calculatedRepaymentsStartingFromDate; final HolidayDetailDTO holidayDetailDTO; final CalendarInstance calendarInstanceForInterestRecalculation; @@ -47,7 +47,7 @@ public class ScheduleGeneratorDTO { final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI; final boolean isPrincipalCompoundingDisabledForOverdueLoans; - public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency, + public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final CurrencyData currency, final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO, final CalendarInstance calendarInstanceForInterestRecalculation, final CalendarInstance compoundingCalendarInstance, final LocalDate recalculateFrom, final Long overdurPenaltyWaitPeriod, final FloatingRateDTO floatingRateDTO, @@ -58,7 +58,7 @@ public class ScheduleGeneratorDTO { final boolean isPrincipalCompoundingDisabledForOverdueLoans) { this.loanScheduleFactory = loanScheduleFactory; - this.applicationCurrency = applicationCurrency; + this.currency = currency; this.calculatedRepaymentsStartingFromDate = calculatedRepaymentsStartingFromDate; this.calendarInstanceForInterestRecalculation = calendarInstanceForInterestRecalculation; this.compoundingCalendarInstance = compoundingCalendarInstance; @@ -81,8 +81,8 @@ public class ScheduleGeneratorDTO { return this.loanScheduleFactory; } - public ApplicationCurrency getApplicationCurrency() { - return this.applicationCurrency; + public CurrencyData getCurrency() { + return this.currency; } public LocalDate getCalculatedRepaymentsStartingFromDate() { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 7107b8d8b..280693da5 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -2781,8 +2781,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { interestChargedFromDate = getDisbursementDate(); } - return LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getApplicationCurrency(), loanTermFrequency, - loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), + return LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getCurrency(), loanTermFrequency, loanTermPeriodFrequencyType, + nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance, interestChargedFromDate, this.loanProduct.getPrincipalThresholdForLastInstallment(), @@ -3053,7 +3053,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { List loanTermVariations = new ArrayList<>(); annualNominalInterestRate = constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations); - return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, + return LoanApplicationTerms.assembleFrom(applicationCurrency.toData(), loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData, maxOutstandingBalance, interestChargedFromDate, this.loanProduct.getPrincipalThresholdForLastInstallment(), diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleParams.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleParams.java index 635b62bd7..2c4e95993 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleParams.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleParams.java @@ -26,7 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -97,7 +97,7 @@ public final class LoanScheduleParams { private final LocalDate scheduleTillDate; private final boolean partialUpdate; private int loanTermInDays; - private final MonetaryCurrency currency; + private final CurrencyData currency; private final boolean applyInterestRecalculation; private final MathContext mc; @@ -110,7 +110,7 @@ public final class LoanScheduleParams { final Money outstandingBalanceAsPerRest, final List installments, final Collection recalculationDetails, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate, - final boolean partialUpdate, final MonetaryCurrency currency, final boolean applyInterestRecalculation, final MathContext mc) { + final boolean partialUpdate, final CurrencyData currency, final boolean applyInterestRecalculation, final MathContext mc) { this.periodNumber = periodNumber; this.instalmentNumber = instalmentNumber; this.loanTermInDays = loanTermInDays; @@ -153,7 +153,7 @@ public final class LoanScheduleParams { final Money principalToBeScheduled, final Money outstandingBalance, final Money outstandingBalanceAsPerRest, final List installments, final Collection recalculationDetails, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate, - final MonetaryCurrency currency, final boolean applyInterestRecalculation, final MathContext mc) { + final CurrencyData currency, final boolean applyInterestRecalculation, final MathContext mc) { final boolean partialUpdate = true; return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate, totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged, @@ -187,7 +187,7 @@ public final class LoanScheduleParams { final boolean partialUpdate = false; final int loanTermInDays = 0; final Money totalOutstandingInterestPaymentDueToGrace = null; - final MonetaryCurrency currency = null; + final CurrencyData currency = null; final Money unCompoundedAmount = null; return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate, totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged, @@ -197,7 +197,7 @@ public final class LoanScheduleParams { scheduleTillDate, partialUpdate, currency, applyInterestRecalculation, mc); } - public static LoanScheduleParams createLoanScheduleParams(final MonetaryCurrency currency, final Money chargesDueAtTimeOfDisbursement, + public static LoanScheduleParams createLoanScheduleParams(final CurrencyData currency, final Money chargesDueAtTimeOfDisbursement, final LocalDate periodStartDate, final Money principalToBeScheduled, final MathContext mc) { final int loanTermInDays = 0; final int periodNumber = 1; @@ -231,7 +231,7 @@ public final class LoanScheduleParams { scheduleTillDate, partialUpdate, currency, applyInterestRecalculation, mc); } - public static LoanScheduleParams createLoanScheduleParams(final MonetaryCurrency currency, final Money chargesDueAtTimeOfDisbursement, + public static LoanScheduleParams createLoanScheduleParams(final CurrencyData currency, final Money chargesDueAtTimeOfDisbursement, final LocalDate periodStartDate, final Money principalToBeScheduled, final LoanScheduleParams loanScheduleParams, MathContext mc) { final int loanTermInDays = 0; @@ -500,7 +500,7 @@ public final class LoanScheduleParams { return this.compoundingDateVariations; } - public MonetaryCurrency getCurrency() { + public CurrencyData getCurrency() { return this.currency; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java index 3f2b4907e..149654f74 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java @@ -37,7 +37,7 @@ import java.util.Set; import java.util.TreeMap; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; -import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO; @@ -74,7 +74,6 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, final Set loanCharges, final HolidayDetailDTO holidayDetailDTO, final LoanScheduleParams loanScheduleParams) { - final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency(); // generate list of proposed schedule due dates LocalDate loanEndDate = getScheduledDateGenerator().getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO); LoanTermVariationsData lastDueDateVariation = loanApplicationTerms.getLoanTermVariations() @@ -90,7 +89,8 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch // setup variables for tracking important facts required for loan // schedule generation. - final MonetaryCurrency currency = loanApplicationTerms.getCurrency(); + final CurrencyData currency = loanApplicationTerms.getCurrency(); + final MonetaryCurrency monetaryCurrency = MonetaryCurrency.fromCurrencyData(currency); LoanScheduleParams scheduleParams; LocalDate periodStartDate = RepaymentStartDateType.DISBURSEMENT_DATE.equals(loanApplicationTerms.getRepaymentStartDateType()) ? loanApplicationTerms.getExpectedDisbursementDate() @@ -252,7 +252,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch if (loanApplicationTerms.isInterestRecalculationEnabled()) { populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), scheduledDueDate, loanApplicationTerms, - holidayDetailDTO, scheduleParams, loanCharges, currency, mc); + holidayDetailDTO, scheduleParams, loanCharges, monetaryCurrency, mc); } // populates the collection with transactions till the due date of @@ -330,7 +330,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch } // applies charges for the period - applyChargesForCurrentPeriod(loanCharges, currency, scheduleParams, scheduledDueDate, currentPeriodParams, mc); + applyChargesForCurrentPeriod(loanCharges, monetaryCurrency, scheduleParams, scheduledDueDate, currentPeriodParams, mc); // sum up real totalInstallmentDue from components final Money totalInstallmentDue = currentPeriodParams.fetchTotalAmountForPeriod(); @@ -354,7 +354,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment); // apply loan transactions on installments to identify early/late // payments for interest recalculation - installment = handleRecalculationForTransactions(mc, loanApplicationTerms, holidayDetailDTO, currency, scheduleParams, + installment = handleRecalculationForTransactions(mc, loanApplicationTerms, holidayDetailDTO, monetaryCurrency, scheduleParams, loanRepaymentScheduleTransactionProcessor, loanApplicationTerms.getTotalInterestDue(), lastRestDate, scheduledDueDate, periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams, lastTotalOutstandingInterestPaymentDueToGrace, installment, loanCharges); @@ -398,7 +398,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch // determine fees and penalties for charges which depends on total // loan interest - updatePeriodsWithCharges(currency, scheduleParams, periods, nonCompoundingCharges, mc); + updatePeriodsWithCharges(monetaryCurrency, scheduleParams, periods, nonCompoundingCharges, mc); // this block is to add extra re-payment schedules with interest portion // if the loan not paid with in loan term @@ -408,8 +408,9 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch } if (scheduleParams.applyInterestRecalculation() && scheduleParams.getLatePaymentMap().size() > 0 && DateUtils.isAfter(currentDate, scheduleParams.getPeriodStartDate())) { - Money totalInterest = addInterestOnlyRepaymentScheduleForCurrentDate(mc, loanApplicationTerms, holidayDetailDTO, currency, - periods, currentDate, loanRepaymentScheduleTransactionProcessor, transactions, loanCharges, scheduleParams); + Money totalInterest = addInterestOnlyRepaymentScheduleForCurrentDate(mc, loanApplicationTerms, holidayDetailDTO, + monetaryCurrency, periods, currentDate, loanRepaymentScheduleTransactionProcessor, transactions, loanCharges, + scheduleParams); scheduleParams.addTotalCumulativeInterest(totalInterest); } @@ -418,7 +419,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch final BigDecimal totalOutstanding = BigDecimal.ZERO; updateCompoundingDetails(periods, scheduleParams, loanApplicationTerms); - return LoanScheduleModel.from(periods, applicationCurrency, scheduleParams.getLoanTermInDays(), + return LoanScheduleModel.from(periods, currency, scheduleParams.getLoanTermInDays(), scheduleParams.getPrincipalToBeScheduled().plus(loanApplicationTerms.getDownPaymentAmount()), scheduleParams.getTotalCumulativePrincipal().plus(loanApplicationTerms.getDownPaymentAmount()).getAmount(), totalPrincipalPaid, scheduleParams.getTotalCumulativeInterest().getAmount(), @@ -534,7 +535,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch modifiedInstallment = handlePrepaymentOfLoan(mc, loanApplicationTerms, holidayDetailDTO, scheduleParams, totalInterestChargedForFullLoanTerm, scheduledDueDate, periodStartDateApplicableForInterest, currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(), currentPeriodParams, - lastTotalOutstandingInterestPaymentDueToGrace, transactionDate, modifiedInstallment, loanCharges); + lastTotalOutstandingInterestPaymentDueToGrace, transactionDate, modifiedInstallment, loanCharges, currency); Money addToPrincipal = Money.zero(currency); if (scheduleParams.getOutstandingBalance().isLessThanZero()) { @@ -575,17 +576,18 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch final Money totalInterestChargedForFullLoanTerm, final LocalDate scheduledDueDate, LocalDate periodStartDateApplicableForInterest, final BigDecimal interestCalculationGraceOnRepaymentPeriodFraction, final ScheduleCurrentPeriodParams currentPeriodParams, final Money lastTotalOutstandingInterestPaymentDueToGrace, - final LocalDate transactionDate, final LoanScheduleModelPeriod installment, Set loanCharges) { + final LocalDate transactionDate, final LoanScheduleModelPeriod installment, Set loanCharges, + MonetaryCurrency currency) { LoanScheduleModelPeriod modifiedInstallment = installment; Money outstanding = scheduleParams.getOutstandingBalance(); PrincipalInterest tempPrincipalInterest = new PrincipalInterest(currentPeriodParams.getPrincipalForThisPeriod(), currentPeriodParams.getInterestForThisPeriod(), null); - outstanding = outstanding.minus(cumulativeFeeChargesDueWithin(transactionDate, scheduledDueDate, loanCharges, - totalInterestChargedForFullLoanTerm.getCurrency(), tempPrincipalInterest, scheduleParams.getPrincipalToBeScheduled(), - scheduleParams.getTotalCumulativeInterest(), true, scheduleParams.isFirstPeriod(), mc)); - outstanding = outstanding.minus(cumulativePenaltyChargesDueWithin(transactionDate, scheduledDueDate, loanCharges, - totalInterestChargedForFullLoanTerm.getCurrency(), tempPrincipalInterest, scheduleParams.getPrincipalToBeScheduled(), - scheduleParams.getTotalCumulativeInterest(), true, scheduleParams.isFirstPeriod(), mc)); + outstanding = outstanding.minus(cumulativeFeeChargesDueWithin(transactionDate, scheduledDueDate, loanCharges, currency, + tempPrincipalInterest, scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativeInterest(), true, + scheduleParams.isFirstPeriod(), mc)); + outstanding = outstanding.minus(cumulativePenaltyChargesDueWithin(transactionDate, scheduledDueDate, loanCharges, currency, + tempPrincipalInterest, scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativeInterest(), true, + scheduleParams.isFirstPeriod(), mc)); if (!outstanding.isGreaterThan(currentPeriodParams.getInterestForThisPeriod()) && !scheduledDueDate.equals(transactionDate)) { final Collection interestRates = loanApplicationTerms.getLoanTermVariations().getInterestRateChanges(); @@ -607,7 +609,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch new TreeMap<>(scheduleParams.getCompoundingMap())); scheduleParams.getCompoundingMap().clear(); populateCompoundingDatesInPeriod(periodStartDateApplicableForInterest, calculateTill, loanApplicationTerms, holidayDetailDTO, - scheduleParams, loanCharges, totalInterestChargedForFullLoanTerm.getCurrency(), mc); + scheduleParams, loanCharges, currency, mc); // this is to make sure we are recalculating using correct interest // rate @@ -628,11 +630,10 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch loanApplicationTerms.updateAnnualNominalInterestRate(currentInterestRate); // applies charges for the period - final ScheduleCurrentPeriodParams tempPeriod = new ScheduleCurrentPeriodParams( - totalInterestChargedForFullLoanTerm.getCurrency(), interestCalculationGraceOnRepaymentPeriodFraction); + final ScheduleCurrentPeriodParams tempPeriod = new ScheduleCurrentPeriodParams(loanApplicationTerms.getCurrency(), + interestCalculationGraceOnRepaymentPeriodFraction); tempPeriod.setInterestForThisPeriod(interestTillDate.interest()); - applyChargesForCurrentPeriod(loanCharges, totalInterestChargedForFullLoanTerm.getCurrency(), scheduleParams, calculateTill, - tempPeriod, mc); + applyChargesForCurrentPeriod(loanCharges, currency, scheduleParams, calculateTill, tempPeriod, mc); Money interestDiff = currentPeriodParams.getInterestForThisPeriod().minus(tempPeriod.getInterestForThisPeriod()); Money chargeDiff = currentPeriodParams.getFeeChargesForInstallment().minus(tempPeriod.getFeeChargesForInstallment()); Money penaltyDiff = currentPeriodParams.getPenaltyChargesForInstallment().minus(tempPeriod.getPenaltyChargesForInstallment()); @@ -684,8 +685,8 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch .reduceOutstandingBalance(currentPeriodParams.getPrincipalForThisPeriod().minus(currentPeriodParams.getReducedBalance())); } - private void updatePrincipalPortionBasedOnPreviousEarlyPayments(final MonetaryCurrency currency, - final LoanScheduleParams scheduleParams, final ScheduleCurrentPeriodParams currentPeriodParams) { + private void updatePrincipalPortionBasedOnPreviousEarlyPayments(final CurrencyData currency, final LoanScheduleParams scheduleParams, + final ScheduleCurrentPeriodParams currentPeriodParams) { if (currentPeriodParams.getPrincipalForThisPeriod().isGreaterThan(scheduleParams.getReducePrincipal())) { currentPeriodParams.minusPrincipalForThisPeriod(scheduleParams.getReducePrincipal()); scheduleParams.setReducePrincipal(Money.zero(currency)); @@ -711,7 +712,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch final LocalDate scheduledDueDate, final LocalDate periodStartDateForInterest, final Collection applicableTransactions, final ScheduleCurrentPeriodParams currentPeriodParams) { if (scheduleParams.applyInterestRecalculation()) { - final MonetaryCurrency currency = scheduleParams.getCurrency(); + final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(scheduleParams.getCurrency()); final Collection interestRates = loanApplicationTerms.getLoanTermVariations().getInterestRateChanges(); boolean checkForOutstanding = true; List unprocessedTransactions = new ArrayList<>(); @@ -1682,7 +1683,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch final LoanScheduleParams params, final LocalDate lastRestDate, final LocalDate scheduledDueDate) { if (loanApplicationTerms.isInterestRecalculationEnabled() && loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) { - final MonetaryCurrency currency = params.getCurrency(); + final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(params.getCurrency()); Money totalCompoundedAmount = Money.zero(currency); boolean lastInstallmentIsPastDate = false; for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : params.getInstallments()) { @@ -1802,7 +1803,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch private void adjustCompoundedAmountWithPaidDetail(final LoanScheduleParams params, final LocalDate lastRestDate, final LocalDate amountApplicableDate, final LoanTransaction transaction, final LoanApplicationTerms loanApplicationTerms) { adjustCompoundedAmountWithPaidDetail(params.getPrincipalPortionMap(), lastRestDate, amountApplicableDate, transaction, - loanApplicationTerms, params.getCurrency()); + loanApplicationTerms, MonetaryCurrency.fromCurrencyData(params.getCurrency())); } private void adjustCompoundedAmountWithPaidDetail(final Map principalPortionMap, final LocalDate lastRestDate, @@ -2493,7 +2494,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, uncompoundedAmount, disburseDetailMap, principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, newRepaymentScheduleInstallments, recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, - currency, applyInterestRecalculation, mc); + currency.toData(), applyInterestRecalculation, mc); retainedInstallments.addAll(newRepaymentScheduleInstallments); loanScheduleParams.getCompoundingDateVariations().putAll(compoundingDateVariations); loanApplicationTerms.updateTotalInterestDue(Money.of(currency, loan.getSummary().getTotalInterestCharged())); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java index 2b9e1b77c..ad25cee25 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java @@ -20,10 +20,12 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain; import java.math.MathContext; import java.time.LocalDate; +import java.time.YearMonth; +import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; import java.util.ArrayList; import java.util.List; -import lombok.extern.slf4j.Slf4j; import net.fortuna.ical4j.model.Recur; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.organisation.holiday.domain.Holiday; @@ -42,7 +44,6 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.springframework.stereotype.Component; @Component -@Slf4j public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { @Override @@ -121,7 +122,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { Calendar currentCalendar = loanApplicationTerms.getLoanCalendar(); dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), lastRepaymentDate); - dueRepaymentPeriodDate = (LocalDate) CalendarUtils.adjustDate(dueRepaymentPeriodDate, loanApplicationTerms.getSeedDate(), + dueRepaymentPeriodDate = (LocalDate) adjustDate(dueRepaymentPeriodDate, loanApplicationTerms.getSeedDate(), loanApplicationTerms.getRepaymentPeriodFrequencyType()); if (currentCalendar != null) { // If we have currentCalendar object, this means there is a @@ -154,6 +155,21 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { return dueRepaymentPeriodDate; } + /* + * NOTE: This method a copy of CalendarUtils.adjustDate() method. Purpose of this method copy here is to eliminate + * the whole "ical4j" dependency for the small stateless embeddable progressive loan jar. Registered exception + * brings the whole ical4j deps. + */ + private Temporal adjustDate(final Temporal date, final Temporal seedDate, final PeriodFrequencyType frequencyType) { + if (frequencyType.isMonthly() && seedDate.get(ChronoField.DAY_OF_MONTH) > 28 && date.get(ChronoField.DAY_OF_MONTH) >= 28) { + int noOfDaysInCurrentMonth = YearMonth.from(date).lengthOfMonth(); + int seedDay = seedDate.get(ChronoField.DAY_OF_MONTH); + int adjustedDay = Math.min(noOfDaysInCurrentMonth, seedDay); + return date.with(ChronoField.DAY_OF_MONTH, adjustedDay); + } + return date; + } + @Override public LocalDate generateNextRepaymentDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms, final boolean isFirstRepayment, final Integer periodNumber) { @@ -305,7 +321,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { case INVALID: break; case WHOLE_TERM: - log.error("TODO Implement getRepaymentPeriodDate for WHOLE_TERM"); + // TODO: Implement getRepaymentPeriodDate for WHOLE_TERM break; } return dueRepaymentPeriodDate; @@ -347,7 +363,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { case INVALID: break; case WHOLE_TERM: - log.error("TODO Implement isDateFallsInSchedule for WHOLE_TERM"); + // TODO: Implement getRepaymentPeriodDate for WHOLE_TERM break; } return isScheduledDate; @@ -384,7 +400,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { case INVALID: break; case WHOLE_TERM: - log.error("TODO Implement repaymentPeriodFrequencyType for WHOLE_TERM"); + // TODO: Implement getRepaymentPeriodDate for WHOLE_TERM break; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java index f9a3373e1..aff822083 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java @@ -27,11 +27,9 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; -import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper; @@ -48,21 +46,22 @@ import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour; +import org.apache.fineract.portfolio.loanproduct.data.LoanProductRelatedDetailMinimumData; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; import org.apache.fineract.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod; import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes; import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType; import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType; -@Slf4j public final class LoanApplicationTerms { - private ApplicationCurrency currency; + private CurrencyData currency; private Calendar loanCalendar; private Integer loanTermFrequency; @@ -268,7 +267,7 @@ public final class LoanApplicationTerms { public static class Builder { - private ApplicationCurrency currency; + private CurrencyData currency; private Integer loanTermFrequency; private PeriodFrequencyType loanTermPeriodFrequencyType; private Integer numberOfRepayments; @@ -292,7 +291,7 @@ public final class LoanApplicationTerms { private LocalDate seedDate; private MathContext mc; - public Builder currency(ApplicationCurrency currency) { + public Builder currency(CurrencyData currency) { this.currency = currency; return this; } @@ -408,7 +407,7 @@ public final class LoanApplicationTerms { } public static LoanApplicationTerms assembleFrom(LoanRepaymentScheduleModelData modelData, MathContext mc) { - Money principal = Money.of(modelData.currency().toData(), modelData.disbursementAmount(), mc); + Money principal = Money.of(modelData.currency(), modelData.disbursementAmount(), mc); BigDecimal downPaymentPercentage = modelData.downPaymentPercentage(); LocalDate seedDate; @@ -428,12 +427,12 @@ public final class LoanApplicationTerms { .annualNominalInterestRate(modelData.annualNominalInterestRate()).principal(principal) .expectedDisbursementDate(modelData.disbursementDate()).repaymentsStartingFromDate(modelData.scheduleGenerationStartDate()) .daysInMonthType(modelData.daysInMonth()).daysInYearType(modelData.daysInYear()).fixedLength(modelData.fixedLength()) - .inArrearsTolerance(Money.zero(modelData.currency().toData(), mc)).disbursementDatas(new ArrayList<>()) + .inArrearsTolerance(Money.zero(modelData.currency(), mc)).disbursementDatas(new ArrayList<>()) .isDownPaymentEnabled(modelData.downPaymentEnabled()).downPaymentPercentage(downPaymentPercentage) .submittedOnDate(modelData.scheduleGenerationStartDate()).seedDate(seedDate).mc(mc).build(); } - public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency, + public static LoanApplicationTerms assembleFrom(final CurrencyData currency, final Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType, final AmortizationMethod amortizationMethod, final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod, @@ -484,7 +483,7 @@ public final class LoanApplicationTerms { } - public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency, + public static LoanApplicationTerms assembleFrom(final CurrencyData currency, final Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, NthDayType nthDay, DayOfWeekType dayOfWeek, final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate, final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance, @@ -537,27 +536,26 @@ public final class LoanApplicationTerms { LoanScheduleType loanScheduleType = loanProductRelatedDetail.getLoanScheduleType(); LoanScheduleProcessingType loanScheduleProcessingType = loanProductRelatedDetail.getLoanScheduleProcessingType(); final Integer fixedLength = loanProductRelatedDetail.getFixedLength(); - return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, - repaymentEvery, repaymentPeriodFrequencyType, ((nthDay != null) ? nthDay.getValue() : null), dayOfWeek, amortizationMethod, - interestMethod, interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, - interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, principalMoney, expectedDisbursementDate, - repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, - recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, - inArrearsTolerance, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, - loanProductRelatedDetail.getGraceOnArrearsAgeing(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled, - rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance, - compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, - loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper, - isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, - allowCompoundingOnEod, isEqualAmortization, isFirstRepaymentDateAllowedOnHoliday, - isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment, - isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, disbursedAmountPercentageForDownPayment, - isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType, - fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(), + return new LoanApplicationTerms(currency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery, + repaymentPeriodFrequencyType, ((nthDay != null) ? nthDay.getValue() : null), dayOfWeek, amortizationMethod, interestMethod, + interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, + allowPartialPeriodInterestCalcualtion, principalMoney, expectedDisbursementDate, repaymentsStartingFromDate, + calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, + graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount, + disbursementDatas, maxOutstandingBalance, loanProductRelatedDetail.getGraceOnArrearsAgeing(), daysInMonthType, + daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, + recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, + installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, + calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, + isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, + isFirstRepaymentDateAllowedOnHoliday, isInterestToBeRecoveredFirstWhenGreaterThanEMI, + fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, + disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, + loanScheduleType, loanScheduleProcessingType, fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(), loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour()); } - private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency, + private LoanApplicationTerms(final CurrencyData currency, final Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer nthDay, final DayOfWeekType weekDayType, final AmortizationMethod amortizationMethod, final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod, @@ -830,7 +828,7 @@ public final class LoanApplicationTerms { case INVALID: break; case WHOLE_TERM: - log.error("TODO Implement getPeriodEndDate for WHOLE_TERM"); + // TODO: Implement getPeriodEndDate for WHOLE_TERM break; } return dueRepaymentPeriodDate; @@ -1249,7 +1247,7 @@ public final class LoanApplicationTerms { periodicInterestRate = oneDayOfYearInterestRate.multiply(numberOfDaysInPeriod, mc); break; case WHOLE_TERM: - log.error("TODO Implement periodicInterestRate for WHOLE_TERM"); + // TODO: Implement getPeriodEndDate for WHOLE_TERM break; } break; @@ -1533,8 +1531,8 @@ public final class LoanApplicationTerms { } public LoanProductRelatedDetail toLoanProductRelatedDetail() { - final MonetaryCurrency currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDecimalPlaces(), - this.currency.getCurrencyInMultiplesOf()); + final CurrencyData currency = new CurrencyData(this.currency.getCode(), this.currency.getDecimalPlaces(), + this.currency.getInMultiplesOf()); return LoanProductRelatedDetail.createFrom(currency, this.principal.getAmount(), this.interestRatePerPeriod, this.interestRatePeriodFrequencyType, this.annualNominalInterestRate, this.interestMethod, @@ -1548,6 +1546,15 @@ public final class LoanApplicationTerms { this.chargeOffBehaviour); } + public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() { + final CurrencyData currency = new CurrencyData(this.currency.getCode(), this.currency.getDecimalPlaces(), + this.currency.getInMultiplesOf()); + return new LoanProductRelatedDetailMinimumData(currency, principal, inArrearsTolerance, interestRatePerPeriod, + annualNominalInterestRate, interestChargingGrace, interestPaymentGrace, principalGrace, + recurringMoratoriumOnPrincipalPeriods, interestMethod, interestCalculationPeriodMethod, daysInYearType, daysInMonthType, + amortizationMethod, repaymentPeriodFrequencyType, repaymentEvery, numberOfRepayments); + } + public Integer getLoanTermFrequency() { return this.loanTermFrequency; } @@ -1592,8 +1599,8 @@ public final class LoanApplicationTerms { return this.amortizationMethod; } - public MonetaryCurrency getCurrency() { - return this.principal.getCurrency(); + public CurrencyData getCurrency() { + return currency; } public Integer getNumberOfRepayments() { @@ -1787,10 +1794,6 @@ public final class LoanApplicationTerms { this.totalInterestDue = totalInterestDue; } - public ApplicationCurrency getApplicationCurrency() { - return this.currency; - } - public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() { return this.interestCalculationPeriodMethod; } @@ -2074,7 +2077,7 @@ public final class LoanApplicationTerms { case INVALID: break; case WHOLE_TERM: - log.error("TODO Implement repaymentPeriodFrequencyType for WHOLE_TERM"); + // TODO: Implement getPeriodEndDate for WHOLE_TERM break; } return maxDateForFixedLength; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanRepaymentScheduleModelData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanRepaymentScheduleModelData.java index 9695fe979..3a1894d30 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanRepaymentScheduleModelData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanRepaymentScheduleModelData.java @@ -22,11 +22,11 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.math.BigDecimal; import java.time.LocalDate; -import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.common.domain.DaysInMonthType; import org.apache.fineract.portfolio.common.domain.DaysInYearType; -public record LoanRepaymentScheduleModelData(@NotNull LocalDate scheduleGenerationStartDate, @NotNull ApplicationCurrency currency, +public record LoanRepaymentScheduleModelData(@NotNull LocalDate scheduleGenerationStartDate, @NotNull CurrencyData currency, @NotNull BigDecimal disbursementAmount, @NotNull LocalDate disbursementDate, @NotNull int numberOfRepayments, @NotNull int repaymentFrequency, @NotBlank String repaymentFrequencyType, @NotNull BigDecimal annualNominalInterestRate, @NotNull boolean downPaymentEnabled, @NotNull DaysInMonthType daysInMonth, @NotNull DaysInYearType daysInYear, diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java index ee484bfcc..6f28c5376 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.List; import lombok.Getter; import org.apache.fineract.organisation.monetary.data.CurrencyData; -import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData; @@ -36,7 +35,7 @@ public final class LoanScheduleModel { @Getter private final List periods; - private final ApplicationCurrency applicationCurrency; + private final CurrencyData currency; private final int loanTermInDays; private final Money totalPrincipalDisbursed; private final BigDecimal totalPrincipalExpected; @@ -49,20 +48,19 @@ public final class LoanScheduleModel { private final BigDecimal totalRepaymentExpected; private final BigDecimal totalOutstanding; - public static LoanScheduleModel from(final List periods, final ApplicationCurrency applicationCurrency, - final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected, - final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged, - final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) { + public static LoanScheduleModel from(final List periods, final CurrencyData currency, final int loanTermInDays, + final Money principalDisbursed, final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid, + final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, + final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) { - return new LoanScheduleModel(periods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected, - totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected, - totalOutstanding); + return new LoanScheduleModel(periods, currency, loanTermInDays, principalDisbursed, totalPrincipalExpected, totalPrincipalPaid, + totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected, totalOutstanding); } public static LoanScheduleModel withOverdueChargeUpdation(final List periods, final LoanScheduleModel loanScheduleModel, final BigDecimal totalPenaltyChargesCharged) { - return new LoanScheduleModel(periods, loanScheduleModel.applicationCurrency, loanScheduleModel.loanTermInDays, + return new LoanScheduleModel(periods, loanScheduleModel.currency, loanScheduleModel.loanTermInDays, loanScheduleModel.totalPrincipalDisbursed, loanScheduleModel.totalPrincipalExpected, loanScheduleModel.totalPrincipalPaid, loanScheduleModel.totalInterestCharged, loanScheduleModel.totalFeeChargesCharged, totalPenaltyChargesCharged, loanScheduleModel.totalRepaymentExpected, loanScheduleModel.totalOutstanding); @@ -71,18 +69,18 @@ public final class LoanScheduleModel { public static LoanScheduleModel withLoanScheduleModelPeriods(final List periods, final LoanScheduleModel loanScheduleModel) { - return new LoanScheduleModel(periods, loanScheduleModel.applicationCurrency, loanScheduleModel.loanTermInDays, + return new LoanScheduleModel(periods, loanScheduleModel.currency, loanScheduleModel.loanTermInDays, loanScheduleModel.totalPrincipalDisbursed, loanScheduleModel.totalPrincipalExpected, loanScheduleModel.totalPrincipalPaid, loanScheduleModel.totalInterestCharged, loanScheduleModel.totalFeeChargesCharged, loanScheduleModel.totalPenaltyChargesCharged, loanScheduleModel.totalRepaymentExpected, loanScheduleModel.totalOutstanding); } - private LoanScheduleModel(final List periods, final ApplicationCurrency applicationCurrency, - final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected, - final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged, - final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) { + private LoanScheduleModel(final List periods, final CurrencyData currency, final int loanTermInDays, + final Money principalDisbursed, final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid, + final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, + final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) { this.periods = periods; - this.applicationCurrency = applicationCurrency; + this.currency = currency; this.loanTermInDays = loanTermInDays; this.totalPrincipalDisbursed = principalDisbursed; this.totalPrincipalExpected = totalPrincipalExpected; @@ -95,11 +93,6 @@ public final class LoanScheduleModel { } public LoanScheduleData toData() { - - final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal(); - final Integer inMultiplesOf = this.totalPrincipalDisbursed.getCurrencyInMultiplesOf(); - final CurrencyData currency = this.applicationCurrency.toData(decimalPlaces, inMultiplesOf); - final BigDecimal totalCredits = BigDecimal.ZERO; final List periodsData = new ArrayList<>(); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ScheduleCurrentPeriodParams.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ScheduleCurrentPeriodParams.java index 58ac9a9b1..5d6822b85 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ScheduleCurrentPeriodParams.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ScheduleCurrentPeriodParams.java @@ -19,7 +19,7 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain; import java.math.BigDecimal; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.Money; public class ScheduleCurrentPeriodParams { @@ -36,11 +36,11 @@ public class ScheduleCurrentPeriodParams { boolean isEmiAmountChanged; BigDecimal interestCalculationGraceOnRepaymentPeriodFraction; - ScheduleCurrentPeriodParams(final MonetaryCurrency currency) { + ScheduleCurrentPeriodParams(final CurrencyData currency) { this(currency, BigDecimal.ZERO); } - ScheduleCurrentPeriodParams(final MonetaryCurrency currency, BigDecimal interestCalculationGraceOnRepaymentPeriodFraction) { + ScheduleCurrentPeriodParams(final CurrencyData currency, BigDecimal interestCalculationGraceOnRepaymentPeriodFraction) { this.earlyPaidAmount = Money.zero(currency); this.lastInstallment = null; this.skipCurrentLoop = false; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleModel.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleModel.java deleted file mode 100644 index f9ec64f97..000000000 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleModel.java +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collection; -import org.apache.fineract.organisation.monetary.data.CurrencyData; -import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; -import org.apache.fineract.organisation.monetary.domain.Money; -import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData; -import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData; -import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory; - -public final class LoanRescheduleModel { - - private final Collection periods; - private final Collection oldPeriods; - private final ApplicationCurrency applicationCurrency; - private final int loanTermInDays; - private final Money totalPrincipalDisbursed; - private final BigDecimal totalPrincipalExpected; - private final BigDecimal totalPrincipalPaid; - private final BigDecimal totalInterestCharged; - private final BigDecimal totalFeeChargesCharged; - private final BigDecimal totalPenaltyChargesCharged; - private final BigDecimal totalRepaymentExpected; - private final BigDecimal totalOutstanding; - - private LoanRescheduleModel(final Collection periods, - final Collection oldPeriods, final ApplicationCurrency applicationCurrency, - final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected, - final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged, - final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) { - this.periods = periods; - this.oldPeriods = oldPeriods; - this.applicationCurrency = applicationCurrency; - this.loanTermInDays = loanTermInDays; - this.totalPrincipalDisbursed = principalDisbursed; - this.totalPrincipalExpected = totalPrincipalExpected; - this.totalPrincipalPaid = totalPrincipalPaid; - this.totalInterestCharged = totalInterestCharged; - this.totalFeeChargesCharged = totalFeeChargesCharged; - this.totalPenaltyChargesCharged = totalPenaltyChargesCharged; - this.totalRepaymentExpected = totalRepaymentExpected; - this.totalOutstanding = totalOutstanding; - } - - public static LoanRescheduleModel instance(final Collection periods, - final Collection oldPeriods, final ApplicationCurrency applicationCurrency, - final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected, - final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged, - final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) { - - return new LoanRescheduleModel(periods, oldPeriods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected, - totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected, - totalOutstanding); - } - - public static LoanRescheduleModel createWithSchedulehistory(LoanRescheduleModel loanRescheduleModel, - final Collection oldPeriods) { - - return new LoanRescheduleModel(loanRescheduleModel.periods, oldPeriods, loanRescheduleModel.applicationCurrency, - loanRescheduleModel.loanTermInDays, loanRescheduleModel.totalPrincipalDisbursed, loanRescheduleModel.totalPrincipalExpected, - loanRescheduleModel.totalPrincipalPaid, loanRescheduleModel.totalInterestCharged, - loanRescheduleModel.totalFeeChargesCharged, loanRescheduleModel.totalPenaltyChargesCharged, - loanRescheduleModel.totalRepaymentExpected, loanRescheduleModel.totalOutstanding); - } - - public LoanScheduleData toData() { - - final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal(); - final Integer inMultiplesOf = this.totalPrincipalDisbursed.getCurrencyInMultiplesOf(); - final CurrencyData currency = this.applicationCurrency.toData(decimalPlaces, inMultiplesOf); - - final BigDecimal totalCredits = BigDecimal.ZERO; - - final Collection periodsData = new ArrayList<>(); - for (final LoanRescheduleModalPeriod modelPeriod : this.periods) { - periodsData.add(modelPeriod.toData()); - } - - final BigDecimal totalWaived = null; - final BigDecimal totalWrittenOff = null; - final BigDecimal totalRepayment = null; - final BigDecimal totalPaidInAdvance = null; - final BigDecimal totalPaidLate = null; - - return new LoanScheduleData(currency, periodsData, this.loanTermInDays, this.totalPrincipalDisbursed.getAmount(), - this.totalPrincipalExpected, this.totalPrincipalPaid, this.totalInterestCharged, this.totalFeeChargesCharged, - this.totalPenaltyChargesCharged, totalWaived, totalWrittenOff, this.totalRepaymentExpected, totalRepayment, - totalPaidInAdvance, totalPaidLate, this.totalOutstanding, totalCredits); - } - - public Collection getPeriods() { - return this.periods; - } - - public Collection getOldPeriods() { - return this.oldPeriods; - } -} diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductRelatedDetailMinimumData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductRelatedDetailMinimumData.java new file mode 100644 index 000000000..175d7d605 --- /dev/null +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductRelatedDetailMinimumData.java @@ -0,0 +1,186 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanproduct.data; + +import java.math.BigDecimal; +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.portfolio.common.domain.DaysInMonthType; +import org.apache.fineract.portfolio.common.domain.DaysInYearType; +import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; +import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; +import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; +import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; + +public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRepaymentScheduleRelatedDetail { + + private final CurrencyData currency; + + private final Money principal; + private final Money inArrearsTolerance; + + private final BigDecimal interestRatePerPeriod; + private final BigDecimal annualNominalInterestRate; + + private final Integer interestChargingGrace; + private final Integer interestPaymentGrace; + private final Integer principalGrace; + private final Integer recurringMoratoriumOnPrincipalPeriods; + + private final InterestMethod interestMethod; + private final InterestCalculationPeriodMethod interestCalculationPeriodMethod; + + private final DaysInYearType daysInYearType; + private final DaysInMonthType daysInMonthType; + + private final AmortizationMethod amortizationMethod; + + private final PeriodFrequencyType repaymentPeriodFrequencyType; + private final Integer repaymentEvery; + private final Integer numberOfRepayments; + + public LoanProductRelatedDetailMinimumData(CurrencyData currency, Money principal, Money inArrearsTolerance, + BigDecimal interestRatePerPeriod, BigDecimal annualNominalInterestRate, Integer interestChargingGrace, + Integer interestPaymentGrace, Integer principalGrace, Integer recurringMoratoriumOnPrincipalPeriods, + InterestMethod interestMethod, InterestCalculationPeriodMethod interestCalculationPeriodMethod, DaysInYearType daysInYearType, + DaysInMonthType daysInMonthType, AmortizationMethod amortizationMethod, PeriodFrequencyType repaymentPeriodFrequencyType, + Integer repaymentEvery, Integer numberOfRepayments) { + this.currency = currency; + this.principal = principal; + this.inArrearsTolerance = inArrearsTolerance; + this.interestRatePerPeriod = interestRatePerPeriod; + this.annualNominalInterestRate = annualNominalInterestRate; + this.interestChargingGrace = defaultToNullIfZero(interestChargingGrace); + this.interestPaymentGrace = defaultToNullIfZero(interestPaymentGrace); + this.principalGrace = defaultToNullIfZero(principalGrace); + this.recurringMoratoriumOnPrincipalPeriods = recurringMoratoriumOnPrincipalPeriods; + this.interestMethod = interestMethod; + this.interestCalculationPeriodMethod = interestCalculationPeriodMethod; + this.daysInYearType = daysInYearType; + this.daysInMonthType = daysInMonthType; + this.amortizationMethod = amortizationMethod; + this.repaymentPeriodFrequencyType = repaymentPeriodFrequencyType; + this.repaymentEvery = repaymentEvery; + this.numberOfRepayments = numberOfRepayments; + } + + private Integer defaultToNullIfZero(final Integer value) { + Integer defaultTo = value; + if (Integer.valueOf(0).equals(value)) { + defaultTo = null; + } + return defaultTo; + } + + @Override + public CurrencyData getCurrencyData() { + return currency; + } + + @Override + public Money getPrincipal() { + return principal; + } + + @Override + public Integer getGraceOnInterestCharged() { + return interestChargingGrace; + } + + @Override + public Integer getGraceOnInterestPayment() { + return interestPaymentGrace; + } + + @Override + public Integer getGraceOnPrincipalPayment() { + return principalGrace; + } + + @Override + public Integer getRecurringMoratoriumOnPrincipalPeriods() { + return recurringMoratoriumOnPrincipalPeriods; + } + + @Override + public Money getInArrearsTolerance() { + return inArrearsTolerance; + } + + @Override + public BigDecimal getNominalInterestRatePerPeriod() { + return interestRatePerPeriod; + } + + @Override + public PeriodFrequencyType getInterestPeriodFrequencyType() { + return repaymentPeriodFrequencyType; + } + + @Override + public BigDecimal getAnnualNominalInterestRate() { + return annualNominalInterestRate; + } + + @Override + public InterestMethod getInterestMethod() { + return interestMethod; + } + + @Override + public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() { + return interestCalculationPeriodMethod; + } + + @Override + public Integer getRepayEvery() { + return repaymentEvery; + } + + @Override + public PeriodFrequencyType getRepaymentPeriodFrequencyType() { + return repaymentPeriodFrequencyType; + } + + @Override + public Integer getNumberOfRepayments() { + return numberOfRepayments; + } + + @Override + public AmortizationMethod getAmortizationMethod() { + return amortizationMethod; + } + + @Override + public Integer getGraceOnArrearsAgeing() { + return 0; + } + + @Override + public Integer getDaysInMonthType() { + return daysInMonthType.getValue(); + } + + @Override + public Integer getDaysInYearType() { + return daysInYearType.getValue(); + } +} diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinimumRepaymentScheduleRelatedDetail.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinimumRepaymentScheduleRelatedDetail.java index 5ec2acdaa..2e3f84089 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinimumRepaymentScheduleRelatedDetail.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinimumRepaymentScheduleRelatedDetail.java @@ -19,7 +19,7 @@ package org.apache.fineract.portfolio.loanproduct.domain; import java.math.BigDecimal; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; @@ -28,7 +28,7 @@ import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; */ public interface LoanProductMinimumRepaymentScheduleRelatedDetail { - MonetaryCurrency getCurrency(); + CurrencyData getCurrencyData(); Money getPrincipal(); @@ -61,4 +61,8 @@ public interface LoanProductMinimumRepaymentScheduleRelatedDetail { AmortizationMethod getAmortizationMethod(); Integer getGraceOnArrearsAgeing(); + + Integer getDaysInMonthType(); + + Integer getDaysInYearType(); } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java index e65a78fca..5dc7ecafd 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java @@ -31,6 +31,7 @@ import java.util.Map; import lombok.Getter; import lombok.Setter; import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.common.domain.DaysInMonthType; @@ -164,7 +165,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche @Enumerated(EnumType.STRING) private LoanChargeOffBehaviour chargeOffBehaviour; - public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal, + public static LoanProductRelatedDetail createFrom(final CurrencyData currencyData, final BigDecimal principal, final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final boolean allowPartialPeriodInterestCalcualtion, @@ -179,6 +180,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final boolean enableAccrualActivityPosting, final List supportedInterestRefundTypes, final LoanChargeOffBehaviour chargeOffBehaviour) { + final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData); return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, graceOnPrincipalPayment, @@ -252,19 +254,23 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche return defaultTo; } - @Override public MonetaryCurrency getCurrency() { return this.currency.copy(); } + @Override + public CurrencyData getCurrencyData() { + return currency.toData(); + } + @Override public Money getPrincipal() { - return Money.of(this.currency, this.principal); + return Money.of(getCurrencyData(), this.principal); } @Override public Money getInArrearsTolerance() { - return Money.of(this.currency, this.inArrearsTolerance); + return Money.of(getCurrencyData(), this.inArrearsTolerance); } // TODO: REVIEW diff --git a/fineract-progressive-loan-embeddable-schedule-generator/README.md b/fineract-progressive-loan-embeddable-schedule-generator/README.md new file mode 100644 index 000000000..81e67c4be --- /dev/null +++ b/fineract-progressive-loan-embeddable-schedule-generator/README.md @@ -0,0 +1,63 @@ +# Fineract Progressive Loan Embeddable Schedule Generator + +## Build + +- Generate Embeddable Progressive Schedule Generator Jar + + ```shell + ./gradlew :fineract-progressive-loan-embeddable-schedule-generator:shadowJar + ``` + +- Copy Jar from `fineract-progressive-loan-embeddable-schedule-generator/build/libs/fineract-progressive-loan-embeddable-schedule-generator-*-SNAPSHOT-all.jar` to Your class path. + +## Dependencies + +There is no extra dependency. + + +## Sample Application + +Copy a [Main.java](misc/Main.java) file from the `misc` directory into the working directory: + +The project directory structure: + + - `Main.java` + - `fineract-embeddable-calculator-1.11.0-SNAPSHOT-all.jar` + + +- Check Java minimum version + + ```shell + java -version # openjdk version "17.0.3" 2022-04-19 LTS + ``` + +- Compile the source + + ```shell + javac -cp fineract-progressive-loan-embeddable-schedule-generator-1.11.0-SNAPSHOT-all.jar Main.java + ``` + +- Run with dependencies: + + ```shell + java -cp fineract-progressive-loan-embeddable-schedule-generator-1.11.0-SNAPSHOT-all.jar:. Main + ``` + +This code has the following output: + +``` +#------ Loan Schedule -----------------# + Number of Periods: 6 + Loan Term in Days: 182 + Total Disbursed Amount: 100.00 + Total Interest Amount: 2.05 + Total Repayment Amount: 102.05 +#------ Repayment Schedule ------------# + Disbursement - Date: 2024-01-01, Amount: 100.00 + Repayment Period: #1, Due Date: 2024-02-01, Balance: 83.57, Principal: 16.43, Interest: 0.58, Total: 17.01 + Repayment Period: #2, Due Date: 2024-03-01, Balance: 67.05, Principal: 16.52, Interest: 0.49, Total: 17.01 + Repayment Period: #3, Due Date: 2024-04-01, Balance: 50.43, Principal: 16.62, Interest: 0.39, Total: 17.01 + Repayment Period: #4, Due Date: 2024-05-01, Balance: 33.71, Principal: 16.72, Interest: 0.29, Total: 17.01 + Repayment Period: #5, Due Date: 2024-06-01, Balance: 16.90, Principal: 16.81, Interest: 0.20, Total: 17.01 + Repayment Period: #6, Due Date: 2024-07-01, Balance: 0.00, Principal: 16.90, Interest: 0.10, Total: 17.00 +``` diff --git a/fineract-progressive-loan-embeddable-schedule-generator/build.gradle b/fineract-progressive-loan-embeddable-schedule-generator/build.gradle new file mode 100644 index 000000000..b150208eb --- /dev/null +++ b/fineract-progressive-loan-embeddable-schedule-generator/build.gradle @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +description = 'Fineract Progressive Loan Embeddable Schedule Generator' + +apply plugin: 'com.gradleup.shadow' +apply plugin: 'java' +apply from: 'dependencies.gradle' + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +var requiredModuleNames = [ + 'fineract-core', + 'fineract-loan', + 'fineract-progressive-loan', +] + +tasks.named('shadowJar', ShadowJar) { + dependencies { + exclude((dep) -> !requiredModuleNames.any( (reqModName) -> dep.moduleName.contains(reqModName))) + } + minimize() { + exclude("*.xml") + } +} diff --git a/fineract-progressive-loan-embeddable-schedule-generator/dependencies.gradle b/fineract-progressive-loan-embeddable-schedule-generator/dependencies.gradle new file mode 100644 index 000000000..066464554 --- /dev/null +++ b/fineract-progressive-loan-embeddable-schedule-generator/dependencies.gradle @@ -0,0 +1,26 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +dependencies { + implementation(project(path: ':fineract-progressive-loan')) + implementation(project(path: ':fineract-loan')) + + annotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' +} diff --git a/fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java b/fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java new file mode 100644 index 000000000..eef822b2c --- /dev/null +++ b/fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java @@ -0,0 +1,84 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.portfolio.common.domain.DaysInMonthType; +import org.apache.fineract.portfolio.common.domain.DaysInYearType; +import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlan; +import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanDisbursementPeriod; +import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanDownPaymentPeriod; +import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanPeriod; +import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanRepaymentPeriod; +import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.EmbeddableProgressiveLoanScheduleGenerator; +import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleModelData; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.time.LocalDate; + +/* + * NOTE: This file used by a CI job to test Progressive Loan Embedded Jar compiled successfully and runs smoothly. + */ +public class Main { + public static void main(String[] args) throws InterruptedException { + MathContext mc = new MathContext(12, RoundingMode.HALF_UP); + EmbeddableProgressiveLoanScheduleGenerator calculator = new EmbeddableProgressiveLoanScheduleGenerator(); + + final CurrencyData currency = new CurrencyData("usd", "US Dollar", 2, null, "usd", "$"); + final LocalDate startDate = LocalDate.of(2024, 1, 1); + final LocalDate disbursementDate = LocalDate.of(2024, 1, 1); + final BigDecimal disbursedAmount = BigDecimal.valueOf(100); + + final int noRepayments = 6; + final int repaymentFrequency = 1; + final String repaymentFrequencyType = "MONTHS"; + final BigDecimal downPaymentPercentage = args.length > 0 ? new BigDecimal(args[0]) : BigDecimal.ZERO; + final boolean isDownPaymentEnabled = BigDecimal.ZERO.compareTo(downPaymentPercentage) != 0; + final BigDecimal annualNominalInterestRate = BigDecimal.valueOf(7.0); + final DaysInMonthType daysInMonthType = DaysInMonthType.DAYS_30; + final DaysInYearType daysInYearType = DaysInYearType.DAYS_360; + final Integer installmentAmountInMultiplesOf = null; + final Integer fixedLength = null; + + var config = new LoanRepaymentScheduleModelData(startDate, currency, disbursedAmount, disbursementDate, noRepayments, repaymentFrequency, repaymentFrequencyType, annualNominalInterestRate, isDownPaymentEnabled, daysInMonthType, daysInYearType, downPaymentPercentage, installmentAmountInMultiplesOf, fixedLength); + + final LoanSchedulePlan plan = calculator.generate(mc, config); + printPlan(plan); + } + + static void printPlan(final LoanSchedulePlan plan) throws InterruptedException { + System.out.println("#------ Loan Schedule -----------------#"); + System.out.printf(" Number of Periods: %d%n", plan.getPeriods().stream().filter(period -> !(period instanceof LoanSchedulePlanDisbursementPeriod)).count()); + System.out.printf(" Loan Term in Days: %d%n", plan.getLoanTermInDays()); + System.out.printf(" Total Disbursed Amount: %s%n", plan.getTotalDisbursedAmount()); + System.out.printf(" Total Interest Amount: %s%n", plan.getTotalInterestAmount()); + System.out.printf(" Total Repayment Amount: %s%n", plan.getTotalRepaymentAmount()); + System.out.println("#------ Repayment Schedule ------------#"); + + for (LoanSchedulePlanPeriod period : plan.getPeriods()) { + if (period instanceof LoanSchedulePlanDisbursementPeriod dp) { + System.out.printf(" Disbursement - Date: %s, Amount: %s%n", dp.periodDueDate(), dp.getPrincipalAmount()); + } if (period instanceof LoanSchedulePlanDownPaymentPeriod rp) { + System.out.printf(" Down payment Period: #%d, Due Date: %s, Balance: %s, Principal: %s, Total: %s%n", rp.periodNumber(), rp.periodDueDate(), rp.getOutstandingLoanBalance(), rp.getPrincipalAmount(), rp.getTotalDueAmount()); + } if (period instanceof LoanSchedulePlanRepaymentPeriod rp) { + System.out.printf(" Repayment Period: #%d, Due Date: %s, Balance: %s, Principal: %s, Interest: %s, Total: %s%n", rp.periodNumber(), rp.periodDueDate(), rp.getOutstandingLoanBalance(), rp.getPrincipalAmount(), rp.getInterestAmount(), rp.getTotalDueAmount()); + } + } + } +} diff --git a/fineract-progressive-loan-embeddable-schedule-generator/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGenerator.java b/fineract-progressive-loan-embeddable-schedule-generator/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGenerator.java new file mode 100644 index 000000000..1b7774976 --- /dev/null +++ b/fineract-progressive-loan-embeddable-schedule-generator/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGenerator.java @@ -0,0 +1,42 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanaccount.loanschedule.domain; + +import java.math.MathContext; +import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlan; +import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; +import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator; + +@SuppressWarnings("unused") +public class EmbeddableProgressiveLoanScheduleGenerator { + + private final ProgressiveLoanScheduleGenerator scheduleGenerator; + private final ScheduledDateGenerator scheduledDateGenerator; + private final EMICalculator emiCalculator; + + public EmbeddableProgressiveLoanScheduleGenerator() { + this.emiCalculator = new ProgressiveEMICalculator(); + this.scheduledDateGenerator = new DefaultScheduledDateGenerator(); + this.scheduleGenerator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator); + } + + public LoanSchedulePlan generate(final MathContext mc, final LoanRepaymentScheduleModelData modelData) { + return scheduleGenerator.generate(mc, modelData); + } +} diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java index 833311fd2..3ce525715 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java @@ -48,7 +48,7 @@ public class ProgressiveTransactionCtx extends TransactionCtx { Set charges, MoneyHolder overpaymentHolder, ChangedTransactionDetail changedTransactionDetail, ProgressiveLoanInterestScheduleModel model) { super(currency, installments, charges, overpaymentHolder, changedTransactionDetail); - sumOfInterestRefundAmount = model.getZero(); + sumOfInterestRefundAmount = model.zero(); this.model = model; } } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java index c950bc687..97bdeb54a 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java @@ -91,7 +91,7 @@ public class InterestPeriod implements Comparable { .multipliedBy(getRateFactorTillPeriodDueDate(), mc).getAmount() // .divide(BigDecimal.valueOf(lengthTillPeriodDueDate), mc) // .multiply(BigDecimal.valueOf(getLength()), mc); // - return Money.of(outstandingLoanBalance.getCurrency(), interestDueTillRepaymentDueDate, mc); + return Money.of(outstandingLoanBalance.getCurrencyData(), interestDueTillRepaymentDueDate, mc); } public long getLength() { diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePlan.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePlan.java index 8b82b91e9..cdffe2c7d 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePlan.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePlan.java @@ -75,7 +75,7 @@ public class LoanSchedulePlan { }); return new LoanSchedulePlan(periods, // - model.getApplicationCurrency().toData(), // + model.getCurrency(), // model.getLoanTermInDays(), // model.getTotalPrincipalDisbursed().getAmount(), // model.getTotalPrincipalExpected(), // diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java index 0dec32dcd..1bb00af61 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java @@ -34,7 +34,7 @@ import lombok.Data; import lombok.experimental.Accessors; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.organisation.monetary.domain.Money; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; @Data @Accessors(fluent = true) @@ -42,26 +42,31 @@ public class ProgressiveLoanInterestScheduleModel { private final List repaymentPeriods; private final TreeSet interestRates; - private final LoanProductRelatedDetail loanProductRelatedDetail; + private final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail; private final Integer installmentAmountInMultiplesOf; - private MathContext mc; + private final MathContext mc; + private final Money zero; - public ProgressiveLoanInterestScheduleModel(List repaymentPeriods, LoanProductRelatedDetail loanProductRelatedDetail, - Integer installmentAmountInMultiplesOf, MathContext mc) { + public ProgressiveLoanInterestScheduleModel(final List repaymentPeriods, + final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, + final MathContext mc) { this.repaymentPeriods = repaymentPeriods; this.interestRates = new TreeSet<>(Collections.reverseOrder()); this.loanProductRelatedDetail = loanProductRelatedDetail; this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; this.mc = mc; + this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc); } - private ProgressiveLoanInterestScheduleModel(List repaymentPeriods, final TreeSet interestRates, - LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc) { + private ProgressiveLoanInterestScheduleModel(final List repaymentPeriods, final TreeSet interestRates, + final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, + final MathContext mc) { this.mc = mc; this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods); this.interestRates = new TreeSet<>(interestRates); this.loanProductRelatedDetail = loanProductRelatedDetail; this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; + this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc); } public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) { @@ -197,30 +202,26 @@ public class ProgressiveLoanInterestScheduleModel { previousInterestPeriod.addDisbursementAmount(disbursedAmount); previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount); final InterestPeriod interestPeriod = new InterestPeriod(repaymentPeriod, previousInterestPeriod.getDueDate(), originalDueDate, - BigDecimal.ZERO, BigDecimal.ZERO, getZero(), getZero(), getZero(), mc); + BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, mc); repaymentPeriod.getInterestPeriods().add(interestPeriod); } - public Money getZero() { - return Money.zero(loanProductRelatedDetail.getCurrency(), mc); - } - public Money getTotalDueInterest() { return repaymentPeriods().stream().flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest)) - .reduce(getZero(), Money::plus); + .reduce(zero(), Money::plus); } public Money getTotalDuePrincipal() { return repaymentPeriods.stream().flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount)) - .reduce(getZero(), Money::plus); + .reduce(zero(), Money::plus); } public Money getTotalPaidInterest() { - return repaymentPeriods().stream().map(RepaymentPeriod::getPaidInterest).reduce(getZero(), Money::plus); + return repaymentPeriods().stream().map(RepaymentPeriod::getPaidInterest).reduce(zero, Money::plus); } public Money getTotalPaidPrincipal() { - return repaymentPeriods().stream().map(RepaymentPeriod::getPaidPrincipal).reduce(getZero(), Money::plus); + return repaymentPeriods().stream().map(RepaymentPeriod::getPaidPrincipal).reduce(zero, Money::plus); } public Optional findRepaymentPeriod(@NotNull LocalDate transactionDate) { diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java index 82bef12ab..ab58c4237 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java @@ -31,9 +31,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.core.service.MathUtil; -import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.data.DisbursementData; @@ -55,7 +54,6 @@ import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType; import org.springframework.stereotype.Component; -@Slf4j @Component @RequiredArgsConstructor public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { @@ -72,11 +70,10 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { public LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, final Set loanCharges, final HolidayDetailDTO holidayDetailDTO) { - final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency(); // determine the total charges due at time of disbursement final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges); - final MonetaryCurrency currency = loanApplicationTerms.getCurrency(); + final CurrencyData currency = loanApplicationTerms.getCurrency(); LocalDate periodStartDate = RepaymentStartDateType.DISBURSEMENT_DATE.equals(loanApplicationTerms.getRepaymentStartDateType()) ? loanApplicationTerms.getExpectedDisbursementDate() : loanApplicationTerms.getSubmittedOnDate(); @@ -92,7 +89,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { final List expectedRepaymentPeriods = scheduledDateGenerator.generateRepaymentPeriods(mc, periodStartDate, loanApplicationTerms, holidayDetailDTO); final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator.generatePeriodInterestScheduleModel( - expectedRepaymentPeriods, loanApplicationTerms.toLoanProductRelatedDetail(), + expectedRepaymentPeriods, loanApplicationTerms.toLoanProductRelatedDetailMinimumData(), loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc); final List periods = new ArrayList<>(expectedRepaymentPeriods.size()); @@ -141,7 +138,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { final BigDecimal totalPrincipalPaid = BigDecimal.ZERO; final BigDecimal totalOutstanding = BigDecimal.ZERO; - return LoanScheduleModel.from(periods, applicationCurrency, interestScheduleModel.getLoanTermInDays(), + return LoanScheduleModel.from(periods, currency, interestScheduleModel.getLoanTermInDays(), scheduleParams.getPrincipalToBeScheduled().plus(loanApplicationTerms.getDownPaymentAmount(), mc), scheduleParams.getTotalCumulativePrincipal().plus(loanApplicationTerms.getDownPaymentAmount(), mc).getAmount(), totalPrincipalPaid, scheduleParams.getTotalCumulativeInterest().getAmount(), @@ -308,7 +305,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { } private void applyChargesForCurrentPeriod(final LoanScheduleModelRepaymentPeriod repaymentPeriod, final Set loanCharges, - final LoanScheduleParams scheduleParams, final MonetaryCurrency currency, final MathContext mc) { + final LoanScheduleParams scheduleParams, final CurrencyData currency, final MathContext mc) { final PrincipalInterest principalInterest = new PrincipalInterest(repaymentPeriod.getPrincipalDue(), repaymentPeriod.getInterestDue(), null); @@ -325,10 +322,10 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { } private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set loanCharges, - final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed, + final CurrencyData currency, final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, final boolean isFirstPeriod, final MathContext mc) { - Money cumulative = Money.zero(monetaryCurrency, mc); + Money cumulative = Money.zero(currency, mc); if (loanCharges != null) { for (final LoanCharge loanCharge : loanCharges) { if (!loanCharge.isDueAtDisbursement() && loanCharge.isFeeCharge()) { @@ -358,11 +355,10 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { } private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, - final Set loanCharges, final MonetaryCurrency monetaryCurrency, - final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed, - final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, final boolean isFirstPeriod, - final MathContext mc) { - Money cumulative = Money.zero(monetaryCurrency, mc); + final Set loanCharges, final CurrencyData currency, final PrincipalInterest principalInterestForThisPeriod, + final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, + final boolean isFirstPeriod, final MathContext mc) { + Money cumulative = Money.zero(currency, mc); if (loanCharges != null) { for (final LoanCharge loanCharge : loanCharges) { if (loanCharge.isPenaltyCharge()) { @@ -409,7 +405,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { return cumulative; } - private void updatePeriodsWithCharges(final MonetaryCurrency currency, LoanScheduleParams scheduleParams, + private void updatePeriodsWithCharges(final CurrencyData currency, LoanScheduleParams scheduleParams, final Collection periods, final Set nonCompoundingCharges, MathContext mc) { for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) { if (loanScheduleModelPeriod.isRepaymentPeriod()) { diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java index 10f971448..3f8641600 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java @@ -31,18 +31,20 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.PeriodDueDeta import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.RepaymentPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; public interface EMICalculator { @NotNull ProgressiveLoanInterestScheduleModel generatePeriodInterestScheduleModel(@NotNull List periods, - @NotNull LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc); + @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, + MathContext mc); @NotNull ProgressiveLoanInterestScheduleModel generateInstallmentInterestScheduleModel( - @NotNull List installments, @NotNull LoanProductRelatedDetail loanProductRelatedDetail, - Integer installmentAmountInMultiplesOf, MathContext mc); + @NotNull List installments, + @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, + MathContext mc); Optional findRepaymentPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate dueDate); diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java index 25205779c..4e0383c31 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java @@ -43,7 +43,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.PeriodDueDeta import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.RepaymentPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.springframework.stereotype.Component; @Component @@ -56,8 +56,8 @@ public final class ProgressiveEMICalculator implements EMICalculator { @Override @NotNull public ProgressiveLoanInterestScheduleModel generatePeriodInterestScheduleModel(@NotNull List periods, - @NotNull LoanProductRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, - final MathContext mc) { + @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, + final Integer installmentAmountInMultiplesOf, final MathContext mc) { return generateInterestScheduleModel(periods, LoanScheduleModelRepaymentPeriod::periodFromDate, LoanScheduleModelRepaymentPeriod::periodDueDate, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); } @@ -65,7 +65,8 @@ public final class ProgressiveEMICalculator implements EMICalculator { @Override @NotNull public ProgressiveLoanInterestScheduleModel generateInstallmentInterestScheduleModel( - @NotNull List installments, @NotNull LoanProductRelatedDetail loanProductRelatedDetail, + @NotNull List installments, + @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, final MathContext mc) { installments = installments.stream().filter(installment -> !installment.isDownPayment() && !installment.isAdditional()).toList(); return generateInterestScheduleModel(installments, LoanRepaymentScheduleInstallment::getFromDate, @@ -74,9 +75,9 @@ public final class ProgressiveEMICalculator implements EMICalculator { @NotNull private ProgressiveLoanInterestScheduleModel generateInterestScheduleModel(@NotNull List periods, Function from, - Function to, @NotNull LoanProductRelatedDetail loanProductRelatedDetail, + Function to, @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, final MathContext mc) { - final Money zero = Money.zero(loanProductRelatedDetail.getCurrency(), mc); + final Money zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc); final AtomicReference prev = new AtomicReference<>(); List repaymentPeriods = periods.stream().map(e -> { RepaymentPeriod rp = new RepaymentPeriod(prev.get(), from.apply(e), to.apply(e), zero, mc); @@ -101,7 +102,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { @Override public void addDisbursement(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate disbursementDueDate, final Money disbursedAmount) { - scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(disbursementDueDate, disbursedAmount, scheduleModel.getZero()) + scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(disbursementDueDate, disbursedAmount, scheduleModel.zero()) .ifPresent((repaymentPeriod) -> calculateEMIValueAndRateFactors( getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, disbursementDueDate), scheduleModel)); } @@ -127,8 +128,8 @@ public final class ProgressiveEMICalculator implements EMICalculator { final LocalDate interestRateChangeEffectiveDate = newInterestSubmittedOnDate.minusDays(1); scheduleModel.addInterestRate(interestRateChangeEffectiveDate, newInterestRate); scheduleModel - .changeOutstandingBalanceAndUpdateInterestPeriods(interestRateChangeEffectiveDate, scheduleModel.getZero(), - scheduleModel.getZero()) + .changeOutstandingBalanceAndUpdateInterestPeriods(interestRateChangeEffectiveDate, scheduleModel.zero(), + scheduleModel.zero()) .ifPresent(repaymentPeriod -> calculateEMIValueAndRateFactors( getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, interestRateChangeEffectiveDate), scheduleModel)); } @@ -136,8 +137,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { @Override public void addBalanceCorrection(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate balanceCorrectionDate, Money balanceCorrectionAmount) { - scheduleModel - .changeOutstandingBalanceAndUpdateInterestPeriods(balanceCorrectionDate, scheduleModel.getZero(), balanceCorrectionAmount) + scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(balanceCorrectionDate, scheduleModel.zero(), balanceCorrectionAmount) .ifPresent(repaymentPeriod -> { calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel); calculateOutstandingBalance(scheduleModel); @@ -296,13 +296,13 @@ public final class ProgressiveEMICalculator implements EMICalculator { private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestScheduleModel scheduleModel) { MathContext mc = scheduleModel.mc(); - Money totalDueInterest = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest) - .reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 1.46 - Money totalEMI = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getEmi) - .reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 101.48 + Money totalDueInterest = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest).reduce(scheduleModel.zero(), + (m1, m2) -> m1.plus(m2, mc)); // 1.46 + Money totalEMI = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getEmi).reduce(scheduleModel.zero(), + (m1, m2) -> m1.plus(m2, mc)); // 101.48 Money totalDisbursedAmount = scheduleModel.repaymentPeriods().stream() .flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount)) - .reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 100 + .reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 100 Money diff = totalDisbursedAmount.plus(totalDueInterest, mc).minus(totalEMI, mc); Optional findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid()) @@ -400,7 +400,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { BigDecimal calculateRateFactorPerPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel, final RepaymentPeriod repaymentPeriod, final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate) { final MathContext mc = scheduleModel.mc(); - final LoanProductRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); + final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriodFromDate), scheduleModel.mc()); final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType()); @@ -493,7 +493,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { // TODO: double check final Money outstandingBalance = startPeriod.getInitialBalanceForEmiRecalculation(); - final Money equalMonthlyInstallment = Money.of(outstandingBalance.getCurrency(), + final Money equalMonthlyInstallment = Money.of(outstandingBalance.getCurrencyData(), calculateEMIValue(rateFactorN, outstandingBalance.getAmount(), fnResult, mc), mc); final Money finalEqualMonthlyInstallment = applyInstallmentAmountInMultiplesOf(scheduleModel, equalMonthlyInstallment); @@ -732,7 +732,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { return scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueDate) // .map(repaymentPeriodDueDate -> getDueAmounts(scheduleModel, repaymentPeriodDueDate, subjectDate) // .getDueInterest()) // - .reduce(scheduleModel.getZero(), Money::add); // + .reduce(scheduleModel.zero(), Money::add); // } private long getUncountablePeriods(final List relatedRepaymentPeriods, final Money originalEmi) { diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java index 2d23c01e0..4b7d1e98a 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java @@ -561,7 +561,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest { @Test public void calculateChargebackAllocationMap() { Map result; - MonetaryCurrency currency = mock(MonetaryCurrency.class); + MonetaryCurrency currency = new MonetaryCurrency("usd", 2, null); result = underTest.calculateChargebackAllocationMap(allocationMap(50.0, 100.0, 200.0, 12.0, currency), BigDecimal.valueOf(50.0), List.of(PRINCIPAL, INTEREST, FEE, PENALTY), currency); diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java index 3e42964e1..4a19f2fa8 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java @@ -24,6 +24,7 @@ import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.time.LocalDate; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; import org.apache.fineract.portfolio.common.domain.DaysInMonthType; import org.apache.fineract.portfolio.common.domain.DaysInYearType; @@ -38,6 +39,7 @@ class LoanScheduleGeneratorTest { private static final ProgressiveEMICalculator emiCalculator = new ProgressiveEMICalculator(); private static final ApplicationCurrency APPLICATION_CURRENCY = new ApplicationCurrency("USD", "USD", 2, 1, "USD", "$"); + private static final CurrencyData CURRENCY = APPLICATION_CURRENCY.toData(); private static final BigDecimal DISBURSEMENT_AMOUNT = BigDecimal.valueOf(192.22); private static final BigDecimal DISBURSEMENT_AMOUNT_100 = BigDecimal.valueOf(100); private static final BigDecimal NOMINAL_INTEREST_RATE = BigDecimal.valueOf(9.99); @@ -50,7 +52,7 @@ class LoanScheduleGeneratorTest { @Test void testGenerateLoanSchedule() { - LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), APPLICATION_CURRENCY, + LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), CURRENCY, DISBURSEMENT_AMOUNT, DISBURSEMENT_DATE, NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE, NOMINAL_INTEREST_RATE, false, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, null, null, null); @@ -85,7 +87,7 @@ class LoanScheduleGeneratorTest { @Test void testGenerateLoanScheduleWithDownPayment() { - LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), APPLICATION_CURRENCY, + LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), CURRENCY, DISBURSEMENT_AMOUNT_100, LocalDate.of(2024, 1, 1), NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE, NOMINAL_INTEREST_RATE, true, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, DOWN_PAYMENT_PORTION, null, null); diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java index a49824b7b..a60e7a537 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java @@ -26,8 +26,7 @@ import java.util.ArrayList; import java.util.List; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; -import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.common.domain.DaysInMonthType; @@ -39,7 +38,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.PeriodDueDeta import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.RepaymentPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -59,10 +58,10 @@ class ProgressiveEMICalculatorTest { private static MockedStatic threadLocalContextUtil = Mockito.mockStatic(ThreadLocalContextUtil.class); private static MockedStatic 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 LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = Mockito + .mock(LoanProductMinimumRepaymentScheduleRelatedDetail.class); - private static final MonetaryCurrency monetaryCurrency = MonetaryCurrency - .fromApplicationCurrency(new ApplicationCurrency("USD", "USD", 2, 1, "USD", "$")); + private static final CurrencyData currency = new CurrencyData("USD", "USD", 2, 1, "$", "USD"); private static List periods; private final BigDecimal interestRate = BigDecimal.valueOf(0.094822); @@ -166,7 +165,7 @@ class ProgressiveEMICalculatorTest { 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))); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -205,7 +204,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -249,7 +248,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -285,7 +284,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -321,7 +320,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -367,7 +366,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -403,7 +402,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14)); @@ -445,7 +444,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14)); @@ -492,7 +491,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14)); @@ -538,7 +537,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14)); @@ -584,7 +583,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15)); @@ -663,7 +662,7 @@ class ProgressiveEMICalculatorTest { Assertions.assertEquals(0.1, toDouble(repaymentDetails4th.getDueInterest())); // balance update on the last period, check the right interest interval split - emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 6, 10), Money.of(monetaryCurrency, BigDecimal.ZERO)); + emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 6, 10), Money.of(currency, BigDecimal.ZERO)); final RepaymentPeriod lastRepaymentPeriod = interestSchedule.repaymentPeriods().get(interestSchedule.repaymentPeriods().size() - 1); Assertions.assertEquals(2, lastRepaymentPeriod.getInterestPeriods().size()); Assertions.assertEquals(LocalDate.of(2024, 6, 1), lastRepaymentPeriod.getInterestPeriods().get(0).getFromDate()); @@ -691,7 +690,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15)); @@ -762,7 +761,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15)); @@ -841,7 +840,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -890,7 +889,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -939,7 +938,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -983,7 +982,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -1017,7 +1016,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -1050,7 +1049,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.WEEKS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -1083,7 +1082,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.WEEKS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(2); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -1116,7 +1115,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.INVALID.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.DAYS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(15); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -1148,7 +1147,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -1210,7 +1209,7 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); - Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generatePeriodInterestScheduleModel( expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); @@ -1266,7 +1265,7 @@ class ProgressiveEMICalculatorTest { } private static LoanScheduleModelRepaymentPeriod repayment(int periodNumber, LocalDate fromDate, LocalDate dueDate) { - final Money zeroAmount = Money.zero(monetaryCurrency); + final Money zeroAmount = Money.zero(currency); return LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate, dueDate, zeroAmount, zeroAmount, zeroAmount, zeroAmount, zeroAmount, zeroAmount, false, mc); } @@ -1326,6 +1325,6 @@ class ProgressiveEMICalculatorTest { } private static Money toMoney(final double value) { - return Money.of(monetaryCurrency, BigDecimal.valueOf(value)); + return Money.of(currency, BigDecimal.valueOf(value)); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index b6ddf56c0..84ab2a06a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -518,8 +518,8 @@ public class LoanScheduleAssembler { fixedLength = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(LoanProductConstants.FIXED_LENGTH, element); } - return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, - repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod, + return LoanApplicationTerms.assembleFrom(applicationCurrency.toData(), loanTermFrequency, loanTermPeriodFrequencyType, + numberOfRepayments, repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod, interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, principalMoney, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java index b0b451f1d..482ad8398 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java @@ -122,7 +122,7 @@ public class LoanUtilService { boolean isPrincipalCompoundingDisabledForOverdueLoans = this.configurationDomainService .isPrincipalCompoundingDisabledForOverdueLoans(); - ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency, + ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency.toData(), calculatedRepaymentsStartingFromDate, holidayDetails, restCalendarInstance, compoundingCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod, floatingRateDTO, calendar, calendarHistoryDataWrapper, isInterestChargedFromDateAsDisbursementDateEnabled, numberOfDays, isSkipRepaymentOnFirstMonth, diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java index 3ea0da970..66af9aa8a 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java @@ -106,7 +106,7 @@ public class LoanRepaymentBusinessEventSerializerTest { Loan loanForProcessing = Mockito.mock(Loan.class); LoanSummary loanSummary = Mockito.mock(LoanSummary.class); - MonetaryCurrency loanCurrency = Mockito.mock(MonetaryCurrency.class); + MonetaryCurrency loanCurrency = new MonetaryCurrency("CODE", 1, 1); LoanRepaymentScheduleInstallment repaymentInstallment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1, LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), @@ -122,9 +122,6 @@ public class LoanRepaymentBusinessEventSerializerTest { when(loanForProcessing.getSummary()).thenReturn(loanSummary); when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0)); when(loanForProcessing.getCurrency()).thenReturn(loanCurrency); - when(loanCurrency.getCode()).thenReturn("CODE"); - when(loanCurrency.getCurrencyInMultiplesOf()).thenReturn(1); - when(loanCurrency.getDigitsAfterDecimal()).thenReturn(1); when(mapper.mapLocalDate(any())).thenReturn(loanInstallmentRepaymentDueDate.format(DateTimeFormatter.ISO_DATE)); when(pastDueDataMapper.map(any())).thenReturn(pastDueAmount); when(pastDueService.retrieveLoanRepaymentPastDueAmountTillDate(loanForProcessing)).thenReturn(null); @@ -156,7 +153,7 @@ public class LoanRepaymentBusinessEventSerializerTest { Loan loanForProcessing = Mockito.mock(Loan.class); LoanSummary loanSummary = Mockito.mock(LoanSummary.class); - MonetaryCurrency loanCurrency = Mockito.mock(MonetaryCurrency.class); + MonetaryCurrency loanCurrency = new MonetaryCurrency("CODE", 1, 1); LoanRepaymentScheduleInstallment repaymentInstallment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1, LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), @@ -173,9 +170,6 @@ public class LoanRepaymentBusinessEventSerializerTest { when(loanForProcessing.getSummary()).thenReturn(loanSummary); when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0)); when(loanForProcessing.getCurrency()).thenReturn(loanCurrency); - when(loanCurrency.getCode()).thenReturn("CODE"); - when(loanCurrency.getCurrencyInMultiplesOf()).thenReturn(1); - when(loanCurrency.getDigitsAfterDecimal()).thenReturn(1); when(mapper.mapLocalDate(any())).thenReturn(loanInstallmentRepaymentDueDate.format(DateTimeFormatter.ISO_DATE)); when(pastDueDataMapper.map(any())).thenReturn(pastDueAmount); when(pastDueService.retrieveLoanRepaymentPastDueAmountTillDate(loanForProcessing)).thenReturn(null); diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java index 3cdf6995c..eaa1e85f3 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java @@ -92,8 +92,8 @@ public class DefaultScheduledDateGeneratorTest { LocalDate dueRepaymentPeriodDate = LocalDate.of(2024, 2, 1); LocalDate submittedOnDate = LocalDate.of(2024, 1, 1); - LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(dollarCurrency, 1, MONTHS, 4, 1, MONTHS, null, - INVALID, EQUAL_PRINCIPAL, FLAT, ZERO, MONTHS, ZERO, SAME_AS_REPAYMENT_PERIOD, false, principalAmount, + LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(dollarCurrency.toData(), 1, MONTHS, 4, 1, MONTHS, + null, INVALID, EQUAL_PRINCIPAL, FLAT, ZERO, MONTHS, ZERO, SAME_AS_REPAYMENT_PERIOD, false, principalAmount, expectedDisbursementDate, null, dueRepaymentPeriodDate, null, null, null, null, null, Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST, BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO, null, NONE, null, ZERO, @@ -164,12 +164,13 @@ public class DefaultScheduledDateGeneratorTest { LocalDate expectedDisbursementDate = LocalDate.of(2023, 10, 26); LocalDate submittedOnDate = LocalDate.of(2023, 10, 24); - return LoanApplicationTerms.assembleFrom(dollarCurrency, 1, MONTHS, 1, 1, MONTHS, null, INVALID, EQUAL_PRINCIPAL, FLAT, ZERO, - MONTHS, ZERO, SAME_AS_REPAYMENT_PERIOD, false, principalAmount, expectedDisbursementDate, null, dueRepaymentPeriodDate, - null, null, null, null, null, Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST, - BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO, - null, NONE, null, ZERO, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, false, - DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null); + return LoanApplicationTerms.assembleFrom(dollarCurrency.toData(), 1, MONTHS, 1, 1, MONTHS, null, INVALID, EQUAL_PRINCIPAL, FLAT, + ZERO, MONTHS, ZERO, SAME_AS_REPAYMENT_PERIOD, false, principalAmount, expectedDisbursementDate, null, + dueRepaymentPeriodDate, null, null, null, null, null, Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, + EMPTY_LIST, BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, + null, ZERO, null, NONE, null, ZERO, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, + null, false, DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, + null); } private HolidayDetailDTO createHolidayDTO() { diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueServiceTest.java index 599382e15..9be22f28a 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueServiceTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueServiceTest.java @@ -80,7 +80,7 @@ public class LoanCalculateRepaymentPastDueServiceTest { // given LocalDate businessDate = DateUtils.getBusinessLocalDate(); Loan loanForProcessing = Mockito.mock(Loan.class); - MonetaryCurrency loanCurrency = Mockito.mock(MonetaryCurrency.class); + MonetaryCurrency loanCurrency = new MonetaryCurrency("CODE", 1, 1); // repayments // closed repayment @@ -111,9 +111,6 @@ public class LoanCalculateRepaymentPastDueServiceTest { repaymentInstallment_2, repaymentInstallment_upcoming); when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepayments); when(loanForProcessing.getCurrency()).thenReturn(loanCurrency); - when(loanCurrency.getCode()).thenReturn("CODE"); - when(loanCurrency.getCurrencyInMultiplesOf()).thenReturn(1); - when(loanCurrency.getDigitsAfterDecimal()).thenReturn(1); // when LoanRepaymentPastDueData pastDueAmount = underTest.retrieveLoanRepaymentPastDueAmountTillDate(loanForProcessing); diff --git a/settings.gradle b/settings.gradle index 6347968f2..04999ae3f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -69,6 +69,7 @@ include ':fineract-avro-schemas' include ':fineract-e2e-tests-core' include ':fineract-e2e-tests-runner' include 'fineract-progressive-loan' +include 'fineract-progressive-loan-embeddable-schedule-generator' // NOTE: custom Docker image with all custom modules included include ':custom:docker' // NOTE: dynamically load custom modules with pattern "custom -> company -> category -> module"