From 39a8d5c63e2763bacdcfee132d89289e44f2a88c Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Sat, 31 May 2025 08:50:52 -0500 Subject: [PATCH] FINERACT-2181: Installment amount in multiples of should be inherited from loan product to loan level --- .../domain/LoanApplicationTerms.java | 2 +- .../mapper/LoanTermVariationsMapper.java | 5 +- .../LoanDownPaymentHandlerServiceImpl.java | 5 +- .../loanproduct/domain/LoanProduct.java | 7 +-- .../domain/LoanProductRelatedDetail.java | 12 +++-- .../module/loan/module-changelog-master.xml | 1 + ...allment_amount_in_multiples_of_to_loan.xml | 46 +++++++++++++++++++ ...edPaymentScheduleTransactionProcessor.java | 15 +++--- ...estScheduleModelRepositoryWrapperImpl.java | 2 +- .../InternalProgressiveLoanApiResource.java | 2 +- .../service/LoanScheduleAssembler.java | 15 +++--- .../service/LoanAssemblerImpl.java | 6 +-- .../service/LoanProductUpdateUtil.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 4 +- .../LoanProductReadPlatformServiceImpl.java | 2 +- ...LoanDownPaymentHandlerServiceImplTest.java | 2 +- 16 files changed, 94 insertions(+), 36 deletions(-) create mode 100644 fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1029_add_installment_amount_in_multiples_of_to_loan.xml 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 2c2cad5bb..308cc55ed 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 @@ -1616,7 +1616,7 @@ public final class LoanApplicationTerms { this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting, this.supportedInterestRefundTypes, this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate, this.daysInYearCustomStrategy, this.enableIncomeCapitalization, this.capitalizedIncomeCalculationType, this.capitalizedIncomeStrategy, - this.capitalizedIncomeType); + this.capitalizedIncomeType, this.installmentAmountInMultiplesOf); } public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTermVariationsMapper.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTermVariationsMapper.java index a11480a64..6d21260b5 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTermVariationsMapper.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTermVariationsMapper.java @@ -112,8 +112,9 @@ public class LoanTermVariationsMapper { scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), loan.getInArrearsTolerance(), loan.getLoanRepaymentScheduleDetail(), loan.getLoanProduct().isMultiDisburseLoan(), loan.getFixedEmiAmount(), disbursementData, loan.getMaxOutstandingLoanBalance(), interestChargedFromDate, - loan.getLoanProduct().getPrincipalThresholdForLastInstallment(), loan.getLoanProduct().getInstallmentAmountInMultiplesOf(), - recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, + loan.getLoanProduct().getPrincipalThresholdForLastInstallment(), + loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, + compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loan.getLoanProduct().preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, calendar, loan.getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations, calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), holidayDetailDTO, diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java index 621bad353..5e2004790 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java @@ -197,8 +197,9 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler } Money downPaymentMoney = Money.of(loan.getCurrency(), MathUtil.percentageOf(disbursementTransaction.getAmount(), disbursedAmountPercentageForDownPayment, 19)); - if (loan.getLoanProduct().getInstallmentAmountInMultiplesOf() != null) { - downPaymentMoney = Money.roundToMultiplesOf(downPaymentMoney, loan.getLoanProduct().getInstallmentAmountInMultiplesOf()); + if (loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf() != null) { + downPaymentMoney = Money.roundToMultiplesOf(downPaymentMoney, + loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf()); } final Money adjustedDownPaymentMoney = switch (loan.getLoanProductRelatedDetail().getLoanScheduleType()) { // For Cumulative loan: To check whether the loan was overpaid when the disbursement happened and to get the diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java index d9f7dbd71..f3dae22e9 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java @@ -179,9 +179,6 @@ public class LoanProduct extends AbstractPersistableCustom { @Column(name = "can_define_fixed_emi_amount") private boolean canDefineInstallmentAmount; - @Column(name = "instalment_amount_in_multiples_of") - private Integer installmentAmountInMultiplesOf; - @Column(name = "is_linked_to_floating_interest_rates", nullable = false) private boolean isLinkedToFloatingInterestRate; @@ -336,7 +333,8 @@ public class LoanProduct extends AbstractPersistableCustom { isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy, - enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType); + enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, + installmentAmountInMultiplesOf); this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments, @@ -368,7 +366,6 @@ public class LoanProduct extends AbstractPersistableCustom { this.principalThresholdForLastInstallment = principalThresholdForLastInstallment; this.accountMovesOutOfNPAOnlyOnArrearsCompletion = accountMovesOutOfNPAOnlyOnArrearsCompletion; this.canDefineInstallmentAmount = canDefineEmiAmount; - this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; this.syncExpectedWithDisbursementDate = syncExpectedWithDisbursementDate; this.canUseForTopup = canUseForTopup; this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment; 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 6ca788655..e0723e8a5 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 @@ -186,6 +186,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche @Column(name = "capitalized_income_type") private LoanCapitalizedIncomeType capitalizedIncomeType; + @Column(name = "installment_amount_in_multiples_of") + private Integer installmentAmountInMultiplesOf; + public static LoanProductRelatedDetail createFrom(final CurrencyData currencyData, final BigDecimal principal, final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod, @@ -202,7 +205,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate, final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization, final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType, - final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType) { + final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType, + final Integer installmentAmountInMultiplesOf) { final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData); return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, @@ -213,7 +217,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization, - capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType); + capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, installmentAmountInMultiplesOf); } protected LoanProductRelatedDetail() { @@ -236,7 +240,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate, final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization, final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType, - final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType) { + final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType, + final Integer installmentAmountInMultiplesOf) { this.currency = currency; this.principal = defaultPrincipal; this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod; @@ -278,6 +283,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType; this.capitalizedIncomeStrategy = capitalizedIncomeStrategy; this.capitalizedIncomeType = capitalizedIncomeType; + this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; } private Integer defaultToNullIfZero(final Integer value) { diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml index 89230b66e..50364a23a 100644 --- a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml @@ -51,4 +51,5 @@ + diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1029_add_installment_amount_in_multiples_of_to_loan.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1029_add_installment_amount_in_multiples_of_to_loan.xml new file mode 100644 index 000000000..c36d41c79 --- /dev/null +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1029_add_installment_amount_in_multiples_of_to_loan.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + UPDATE m_loan SET installment_amount_in_multiples_of = lp.installment_amount_in_multiples_of FROM ( + SELECT id, installment_amount_in_multiples_of FROM m_product_loan + ) lp WHERE lp.id = m_loan.product_id + + + + + UPDATE m_loan l inner join m_product_loan lp on lp.id = l.product_id set l.installment_amount_in_multiples_of = lp.installment_amount_in_multiples_of + + + 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 bd3622975..9ffe5c0a4 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 @@ -215,7 +215,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep final Loan loan = loanTransactions.get(0).getLoan(); List loanTermVariations = loan.getActiveLoanTermVariations().stream().map(LoanTermVariations::toData) .collect(Collectors.toCollection(ArrayList::new)); - final Integer installmentAmountInMultiplesOf = loan.getLoanProduct().getInstallmentAmountInMultiplesOf(); + final Integer installmentAmountInMultiplesOf = loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf(); final LoanProductRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail(); ProgressiveLoanInterestScheduleModel scheduleModel = emiCalculator.generateInstallmentInterestScheduleModel(installments, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, overpaymentHolder.getMoneyObject().getMc()); @@ -464,7 +464,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep .max(Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate)).get(); BigDecimal reAmortizationAmountPerInstallment = overallOverDuePrincipal.divide(BigDecimal.valueOf(futureInstallments.size()), MoneyHelper.getRoundingMode()); - Integer installmentAmountInMultiplesOf = loanTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf(); + Integer installmentAmountInMultiplesOf = loanTransaction.getLoan().getLoanProductRelatedDetail() + .getInstallmentAmountInMultiplesOf(); for (LoanRepaymentScheduleInstallment installment : futureInstallments) { if (lastFutureInstallment.equals(installment)) { @@ -1225,7 +1226,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep final MathContext mc = MoneyHelper.getMathContext(); Loan loan = disbursementTransaction.getLoan(); LoanProductRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail(); - Integer installmentAmountInMultiplesOf = loan.getLoanProduct().getInstallmentAmountInMultiplesOf(); + Integer installmentAmountInMultiplesOf = loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf(); List installments = transactionCtx.getInstallments(); LocalDate transactionDate = disbursementTransaction.getTransactionDate(); MonetaryCurrency currency = transactionCtx.getCurrency(); @@ -1259,7 +1260,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep i -> i.getDueDate().isAfter(disbursementTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional()) .toList(); LoanProductRelatedDetail loanProductRelatedDetail = disbursementTransaction.getLoan().getLoanRepaymentScheduleDetail(); - Integer installmentAmountInMultiplesOf = disbursementTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf(); + Integer installmentAmountInMultiplesOf = disbursementTransaction.getLoan().getLoanProductRelatedDetail() + .getInstallmentAmountInMultiplesOf(); Money downPaymentAmount = Money.zero(currency); if (loanProductRelatedDetail.isEnableDownPayment()) { LoanRepaymentScheduleInstallment downPaymentInstallment = installments.stream() @@ -1311,7 +1313,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep List candidateRepaymentInstallments = installments.stream().filter( i -> i.getDueDate().isAfter(capitalizedIncomeTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional()) .toList(); - Integer installmentAmountInMultiplesOf = capitalizedIncomeTransaction.getLoan().getLoanProduct() + Integer installmentAmountInMultiplesOf = capitalizedIncomeTransaction.getLoan().getLoanProduct().getLoanProductRelatedDetail() .getInstallmentAmountInMultiplesOf(); Money amortizableAmount = capitalizedIncomeTransaction.getAmount(currency); @@ -2622,7 +2624,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep if (outstandingPrincipalBalance.get().isGreaterThanZero()) { calculatedPrincipal = outstandingPrincipalBalance.get() .dividedBy(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments(), MoneyHelper.getMathContext()); - Integer installmentAmountInMultiplesOf = loanTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf(); + Integer installmentAmountInMultiplesOf = loanTransaction.getLoan().getLoanProductRelatedDetail() + .getInstallmentAmountInMultiplesOf(); if (installmentAmountInMultiplesOf != null) { calculatedPrincipal = Money.roundToMultiplesOf(calculatedPrincipal, installmentAmountInMultiplesOf); } 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 261e5d25d..2aabbaa89 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 @@ -83,7 +83,7 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched return progressiveLoanModel.map(ProgressiveLoanModel::getJsonModel) // .map(jsonModel -> progressiveLoanInterestScheduleModelParserService.fromJson(jsonModel, progressiveLoanModel.get().getLoan().getLoanProductRelatedDetail(), MoneyHelper.getMathContext(), - progressiveLoanModel.get().getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf())); + progressiveLoanModel.get().getLoan().getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf())); } @Override 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 d2a59a262..f7bbec635 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 @@ -83,7 +83,7 @@ public class InternalProgressiveLoanApiResource implements InitializingBean { } return writePlatformService.readProgressiveLoanInterestScheduleModel(loanId, loan.getLoanRepaymentScheduleDetail(), - loan.getLoanProduct().getInstallmentAmountInMultiplesOf()).orElse(null); + loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf()).orElse(null); } private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(final Loan loan) { 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 5a87e0e90..1e22e540c 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 @@ -432,8 +432,6 @@ public class LoanScheduleAssembler { final BigDecimal principalThresholdForLastInstalment = loanProduct.getPrincipalThresholdForLastInstallment(); - final Integer installmentAmountInMultiplesOf = loanProduct.getInstallmentAmountInMultiplesOf(); - List loanTermVariations = new ArrayList<>(); if (loanProduct.isLinkedToFloatingInterestRate()) { final BigDecimal interestRateDiff = this.fromApiJsonHelper @@ -538,8 +536,9 @@ public class LoanScheduleAssembler { loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, - installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO, - loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipMeetingOnFirstDay, detailDTO, + loanProduct.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf(), + loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO, loanTermVariations, + isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipMeetingOnFirstDay, detailDTO, allowCompoundingOnEod, isEqualAmortization, isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, @@ -673,8 +672,8 @@ public class LoanScheduleAssembler { } } - public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final JsonElement element, final LoanProduct loanProduct) { - final LoanApplicationTerms loanApplicationTerms = assembleLoanApplicationTermsFrom(element, loanProduct); + public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final LoanApplicationTerms loanApplicationTerms, + final JsonElement element) { LoanProductRelatedDetail loanProductRelatedDetail = loanApplicationTerms.toLoanProductRelatedDetail(); final String interestRateFrequencyTypeParamName = "interestRateFrequencyType"; if (this.fromApiJsonHelper.parameterExists(interestRateFrequencyTypeParamName, element)) { @@ -684,6 +683,10 @@ public class LoanScheduleAssembler { return loanProductRelatedDetail; } + public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final JsonElement element, final LoanProduct loanProduct) { + return assembleLoanProductRelatedDetail(assembleLoanApplicationTermsFrom(element, loanProduct), element); + } + public LoanScheduleModel assembleLoanScheduleFrom(final JsonElement element) { // This method is getting called from calculate loan schedule. final LoanApplicationTerms loanApplicationTerms = assembleLoanTerms(element); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java index a6e6ebc3d..272532567 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java @@ -221,8 +221,9 @@ public class LoanAssemblerImpl implements LoanAssembler { // Here we add Rates to LoanApplication final List rates = this.rateAssembler.fromParsedJson(element); - final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element, - loanProduct); + final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element); + final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler + .assembleLoanProductRelatedDetail(loanApplicationTerms, element); final BigDecimal interestRateDifferential = this.fromApiJsonHelper .extractBigDecimalWithLocaleNamed(LoanApiConstants.interestRateDifferentialParameterName, element); @@ -247,7 +248,6 @@ public class LoanAssemblerImpl implements LoanAssembler { isEnableInstallmentLevelDelinquency = loanProduct.isEnableInstallmentLevelDelinquency(); } - final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); Long officeId = client != null ? client.getOffice().getId() : group.getOffice().getId(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductUpdateUtil.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductUpdateUtil.java index af8d77cc3..ac28a3ea2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductUpdateUtil.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductUpdateUtil.java @@ -410,11 +410,11 @@ public class LoanProductUpdateUtil { } if (command.isChangeInIntegerParameterNamedWithNullCheck(LoanProductConstants.installmentAmountInMultiplesOfParamName, - loanProduct.getInstallmentAmountInMultiplesOf())) { + loanProduct.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf())) { final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.installmentAmountInMultiplesOfParamName); actualChanges.put(LoanProductConstants.installmentAmountInMultiplesOfParamName, newValue); actualChanges.put("locale", localeAsInput); - loanProduct.setInstallmentAmountInMultiplesOf(newValue); + loanProduct.getLoanProductRelatedDetail().setInstallmentAmountInMultiplesOf(newValue); } if (command.isChangeInBooleanParameterNamed(LoanProductConstants.CAN_USE_FOR_TOPUP, loanProduct.isCanUseForTopup())) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index c99e1cf08..bbd915081 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -444,9 +444,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .getDisbursedAmountPercentageForDownPayment(); Money downPaymentMoney = Money.of(loan.getCurrency(), MathUtil.percentageOf(amountToDisburse.getAmount(), disbursedAmountPercentageForDownPayment, 19)); - if (loan.getLoanProduct().getInstallmentAmountInMultiplesOf() != null) { + if (loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf() != null) { downPaymentMoney = Money.roundToMultiplesOf(downPaymentMoney, - loan.getLoanProduct().getInstallmentAmountInMultiplesOf()); + loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf()); } final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, downPaymentMoney.getAmount(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index 56c2ef8c9..181410b91 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -254,7 +254,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo + "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, " + "lp.disallow_expected_disbursements as disallowExpectedDisbursements, lp.allow_approved_disbursed_amounts_over_applied as allowApprovedDisbursedAmountsOverApplied, lp.over_applied_calculation_type as overAppliedCalculationType, over_applied_number as overAppliedNumber, " + "lp.days_in_month_enum as daysInMonth, lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as isInterestRecalculationEnabled, " - + "lp.can_define_fixed_emi_amount as canDefineInstallmentAmount, lp.instalment_amount_in_multiples_of as installmentAmountInMultiplesOf, " + + "lp.can_define_fixed_emi_amount as canDefineInstallmentAmount, lp.installment_amount_in_multiples_of as installmentAmountInMultiplesOf, " + "lp.due_days_for_repayment_event as dueDaysForRepaymentEvent, lp.overdue_days_for_repayment_event as overDueDaysForRepaymentEvent, lp.enable_down_payment as enableDownPayment, lp.disbursed_amount_percentage_for_down_payment as disbursedAmountPercentageForDownPayment, lp.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment, lp.repayment_start_date_type_enum as repaymentStartDateType, " + "lp.enable_installment_level_delinquency as enableInstallmentLevelDelinquency, " + "lpr.pre_close_interest_calculation_strategy as preCloseInterestCalculationStrategy, " diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java index 7323ea61b..231e1bad3 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java @@ -160,6 +160,7 @@ public class LoanDownPaymentHandlerServiceImplTest { when(downPaymentMoney.getCurrencyCode()).thenReturn(loanCurrency.getCode()); when(overPaymentPortionMoney.getCurrencyCode()).thenReturn(loanCurrency.getCode()); + when(loanRepaymentRelatedDetail.getInstallmentAmountInMultiplesOf()).thenReturn(10); when(loanForProcessing.getLoanRepaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail); when(loanForProcessing.repaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail); when(loanRepaymentRelatedDetail.isInterestRecalculationEnabled()).thenReturn(true); @@ -168,7 +169,6 @@ public class LoanDownPaymentHandlerServiceImplTest { when(loanForProcessing.getCurrency()).thenReturn(loanCurrency); when(loanForProcessing.loanCurrency()).thenReturn(loanCurrency); when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct); - when(loanProduct.getInstallmentAmountInMultiplesOf()).thenReturn(10); when(loanForProcessing.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail); when(loanTransactionProcessingService.reprocessLoanTransactions(any(), any(), any(), any(), any(), any())) .thenReturn(changedTransactionDetail);