From 233cb5af57b5fe48c5f686e3863ca1b159a010a4 Mon Sep 17 00:00:00 2001 From: Ruchi Dhamankar Date: Fri, 8 Sep 2023 20:54:05 +0530 Subject: [PATCH] FINERACT-1958-Configuration-extending-schedule-with-downpayment --- .../domain/LoanApplicationTerms.java | 20 +++- .../loanproduct/LoanProductConstants.java | 1 + .../loanproduct/domain/LoanProduct.java | 19 +++- .../domain/LoanProductRelatedDetail.java | 19 +++- .../module/loan/module-changelog-master.xml | 1 + ...tension_for_down_payment_configuration.xml | 39 ++++++++ .../api/LoansApiResourceSwagger.java | 2 + .../loanaccount/data/LoanAccountData.java | 10 +- .../service/LoanScheduleAssembler.java | 7 +- .../service/LoanReadPlatformServiceImpl.java | 5 +- .../api/LoanProductsApiResource.java | 4 +- .../api/LoanProductsApiResourceSwagger.java | 6 ++ .../loanproduct/data/LoanProductData.java | 18 +++- .../LoanProductDataValidator.java | 40 +++++++- .../LoanProductReadPlatformServiceImpl.java | 4 +- ...oductWithDownPaymentConfigurationTest.java | 97 +++++++++++++++++++ .../common/loans/LoanProductTestBuilder.java | 9 ++ 17 files changed, 275 insertions(+), 26 deletions(-) create mode 100644 fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1007_add_loan_product_schedule_extension_for_down_payment_configuration.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 5c0e6d141..a8fee74d8 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 @@ -214,6 +214,7 @@ public final class LoanApplicationTerms { private RepaymentStartDateType repaymentStartDateType; private LocalDate submittedOnDate; + private boolean isScheduleExtensionForDownPaymentDisabled; public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery, @@ -240,7 +241,7 @@ public final class LoanApplicationTerms { final BigDecimal fixedPrincipalPercentagePerInstallment, final boolean isPrincipalCompoundingDisabledForOverdueLoans, final Boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, final Boolean isAutoRepaymentForDownPaymentEnabled, final RepaymentStartDateType repaymentStartDateType, - final LocalDate submittedOnDate) { + final LocalDate submittedOnDate, final Boolean isScheduleExtensionForDownPaymentDisabled) { final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null; final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null; @@ -258,7 +259,7 @@ public final class LoanApplicationTerms { isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment, - isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate); + isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, isScheduleExtensionForDownPaymentDisabled); } @@ -308,9 +309,11 @@ public final class LoanApplicationTerms { final boolean isDownPaymentEnabled = loanProductRelatedDetail.isEnableDownPayment(); BigDecimal disbursedAmountPercentageForDownPayment = null; boolean isAutoRepaymentForDownPaymentEnabled = false; + boolean isScheduleExtensionForDownPaymentDisabled = false; if (isDownPaymentEnabled) { disbursedAmountPercentageForDownPayment = loanProductRelatedDetail.getDisbursedAmountPercentageForDownPayment(); isAutoRepaymentForDownPaymentEnabled = loanProductRelatedDetail.isEnableAutoRepaymentForDownPayment(); + isScheduleExtensionForDownPaymentDisabled = loanProductRelatedDetail.isDisableScheduleExtensionForDownPayment(); } return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentPeriodFrequencyType, ((nthDay != null) ? nthDay.getValue() : null), dayOfWeek, amortizationMethod, @@ -327,7 +330,7 @@ public final class LoanApplicationTerms { allowCompoundingOnEod, isEqualAmortization, isFirstRepaymentDateAllowedOnHoliday, isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, disbursedAmountPercentageForDownPayment, - isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate); + isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, isScheduleExtensionForDownPaymentDisabled); } private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency, @@ -355,7 +358,8 @@ public final class LoanApplicationTerms { final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI, final BigDecimal fixedPrincipalPercentagePerInstallment, final boolean isPrincipalCompoundingDisabledForOverdueLoans, final boolean isDownPaymentEnabled, final BigDecimal disbursedAmountPercentageForDownPayment, final boolean isAutoRepaymentForDownPaymentEnabled, - final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate) { + final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate, + final boolean isScheduleExtensionForDownPaymentDisabled) { this.currency = currency; this.loanTermFrequency = loanTermFrequency; @@ -437,6 +441,7 @@ public final class LoanApplicationTerms { this.isAutoRepaymentForDownPaymentEnabled = isAutoRepaymentForDownPaymentEnabled; this.repaymentStartDateType = repaymentStartDateType; this.submittedOnDate = submittedOnDate; + this.isScheduleExtensionForDownPaymentDisabled = isScheduleExtensionForDownPaymentDisabled; } public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate, @@ -1281,7 +1286,8 @@ public final class LoanApplicationTerms { this.interestPaymentGrace, this.interestChargingGrace, this.amortizationMethod, this.inArrearsTolerance.getAmount(), this.graceOnArrearsAgeing, this.daysInMonthType.getValue(), this.daysInYearType.getValue(), this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled, - this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled); + this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, + this.isScheduleExtensionForDownPaymentDisabled); } public Integer getLoanTermFrequency() { @@ -1762,4 +1768,8 @@ public final class LoanApplicationTerms { public LocalDate getSubmittedOnDate() { return submittedOnDate; } + + public boolean isScheduleExtensionForDownPaymentDisabled() { + return isScheduleExtensionForDownPaymentDisabled; + } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java index 3a3b75523..e3cd73f6d 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java @@ -152,5 +152,6 @@ public interface LoanProductConstants { String DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT = "disbursedAmountPercentageForDownPayment"; String ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT = "enableAutoRepaymentForDownPayment"; String REPAYMENT_START_DATE_TYPE = "repaymentStartDateType"; + String DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT = "disableScheduleExtensionForDownPayment"; } 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 4910a7548..2d73d2aa6 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 @@ -400,6 +400,9 @@ public class LoanProduct extends AbstractPersistableCustom { final RepaymentStartDateType repaymentStartDateType = RepaymentStartDateType .fromInt(command.integerValueOfParameterNamed(LoanProductConstants.REPAYMENT_START_DATE_TYPE)); + final boolean disableScheduleExtensionForDownPayment = command + .booleanPrimitiveValueOfParameterNamed(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT); + return new LoanProduct(fund, loanTransactionProcessingStrategy, loanProductPaymentAllocationRules, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, @@ -417,7 +420,7 @@ public class LoanProduct extends AbstractPersistableCustom { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, productRates, fixedPrincipalPercentagePerInstallment, disallowExpectedDisbursements, allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, - enableAutoRepaymentForDownPayment, repaymentStartDateType); + enableAutoRepaymentForDownPayment, repaymentStartDateType, disableScheduleExtensionForDownPayment); } @@ -631,7 +634,8 @@ public class LoanProduct extends AbstractPersistableCustom { final boolean allowApprovedDisbursedAmountsOverApplied, final String overAppliedCalculationType, final Integer overAppliedNumber, final Integer dueDaysForRepaymentEvent, final Integer overDueDaysForRepaymentEvent, final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, - final boolean enableAutoRepaymentForDownPayment, final RepaymentStartDateType repaymentStartDateType) { + final boolean enableAutoRepaymentForDownPayment, final RepaymentStartDateType repaymentStartDateType, + final boolean disableScheduleExtensionForDownPayment) { this.fund = fund; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode; @@ -673,7 +677,7 @@ public class LoanProduct extends AbstractPersistableCustom { recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod, inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(), isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, - enableAutoRepaymentForDownPayment); + enableAutoRepaymentForDownPayment, disableScheduleExtensionForDownPayment); this.loanProductRelatedDetail.validateRepaymentPeriodWithGraceSettings(); @@ -1294,6 +1298,15 @@ public class LoanProduct extends AbstractPersistableCustom { actualChanges.put(LoanProductConstants.REPAYMENT_START_DATE_TYPE, newValue); this.repaymentStartDateType = RepaymentStartDateType.fromInt(newValue); } + + if (command.isChangeInBooleanParameterNamed(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT, + this.loanProductRelatedDetail.isDisableScheduleExtensionForDownPayment())) { + final boolean newValue = command + .booleanPrimitiveValueOfParameterNamed(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT); + actualChanges.put(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT, newValue); + this.loanProductRelatedDetail.updateDisableScheduleExtensionForDownPayment(newValue); + } + return actualChanges; } 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 9b06c20d1..0ac619ded 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 @@ -138,6 +138,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche @Column(name = "enable_auto_repayment_for_down_payment", nullable = false) private boolean enableAutoRepaymentForDownPayment; + @Column(name = "disable_schedule_extension_for_down_payment", nullable = false) + private boolean disableScheduleExtensionForDownPayment; + public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal, final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod, @@ -148,14 +151,15 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final BigDecimal inArrearsTolerance, final Integer graceOnArrearsAgeing, final Integer daysInMonthType, final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization, final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, - final boolean enableAutoRepaymentForDownPayment) { + final boolean enableAutoRepaymentForDownPayment, final boolean disableScheduleExtensionForDownPayment) { return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod, inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, - isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment); + isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment, + disableScheduleExtensionForDownPayment); } protected LoanProductRelatedDetail() { @@ -172,7 +176,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final BigDecimal inArrearsTolerance, final Integer graceOnArrearsAgeing, final Integer daysInMonthType, final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization, final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, - final boolean enableAutoRepaymentForDownPayment) { + final boolean enableAutoRepaymentForDownPayment, final boolean disableScheduleExtensionForDownPayment) { this.currency = currency; this.principal = defaultPrincipal; this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod; @@ -202,6 +206,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche this.enableDownPayment = enableDownPayment; this.disbursedAmountPercentageForDownPayment = disbursedAmountPercentageForDownPayment; this.enableAutoRepaymentForDownPayment = enableAutoRepaymentForDownPayment; + this.disableScheduleExtensionForDownPayment = disableScheduleExtensionForDownPayment; } private Integer defaultToNullIfZero(final Integer value) { @@ -715,4 +720,12 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche public void updateEnableAutoRepaymentForDownPayment(boolean enableAutoRepaymentForDownPayment) { this.enableAutoRepaymentForDownPayment = enableAutoRepaymentForDownPayment; } + + public boolean isDisableScheduleExtensionForDownPayment() { + return disableScheduleExtensionForDownPayment; + } + + public void updateDisableScheduleExtensionForDownPayment(boolean disableScheduleExtensionForDownPayment) { + this.disableScheduleExtensionForDownPayment = disableScheduleExtensionForDownPayment; + } } 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 1a05adbf9..f2a28a645 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 @@ -29,4 +29,5 @@ + diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1007_add_loan_product_schedule_extension_for_down_payment_configuration.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1007_add_loan_product_schedule_extension_for_down_payment_configuration.xml new file mode 100644 index 000000000..c58174e3e --- /dev/null +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1007_add_loan_product_schedule_extension_for_down_payment_configuration.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java index d450850c4..f558eebbf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java @@ -1080,6 +1080,8 @@ final class LoansApiResourceSwagger { public BigDecimal disbursedAmountPercentageForDownPayment; @Schema(example = "false") public Boolean enableAutoRepaymentForDownPayment; + @Schema(example = "false") + public Boolean disableScheduleExtensionForDownPayment; } @Schema(description = "GetLoansResponse") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java index 575d17c65..037f0758f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java @@ -259,6 +259,7 @@ public class LoanAccountData { private Boolean enableDownPayment; private BigDecimal disbursedAmountPercentageForDownPayment; private Boolean enableAutoRepaymentForDownPayment; + private Boolean disableScheduleExtensionForDownPayment; public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId, Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments, @@ -661,7 +662,8 @@ public class LoanAccountData { final BigDecimal topupAmount, final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment, final DelinquencyRangeData delinquencyRange, final boolean disallowExpectedDisbursements, final boolean fraud, LocalDate lastClosedBusinessDate, LocalDate overpaidOnDate, final boolean chargedOff, final boolean enableDownPayment, - final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment) { + final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment, + final boolean disableScheduleExtensionForDownPayment) { final CollectionData delinquent = CollectionData.template(); @@ -703,7 +705,8 @@ public class LoanAccountData { .setDelinquencyRange(delinquencyRange).setDisallowExpectedDisbursements(disallowExpectedDisbursements).setFraud(fraud) .setLastClosedBusinessDate(lastClosedBusinessDate).setOverpaidOnDate(overpaidOnDate).setChargedOff(chargedOff) .setEnableDownPayment(enableDownPayment).setDisbursedAmountPercentageForDownPayment(disbursedAmountPercentageForDownPayment) - .setEnableAutoRepaymentForDownPayment(enableAutoRepaymentForDownPayment); + .setEnableAutoRepaymentForDownPayment(enableAutoRepaymentForDownPayment) + .setDisableScheduleExtensionForDownPayment(disableScheduleExtensionForDownPayment); } /* @@ -790,7 +793,8 @@ public class LoanAccountData { .setFraud(acc.fraud).setLastClosedBusinessDate(acc.getLastClosedBusinessDate()).setOverpaidOnDate(acc.overpaidOnDate) .setChargedOff(acc.chargedOff).setEnableDownPayment(acc.enableDownPayment) .setDisbursedAmountPercentageForDownPayment(acc.disbursedAmountPercentageForDownPayment) - .setEnableAutoRepaymentForDownPayment(acc.enableAutoRepaymentForDownPayment); + .setEnableAutoRepaymentForDownPayment(acc.enableAutoRepaymentForDownPayment) + .setDisableScheduleExtensionForDownPayment(acc.disableScheduleExtensionForDownPayment); } public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection productOptions, 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 9b7d5e077..f0e31f2ce 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 @@ -462,10 +462,14 @@ public class LoanScheduleAssembler { final boolean isDownPaymentEnabled = loanProduct.getLoanProductRelatedDetail().isEnableDownPayment(); BigDecimal disbursedAmountPercentageForDownPayment = null; boolean isAutoRepaymentForDownPaymentEnabled = false; + boolean isScheduleExtensionForDownPaymentDisabled = false; if (isDownPaymentEnabled) { disbursedAmountPercentageForDownPayment = loanProduct.getLoanProductRelatedDetail() .getDisbursedAmountPercentageForDownPayment(); isAutoRepaymentForDownPaymentEnabled = loanProduct.getLoanProductRelatedDetail().isEnableAutoRepaymentForDownPayment(); + isScheduleExtensionForDownPaymentDisabled = loanProduct.getLoanProductRelatedDetail() + .isDisableScheduleExtensionForDownPayment(); + } return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod, @@ -480,7 +484,8 @@ public class LoanScheduleAssembler { loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipMeetingOnFirstDay, detailDTO, allowCompoundingOnEod, isEqualAmortization, isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, - disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate); + disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, + isScheduleExtensionForDownPaymentDisabled); } private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 6834d360a..891d2bcd0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -697,7 +697,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa + " lp.can_use_for_topup as canUseForTopup, l.is_topup as isTopup, topup.closure_loan_id as closureLoanId, " + " l.total_recovered_derived as totalRecovered, topuploan.account_no as closureLoanAccountNo, " + " topup.topup_amount as topupAmount, l.last_closed_business_date as lastClosedBusinessDate,l.overpaidon_date as overpaidOnDate, " - + " l.is_charged_off as isChargedOff, l.charge_off_reason_cv_id as chargeOffReasonId, codec.code_value as chargeOffReason, l.charged_off_on_date as chargedOffOnDate, l.enable_down_payment as enableDownPayment, l.disbursed_amount_percentage_for_down_payment as disbursedAmountPercentageForDownPayment, l.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment," + + " l.is_charged_off as isChargedOff, l.charge_off_reason_cv_id as chargeOffReasonId, codec.code_value as chargeOffReason, l.charged_off_on_date as chargedOffOnDate, l.enable_down_payment as enableDownPayment, l.disbursed_amount_percentage_for_down_payment as disbursedAmountPercentageForDownPayment, l.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment, l.disable_schedule_extension_for_down_payment as disableScheduleExtensionForDownPayment," + " cobu.username as chargedOffByUsername, cobu.firstname as chargedOffByFirstname, cobu.lastname as chargedOffByLastname " + " from m_loan l" // + " join m_product_loan lp on lp.id = l.product_id" // @@ -1044,6 +1044,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa final boolean enableDownPayment = rs.getBoolean("enableDownPayment"); final BigDecimal disbursedAmountPercentageForDownPayment = rs.getBigDecimal("disbursedAmountPercentageForDownPayment"); final boolean enableAutoRepaymentForDownPayment = rs.getBoolean("enableAutoRepaymentForDownPayment"); + final boolean disableScheduleExtensionForDownPayment = rs.getBoolean("disableScheduleExtensionForDownPayment"); return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, @@ -1061,7 +1062,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud, lastClosedBusinessDate, overpaidOnDate, isChargedOff, enableDownPayment, disbursedAmountPercentageForDownPayment, - enableAutoRepaymentForDownPayment); + enableAutoRepaymentForDownPayment, disableScheduleExtensionForDownPayment); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java index c607f4314..1f06f9f69 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java @@ -116,7 +116,7 @@ public class LoanProductsApiResource { LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, - LoanProductConstants.REPAYMENT_START_DATE_TYPE)); + LoanProductConstants.REPAYMENT_START_DATE_TYPE, LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT)); private static final Set PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>( Arrays.asList("productId", "productName", "restrictedProducts", "allowedProducts", "productOptions")); @@ -151,7 +151,7 @@ public class LoanProductsApiResource { @Operation(summary = "Create a Loan Product", description = "Depending of the Accounting Rule (accountingRule) selected, additional fields with details of the appropriate Ledger Account identifiers would need to be passed in.\n" + "\n" + "Refer MifosX Accounting Specs Draft for more details regarding the significance of the selected accounting rule\n\n" + "Mandatory Fields: name, shortName, currencyCode, digitsAfterDecimal, inMultiplesOf, principal, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyCode, accountingRule, isInterestRecalculationEnabled, daysInYearType, daysInMonthType\n\n" - + "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, includeInBorrowerCycle, useBorrowerCycle,principalVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides, allowPartialPeriodInterestCalcualtion,dueDaysForRepaymentEvent,overDueDaysForRepaymentEvent,enableDownPayment,disbursedAmountPercentageDownPayment,enableAutoRepaymentForDownPayment,repaymentStartDateType\n\n" + + "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, includeInBorrowerCycle, useBorrowerCycle,principalVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides, allowPartialPeriodInterestCalcualtion,dueDaysForRepaymentEvent,overDueDaysForRepaymentEvent,enableDownPayment,disbursedAmountPercentageDownPayment,enableAutoRepaymentForDownPayment,repaymentStartDateType,disableScheduleExtensionForDownPayment\n\n" + "Additional Mandatory Fields for Cash(2) based accounting: fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n" + "Additional Mandatory Fields for periodic (3) and upfront (4)accrual accounting: fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId, transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n" + "Additional Mandatory Fields if interest recalculation is enabled(true): interestRecalculationCompoundingMethod, rescheduleStrategyMethod, recalculationRestFrequencyType\n\n" diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java index 024cb74da..4f891f80e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java @@ -164,6 +164,8 @@ final class LoanProductsApiResourceSwagger { public Boolean enableAutoRepaymentForDownPayment; @Schema(example = "1") public Integer repaymentStartDateType; + @Schema(example = "false") + public Boolean disableScheduleExtensionForDownPayment; // Interest Recalculation @Schema(example = "false") @@ -1250,6 +1252,8 @@ final class LoanProductsApiResourceSwagger { @Schema(example = "false") public Boolean enableAutoRepaymentForDownPayment; public GetLoanProductsRepaymentStartDateType repaymentStartDateType; + @Schema(example = "false") + public Boolean disableScheduleExtensionForDownPayment; } @Schema(description = "PutLoanProductsProductIdRequest") @@ -1380,6 +1384,8 @@ final class LoanProductsApiResourceSwagger { public Boolean enableAutoRepaymentForDownPayment; @Schema(example = "1") public Integer repaymentStartDateType; + @Schema(example = "false") + public Boolean disableScheduleExtensionForDownPayment; // Interest Recalculation @Schema(example = "false") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java index 8cba2c7ff..6a2f235c3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java @@ -203,6 +203,7 @@ public class LoanProductData implements Serializable { private final boolean enableDownPayment; private final BigDecimal disbursedAmountPercentageForDownPayment; private final boolean enableAutoRepaymentForDownPayment; + private final boolean disableScheduleExtensionForDownPayment; /** * Used when returning lookup information about loan product for dropdowns. @@ -297,6 +298,7 @@ public class LoanProductData implements Serializable { final Collection paymentAllocation = null; final boolean enableAutoRepaymentForDownPayment = false; final EnumOptionData repaymentStartDateType = null; + final boolean disableScheduleExtensionForDownPayment = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -316,7 +318,7 @@ public class LoanProductData implements Serializable { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, - paymentAllocation, repaymentStartDateType); + paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment); } @@ -411,6 +413,7 @@ public class LoanProductData implements Serializable { final boolean enableAutoRepaymentForDownPayment = false; final Collection paymentAllocation = null; final EnumOptionData repaymentStartDateType = null; + final boolean disableScheduleExtensionForDownPayment = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -430,7 +433,7 @@ public class LoanProductData implements Serializable { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, - paymentAllocation, repaymentStartDateType); + paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment); } @@ -532,6 +535,7 @@ public class LoanProductData implements Serializable { final boolean enableAutoRepaymentForDownPayment = false; final Collection paymentAllocation = null; final EnumOptionData repaymentStartDateType = LoanEnumerations.repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE); + final boolean disableScheduleExtensionForDownPayment = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -551,7 +555,7 @@ public class LoanProductData implements Serializable { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, - paymentAllocation, repaymentStartDateType); + paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment); } @@ -647,6 +651,7 @@ public class LoanProductData implements Serializable { final boolean enableAutoRepaymentForDownPayment = false; final Collection paymentAllocation = null; final EnumOptionData repaymentStartDateType = LoanEnumerations.repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE); + final boolean disableScheduleExtensionForDownPayment = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -666,7 +671,7 @@ public class LoanProductData implements Serializable { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, - paymentAllocation, repaymentStartDateType); + paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment); } public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map accountingMappings, @@ -714,7 +719,8 @@ public class LoanProductData implements Serializable { final DelinquencyBucketData delinquencyBucket, final Integer dueDaysForRepaymentEvent, final Integer overDueDaysForRepaymentEvent, final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment, - final Collection paymentAllocation, final EnumOptionData repaymentStartDateType) { + final Collection paymentAllocation, final EnumOptionData repaymentStartDateType, + final boolean disableScheduleExtensionForDownPayment) { this.id = id; this.name = name; this.shortName = shortName; @@ -841,6 +847,7 @@ public class LoanProductData implements Serializable { this.advancedPaymentAllocationTransactionTypes = null; this.advancedPaymentAllocationFutureInstallmentAllocationRules = null; this.advancedPaymentAllocationTypes = null; + this.disableScheduleExtensionForDownPayment = disableScheduleExtensionForDownPayment; } public LoanProductData(final LoanProductData productData, final Collection chargeOptions, @@ -1004,6 +1011,7 @@ public class LoanProductData implements Serializable { this.advancedPaymentAllocationTransactionTypes = advancedPaymentAllocationTransactionTypes; this.advancedPaymentAllocationFutureInstallmentAllocationRules = advancedPaymentAllocationFutureInstallmentAllocationRules; this.advancedPaymentAllocationTypes = advancedPaymentAllocationTypes; + this.disableScheduleExtensionForDownPayment = productData.disableScheduleExtensionForDownPayment; } private Collection nullIfEmpty(final Collection charges) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java index c130c51a8..1e3a7eacb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -162,7 +162,8 @@ public final class LoanProductDataValidator { LoanProductConstants.OVER_APPLIED_NUMBER, LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, - LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.REPAYMENT_START_DATE_TYPE)); + LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.REPAYMENT_START_DATE_TYPE, + LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT)); private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName, LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName, @@ -753,6 +754,7 @@ public final class LoanProductDataValidator { .validateForBooleanValue(); validateDownPaymentPercentage(enableDownPayment, baseDataValidator, element); validateAutoRepaymentForDownPayment(enableDownPayment, baseDataValidator, element); + validateScheduleExtensionForDownPayment(enableDownPayment, baseDataValidator, element, null); } if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.REPAYMENT_START_DATE_TYPE, element)) { @@ -765,6 +767,41 @@ public final class LoanProductDataValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } + private void validateScheduleExtensionForDownPayment(Boolean enableDownPayment, DataValidatorBuilder baseDataValidator, + JsonElement element, final LoanProduct loanProduct) { + + Boolean multiDisburseLoan = null; + if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME, element)) { + multiDisburseLoan = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME, + element); + } else if (loanProduct != null) { + multiDisburseLoan = loanProduct.isMultiDisburseLoan(); + } + + if (multiDisburseLoan != null && multiDisburseLoan) { + if (enableDownPayment) { + if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT, element)) { + final Boolean disableScheduleExtensionForDownPayment = this.fromApiJsonHelper + .extractBooleanNamed(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT, element); + baseDataValidator.reset().parameter(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT) + .value(disableScheduleExtensionForDownPayment).ignoreIfNull().validateForBooleanValue(); + } + } else { + if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT, element)) { + baseDataValidator.reset().parameter(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT).failWithCode( + "supported.only.for.multi.disburse.loan.with.enable.down.payment.true", + "Disable repayment schedule extension for down-payment is supported only for multi disburse loan with enable down-payment true"); + } + } + } else { + if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT, element)) { + baseDataValidator.reset().parameter(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT).failWithCode( + "supported.only.for.multi.disburse.loan.with.enable.down.payment.true", + "Disable repayment schedule extension for down-payment is supported only for multi disburse loan with enable down-payment true"); + } + } + } + private void validateAutoRepaymentForDownPayment(Boolean enableDownPayment, DataValidatorBuilder baseDataValidator, JsonElement element) { if (enableDownPayment) { @@ -1685,6 +1722,7 @@ public final class LoanProductDataValidator { .validateForBooleanValue(); validateDownPaymentPercentage(enableDownPayment, baseDataValidator, element); validateAutoRepaymentForDownPayment(enableDownPayment, baseDataValidator, element); + validateScheduleExtensionForDownPayment(enableDownPayment, baseDataValidator, element, loanProduct); } Integer repaymentStartDateType = loanProduct.getRepaymentStartDateType().getValue(); 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 d8e890604..a4f990b27 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 @@ -230,6 +230,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo + "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.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.disable_schedule_extension_for_down_payment as disableScheduleExtensionForDownPayment, " + "lpr.pre_close_interest_calculation_strategy as preCloseInterestCalculationStrategy, " + "lpr.id as lprId, lpr.product_id as productId, lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as rescheduleStrategy, " + "lpr.rest_frequency_type_enum as restFrequencyEnum, lpr.rest_frequency_interval as restFrequencyInterval, " @@ -366,6 +367,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final boolean enableAutoRepaymentForDownPayment = rs.getBoolean("enableAutoRepaymentForDownPayment"); final Integer repaymentStartDateTypeId = JdbcSupport.getInteger(rs, "repaymentStartDateType"); final EnumOptionData repaymentStartDateType = LoanEnumerations.repaymentStartDateType(repaymentStartDateTypeId); + final boolean disableScheduleExtensionForDownPayment = rs.getBoolean("disableScheduleExtensionForDownPayment"); String status = ""; if (closeDate != null && closeDate.isBefore(DateUtils.getBusinessLocalDate())) { @@ -523,7 +525,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, this.rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageForDownPayment, - enableAutoRepaymentForDownPayment, advancedPaymentData, repaymentStartDateType); + enableAutoRepaymentForDownPayment, advancedPaymentData, repaymentStartDateType, disableScheduleExtensionForDownPayment); } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductWithDownPaymentConfigurationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductWithDownPaymentConfigurationTest.java index 8ecb2be3c..b07561d42 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductWithDownPaymentConfigurationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductWithDownPaymentConfigurationTest.java @@ -459,6 +459,89 @@ public class LoanProductWithDownPaymentConfigurationTest { } + @Test + public void loanProductAndLoanAccountCreationWithEnableDownPaymentAndDisableRepaymentScheduleExtensionConfigurationTest() { + // Loan ExternalId + String loanExternalIdStr = UUID.randomUUID().toString(); + + // down-payment configuration + Boolean enableDownPayment = true; + BigDecimal disbursedAmountPercentageForDownPayment = BigDecimal.valueOf(25); + Boolean enableAutoRepaymentForDownPayment = false; + Boolean disableScheduleExtensionForDownPayment = true; + + final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue(); + + // Loan Product creation with down-payment configuration + GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProductWithEnableDownPaymentAndMultipleDisbursementsWithDisableRepaymentConfiguration( + loanTransactionHelper, enableDownPayment, "25", enableAutoRepaymentForDownPayment, disableScheduleExtensionForDownPayment); + assertNotNull(getLoanProductsProductResponse); + assertEquals(enableDownPayment, getLoanProductsProductResponse.getEnableDownPayment()); + assertEquals(0, getLoanProductsProductResponse.getDisbursedAmountPercentageForDownPayment() + .compareTo(disbursedAmountPercentageForDownPayment)); + assertEquals(enableAutoRepaymentForDownPayment, getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment()); + assertEquals(disableScheduleExtensionForDownPayment, getLoanProductsProductResponse.getDisableScheduleExtensionForDownPayment()); + + final Integer loanId = createLoanAccountMultipleRepaymentsDisbursement(clientId, getLoanProductsProductResponse.getId(), + loanExternalIdStr); + + // Retrieve Loan with loanId + + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); + + // verify down-payment details for Loan + assertNotNull(loanDetails); + assertEquals(enableDownPayment, loanDetails.getEnableDownPayment()); + assertEquals(0, loanDetails.getDisbursedAmountPercentageForDownPayment().compareTo(disbursedAmountPercentageForDownPayment)); + assertEquals(enableAutoRepaymentForDownPayment, loanDetails.getEnableAutoRepaymentForDownPayment()); + assertEquals(disableScheduleExtensionForDownPayment, loanDetails.getDisableScheduleExtensionForDownPayment()); + } + + @Test + public void loanProductCreationWithEnableDownPaymentAndDisableRepaymentScheduleExtensionConfigurationValidationTest() { + final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(400).build(); + final LoanTransactionHelper validationErrorHelper = new LoanTransactionHelper(this.requestSpec, errorResponse); + + // down-payment configuration + Boolean enableDownPayment = true; + Boolean enableAutoRepaymentForDownPayment = false; + Boolean disableScheduleExtensionForDownPayment = true; + + // Loan Product with no multi disbursement settings and enable down payment and with disable Schedule Extension + // For DownPayment + String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth().withRepaymentAfterEvery("1") + .withNumberOfRepayments("3").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0") + .withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance() + .withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30").withDaysInYear("365") + .withMoratorium("0", "0").withEnableDownPayment(enableDownPayment, "25", enableAutoRepaymentForDownPayment) + .withDisableScheduleExtensionForDownPayment(disableScheduleExtensionForDownPayment).build(null); + + ArrayList> loanProductErrorData = validationErrorHelper.getLoanProductError(loanProductJSON, + CommonConstants.RESPONSE_ERROR); + assertNotNull(loanProductErrorData); + assertEquals( + "validation.msg.loanproduct.disableScheduleExtensionForDownPayment.supported.only.for.multi.disburse.loan.with.enable.down.payment.true", + loanProductErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); + + // Loan Product with multi disbursement settings and disable down payment and with disable Schedule Extension + // For DownPayment + enableDownPayment = false; + loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth().withRepaymentAfterEvery("1") + .withNumberOfRepayments("3").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0") + .withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance() + .withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30").withDaysInYear("365") + .withMoratorium("0", "0").withMultiDisburse().withDisallowExpectedDisbursements(true) + .withEnableDownPayment(enableDownPayment, null, enableAutoRepaymentForDownPayment) + .withDisableScheduleExtensionForDownPayment(disableScheduleExtensionForDownPayment).build(null); + + loanProductErrorData = validationErrorHelper.getLoanProductError(loanProductJSON, CommonConstants.RESPONSE_ERROR); + assertNotNull(loanProductErrorData); + assertEquals( + "validation.msg.loanproduct.disableScheduleExtensionForDownPayment.supported.only.for.multi.disburse.loan.with.enable.down.payment.true", + loanProductErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); + + } + private void checkNoDownPaymentTransaction(final Integer loanID) { ArrayList transactions = (ArrayList) loanTransactionHelper.getLoanTransactions(this.requestSpec, this.responseSpec, loanID); @@ -576,4 +659,18 @@ public class LoanProductWithDownPaymentConfigurationTest { return loanId; } + private GetLoanProductsProductIdResponse createLoanProductWithEnableDownPaymentAndMultipleDisbursementsWithDisableRepaymentConfiguration( + LoanTransactionHelper loanTransactionHelper, Boolean enableDownPayment, String disbursedAmountPercentageForDownPayment, + boolean enableAutoRepaymentForDownPayment, boolean disableScheduleExtensionForDownPayment) { + final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth() + .withRepaymentAfterEvery("1").withNumberOfRepayments("3").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0") + .withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance() + .withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30").withDaysInYear("365") + .withMoratorium("0", "0").withMultiDisburse().withDisallowExpectedDisbursements(true) + .withEnableDownPayment(enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment) + .withDisableScheduleExtensionForDownPayment(disableScheduleExtensionForDownPayment).build(null); + final Integer loanProductId = loanTransactionHelper.getLoanProductId(loanProductJSON); + return loanTransactionHelper.getLoanProduct(loanProductId); + } + } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java index c648c544c..74a16498c 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java @@ -150,6 +150,7 @@ public class LoanProductTestBuilder { private String disbursedAmountPercentageForDownPayment = null; private boolean enableAutoRepaymentForDownPayment = false; private Integer repaymentStartDateType = null; + private boolean disableScheduleExtensionForDownPayment = false; public String build() { final HashMap map = build(null, null); @@ -303,6 +304,9 @@ public class LoanProductTestBuilder { if (this.repaymentStartDateType != null) { map.put("repaymentStartDateType", repaymentStartDateType); } + if (disableScheduleExtensionForDownPayment) { + map.put("disableScheduleExtensionForDownPayment", disableScheduleExtensionForDownPayment); + } return map; } @@ -733,4 +737,9 @@ public class LoanProductTestBuilder { return this; } + public LoanProductTestBuilder withDisableScheduleExtensionForDownPayment(final Boolean disableScheduleExtensionForDownPayment) { + this.disableScheduleExtensionForDownPayment = disableScheduleExtensionForDownPayment; + return this; + } + }