From de64de876cf5a13920b3f6d4ca3ff07653933750 Mon Sep 17 00:00:00 2001 From: John Woodlock Date: Thu, 3 Mar 2022 12:03:14 +0000 Subject: [PATCH] FINERACT-1515 Allow multi-disbursements without setting up expected tranche details --- .../portfolio/loanaccount/domain/Loan.java | 124 +++++++++++++----- ...tiDisbursementDataNotAllowedException.java | 30 +++++ .../loanaccount/service/LoanAssembler.java | 17 ++- ...WritePlatformServiceJpaRepositoryImpl.java | 44 +++++-- .../loanproduct/domain/LoanProduct.java | 28 ++++ .../db/changelog/tenant/changelog-tenant.xml | 2 + ...t_loan_disallow_expected_disbursements.xml | 32 +++++ ...er_than_applied_loan_amount_management.xml | 34 +++++ 8 files changed, 263 insertions(+), 48 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/MultiDisbursementDataNotAllowedException.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0006_product_loan_disallow_expected_disbursements.xml create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0007_product_loan_higher_than_applied_loan_amount_management.xml diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 864536a8f..2ccb66772 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -121,6 +121,7 @@ import org.apache.fineract.portfolio.loanaccount.exception.LoanForeclosureExcept import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerAssignmentDateException; import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerUnassignmentDateException; +import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataNotAllowedException; import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException; import org.apache.fineract.portfolio.loanaccount.exception.UndoLastTrancheDisbursementException; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO; @@ -1330,8 +1331,10 @@ public class Loan extends AbstractPersistableCustom { } public void updateLoanScheduleDependentDerivedFields() { - this.expectedMaturityDate = Date.from(determineExpectedMaturityDate().atStartOfDay(ZoneId.systemDefault()).toInstant()); - this.actualMaturityDate = Date.from(determineExpectedMaturityDate().atStartOfDay(ZoneId.systemDefault()).toInstant()); + if (this.getLoanRepaymentScheduleInstallmentsSize() > 0) { + this.expectedMaturityDate = Date.from(determineExpectedMaturityDate().atStartOfDay(ZoneId.systemDefault()).toInstant()); + this.actualMaturityDate = Date.from(determineExpectedMaturityDate().atStartOfDay(ZoneId.systemDefault()).toInstant()); + } } private void updateLoanSummaryDerivedFields() { @@ -1620,9 +1623,16 @@ public class Loan extends AbstractPersistableCustom { } final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName); - if (disbursementDataArray == null || disbursementDataArray.size() == 0) { - final String errorMessage = "For this loan product, disbursement details must be provided"; - throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + if (loanProduct.isDisallowExpectedDisbursements()) { + if (disbursementDataArray != null) { + final String errorMessage = "For this loan product, disbursement details are not allowed"; + throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } + } else { + if (disbursementDataArray == null || disbursementDataArray.size() == 0) { + final String errorMessage = "For this loan product, disbursement details must be provided"; + throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } } if (disbursementDataArray.size() > loanProduct.maxTrancheCount()) { final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount(); @@ -2224,31 +2234,19 @@ public class Loan extends AbstractPersistableCustom { LocalDate expecteddisbursementDate = command.localDateValueOfParameterNamed("expectedDisbursementDate"); BigDecimal approvedLoanAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.approvedLoanAmountParameterName); - if (approvedLoanAmount != null) { + compareApprovedToProposedPrincipal(approvedLoanAmount); - // Approved amount has to be less than or equal to principal - // amount demanded + /* + * All the calculations are done based on the principal amount, so it is necessary to set principal + * amount to approved amount + */ + this.approvedPrincipal = approvedLoanAmount; - if (approvedLoanAmount.compareTo(this.proposedPrincipal) < 0) { - - this.approvedPrincipal = approvedLoanAmount; - - /* - * All the calculations are done based on the principal amount, so it is necessary to set principal - * amount to approved amount - */ - - this.loanRepaymentScheduleDetail.setPrincipal(approvedLoanAmount); - - actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, approvedLoanAmount); - actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, approvedLoanAmount); - actualChanges.put(LoanApiConstants.disbursementNetDisbursalAmountParameterName, netDisbursalAmount); - } else if (approvedLoanAmount.compareTo(this.proposedPrincipal) > 0) { - final String errorMessage = "Loan approved amount can't be greater than loan amount demanded."; - throw new InvalidLoanStateTransitionException("approval", "amount.can't.be.greater.than.loan.amount.demanded", - errorMessage, this.proposedPrincipal, approvedLoanAmount); - } + this.loanRepaymentScheduleDetail.setPrincipal(approvedLoanAmount); + actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, approvedLoanAmount); + actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, approvedLoanAmount); + actualChanges.put(LoanApiConstants.disbursementNetDisbursalAmountParameterName, netDisbursalAmount); /* Update disbursement details */ if (disbursementDataArray != null) { @@ -2259,10 +2257,16 @@ public class Loan extends AbstractPersistableCustom { recalculateAllCharges(); if (loanProduct.isMultiDisburseLoan()) { - - if (this.disbursementDetails.isEmpty()) { - final String errorMessage = "For this loan product, disbursement details must be provided"; - throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + if (loanProduct.isDisallowExpectedDisbursements()) { + if (!disbursementDetails.isEmpty()) { + final String errorMessage = "For this loan product, disbursement details are not allowed"; + throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } + } else { + if (disbursementDetails.isEmpty()) { + final String errorMessage = "For this loan product, disbursement details must be provided"; + throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } } if (this.disbursementDetails.size() > loanProduct.maxTrancheCount()) { @@ -2313,6 +2317,39 @@ public class Loan extends AbstractPersistableCustom { } return actualChanges; + + } + + private void compareApprovedToProposedPrincipal(BigDecimal approvedLoanAmount) { + + if (this.loanProduct().isDisallowExpectedDisbursements() && this.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) { + BigDecimal maxApprovedLoanAmount = getOverAppliedMax(); + if (approvedLoanAmount.compareTo(maxApprovedLoanAmount) > 0) { + final String errorMessage = "Loan approved amount can't be greater than maximum applied loan amount calculation."; + throw new InvalidLoanStateTransitionException("approval", + "amount.can't.be.greater.than.maximum.applied.loan.amount.calculation", errorMessage, approvedLoanAmount, + maxApprovedLoanAmount); + } + } else { + if (approvedLoanAmount.compareTo(this.proposedPrincipal) > 0) { + final String errorMessage = "Loan approved amount can't be greater than loan amount demanded."; + throw new InvalidLoanStateTransitionException("approval", "amount.can't.be.greater.than.loan.amount.demanded", errorMessage, + this.proposedPrincipal, approvedLoanAmount); + } + } + } + + private BigDecimal getOverAppliedMax() { + BigDecimal maxAmount = null; + if (this.getLoanProduct().getOverAppliedCalculationType().equals("percentage")) { + BigDecimal overAppliedNumber = BigDecimal.valueOf(getLoanProduct().getOverAppliedNumber()); + BigDecimal x = overAppliedNumber.divide(BigDecimal.valueOf(100)); + BigDecimal totalPercentage = BigDecimal.valueOf(1).add(x); + maxAmount = this.proposedPrincipal.multiply(totalPercentage); + } else { + maxAmount = this.proposedPrincipal.add(BigDecimal.valueOf(getLoanProduct().getOverAppliedNumber())); + } + return maxAmount; } public Map undoApproval(final LoanLifecycleStateMachine loanLifecycleStateMachine) { @@ -2542,11 +2579,7 @@ public class Loan extends AbstractPersistableCustom { totalAmount = totalAmount.add(disbursementDetails.principal()); } this.loanRepaymentScheduleDetail.setPrincipal(setPrincipalAmount); - if (totalAmount.compareTo(this.approvedPrincipal) > 0) { - final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved principal "; - throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.principal", principalDisbursed, - this.approvedPrincipal); - } + compareDisbursedToApprovedOrProposedPrincipal(disburseAmount.getAmount(), totalAmount); } else { this.loanRepaymentScheduleDetail.setPrincipal(this.loanRepaymentScheduleDetail.getPrincipal().minus(diff).getAmount()); } @@ -2559,6 +2592,25 @@ public class Loan extends AbstractPersistableCustom { return disburseAmount; } + private void compareDisbursedToApprovedOrProposedPrincipal(BigDecimal disbursedAmount, BigDecimal totalDisbursed) { + + if (this.loanProduct().isDisallowExpectedDisbursements() && this.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) { + BigDecimal maxDisbursedAmount = getOverAppliedMax(); + if (disbursedAmount.compareTo(maxDisbursedAmount) > 0) { + final String errorMessage = "Loan disbursal amount can't be greater than maximum applied loan amount calculation."; + throw new InvalidLoanStateTransitionException("disbursal", + "amount.can't.be.greater.than.maximum.applied.loan.amount.calculation", errorMessage, disbursedAmount, + maxDisbursedAmount); + } + } else { + if (totalDisbursed.compareTo(this.approvedPrincipal) > 0) { + final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved principal "; + throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.principal", totalDisbursed, + this.approvedPrincipal); + } + } + } + private ChangedTransactionDetail reprocessTransactionForDisbursement() { ChangedTransactionDetail changedTransactionDetail = null; if (this.loanProduct.isMultiDisburseLoan()) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/MultiDisbursementDataNotAllowedException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/MultiDisbursementDataNotAllowedException.java new file mode 100644 index 000000000..9a9caddd5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/MultiDisbursementDataNotAllowedException.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanaccount.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +public class MultiDisbursementDataNotAllowedException extends AbstractPlatformDomainRuleException { + + public MultiDisbursementDataNotAllowedException(final String entity, final String defaultUserMessage, + final Object... defaultUserMessageArgs) { + super("error.msg." + entity + ".not.allowed", defaultUserMessage, defaultUserMessageArgs); + } + +} 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 ffaa632c1..60f31d118 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 @@ -75,6 +75,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionProcessin import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException; import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals; import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException; +import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataNotAllowedException; import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel; @@ -200,13 +201,21 @@ public class LoanAssembler { } BigDecimal maxOutstandingLoanBalance = null; if (loanProduct.isMultiDisburseLoan()) { - disbursementDetails = this.loanUtilService.fetchDisbursementData(element.getAsJsonObject()); final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject()); maxOutstandingLoanBalance = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.maxOutstandingBalanceParameterName, element, locale); - if (disbursementDetails.isEmpty()) { - final String errorMessage = "For this loan product, disbursement details must be provided"; - throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + + disbursementDetails = this.loanUtilService.fetchDisbursementData(element.getAsJsonObject()); + if (loanProduct.isDisallowExpectedDisbursements()) { + if (!disbursementDetails.isEmpty()) { + final String errorMessage = "For this loan product, disbursement details are not allowed"; + throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } + } else { + if (disbursementDetails.isEmpty()) { + final String errorMessage = "For this loan product, disbursement details must be provided"; + throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } } if (disbursementDetails.size() > loanProduct.maxTrancheCount()) { 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 7389c9de6..30940fc0c 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 @@ -173,6 +173,7 @@ import org.apache.fineract.portfolio.loanaccount.exception.LoanMultiDisbursement import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException; +import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataNotAllowedException; import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException; import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDomainService; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData; @@ -373,6 +374,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } final Loan loan = this.loanAssembler.assembleFrom(loanId); + if (loan.loanProduct().isDisallowExpectedDisbursements()) { + // create artificial 'tranche/expected disbursal' as current disburse code expects it for multi-disbursal + // products + final Date artificialExpectedDate = Date + .from(loan.getExpectedDisbursedOnLocalDate().atStartOfDay(ZoneId.systemDefault()).toInstant()); + LoanDisbursementDetails disbursementDetail = new LoanDisbursementDetails(artificialExpectedDate, null, + loan.getDisbursedAmount(), null); + disbursementDetail.updateLoan(loan); + loan.getDisbursementDetails().add(disbursementDetail); + } // Get disbursedAmount final BigDecimal disbursedAmount = loan.getDisbursedAmount(); @@ -2900,17 +2911,27 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .build(); } - private void validateMultiDisbursementData(final JsonCommand command, LocalDate expectedDisbursementDate) { + private void validateMultiDisbursementData(final JsonCommand command, LocalDate expectedDisbursementDate, + boolean isDisallowExpectedDisbursements) { final String json = command.json(); final JsonElement element = this.fromApiJsonHelper.parse(json); final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan"); final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName); - if (disbursementDataArray == null || disbursementDataArray.size() == 0) { - final String errorMessage = "For this loan product, disbursement details must be provided"; - throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + + if (isDisallowExpectedDisbursements) { + if (disbursementDataArray != null) { + final String errorMessage = "For this loan product, disbursement details are not allowed"; + throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } + } else { + if (disbursementDataArray == null || disbursementDataArray.size() == 0) { + final String errorMessage = "For this loan product, disbursement details must be provided"; + throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } } + final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("approvedLoanAmount", element); loanApplicationCommandFromApiJsonHelper.validateLoanMultiDisbursementDate(element, baseDataValidator, expectedDisbursementDate, @@ -2952,15 +2973,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final String errorMessage = "cannot.modify.tranches.if.loan.is.pendingapproval.closed.overpaid.writtenoff"; throw new LoanMultiDisbursementException(errorMessage); } - validateMultiDisbursementData(command, expectedDisbursementDate); + validateMultiDisbursementData(command, expectedDisbursementDate, loan.loanProduct().isDisallowExpectedDisbursements()); this.validateForAddAndDeleteTranche(loan); loan.updateDisbursementDetails(command, actualChanges); - if (loan.getDisbursementDetails().isEmpty()) { - final String errorMessage = "For this loan product, disbursement details must be provided"; - throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + if (loan.loanProduct().isDisallowExpectedDisbursements()) { + if (!loan.getDisbursementDetails().isEmpty()) { + final String errorMessage = "For this loan product, disbursement details are not allowed"; + throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } + } else { + if (loan.getDisbursementDetails().isEmpty()) { + final String errorMessage = "For this loan product, disbursement details must be provided"; + throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); + } } if (loan.getDisbursementDetails().size() > loan.loanProduct().maxTrancheCount()) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java index 3df21b707..c3c2fc544 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java @@ -191,6 +191,18 @@ public class LoanProduct extends AbstractPersistableCustom { @Column(name = "fixed_principal_percentage_per_installment", scale = 2, precision = 5, nullable = true) private BigDecimal fixedPrincipalPercentagePerInstallment; + @Column(name = "disallow_expected_disbursements", nullable = false) + private boolean disallowExpectedDisbursements; + + @Column(name = "allow_approved_disbursed_amounts_over_applied", nullable = false) + private boolean allowApprovedDisbursedAmountsOverApplied; + + @Column(name = "over_applied_calculation_type", nullable = true) + private String overAppliedCalculationType; + + @Column(name = "over_applied_number", nullable = true) + private Integer overAppliedNumber; + public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy, final List productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate, final List productRates) { @@ -1461,4 +1473,20 @@ public class LoanProduct extends AbstractPersistableCustom { public BigDecimal getFixedPrincipalPercentagePerInstallment() { return fixedPrincipalPercentagePerInstallment; } + + public boolean isDisallowExpectedDisbursements() { + return disallowExpectedDisbursements; + } + + public boolean isAllowApprovedDisbursedAmountsOverApplied() { + return allowApprovedDisbursedAmountsOverApplied; + } + + public String getOverAppliedCalculationType() { + return overAppliedCalculationType; + } + + public Integer getOverAppliedNumber() { + return overAppliedNumber; + } } 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 1afbbe245..d340eb299 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 @@ -25,4 +25,6 @@ + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0006_product_loan_disallow_expected_disbursements.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0006_product_loan_disallow_expected_disbursements.xml new file mode 100644 index 000000000..8b044bcc9 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0006_product_loan_disallow_expected_disbursements.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0007_product_loan_higher_than_applied_loan_amount_management.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0007_product_loan_higher_than_applied_loan_amount_management.xml new file mode 100644 index 000000000..fbb98be84 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0007_product_loan_higher_than_applied_loan_amount_management.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + +