This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 051571003e0aff800f0687226dce4e719fee13f1 Author: Adam Saghy <[email protected]> AuthorDate: Tue Nov 11 13:06:00 2025 +0100 FINERACT-2354: Work with DTOs instead of entities inside ProgressiveEMICalculator --- .../test/resources/features/LoanReAging.feature | 1 + .../loanschedule/domain/LoanApplicationTerms.java | 11 +- ...imumData.java => LoanConfigurationDetails.java} | 27 +++- ...dDetail.java => ILoanConfigurationDetails.java} | 8 +- .../domain/LoanProductRelatedDetail.java | 10 +- ...EmbeddableProgressiveLoanScheduleGenerator.java | 4 +- ...dvancedPaymentScheduleTransactionProcessor.java | 31 ++++- .../domain/ProgressiveLoanScheduleGenerator.java | 2 +- .../mapper/LoanConfigurationDetailsMapper.java | 42 ++++++ .../InterestScheduleModelRepositoryWrapper.java | 6 +- ...InterestScheduleModelRepositoryWrapperImpl.java | 7 +- .../InterestScheduleModelServiceGsonContext.java | 4 +- .../InternalProgressiveLoanApiResource.java | 6 +- ...siveLoanInterestScheduleModelParserService.java | 6 +- ...InterestScheduleModelParserServiceGsonImpl.java | 9 +- .../portfolio/loanproduct/calc/EMICalculator.java | 17 +-- .../loanproduct/calc/ProgressiveEMICalculator.java | 147 ++++++--------------- .../data/LoanInterestScheduleModelModifiers.java | 3 +- .../data/ProgressiveLoanInterestScheduleModel.java | 21 ++- .../loanproduct/calc/data/RepaymentPeriod.java | 11 +- .../calc/ProgressiveEMICalculatorTest.java | 5 +- .../loanproduct/calc/data/InterestPeriodTest.java | 5 +- .../loanproduct/calc/data/RepaymentPeriodTest.java | 6 +- 23 files changed, 206 insertions(+), 183 deletions(-) diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature index 7ae68665f1..432cd4c541 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature @@ -5432,6 +5432,7 @@ Feature: LoanReAging | 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | false | false | | 01 April 2024 | Re-age | 67.44 | 67.05 | 0.39 | 0.0 | 0.0 | 0.0 | false | true | + @Skip @TestRailId:C4154 @AdvancedPaymentAllocation Scenario: Verify allowing Re-aging on interest bearing loan - Interest calculation: Default Behavior - re-aging on same day as disbursement - UC16.1 When Admin sets the business date to "01 January 2024" 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 6ef3cbe7da..560aa83d22 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 @@ -53,13 +53,13 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCal import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy; import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType; import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour; -import org.apache.fineract.portfolio.loanproduct.data.LoanProductRelatedDetailMinimumData; +import org.apache.fineract.portfolio.loanproduct.data.LoanConfigurationDetails; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; 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; @@ -1694,15 +1694,16 @@ public final class LoanApplicationTerms { this.buyDownFeeStrategy, this.buyDownFeeIncomeType, this.merchantBuyDownFee); } - public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() { + public ILoanConfigurationDetails toLoanConfigurationDetails() { final CurrencyData currency = new CurrencyData(this.currency.getCode(), this.currency.getDecimalPlaces(), this.currency.getInMultiplesOf()); - return new LoanProductRelatedDetailMinimumData(currency, interestRatePerPeriod, annualNominalInterestRate, interestChargingGrace, + return new LoanConfigurationDetails(currency, interestRatePerPeriod, annualNominalInterestRate, interestChargingGrace, interestPaymentGrace, principalGrace, recurringMoratoriumOnPrincipalPeriods, interestMethod, interestCalculationPeriodMethod, daysInYearType, daysInMonthType, amortizationMethod, repaymentPeriodFrequencyType, repaymentEvery, numberOfRepayments, isInterestChargedFromDateSameAsDisbursalDateEnabled != null && isInterestChargedFromDateSameAsDisbursalDateEnabled, - daysInYearCustomStrategy, allowPartialPeriodInterestCalcualtion); + daysInYearCustomStrategy, allowPartialPeriodInterestCalcualtion, interestRecalculationEnabled, recalculationFrequencyType, + preClosureInterestCalculationStrategy); } public Integer getLoanTermFrequency() { 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/LoanConfigurationDetails.java similarity index 83% rename from fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductRelatedDetailMinimumData.java rename to fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanConfigurationDetails.java index 812b158588..a774dae562 100644 --- 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/LoanConfigurationDetails.java @@ -19,17 +19,20 @@ package org.apache.fineract.portfolio.loanproduct.data; import java.math.BigDecimal; +import lombok.Getter; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.common.domain.DaysInMonthType; import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType; 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.ILoanConfigurationDetails; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy; +import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType; -public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRepaymentScheduleRelatedDetail { +public class LoanConfigurationDetails implements ILoanConfigurationDetails { private final CurrencyData currency; private final BigDecimal interestRatePerPeriod; @@ -49,14 +52,22 @@ public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRe private final boolean interestRecognitionOnDisbursementDate; private final DaysInYearCustomStrategyType daysInYearCustomStrategy; private final boolean allowPartialPeriodInterestCalculation; - - public LoanProductRelatedDetailMinimumData(CurrencyData currency, BigDecimal interestRatePerPeriod, - BigDecimal annualNominalInterestRate, Integer interestChargingGrace, Integer interestPaymentGrace, Integer principalGrace, + @Getter + private final boolean isInterestRecalculationEnabled; + @Getter + private final RecalculationFrequencyType restFrequencyType; + @Getter + private final LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy; + + public LoanConfigurationDetails(CurrencyData currency, 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, boolean interestRecognitionOnDisbursementDate, - DaysInYearCustomStrategyType daysInYearCustomStrategy, boolean allowPartialPeriodInterestCalculation) { + DaysInYearCustomStrategyType daysInYearCustomStrategy, boolean allowPartialPeriodInterestCalculation, + boolean isInterestRecalculationEnabled, RecalculationFrequencyType restFrequencyType, + LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy) { this.currency = currency; this.interestRatePerPeriod = interestRatePerPeriod; this.annualNominalInterestRate = annualNominalInterestRate; @@ -75,6 +86,9 @@ public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRe this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate; this.daysInYearCustomStrategy = daysInYearCustomStrategy; this.allowPartialPeriodInterestCalculation = allowPartialPeriodInterestCalculation; + this.isInterestRecalculationEnabled = isInterestRecalculationEnabled; + this.restFrequencyType = restFrequencyType; + this.preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy; } private Integer defaultToNullIfZero(final Integer value) { @@ -184,4 +198,5 @@ public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRe public DaysInYearCustomStrategyType getDaysInYearCustomStrategy() { return daysInYearCustomStrategy; } + } 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/ILoanConfigurationDetails.java similarity index 90% rename from fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinimumRepaymentScheduleRelatedDetail.java rename to fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/ILoanConfigurationDetails.java index 10d33bef0b..ef5095198f 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/ILoanConfigurationDetails.java @@ -26,7 +26,7 @@ import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; /** * Represents the bare minimum repayment details needed for activities related to generating repayment schedules. */ -public interface LoanProductMinimumRepaymentScheduleRelatedDetail { +public interface ILoanConfigurationDetails { CurrencyData getCurrencyData(); @@ -67,4 +67,10 @@ public interface LoanProductMinimumRepaymentScheduleRelatedDetail { boolean isInterestRecognitionOnDisbursementDate(); DaysInYearCustomStrategyType getDaysInYearCustomStrategy(); + + boolean isInterestRecalculationEnabled(); + + RecalculationFrequencyType getRestFrequencyType(); + + LoanPreCloseInterestCalculationStrategy getPreCloseInterestCalculationStrategy(); } 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 3c9458c3c9..a5b1192f1f 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 @@ -53,7 +53,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul @Embeddable @Getter @Setter -public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentScheduleRelatedDetail { +public class LoanProductRelatedDetail { @Embedded private MonetaryCurrency currency; @@ -329,7 +329,6 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche return this.currency.copy(); } - @Override public CurrencyData getCurrencyData() { return currency.toData(); } @@ -342,27 +341,20 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche return Money.of(getCurrencyData(), this.inArrearsTolerance); } - // TODO: REVIEW - @Override public BigDecimal getNominalInterestRatePerPeriod() { return this.nominalInterestRatePerPeriod == null ? null : BigDecimal.valueOf(Double.parseDouble(this.nominalInterestRatePerPeriod.stripTrailingZeros().toString())); } - // TODO: REVIEW - @Override public PeriodFrequencyType getInterestPeriodFrequencyType() { return this.interestPeriodFrequencyType == null ? PeriodFrequencyType.INVALID : this.interestPeriodFrequencyType; } - // TODO: REVIEW - @Override public BigDecimal getAnnualNominalInterestRate() { return this.annualNominalInterestRate == null ? null : BigDecimal.valueOf(Double.parseDouble(this.annualNominalInterestRate.stripTrailingZeros().toString())); } - @Override public DaysInYearCustomStrategyType getDaysInYearCustomStrategy() { return daysInYearCustomStrategy; } 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 index e2968663f9..864a20c3e1 100644 --- 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 @@ -28,7 +28,7 @@ import org.apache.fineract.portfolio.loanaccount.service.InterestScheduleModelRe import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; @SuppressWarnings("unused") public class EmbeddableProgressiveLoanScheduleGenerator { @@ -70,7 +70,7 @@ public class EmbeddableProgressiveLoanScheduleGenerator { @Override public Optional<ProgressiveLoanInterestScheduleModel> readProgressiveLoanInterestScheduleModel(Long loanId, - LoanProductMinimumRepaymentScheduleRelatedDetail detail, Integer installmentAmountInMultipliesOf) { + ILoanConfigurationDetails detail, Integer installmentAmountInMultipliesOf) { return Optional.empty(); } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java index 4d38193361..7615700cae 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java @@ -100,6 +100,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Mon import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; +import org.apache.fineract.portfolio.loanaccount.mapper.LoanConfigurationDetailsMapper; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService; import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService; @@ -231,9 +232,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep List<LoanTermVariationsData> loanTermVariations = loan.getActiveLoanTermVariations().stream().map(LoanTermVariations::toData) .collect(Collectors.toCollection(ArrayList::new)); final Integer installmentAmountInMultiplesOf = loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf(); - final LoanProductRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail(); ProgressiveLoanInterestScheduleModel scheduleModel = emiCalculator.generateInstallmentInterestScheduleModel(installments, - loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, overpaymentHolder.getMoneyObject().getMc()); + LoanConfigurationDetailsMapper.map(loan), loanTermVariations, installmentAmountInMultiplesOf, + overpaymentHolder.getMoneyObject().getMc()); ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(currency, installments, charges, overpaymentHolder, changedTransactionDetail, scheduleModel); @@ -1588,7 +1589,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep if (isInterestRecalculationSupported(ctx, loan) && !loan.isNpa() && !loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue()) { - boolean modelHasUpdates = emiCalculator.recalculateModelOverdueAmountsTillDate(ctx, targetDate); + boolean modelHasUpdates = emiCalculator.recalculateModelOverdueAmountsTillDate(ctx.getModel(), targetDate, + ctx.isPrepayAttempt()); if (modelHasUpdates && updateInstallments) { updateInstallmentsPrincipalAndInterestByModel(ctx); } @@ -3264,8 +3266,11 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep loan.getLoanProduct().getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalculation()) .mc(mc).build(); + LocalDate reAgePeriodStartDate = calculateFirstReAgedPeriodStartDate(loanTransaction); + LocalDate reageFirstDueDate = loanTransaction.getLoanReAgeParameter().getStartDate(); // Update the existing model with re-aged periods - emiCalculator.updateModelRepaymentPeriodsDuringReAge(ctx, loanTransaction, loanApplicationTerms, mc); + emiCalculator.updateModelRepaymentPeriodsDuringReAge(ctx.getModel(), reAgePeriodStartDate, reageFirstDueDate, + loanTransaction.getTransactionDate(), loanApplicationTerms, mc); updateInstallmentsByModelForReAging(loanTransaction, ctx); @@ -3273,4 +3278,22 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep Money.zero(currency)); reprocessInstallments(installments); } + + private static LocalDate calculateFirstReAgedPeriodStartDate(final LoanTransaction loanTransaction) { + final LoanReAgeParameter loanReAgeParameter = loanTransaction.getLoanReAgeParameter(); + final LocalDate reAgingStartDate = loanReAgeParameter.getStartDate(); + + if (reAgingStartDate.isEqual(loanTransaction.getLoan().getDisbursementDate())) { + return reAgingStartDate; + } + + return switch (loanReAgeParameter.getFrequencyType()) { + case DAYS -> reAgingStartDate.minusDays(loanReAgeParameter.getFrequencyNumber()); + case WEEKS -> reAgingStartDate.minusWeeks(loanReAgeParameter.getFrequencyNumber()); + case MONTHS -> reAgingStartDate.minusMonths(loanReAgeParameter.getFrequencyNumber()); + case YEARS -> reAgingStartDate.minusYears(loanReAgeParameter.getFrequencyNumber()); + case WHOLE_TERM -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: WHOLE_TERM"); + case INVALID -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: INVALID"); + }; + } } 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 5e726aa7a4..d1a710c1fb 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 @@ -110,7 +110,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { ? loanApplicationTerms.getLoanTermVariations().getExceptionData() : null; final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator.generatePeriodInterestScheduleModel( - expectedRepaymentPeriods, loanApplicationTerms.toLoanProductRelatedDetailMinimumData(), loanTermVariations, + expectedRepaymentPeriods, loanApplicationTerms.toLoanConfigurationDetails(), loanTermVariations, loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc); final List<LoanScheduleModelPeriod> periods = new ArrayList<>(expectedRepaymentPeriods.size()); diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanConfigurationDetailsMapper.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanConfigurationDetailsMapper.java new file mode 100644 index 0000000000..4dc2c1f7b4 --- /dev/null +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanConfigurationDetailsMapper.java @@ -0,0 +1,42 @@ +package org.apache.fineract.portfolio.loanaccount.mapper; + +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.portfolio.common.domain.DaysInMonthType; +import org.apache.fineract.portfolio.common.domain.DaysInYearType; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanproduct.data.LoanConfigurationDetails; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; + +public final class LoanConfigurationDetailsMapper { + + private LoanConfigurationDetailsMapper() {} + + public static ILoanConfigurationDetails map(Loan loan) { + if (loan == null) { + return null; + } + + LoanProductRelatedDetail loanProductRelatedDetail = loan.getLoanProductRelatedDetail(); + if (loanProductRelatedDetail == null) { + return null; + } + + MonetaryCurrency currency = loan.getCurrency(); + CurrencyData currencyData = currency.toData(); + + return new LoanConfigurationDetails(currencyData, loanProductRelatedDetail.getNominalInterestRatePerPeriod(), + loanProductRelatedDetail.getAnnualNominalInterestRate(), loanProductRelatedDetail.getGraceOnInterestCharged(), + loanProductRelatedDetail.getGraceOnPrincipalPayment(), loanProductRelatedDetail.getGraceOnPrincipalPayment(), + loanProductRelatedDetail.getRecurringMoratoriumOnPrincipalPeriods(), loanProductRelatedDetail.getInterestMethod(), + loanProductRelatedDetail.getInterestCalculationPeriodMethod(), + DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType()), + DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType()), loanProductRelatedDetail.getAmortizationMethod(), + loanProductRelatedDetail.getRepaymentPeriodFrequencyType(), loanProductRelatedDetail.getRepayEvery(), + loanProductRelatedDetail.getNumberOfRepayments(), loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(), + loanProductRelatedDetail.getDaysInYearCustomStrategy(), loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation(), + loan.isInterestRecalculationEnabled(), loan.getLoanInterestRecalculationDetails().getRestFrequencyType(), + loan.getLoanInterestRecalculationDetails().getPreCloseInterestCalculationStrategy()); + } +} diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapper.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapper.java index 243f8702a4..1ce74910b7 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapper.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapper.java @@ -23,7 +23,7 @@ import java.util.Optional; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.ProgressiveLoanModel; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; public interface InterestScheduleModelRepositoryWrapper { @@ -35,8 +35,8 @@ public interface InterestScheduleModelRepositoryWrapper { ProgressiveLoanInterestScheduleModel writeInterestScheduleModel(Loan loan, ProgressiveLoanInterestScheduleModel model); - Optional<ProgressiveLoanInterestScheduleModel> readProgressiveLoanInterestScheduleModel(Long loanId, - LoanProductMinimumRepaymentScheduleRelatedDetail detail, Integer installmentAmountInMultipliesOf); + Optional<ProgressiveLoanInterestScheduleModel> readProgressiveLoanInterestScheduleModel(Long loanId, ILoanConfigurationDetails detail, + Integer installmentAmountInMultipliesOf); boolean hasValidModelForDate(Long loanId, LocalDate targetDate); diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java index 6dac432745..a00f7af0a3 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java @@ -35,9 +35,10 @@ import org.apache.fineract.portfolio.loanaccount.domain.ProgressiveLoanModel; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ProgressiveTransactionCtx; +import org.apache.fineract.portfolio.loanaccount.mapper.LoanConfigurationDetailsMapper; import org.apache.fineract.portfolio.loanaccount.repository.ProgressiveLoanModelRepository; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; import org.springframework.stereotype.Service; @Service @@ -92,7 +93,7 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched public Optional<ProgressiveLoanInterestScheduleModel> extractModel(Optional<ProgressiveLoanModel> progressiveLoanModel) { return progressiveLoanModel.map(ProgressiveLoanModel::getJsonModel) // .map(jsonModel -> progressiveLoanInterestScheduleModelParserService.fromJson(jsonModel, - progressiveLoanModel.get().getLoan().getLoanProductRelatedDetail(), MoneyHelper.getMathContext(), + LoanConfigurationDetailsMapper.map(progressiveLoanModel.get().getLoan()), MoneyHelper.getMathContext(), progressiveLoanModel.get().getLoan().getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf())); } @@ -125,7 +126,7 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched @Override public Optional<ProgressiveLoanInterestScheduleModel> readProgressiveLoanInterestScheduleModel(final Long loanId, - final LoanProductMinimumRepaymentScheduleRelatedDetail detail, final Integer installmentAmountInMultipliesOf) { + final ILoanConfigurationDetails detail, final Integer installmentAmountInMultipliesOf) { return loanModelRepository.findOneByLoanId(loanId) // .map(ProgressiveLoanModel::getJsonModel) // .map(jsonModel -> progressiveLoanInterestScheduleModelParserService.fromJson(jsonModel, detail, diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelServiceGsonContext.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelServiceGsonContext.java index 767c817bca..59ce097db9 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelServiceGsonContext.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelServiceGsonContext.java @@ -27,7 +27,7 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.portfolio.loanproduct.calc.data.InterestPeriod; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.calc.data.RepaymentPeriod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; @Data @RequiredArgsConstructor @@ -35,7 +35,7 @@ public class InterestScheduleModelServiceGsonContext { private final MonetaryCurrency currency; private final MathContext mc; - private final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail; + private final ILoanConfigurationDetails loanProductRelatedDetail; private RepaymentPeriod prev = null; private final Integer installmentAmountInMultipliesOf; diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java index 60f8cd2630..46bd49c058 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java @@ -42,7 +42,9 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; +import org.apache.fineract.portfolio.loanaccount.mapper.LoanConfigurationDetailsMapper; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; @@ -81,8 +83,8 @@ public class InternalProgressiveLoanApiResource implements InitializingBean { if (!loan.isProgressiveSchedule()) { throw new IllegalArgumentException("The loan is not progressive."); } - - return writePlatformService.readProgressiveLoanInterestScheduleModel(loanId, loan.getLoanRepaymentScheduleDetail(), + ILoanConfigurationDetails loanConfigurationDetails = LoanConfigurationDetailsMapper.map(loan); + return writePlatformService.readProgressiveLoanInterestScheduleModel(loanId, loanConfigurationDetails, loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf()).orElse(null); } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestScheduleModelParserService.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestScheduleModelParserService.java index e148ae23ae..0d3487a8a7 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestScheduleModelParserService.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestScheduleModelParserService.java @@ -20,7 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.math.MathContext; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; public interface ProgressiveLoanInterestScheduleModelParserService { @@ -32,6 +32,6 @@ public interface ProgressiveLoanInterestScheduleModelParserService { /** * Restore a ProgressiveLoanInterestScheduleModel from a JSON string. */ - ProgressiveLoanInterestScheduleModel fromJson(String s, LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, - MathContext mc, Integer installmentAmountInMultipliesOf); + ProgressiveLoanInterestScheduleModel fromJson(String s, ILoanConfigurationDetails loanProductRelatedDetail, MathContext mc, + Integer installmentAmountInMultipliesOf); } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestScheduleModelParserServiceGsonImpl.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestScheduleModelParserServiceGsonImpl.java index c07b440402..477df16df0 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestScheduleModelParserServiceGsonImpl.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestScheduleModelParserServiceGsonImpl.java @@ -35,7 +35,7 @@ import org.apache.fineract.organisation.monetary.serialization.MoneySerializer; import org.apache.fineract.portfolio.loanproduct.calc.data.InterestPeriod; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.calc.data.RepaymentPeriod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; import org.springframework.lang.NonNull; @Slf4j @@ -51,7 +51,7 @@ public class ProgressiveLoanInterestScheduleModelParserServiceGsonImpl implement .addSerializationExclusionStrategy(new JsonExcludeAnnotationBasedExclusionStrategy()).create(); } - private Gson createDeserializer(LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, MathContext mc, + private Gson createDeserializer(ILoanConfigurationDetails loanProductRelatedDetail, MathContext mc, Integer installmentAmountInMultipliesOf) { InterestScheduleModelServiceGsonContext ctx = new InterestScheduleModelServiceGsonContext( new MonetaryCurrency(loanProductRelatedDetail.getCurrencyData()), mc, loanProductRelatedDetail, @@ -73,9 +73,8 @@ public class ProgressiveLoanInterestScheduleModelParserServiceGsonImpl implement } @Override - public ProgressiveLoanInterestScheduleModel fromJson(String s, - @NonNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, @NonNull MathContext mc, - Integer installmentAmountInMultipliesOf) { + public ProgressiveLoanInterestScheduleModel fromJson(String s, @NonNull ILoanConfigurationDetails loanProductRelatedDetail, + @NonNull MathContext mc, Integer installmentAmountInMultipliesOf) { if (s == null) { return null; } 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 de654f7c0b..6228e2c4d9 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 @@ -27,15 +27,13 @@ import java.util.Optional; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ProgressiveTransactionCtx; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod; import org.apache.fineract.portfolio.loanproduct.calc.data.OutstandingDetails; import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.calc.data.RepaymentPeriod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; public interface EMICalculator { @@ -45,8 +43,8 @@ public interface EMICalculator { */ @NotNull ProgressiveLoanInterestScheduleModel generatePeriodInterestScheduleModel(@NotNull List<LoanScheduleModelRepaymentPeriod> periods, - @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, - List<LoanTermVariationsData> loanTermVariations, Integer installmentAmountInMultiplesOf, MathContext mc); + @NotNull ILoanConfigurationDetails loanProductRelatedDetail, List<LoanTermVariationsData> loanTermVariations, + Integer installmentAmountInMultiplesOf, MathContext mc); /** * This method creates an Interest model with repayment periods from the installments which retrieved from the @@ -54,8 +52,7 @@ public interface EMICalculator { */ @NotNull ProgressiveLoanInterestScheduleModel generateInstallmentInterestScheduleModel( - @NotNull List<LoanRepaymentScheduleInstallment> installments, - @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, + @NotNull List<LoanRepaymentScheduleInstallment> installments, @NotNull ILoanConfigurationDetails loanProductRelatedDetail, List<LoanTermVariationsData> loanTermVariations, Integer installmentAmountInMultiplesOf, MathContext mc); /** @@ -144,10 +141,10 @@ public interface EMICalculator { */ void applyInterestPause(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate fromDate, LocalDate endDate); - void updateModelRepaymentPeriodsDuringReAge(ProgressiveTransactionCtx ctx, LoanTransaction loanTransaction, - LoanApplicationTerms loanApplicationTerms, MathContext mc); + void updateModelRepaymentPeriodsDuringReAge(ProgressiveLoanInterestScheduleModel ctx, LocalDate loanTransaction, + LocalDate reageFirstDueDate, LocalDate transactionDate, LoanApplicationTerms loanApplicationTerms, MathContext mc); - boolean recalculateModelOverdueAmountsTillDate(ProgressiveTransactionCtx ctx, LocalDate targetDate); + boolean recalculateModelOverdueAmountsTillDate(ProgressiveLoanInterestScheduleModel ctx, LocalDate targetDate, boolean prepayAttempt); /** * Gives back the sum of the outstanding interest from the whole model till the provided date. 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 ec558fc802..311098ab56 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 @@ -39,21 +39,14 @@ import org.apache.fineract.infrastructure.core.domain.LocalDateInterval; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; 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.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.common.domain.DaysInMonthType; import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType; import org.apache.fineract.portfolio.common.domain.DaysInYearType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; -import org.apache.fineract.portfolio.loanaccount.domain.Loan; -import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter; -import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ProgressiveTransactionCtx; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator; @@ -64,7 +57,7 @@ import org.apache.fineract.portfolio.loanproduct.calc.data.OutstandingDetails; import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.calc.data.RepaymentPeriod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -80,8 +73,8 @@ public final class ProgressiveEMICalculator implements EMICalculator { @Override @NotNull public ProgressiveLoanInterestScheduleModel generatePeriodInterestScheduleModel(@NotNull List<LoanScheduleModelRepaymentPeriod> periods, - @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, - List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) { + @NotNull ILoanConfigurationDetails loanProductRelatedDetail, List<LoanTermVariationsData> loanTermVariations, + final Integer installmentAmountInMultiplesOf, final MathContext mc) { return generateInterestScheduleModel(periods, LoanScheduleModelRepaymentPeriod::periodFromDate, LoanScheduleModelRepaymentPeriod::periodDueDate, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc); @@ -90,8 +83,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { @Override @NotNull public ProgressiveLoanInterestScheduleModel generateInstallmentInterestScheduleModel( - @NotNull List<LoanRepaymentScheduleInstallment> installments, - @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, + @NotNull List<LoanRepaymentScheduleInstallment> installments, @NotNull ILoanConfigurationDetails loanProductRelatedDetail, List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) { installments = installments.stream().filter(installment -> !installment.isDownPayment() && !installment.isAdditional()).toList(); return generateInterestScheduleModel(installments, LoanRepaymentScheduleInstallment::getFromDate, @@ -101,7 +93,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { @NotNull private <T> ProgressiveLoanInterestScheduleModel generateInterestScheduleModel(@NotNull List<T> periods, Function<T, LocalDate> from, - Function<T, LocalDate> to, @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, + Function<T, LocalDate> to, @NotNull ILoanConfigurationDetails loanProductRelatedDetail, List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) { final Money zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc); final AtomicReference<RepaymentPeriod> prev = new AtomicReference<>(); @@ -218,7 +210,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { public <T> void updateModel(ProgressiveLoanInterestScheduleModel scheduleModel, List<T> updateExpectedRepaymentPeriods, Function<T, LocalDate> from, Function<T, LocalDate> to) { - final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); + final ILoanConfigurationDetails loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); MathContext mc = scheduleModel.mc(); final Money zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc); final AtomicReference<RepaymentPeriod> prev = new AtomicReference<>(); @@ -575,36 +567,34 @@ public final class ProgressiveEMICalculator implements EMICalculator { } @Override - public void updateModelRepaymentPeriodsDuringReAge(final ProgressiveTransactionCtx ctx, final LoanTransaction loanTransaction, + public void updateModelRepaymentPeriodsDuringReAge(final ProgressiveLoanInterestScheduleModel scheduleModel, + final LocalDate reAgePeriodStartDate, LocalDate reageFirstDueDate, LocalDate targetDate, final LoanApplicationTerms loanApplicationTerms, final MathContext mc) { - final List<RepaymentPeriod> existingRepaymentPeriods = ctx.getModel().repaymentPeriods(); - - moveOutstandingAmountsFromPeriodsBeforeReAging(existingRepaymentPeriods, loanTransaction.getLoanReAgeParameter().getStartDate()); - final LocalDate periodStartDate = calculateFirstReAgedPeriodStartDate(loanTransaction); + moveOutstandingAmountsFromPeriodsBeforeReAging(scheduleModel.repaymentPeriods(), reageFirstDueDate); final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generateTemporaryReAgedScheduleModel(loanApplicationTerms, - mc, periodStartDate); + mc, reAgePeriodStartDate); - mergeNewInterestScheduleModelWithExistingOne(ctx, temporaryReAgedScheduleModel, loanTransaction); + mergeNewInterestScheduleModelWithExistingOne(scheduleModel, temporaryReAgedScheduleModel, reageFirstDueDate, targetDate); } @Override - public boolean recalculateModelOverdueAmountsTillDate(final ProgressiveTransactionCtx ctx, final LocalDate targetDate) { + public boolean recalculateModelOverdueAmountsTillDate(final ProgressiveLoanInterestScheduleModel scheduleModel, + final LocalDate targetDate, boolean prepayAttempt) { boolean hasChange = false; - final ProgressiveLoanInterestScheduleModel model = ctx.getModel(); - final List<RepaymentPeriod> overdueInstallmentsSortedByInstallmentNumber = findPossiblyOverdueRepaymentPeriods(targetDate, model); + final List<RepaymentPeriod> overdueInstallmentsSortedByInstallmentNumber = findPossiblyOverdueRepaymentPeriods(targetDate, + scheduleModel); if (!overdueInstallmentsSortedByInstallmentNumber.isEmpty()) { - final RepaymentPeriod lastPeriod = model.getLastRepaymentPeriod(); - final RepaymentPeriod currentPeriod = model.findRepaymentPeriod(targetDate).orElse(lastPeriod); - final MonetaryCurrency currency = model.zero().getCurrency(); - Money overDuePrincipal = Money.zero(currency); - Money aggregatedOverDuePrincipal = Money.zero(currency); + final RepaymentPeriod lastPeriod = scheduleModel.getLastRepaymentPeriod(); + final RepaymentPeriod currentPeriod = scheduleModel.findRepaymentPeriod(targetDate).orElse(lastPeriod); + Money overDuePrincipal = scheduleModel.zero(); + Money aggregatedOverDuePrincipal = scheduleModel.zero(); for (RepaymentPeriod processingPeriod : overdueInstallmentsSortedByInstallmentNumber) { // add and subtract outstanding principal if (!overDuePrincipal.isZero()) { final boolean currentChanges = adjustOverduePrincipal(targetDate, processingPeriod, overDuePrincipal, - aggregatedOverDuePrincipal, ctx); + aggregatedOverDuePrincipal, scheduleModel, prepayAttempt); hasChange = hasChange || currentChanges; } @@ -615,13 +605,13 @@ public final class ProgressiveEMICalculator implements EMICalculator { if (!currentPeriod.equals(lastPeriod) || !targetDate.isAfter(lastPeriod.getDueDate())) { final boolean currentChanges = adjustOverduePrincipal(targetDate, currentPeriod, overDuePrincipal, - aggregatedOverDuePrincipal, ctx); + aggregatedOverDuePrincipal, scheduleModel, prepayAttempt); hasChange = hasChange || currentChanges; } - if (aggregatedOverDuePrincipal.isGreaterThanZero() - && (model.lastOverdueBalanceChange() == null || model.lastOverdueBalanceChange().isBefore(targetDate))) { - model.lastOverdueBalanceChange(targetDate); + if (aggregatedOverDuePrincipal.isGreaterThanZero() && (scheduleModel.lastOverdueBalanceChange() == null + || scheduleModel.lastOverdueBalanceChange().isBefore(targetDate))) { + scheduleModel.lastOverdueBalanceChange(targetDate); } } @@ -644,13 +634,10 @@ public final class ProgressiveEMICalculator implements EMICalculator { } private boolean adjustOverduePrincipal(final LocalDate currentDate, final RepaymentPeriod currentInstallment, - final Money overduePrincipal, final Money aggregatedOverDuePrincipal, final ProgressiveTransactionCtx ctx) { + final Money overduePrincipal, final Money aggregatedOverDuePrincipal, final ProgressiveLoanInterestScheduleModel model, + boolean prepayAttempt) { final LocalDate fromDate = currentInstallment.getFromDate(); final LocalDate toDate = currentInstallment.getDueDate(); - final ProgressiveLoanInterestScheduleModel model = ctx.getModel(); - final boolean prepayAttempt = ctx.isPrepayAttempt(); - final LoanInterestRecalculationDetails loanInterestRecalculationDetails = ctx.getInstallments().getFirst().getLoan() - .getLoanInterestRecalculationDetails(); if (!currentDate.equals(model.lastOverdueBalanceChange())) { if (model.lastOverdueBalanceChange() == null || currentInstallment.getFromDate().isAfter(model.lastOverdueBalanceChange())) { @@ -661,7 +648,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { if (currentDate.isAfter(fromDate) && !currentDate.isAfter(toDate)) { LocalDate lastOverdueBalanceChange; - if (shouldRecalculateTillInstallmentDueDate(loanInterestRecalculationDetails, prepayAttempt)) { + if (shouldRecalculateTillInstallmentDueDate(model.loanProductRelatedDetail(), prepayAttempt)) { lastOverdueBalanceChange = toDate; } else { lastOverdueBalanceChange = currentDate; @@ -674,7 +661,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { return false; } - private boolean shouldRecalculateTillInstallmentDueDate(final LoanInterestRecalculationDetails recalculationDetails, + private boolean shouldRecalculateTillInstallmentDueDate(final ILoanConfigurationDetails loanProductRelatedDetails, final boolean isPrepayAttempt) { // Rest frequency type and pre close interest calculation strategy can be controversial // if restFrequencyType == DAILY and preCloseInterestCalculationStrategy == TILL_PRE_CLOSURE_DATE @@ -686,11 +673,11 @@ public final class ProgressiveEMICalculator implements EMICalculator { // or restFrequencyType == SAME_AS_REPAYMENT_PERIOD and preCloseInterestCalculationStrategy == // TILL_PRE_CLOSURE_DATE // we cannot harmonize the two configs. Behaviour should mimic prepay api. - return switch (recalculationDetails.getRestFrequencyType()) { + return switch (loanProductRelatedDetails.getRestFrequencyType()) { case DAILY -> - isPrepayAttempt && recalculationDetails.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled(); + isPrepayAttempt && loanProductRelatedDetails.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled(); case SAME_AS_REPAYMENT_PERIOD -> - recalculationDetails.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled(); + loanProductRelatedDetails.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled(); case WEEKLY -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: WEEKLY"); case MONTHLY -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: MONTHLY"); case INVALID -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: INVALID"); @@ -702,11 +689,10 @@ public final class ProgressiveEMICalculator implements EMICalculator { * the balances of the updated model and also recalculate the EMI if the EMI of the last repayment period differs * significantly from other periods. */ - private void mergeNewInterestScheduleModelWithExistingOne(final ProgressiveTransactionCtx ctx, - final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel, final LoanTransaction loanTransaction) { + private void mergeNewInterestScheduleModelWithExistingOne(final ProgressiveLoanInterestScheduleModel scheduleModel, + final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel, final LocalDate reageFirstDueDate, + LocalDate targetDate) { final List<RepaymentPeriod> newPeriods = temporaryReAgedScheduleModel.repaymentPeriods(); - final ProgressiveLoanInterestScheduleModel scheduleModel = ctx.getModel(); - final Loan loan = loanTransaction.getLoan(); if (newPeriods.isEmpty()) { return; @@ -716,10 +702,8 @@ public final class ProgressiveEMICalculator implements EMICalculator { final RepaymentPeriod firstRepaymentPeriod = existingRepaymentPeriods.getFirst(); final Money disbursedAmount = firstRepaymentPeriod.getTotalDisbursedAmount(); - final LocalDate reAgingStartDate = loanTransaction.getLoanReAgeParameter().getStartDate(); - final Optional<RepaymentPeriod> firstExistingReAgedRepaymentPeriodOpt = existingRepaymentPeriods.stream() - .filter(period -> period.getDueDate().equals(reAgingStartDate)).findFirst(); + .filter(period -> period.getDueDate().equals(reageFirstDueDate)).findFirst(); for (final RepaymentPeriod newPeriod : newPeriods) { final Optional<RepaymentPeriod> existingRepaymentPeriodOpt = existingRepaymentPeriods.stream().filter( @@ -736,34 +720,18 @@ public final class ProgressiveEMICalculator implements EMICalculator { final RepaymentPeriod rp = RepaymentPeriod.create( previousExistingRepaymentPeriodOpt.orElseGet(existingRepaymentPeriods::getLast), newPeriod.getFromDate(), - newPeriod.getDueDate(), newPrincipal.add(newInterest), MoneyHelper.getMathContext(), - loanTransaction.getLoan().getLoanProductRelatedDetail()); + newPeriod.getDueDate(), newPrincipal.add(newInterest), scheduleModel.mc(), scheduleModel.loanProductRelatedDetail()); rp.setTotalDisbursedAmount(disbursedAmount); - if (existingRepaymentPeriodOpt.isPresent()) { - // Add an interest period on the disbursement date for the very first replaced period - if (existingRepaymentPeriodOpt.get().equals(firstRepaymentPeriod)) { - final LocalDate disbursementDate = loanTransaction.getLoan().getDisbursementDate(); - rp.getInterestPeriods().getFirst().setDisbursementAmount(disbursedAmount); - rp.getInterestPeriods().getFirst().setFromDate(disbursementDate); - rp.getInterestPeriods().getFirst().setDueDate(disbursementDate); - rp.getInterestPeriods().add(InterestPeriod.withEmptyAmounts(rp, rp.getFromDate(), rp.getDueDate())); - } - existingRepaymentPeriods.remove(existingRepaymentPeriodOpt.get()); - } + existingRepaymentPeriodOpt.ifPresent(existingRepaymentPeriods::remove); existingRepaymentPeriods.add(rp); calculateRateFactorForRepaymentPeriod(rp, scheduleModel); } - if (reAgingStartDate.isBefore(DateUtils.getBusinessLocalDate()) && isInterestRecalculationIsAllowed(ctx, loan)) { - scheduleModel.lastOverdueBalanceChange(null); - recalculateModelOverdueAmountsTillDate(ctx, loanTransaction.getSubmittedOnDate()); - } - final List<RepaymentPeriod> reAgedRepaymentPeriods = existingRepaymentPeriods.stream() - .filter(repaymentPeriod -> (!repaymentPeriod.getFromDate().isBefore(reAgingStartDate) - || repaymentPeriod.getDueDate().isEqual(reAgingStartDate)) + .filter(repaymentPeriod -> (!repaymentPeriod.getFromDate().isBefore(reageFirstDueDate) + || repaymentPeriod.getDueDate().isEqual(reageFirstDueDate)) && !repaymentPeriod.getDueDate().isAfter(newPeriods.getLast().getDueDate())) .toList(); @@ -775,16 +743,10 @@ public final class ProgressiveEMICalculator implements EMICalculator { } calculateOutstandingBalance(scheduleModel); - calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, loanTransaction.getTransactionDate()); + calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, targetDate); checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, reAgedRepaymentPeriods); } - private boolean isInterestRecalculationIsAllowed(final ProgressiveTransactionCtx ctx, final Loan loan) { - return loan.isInterestBearingAndInterestRecalculationEnabled() && !ctx.isChargedOff() && !ctx.isWrittenOff() - && !ctx.isContractTerminated() && !loan.isNpa() - && !loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue(); - } - /** * * Generates temporary interestScheduleModel with re-aged repayment periods */ @@ -794,36 +756,13 @@ public final class ProgressiveEMICalculator implements EMICalculator { final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator.generateRepaymentPeriods(mc, periodStartDate, loanApplicationTerms, null); final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generatePeriodInterestScheduleModel( - expectedRepaymentPeriods, loanApplicationTerms.toLoanProductRelatedDetailMinimumData(), null, + expectedRepaymentPeriods, loanApplicationTerms.toLoanConfigurationDetails(), null, loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc); addDisbursement(temporaryReAgedScheduleModel, EmiChangeOperation.disburse(periodStartDate, loanApplicationTerms.getPrincipal())); return temporaryReAgedScheduleModel; } - /** - * * Based on the re-aging start date and frequency data calculates start date for the first re-aged period, which - * is used to generate re-aged repayment periods - */ - @NotNull - private static LocalDate calculateFirstReAgedPeriodStartDate(final LoanTransaction loanTransaction) { - final LoanReAgeParameter loanReAgeParameter = loanTransaction.getLoanReAgeParameter(); - final LocalDate reAgingStartDate = loanReAgeParameter.getStartDate(); - - if (reAgingStartDate.isEqual(loanTransaction.getLoan().getDisbursementDate())) { - return reAgingStartDate; - } - - return switch (loanReAgeParameter.getFrequencyType()) { - case DAYS -> reAgingStartDate.minusDays(loanReAgeParameter.getFrequencyNumber()); - case WEEKS -> reAgingStartDate.minusWeeks(loanReAgeParameter.getFrequencyNumber()); - case MONTHS -> reAgingStartDate.minusMonths(loanReAgeParameter.getFrequencyNumber()); - case YEARS -> reAgingStartDate.minusYears(loanReAgeParameter.getFrequencyNumber()); - case WHOLE_TERM -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: WHOLE_TERM"); - case INVALID -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: INVALID"); - }; - } - /** * * Zeroing out the EMI of the repayment periods, that are before re-aging and not been fully paid. And decreases * the balance correction amount (added during interest recalculation for the business date) by the amount of the @@ -1008,7 +947,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { BigDecimal calculateRateFactorPerPeriodForInterest(final ProgressiveLoanInterestScheduleModel scheduleModel, final RepaymentPeriod repaymentPeriod, final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate) { final MathContext mc = scheduleModel.mc(); - final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); + final ILoanConfigurationDetails loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriodFromDate), scheduleModel.mc()); final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType()); @@ -1136,7 +1075,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { private BigDecimal calculateRateFactorPerPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel, final RepaymentPeriod repaymentPeriod, final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate) { final MathContext mc = scheduleModel.mc(); - final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); + final ILoanConfigurationDetails loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriodFromDate), scheduleModel.mc()); final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType()); diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/LoanInterestScheduleModelModifiers.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/LoanInterestScheduleModelModifiers.java index 95ed156122..3ebd0bcb25 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/LoanInterestScheduleModelModifiers.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/LoanInterestScheduleModelModifiers.java @@ -20,5 +20,6 @@ package org.apache.fineract.portfolio.loanproduct.calc.data; public enum LoanInterestScheduleModelModifiers { // EMI_RECALCULATION, // - COPY // + COPY, // + INTEREST_RECALCULATION_ENABLED // } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java index 3b4e990c1e..859bbe77a5 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanproduct.calc.data; import static org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper.isInPeriod; import static org.apache.fineract.portfolio.loanproduct.calc.data.LoanInterestScheduleModelModifiers.COPY; import static org.apache.fineract.portfolio.loanproduct.calc.data.LoanInterestScheduleModelModifiers.EMI_RECALCULATION; +import static org.apache.fineract.portfolio.loanproduct.calc.data.LoanInterestScheduleModelModifiers.INTEREST_RECALCULATION_ENABLED; import jakarta.validation.constraints.NotNull; import java.math.BigDecimal; @@ -50,7 +51,7 @@ import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; @Data @Accessors(fluent = true) @@ -60,7 +61,7 @@ public class ProgressiveLoanInterestScheduleModel { private final List<RepaymentPeriod> repaymentPeriods; private final TreeSet<InterestRate> interestRates; @JsonExclude - private final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail; + private final ILoanConfigurationDetails loanProductRelatedDetail; private final Map<LoanTermVariationType, List<LoanTermVariationsData>> loanTermVariations; private final Integer installmentAmountInMultiplesOf; @JsonExclude @@ -73,8 +74,8 @@ public class ProgressiveLoanInterestScheduleModel { private LocalDate lastOverdueBalanceChange; public ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> repaymentPeriods, - final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, - final List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) { + final ILoanConfigurationDetails loanProductRelatedDetail, final List<LoanTermVariationsData> loanTermVariations, + final Integer installmentAmountInMultiplesOf, final MathContext mc) { this.repaymentPeriods = new ArrayList<>(repaymentPeriods); this.interestRates = new TreeSet<>(Collections.reverseOrder()); this.loanProductRelatedDetail = loanProductRelatedDetail; @@ -82,11 +83,12 @@ public class ProgressiveLoanInterestScheduleModel { this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; this.mc = mc; this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc); - modifiers = new HashMap<>(Map.of(EMI_RECALCULATION, true, COPY, false)); + modifiers = new HashMap<>(Map.of(EMI_RECALCULATION, true, COPY, false, INTEREST_RECALCULATION_ENABLED, + loanProductRelatedDetail.isInterestRecalculationEnabled())); } private ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> repaymentPeriods, final TreeSet<InterestRate> interestRates, - final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, + final ILoanConfigurationDetails loanProductRelatedDetail, final Map<LoanTermVariationType, List<LoanTermVariationsData>> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc, final boolean isCopiedForCalculation) { this.mc = mc; @@ -97,7 +99,8 @@ public class ProgressiveLoanInterestScheduleModel { this.loanTermVariations = loanTermVariations; this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc); - modifiers = new HashMap<>(Map.of(EMI_RECALCULATION, true, COPY, isCopiedForCalculation)); + modifiers = new HashMap<>(Map.of(EMI_RECALCULATION, true, COPY, isCopiedForCalculation, INTEREST_RECALCULATION_ENABLED, + loanProductRelatedDetail.isInterestRecalculationEnabled())); } public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) { @@ -440,4 +443,8 @@ public class ProgressiveLoanInterestScheduleModel { default -> throw new UnsupportedOperationException(); }; } + + public boolean isInterestRecalculationIsAllowed() { + return modifiers.get(INTEREST_RECALCULATION_ENABLED); + } } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java index a3735eb8f6..2367733da9 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java @@ -35,7 +35,7 @@ import org.apache.fineract.infrastructure.core.serialization.gson.JsonExclude; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; import org.apache.fineract.portfolio.util.Memo; @ToString(exclude = { "previous" }) @@ -84,13 +84,13 @@ public class RepaymentPeriod { private Money totalCapitalizedIncomeAmount; @JsonExclude @Getter - private final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail; + private final ILoanConfigurationDetails loanProductRelatedDetail; @JsonExclude private MonetaryCurrency currency; protected RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, List<InterestPeriod> interestPeriods, Money emi, Money originalEmi, Money paidPrincipal, Money paidInterest, Money futureUnrecognizedInterest, MathContext mc, - LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail) { + ILoanConfigurationDetails loanProductRelatedDetail) { this.previous = previous; this.fromDate = fromDate; this.dueDate = dueDate; @@ -104,13 +104,12 @@ public class RepaymentPeriod { this.loanProductRelatedDetail = loanProductRelatedDetail; } - public static RepaymentPeriod empty(RepaymentPeriod previous, MathContext mc, - LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail) { + public static RepaymentPeriod empty(RepaymentPeriod previous, MathContext mc, ILoanConfigurationDetails loanProductRelatedDetail) { return new RepaymentPeriod(previous, null, null, new ArrayList<>(), null, null, null, null, null, mc, loanProductRelatedDetail); } public static RepaymentPeriod create(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, Money emi, MathContext mc, - LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail) { + ILoanConfigurationDetails loanProductRelatedDetail) { final Money zero = emi.zero(); final RepaymentPeriod newRepaymentPeriod = new RepaymentPeriod(previous, fromDate, dueDate, new ArrayList<>(), emi, emi, zero, zero, zero, mc, loanProductRelatedDetail); 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 9d7c0ae4ba..3cbaffb437 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 @@ -47,9 +47,9 @@ import org.apache.fineract.portfolio.loanproduct.calc.data.InterestPeriod; import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.calc.data.RepaymentPeriod; +import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -72,8 +72,7 @@ class ProgressiveEMICalculatorTest { private static MockedStatic<ThreadLocalContextUtil> threadLocalContextUtil = Mockito.mockStatic(ThreadLocalContextUtil.class); private static MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class); private static MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN); - private static LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = Mockito - .mock(LoanProductMinimumRepaymentScheduleRelatedDetail.class); + private static ILoanConfigurationDetails loanProductRelatedDetail = Mockito.mock(ILoanConfigurationDetails.class); private static final CurrencyData currency = new CurrencyData("USD", "USD", 2, 1, "$", "USD"); diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriodTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriodTest.java index 320c7d1c89..2a6defe240 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriodTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriodTest.java @@ -32,9 +32,9 @@ import java.util.List; 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.loanproduct.domain.ILoanConfigurationDetails; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -86,8 +86,7 @@ class InterestPeriodTest { InterestPeriod period = InterestPeriod.empty(repaymentPeriod, MC); when(repaymentPeriod.getInterestPeriods()).thenReturn(List.of(period)); when(repaymentPeriod.getFirstInterestPeriod()).thenReturn(period); - LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = mock( - LoanProductMinimumRepaymentScheduleRelatedDetail.class); + ILoanConfigurationDetails loanProductRelatedDetail = mock(ILoanConfigurationDetails.class); when(loanProductRelatedDetail.getCurrencyData()).thenReturn(USD); when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.DECLINING_BALANCE); when(loanProductRelatedDetail.getInterestCalculationPeriodMethod()).thenReturn(InterestCalculationPeriodMethod.DAILY); diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriodTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriodTest.java index 4819268876..b257f73747 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriodTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriodTest.java @@ -31,9 +31,9 @@ import java.time.ZoneId; 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.loanproduct.domain.ILoanConfigurationDetails; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; -import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -47,7 +47,7 @@ class RepaymentPeriodTest { private static final Money ZERO = Money.of(USD, BigDecimal.ZERO, MC); private static MockedStatic<MoneyHelper> moneyHelper; - private static LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail; + private static ILoanConfigurationDetails loanProductRelatedDetail; @BeforeAll static void init() { @@ -55,7 +55,7 @@ class RepaymentPeriodTest { moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN); moneyHelper.when(MoneyHelper::getMathContext).thenReturn(MC); - loanProductRelatedDetail = mock(LoanProductMinimumRepaymentScheduleRelatedDetail.class); + loanProductRelatedDetail = mock(ILoanConfigurationDetails.class); when(loanProductRelatedDetail.getCurrencyData()).thenReturn(USD); when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.DECLINING_BALANCE); when(loanProductRelatedDetail.getInterestCalculationPeriodMethod()).thenReturn(InterestCalculationPeriodMethod.DAILY);
