From 73b77b94144e23faabed7b631ad209d8c966df62 Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Sun, 15 Oct 2023 21:38:27 -0600 Subject: [PATCH] FINERACT-1992: Multi-dlnqcy - Configuration of Installment level delinquency --- .../portfolio/loanaccount/domain/Loan.java | 12 ++++++ .../loanproduct/LoanProductConstants.java | 2 + .../loanproduct/domain/LoanProduct.java | 29 +++++++++++++- .../domain/LoanProductRelatedDetail.java | 1 + .../api/LoansApiResourceSwagger.java | 2 + .../loanaccount/data/LoanAccountData.java | 24 ++++++++---- .../loanaccount/service/LoanAssembler.java | 2 + .../service/LoanReadPlatformServiceImpl.java | 5 ++- .../api/LoanProductsApiResourceSwagger.java | 6 +++ .../loanproduct/data/LoanProductData.java | 17 +++++--- .../LoanProductDataValidator.java | 16 +++++++- .../LoanProductReadPlatformServiceImpl.java | 6 ++- .../db/changelog/tenant/changelog-tenant.xml | 1 + ..._product_installment_level_delinquency.xml | 39 +++++++++++++++++++ 14 files changed, 142 insertions(+), 20 deletions(-) create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0126_add_loan_product_installment_level_delinquency.xml diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index bb5227f31..6bd37070f 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -460,6 +460,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { @JoinColumn(name = "charged_off_by_userid") private AppUser chargedOffBy; + @Column(name = "enable_installment_level_delinquency", nullable = false) + private boolean enableInstallmentLevelDelinquency = false; + public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose, final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail, @@ -7146,4 +7149,13 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { public String getTransactionProcessingStrategyName() { return transactionProcessingStrategyName; } + + public boolean isEnableInstallmentLevelDelinquency() { + return this.enableInstallmentLevelDelinquency; + } + + public void updateEnableInstallmentLevelDelinquency(boolean enableInstallmentLevelDelinquency) { + this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency; + } + } 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 e3cd73f6d..ee1f6ad42 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 @@ -154,4 +154,6 @@ public interface LoanProductConstants { String REPAYMENT_START_DATE_TYPE = "repaymentStartDateType"; String DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT = "disableScheduleExtensionForDownPayment"; + String ENABLE_INSTALLMENT_LEVEL_DELINQUENCY = "enableInstallmentLevelDelinquency"; + } 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 2d73d2aa6..66c2dc171 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 @@ -207,6 +207,9 @@ public class LoanProduct extends AbstractPersistableCustom { @JoinColumn(name = "delinquency_bucket_id") private DelinquencyBucket delinquencyBucket; + @Column(name = "enable_installment_level_delinquency", nullable = false) + private boolean enableInstallmentLevelDelinquency = false; + @Column(name = "due_days_for_repayment_event") private Integer dueDaysForRepaymentEvent; @@ -403,6 +406,9 @@ public class LoanProduct extends AbstractPersistableCustom { final boolean disableScheduleExtensionForDownPayment = command .booleanPrimitiveValueOfParameterNamed(LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT); + final boolean enableInstallmentLevelDelinquency = command + .booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY); + return new LoanProduct(fund, loanTransactionProcessingStrategy, loanProductPaymentAllocationRules, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, @@ -420,7 +426,8 @@ public class LoanProduct extends AbstractPersistableCustom { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, productRates, fixedPrincipalPercentagePerInstallment, disallowExpectedDisbursements, allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, - enableAutoRepaymentForDownPayment, repaymentStartDateType, disableScheduleExtensionForDownPayment); + enableAutoRepaymentForDownPayment, repaymentStartDateType, disableScheduleExtensionForDownPayment, + enableInstallmentLevelDelinquency); } @@ -635,7 +642,7 @@ public class LoanProduct extends AbstractPersistableCustom { final Integer overAppliedNumber, final Integer dueDaysForRepaymentEvent, final Integer overDueDaysForRepaymentEvent, final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment, final RepaymentStartDateType repaymentStartDateType, - final boolean disableScheduleExtensionForDownPayment) { + final boolean disableScheduleExtensionForDownPayment, final boolean enableInstallmentLevelDelinquency) { this.fund = fund; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode; @@ -731,6 +738,8 @@ public class LoanProduct extends AbstractPersistableCustom { this.overDueDaysForRepaymentEvent = overDueDaysForRepaymentEvent; this.repaymentStartDateType = repaymentStartDateType; + this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency; + validateLoanProductPreSave(); } @@ -1307,6 +1316,14 @@ public class LoanProduct extends AbstractPersistableCustom { this.loanProductRelatedDetail.updateDisableScheduleExtensionForDownPayment(newValue); } + if (command.isChangeInBooleanParameterNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, + this.isEnableInstallmentLevelDelinquency())) { + final boolean newValue = command + .booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY); + actualChanges.put(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, newValue); + this.updateEnableInstallmentLevelDelinquency(newValue); + } + return actualChanges; } @@ -1706,4 +1723,12 @@ public class LoanProduct extends AbstractPersistableCustom { return this.repaymentStartDateType == null ? RepaymentStartDateType.INVALID : this.repaymentStartDateType; } + public boolean isEnableInstallmentLevelDelinquency() { + return enableInstallmentLevelDelinquency; + } + + public void updateEnableInstallmentLevelDelinquency(boolean enableInstallmentLevelDelinquency) { + this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency; + } + } 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 0ac619ded..b255ff9ea 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 @@ -728,4 +728,5 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche public void updateDisableScheduleExtensionForDownPayment(boolean disableScheduleExtensionForDownPayment) { this.disableScheduleExtensionForDownPayment = disableScheduleExtensionForDownPayment; } + } 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 e71f4203e..b27bccbf7 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 @@ -1067,6 +1067,8 @@ final class LoansApiResourceSwagger { public GetDelinquencyRangesResponse delinquencyRange; @Schema(example = "false") public Boolean fraud; + @Schema(example = "false") + public Boolean enableInstallmentLevelDelinquency; @Schema(example = "250.000000") public Double totalOverpaid; public LocalDate lastClosedBusinessDate; 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 037f0758f..105626159 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 @@ -253,6 +253,7 @@ public class LoanAccountData { private LocalDate overpaidOnDate; private CollectionData delinquent; private DelinquencyRangeData delinquencyRange; + private Boolean enableInstallmentLevelDelinquency; private LocalDate lastClosedBusinessDate; private Boolean chargedOff; @@ -663,7 +664,7 @@ public class LoanAccountData { 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 boolean disableScheduleExtensionForDownPayment) { + final boolean disableScheduleExtensionForDownPayment, final boolean enableInstallmentLevelDelinquency) { final CollectionData delinquent = CollectionData.template(); @@ -706,7 +707,8 @@ public class LoanAccountData { .setLastClosedBusinessDate(lastClosedBusinessDate).setOverpaidOnDate(overpaidOnDate).setChargedOff(chargedOff) .setEnableDownPayment(enableDownPayment).setDisbursedAmountPercentageForDownPayment(disbursedAmountPercentageForDownPayment) .setEnableAutoRepaymentForDownPayment(enableAutoRepaymentForDownPayment) - .setDisableScheduleExtensionForDownPayment(disableScheduleExtensionForDownPayment); + .setDisableScheduleExtensionForDownPayment(disableScheduleExtensionForDownPayment) + .setEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency); } /* @@ -794,7 +796,8 @@ public class LoanAccountData { .setChargedOff(acc.chargedOff).setEnableDownPayment(acc.enableDownPayment) .setDisbursedAmountPercentageForDownPayment(acc.disbursedAmountPercentageForDownPayment) .setEnableAutoRepaymentForDownPayment(acc.enableAutoRepaymentForDownPayment) - .setDisableScheduleExtensionForDownPayment(acc.disableScheduleExtensionForDownPayment); + .setDisableScheduleExtensionForDownPayment(acc.disableScheduleExtensionForDownPayment) + .setEnableInstallmentLevelDelinquency(acc.enableInstallmentLevelDelinquency); } public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection productOptions, @@ -869,7 +872,8 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff) + .setEnableInstallmentLevelDelinquency(acc.enableInstallmentLevelDelinquency); } public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map memberLoanCycle) { @@ -966,7 +970,8 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff) + .setEnableInstallmentLevelDelinquency(acc.enableInstallmentLevelDelinquency); } public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData, @@ -1031,7 +1036,8 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff) + .setEnableInstallmentLevelDelinquency(acc.enableInstallmentLevelDelinquency); } public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) { @@ -1089,7 +1095,8 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff) + .setEnableInstallmentLevelDelinquency(acc.enableInstallmentLevelDelinquency); } public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) { @@ -1150,7 +1157,8 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate).setChargedOff(acc.chargedOff) + .setEnableInstallmentLevelDelinquency(acc.enableInstallmentLevelDelinquency); } public static final Comparator ClientNameComparator = (loan1, loan2) -> { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java index 80f7a0770..2264b9c85 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java @@ -306,6 +306,8 @@ public class LoanAssembler { } } + loanApplication.updateEnableInstallmentLevelDelinquency(loanProduct.isEnableInstallmentLevelDelinquency()); + final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(), 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 4e71c64df..caedd8077 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 @@ -643,7 +643,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa + " l.fixed_principal_percentage_per_installment fixedPrincipalPercentagePerInstallment, " + " l.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion," + " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_code as transactionStrategyCode, " - + " l.loan_transaction_strategy_name as transactionStrategyName, " + + " l.loan_transaction_strategy_name as transactionStrategyName, l.enable_installment_level_delinquency as enableInstallmentLevelDelinquency, " + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc." + sqlGenerator.escape("name") + " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " @@ -1043,6 +1043,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa final BigDecimal disbursedAmountPercentageForDownPayment = rs.getBigDecimal("disbursedAmountPercentageForDownPayment"); final boolean enableAutoRepaymentForDownPayment = rs.getBoolean("enableAutoRepaymentForDownPayment"); final boolean disableScheduleExtensionForDownPayment = rs.getBoolean("disableScheduleExtensionForDownPayment"); + final boolean enableInstallmentLevelDelinquency = rs.getBoolean("enableInstallmentLevelDelinquency"); return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, @@ -1060,7 +1061,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud, lastClosedBusinessDate, overpaidOnDate, isChargedOff, enableDownPayment, disbursedAmountPercentageForDownPayment, - enableAutoRepaymentForDownPayment, disableScheduleExtensionForDownPayment); + enableAutoRepaymentForDownPayment, disableScheduleExtensionForDownPayment, enableInstallmentLevelDelinquency); } } 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 4f891f80e..00fe829a3 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 @@ -152,6 +152,8 @@ final class LoanProductsApiResourceSwagger { public Boolean holdGuaranteeFunds; @Schema(example = "1") public Long delinquencyBucketId; + @Schema(example = "false") + public Boolean enableInstallmentLevelDelinquency; @Schema(example = "3") public Integer dueDaysForRepaymentEvent; @Schema(example = "3") @@ -1237,6 +1239,8 @@ final class LoanProductsApiResourceSwagger { @Schema(example = "50") public Integer principalThresholdForLastInstalment; public GetDelinquencyBucketsResponse delinquencyBucket; + @Schema(example = "false") + public Boolean enableInstallmentLevelDelinquency; @Schema(example = "true") public Boolean disallowExpectedDisbursements; @Schema(example = "3") @@ -1372,6 +1376,8 @@ final class LoanProductsApiResourceSwagger { public Boolean holdGuaranteeFunds; @Schema(example = "1") public Long delinquencyBucketId; + @Schema(example = "false") + public Boolean enableInstallmentLevelDelinquency; @Schema(example = "3") public Integer dueDaysForRepaymentEvent; @Schema(example = "3") 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 6da6c4e33..49b98021a 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 @@ -207,6 +207,7 @@ public class LoanProductData implements Serializable { private final BigDecimal disbursedAmountPercentageForDownPayment; private final boolean enableAutoRepaymentForDownPayment; private final boolean disableScheduleExtensionForDownPayment; + private final boolean enableInstallmentLevelDelinquency; /** * Used when returning lookup information about loan product for dropdowns. @@ -302,6 +303,7 @@ public class LoanProductData implements Serializable { final boolean enableAutoRepaymentForDownPayment = false; final EnumOptionData repaymentStartDateType = null; final boolean disableScheduleExtensionForDownPayment = false; + final boolean enableInstallmentLevelDelinquency = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -321,7 +323,7 @@ public class LoanProductData implements Serializable { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, - paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment); + paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment, enableInstallmentLevelDelinquency); } @@ -417,6 +419,7 @@ public class LoanProductData implements Serializable { final Collection paymentAllocation = null; final EnumOptionData repaymentStartDateType = null; final boolean disableScheduleExtensionForDownPayment = false; + final boolean enableInstallmentLevelDelinquency = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -436,7 +439,7 @@ public class LoanProductData implements Serializable { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, - paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment); + paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment, enableInstallmentLevelDelinquency); } @@ -539,6 +542,7 @@ public class LoanProductData implements Serializable { final Collection paymentAllocation = null; final EnumOptionData repaymentStartDateType = LoanEnumerations.repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE); final boolean disableScheduleExtensionForDownPayment = false; + final boolean enableInstallmentLevelDelinquency = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -558,7 +562,7 @@ public class LoanProductData implements Serializable { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, - paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment); + paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment, enableInstallmentLevelDelinquency); } @@ -655,6 +659,7 @@ public class LoanProductData implements Serializable { final Collection paymentAllocation = null; final EnumOptionData repaymentStartDateType = LoanEnumerations.repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE); final boolean disableScheduleExtensionForDownPayment = false; + final boolean enableInstallmentLevelDelinquency = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -674,7 +679,7 @@ public class LoanProductData implements Serializable { syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, - paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment); + paymentAllocation, repaymentStartDateType, disableScheduleExtensionForDownPayment, enableInstallmentLevelDelinquency); } public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map accountingMappings, @@ -723,7 +728,7 @@ public class LoanProductData implements Serializable { final Integer overDueDaysForRepaymentEvent, final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment, final Collection paymentAllocation, final EnumOptionData repaymentStartDateType, - final boolean disableScheduleExtensionForDownPayment) { + final boolean disableScheduleExtensionForDownPayment, final boolean enableInstallmentLevelDelinquency) { this.id = id; this.name = name; this.shortName = shortName; @@ -851,6 +856,7 @@ public class LoanProductData implements Serializable { this.advancedPaymentAllocationFutureInstallmentAllocationRules = FutureInstallmentAllocationRule.getValuesAsEnumOptionDataList(); this.advancedPaymentAllocationTypes = PaymentAllocationType.getValuesAsEnumOptionDataList(); this.disableScheduleExtensionForDownPayment = disableScheduleExtensionForDownPayment; + this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency; } public LoanProductData(final LoanProductData productData, final Collection chargeOptions, @@ -1015,6 +1021,7 @@ public class LoanProductData implements Serializable { this.advancedPaymentAllocationFutureInstallmentAllocationRules = advancedPaymentAllocationFutureInstallmentAllocationRules; this.advancedPaymentAllocationTypes = advancedPaymentAllocationTypes; this.disableScheduleExtensionForDownPayment = productData.disableScheduleExtensionForDownPayment; + this.enableInstallmentLevelDelinquency = productData.enableInstallmentLevelDelinquency; } 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 4d284303b..996138ce6 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 @@ -163,7 +163,7 @@ public final class LoanProductDataValidator { 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.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT)); + LoanProductConstants.DISABLE_SCHEDULE_EXTENSION_FOR_DOWN_PAYMENT, LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY)); private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName, LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName, @@ -764,6 +764,13 @@ public final class LoanProductDataValidator { .isOneOfTheseValues(1, 2); } + if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, element)) { + final Boolean enableInstallmentLevelDelinquency = this.fromApiJsonHelper + .extractBooleanNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, element); + baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY) + .value(enableInstallmentLevelDelinquency).ignoreIfNull().validateForBooleanValue(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -1733,6 +1740,13 @@ public final class LoanProductDataValidator { baseDataValidator.reset().parameter(LoanProductConstants.REPAYMENT_START_DATE_TYPE).value(repaymentStartDateType).notNull() .isOneOfTheseValues(1, 2); + if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, element)) { + final Boolean enableInstallmentLevelDelinquency = this.fromApiJsonHelper + .extractBooleanNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, element); + baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY) + .value(enableInstallmentLevelDelinquency).ignoreIfNull().validateForBooleanValue(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } 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 718a615e3..91d87a0b3 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 @@ -228,7 +228,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, " + + "lp.disable_schedule_extension_for_down_payment as disableScheduleExtensionForDownPayment, lp.enable_installment_level_delinquency as enableInstallmentLevelDelinquency, " + "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 +366,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final Integer repaymentStartDateTypeId = JdbcSupport.getInteger(rs, "repaymentStartDateType"); final EnumOptionData repaymentStartDateType = LoanEnumerations.repaymentStartDateType(repaymentStartDateTypeId); final boolean disableScheduleExtensionForDownPayment = rs.getBoolean("disableScheduleExtensionForDownPayment"); + final boolean enableInstallmentLevelDelinquency = rs.getBoolean("enableInstallmentLevelDelinquency"); String status = ""; if (closeDate != null && closeDate.isBefore(DateUtils.getBusinessLocalDate())) { @@ -523,7 +524,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, this.rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageForDownPayment, - enableAutoRepaymentForDownPayment, advancedPaymentData, repaymentStartDateType, disableScheduleExtensionForDownPayment); + enableAutoRepaymentForDownPayment, advancedPaymentData, repaymentStartDateType, disableScheduleExtensionForDownPayment, + enableInstallmentLevelDelinquency); } } diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index db944c50e..849dcb903 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -145,4 +145,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0126_add_loan_product_installment_level_delinquency.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0126_add_loan_product_installment_level_delinquency.xml new file mode 100644 index 000000000..6a21a5b41 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0126_add_loan_product_installment_level_delinquency.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + +