This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 2cba4430c7c82f394ef228823dc48fc10a0401be Author: mariiaKraievska <[email protected]> AuthorDate: Wed Aug 20 20:08:50 2025 +0300 FINERACT-2326: Add additional field which would return available disbursement amount with over applied amount --- .../src/main/avro/loan/v1/CollectionDataV1.avsc | 8 +++ .../test/data/loanproduct/DefaultLoanProduct.java | 1 + .../fineract/test/helper/ErrorMessageHelper.java | 6 +++ .../global/LoanProductGlobalInitializerStep.java | 36 +++++++++++++ .../fineract/test/stepdef/loan/LoanStepDef.java | 19 +++++++ .../fineract/test/support/TestContextKey.java | 1 + .../src/test/resources/features/Loan.feature | 63 ++++++++++++++++++++++ .../service/DelinquencyReadPlatformService.java | 5 ++ .../DelinquencyReadPlatformServiceImpl.java | 60 ++++++++++++++++++++- .../portfolio/loanaccount/data/CollectionData.java | 3 +- .../loanaccount/data/LoanApprovalData.java | 5 +- .../loanaccount/data/LoanTransactionData.java | 27 +++++----- .../portfolio/loanaccount/domain/Loan.java | 6 ++- .../loanaccount/api/LoansApiResourceSwagger.java | 2 + .../service/LoanReadPlatformServiceImpl.java | 9 +++- ...cyWritePlatformServiceRangeChangeEventTest.java | 33 ++++++------ 16 files changed, 250 insertions(+), 34 deletions(-) diff --git a/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc b/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc index 68961e5522..af515e5930 100644 --- a/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc +++ b/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc @@ -11,6 +11,14 @@ "bigdecimal" ] }, + { + "default": null, + "name": "availableDisbursementAmountWithOverApplied", + "type": [ + "null", + "bigdecimal" + ] + }, { "default": null, "name": "pastDueDays", diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java index 147e3fba66..1cf977245a 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java @@ -122,6 +122,7 @@ public enum DefaultLoanProduct implements LoanProduct { LP2_ADV_DP_IR_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, // LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_DISBURSEMENT_CHARGES, // LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_MULTIDISBURSE_EXPECT_TRANCHE, // + LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_EXPECT_TRANCHE_APPROVED_OVER_APPLIED, // LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_MULTIDISBURSE, // LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CASH_ACCOUNTING_DISBURSEMENT_CHARGES, // LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF, // diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java index ec00a47093..afa922b09c 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java @@ -1066,4 +1066,10 @@ public final class ErrorMessageHelper { return String.format("%nNumber of lines does not match in Deferred Income tab and expected datatable of resource %s." // + "%nNumber of transaction tab lines: %s %nNumber of expected datatable lines: %s%n", resourceId, actual, expected); } + + public static String wrongAvailableDisbursementAmountWithOverApplied(final double actual, final double expected) { + return String.format( + "Wrong value in LoanDetails/availableDisbursementAmountWithOverApplied. %nActual value is: %s %nExpected Value is: %s", + actual, expected); + } } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java index 4a6eea5170..8dcb56362e 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java @@ -3658,6 +3658,42 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ TestContext.INSTANCE.set( TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_BUYDOWN_FEES_NON_MERCHANT_CHARGE_OFF_REASON, responseLoanProductsRequestLP2ProgressiveAdvPaymentBuyDownFeesNonMerchantWithChargeOffReason); + + // LP2 with progressive loan schedule + horizontal + interest recalculation daily EMI + 360/30 + + // multidisbursement + // Frequency for recalculate Outstanding Principal: Daily, Frequency Interval for recalculation: 1 + // allow approved/disbursed amount over applied amount is enabled with percentage type + // (LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_EXPECT_TRANCHE_APPROVED_OVER_APPLIED) + final String name138 = DefaultLoanProduct.LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_EXPECT_TRANCHE_APPROVED_OVER_APPLIED + .getName(); + final PostLoanProductsRequest loanProductsRequestLP2AdvEmi36030IntRecalcDailyMultiDisbApprovedOverApplied = loanProductsRequestFactory + .defaultLoanProductsRequestLP2Emi()// + .name(name138)// + .daysInYearType(DaysInYearType.DAYS360.value)// + .daysInMonthType(DaysInMonthType.DAYS30.value)// + .isInterestRecalculationEnabled(true)// + .preClosureInterestCalculationStrategy(1)// + .rescheduleStrategyMethod(4)// + .interestRecalculationCompoundingMethod(0)// + .recalculationRestFrequencyType(2)// + .recalculationRestFrequencyInterval(1)// + .paymentAllocation(List.of(// + createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), // + createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), // + createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), // + createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")))// + .multiDisburseLoan(true)// + .disallowExpectedDisbursements(false)// + .allowApprovedDisbursedAmountsOverApplied(true)// + .overAppliedCalculationType(OverAppliedCalculationType.PERCENTAGE.value)// + .overAppliedNumber(50)// + .maxTrancheCount(10)// + .outstandingLoanBalance(10000.0);// + final Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvEmi36030IntRecalcDailyMultiDisbApprovedOverApplied = loanProductsApi + .createLoanProduct(loanProductsRequestLP2AdvEmi36030IntRecalcDailyMultiDisbApprovedOverApplied).execute(); + TestContext.INSTANCE.set( + TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_EXPECT_TRANCHE_APPROVED_OVER_APPLIED, + responseLoanProductsRequestLP2AdvEmi36030IntRecalcDailyMultiDisbApprovedOverApplied); } public static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule, diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java index 5dc28ab36d..9b715b1d8d 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java @@ -3293,6 +3293,25 @@ public class LoanStepDef extends AbstractStepDef { assertThat(fixedLengthactual).as(ErrorMessageHelper.wrongfixedLength(fixedLengthactual, fieldValue)).isEqualTo(fieldValue); } + @Then("Loan has availableDisbursementAmountWithOverApplied field with value: {double}") + public void checkLoanDetailsAvailableDisbursementAmountWithOverAppliedField(final double fieldValue) throws IOException { + final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + assert loanResponse.body() != null; + final long loanId = loanResponse.body().getLoanId(); + + final Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute(); + ErrorHelper.checkSuccessfulApiCall(loanDetails); + + assert loanDetails.body() != null; + assert loanDetails.body().getDelinquent() != null; + assert loanDetails.body().getDelinquent().getAvailableDisbursementAmountWithOverApplied() != null; + final Double availableDisbursementAmountWithOverApplied = loanDetails.body().getDelinquent() + .getAvailableDisbursementAmountWithOverApplied().doubleValue(); + assertThat(availableDisbursementAmountWithOverApplied).as( + ErrorMessageHelper.wrongAvailableDisbursementAmountWithOverApplied(availableDisbursementAmountWithOverApplied, fieldValue)) + .isEqualTo(fieldValue); + } + @Then("Loan emi amount variations has {int} variation, with the following data:") public void loanEmiAmountVariationsCheck(final int linesExpected, final DataTable table) throws IOException { final Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java index 0a5904fcdb..7aa2e832d2 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java @@ -152,6 +152,7 @@ public abstract class TestContextKey { public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALC_EMI_360_30_CHARGEBACK_PRINCIPAL_INTEREST_FEE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalcEmi36030ChargebackPrincipalInterestFee"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_DISBURSEMENT_CHARGES = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyDisbursementCharge"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_MULTIDISBURSE_EXPECT_TRANCHE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseExpectTranche"; + public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_EXPECT_TRANCHE_APPROVED_OVER_APPLIED = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseExpectTrancheApprovedOVerAppliedPercentage"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_MULTIDISBURSE = "loanProductCreateResponseLP2AdvancedPaymentInterestRecalculationDailyEmi36030Multidisburse"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CASH_ACCOUNTING_DISBURSEMENT_CHARGES = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyCashBasedDisbursementCharge"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_CAPITALIZED_INCOME = "loanProductCreateResponseLP2ProgressiveAdvancedPaymentCapitalizedIncome"; diff --git a/fineract-e2e-tests-runner/src/test/resources/features/Loan.feature b/fineract-e2e-tests-runner/src/test/resources/features/Loan.feature index c0f7938569..f8dd5db2ab 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/Loan.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/Loan.feature @@ -7958,3 +7958,66 @@ Feature: Loan | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 | | 01 July 2024 | Repayment | 417.03 | 405.03 | 12.0 | 0.0 | 0.0 | 794.97 | + + @TestRailId:C3999 + Scenario: Verify availableDisbursementAmountWithOverApplied field calculation + When Admin sets the business date to "1 January 2025" + And Admin creates a client with random data + And Admin creates a fully customized loan with the following data: + | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy | + | LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_APPROVED_OVER_APPLIED_CAPITALIZED_INCOME | 01 January 2025 | 1000 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | + Then Loan has availableDisbursementAmountWithOverApplied field with value: 1500 + And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025" + Then Loan has availableDisbursementAmountWithOverApplied field with value: 1500 + And Admin successfully disburse the loan on "1 January 2025" with "900" EUR transaction amount + Then Loan has availableDisbursementAmountWithOverApplied field with value: 600 + And Admin successfully disburse the loan on "1 January 2025" with "300" EUR transaction amount + Then Loan has availableDisbursementAmountWithOverApplied field with value: 300 + And Admin adds capitalized income with "AUTOPAY" payment type to the loan on "1 January 2025" with "100" EUR transaction amount + Then Loan has availableDisbursementAmountWithOverApplied field with value: 200 + And Admin adds capitalized income with "AUTOPAY" payment type to the loan on "1 January 2025" with "150" EUR transaction amount + Then Loan has availableDisbursementAmountWithOverApplied field with value: 50 + When Loan Pay-off is made on "01 January 2025" + Then Loan's all installments have obligations met + + @TestRailId:C4003 + Scenario: Verify availableDisbursementAmountWithOverApplied field calculation for progressive multidisbursal loan that expects tranches + When Admin sets the business date to "01 January 2025" + When Admin creates a client with random data + When Admin creates a fully customized loan with three expected disbursements details and following data: + | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy | 1st_tranche_disb_expected_date | 1st_tr [...] + | LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_EXPECT_TRANCHE_APPROVED_OVER_APPLIED | 01 January 2025 | 1000 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | 01 January 2025 | 300.0 [...] + Then Loan Tranche Details tab has the following data: + | Expected Disbursement On | Disbursed On | Principal | Net Disbursal Amount | + | 01 January 2025 | | 300.0 | | + | 02 January 2025 | | 200.0 | | + | 03 January 2025 | | 500.0 | | + Then Loan has availableDisbursementAmountWithOverApplied field with value: 500 + And Admin successfully approves the loan on "01 January 2025" with "1000" amount and expected disbursement date on "01 January 2025" + Then Loan Tranche Details tab has the following data: + | Expected Disbursement On | Disbursed On | Principal | Net Disbursal Amount | + | 01 January 2025 | | 300.0 | | + | 02 January 2025 | | 200.0 | | + | 03 January 2025 | | 500.0 | | + Then Loan has availableDisbursementAmountWithOverApplied field with value: 500 + When Admin successfully disburse the loan on "01 January 2025" with "300" EUR transaction amount + And Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2025 | Disbursement | 300.0 | 0.0 | 0.0 | 0.0 | 0.0 | 300.0 | false | false | + Then Loan has availableDisbursementAmountWithOverApplied field with value: 500 + When Admin sets the business date to "02 January 2025" + When Admin successfully disburse the loan on "02 January 2025" with "200" EUR transaction amount + Then Loan has availableDisbursementAmountWithOverApplied field with value: 500 + When Admin sets the business date to "03 January 2025" + When Admin successfully disburse the loan on "03 January 2025" with "1000" EUR transaction amount + Then Loan has availableDisbursementAmountWithOverApplied field with value: 0 + Then Loan Tranche Details tab has the following data: + | Expected Disbursement On | Disbursed On | Principal | Net Disbursal Amount | + | 01 January 2025 | 01 January 2025 | 300.0 | | + | 02 January 2025 | 02 January 2025 | 200.0 | | + | 03 January 2025 | 03 January 2025 | 1000.0 | | + And Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2025 | Disbursement | 300.0 | 0.0 | 0.0 | 0.0 | 0.0 | 300.0 | false | false | + | 02 January 2025 | Disbursement | 200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false | + | 03 January 2025 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1500.0 | false | false | \ No newline at end of file diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java index 87b64a52a7..bad79f2e7b 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.portfolio.delinquency.service; +import java.math.BigDecimal; import java.util.Collection; import java.util.List; import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData; @@ -26,6 +27,8 @@ import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryD import org.apache.fineract.portfolio.delinquency.data.LoanInstallmentDelinquencyTagData; import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction; import org.apache.fineract.portfolio.loanaccount.data.CollectionData; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.springframework.lang.NonNull; public interface DelinquencyReadPlatformService { @@ -43,6 +46,8 @@ public interface DelinquencyReadPlatformService { CollectionData calculateLoanCollectionData(Long loanId); + BigDecimal calculateAvailableDisbursementAmountWithOverApplied(@NonNull Loan loan); + Collection<LoanInstallmentDelinquencyTagData> retrieveLoanInstallmentsCurrentDelinquencyTag(Long loanId); List<LoanDelinquencyAction> retrieveLoanDelinquencyActions(Long loanId); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java index 72f215ba62..4ad69707b3 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java @@ -34,6 +34,7 @@ import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDoma import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData; import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData; import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData; @@ -56,11 +57,14 @@ import org.apache.fineract.portfolio.loanaccount.data.CollectionData; import org.apache.fineract.portfolio.loanaccount.data.DelinquencyPausePeriod; import org.apache.fineract.portfolio.loanaccount.data.InstallmentLevelDelinquency; import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; +import org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException; import org.springframework.lang.NonNull; import org.springframework.transaction.annotation.Transactional; @@ -138,7 +142,12 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo // If the Loan is not Active yet or is cancelled (rejected or withdrawn), return template data if (loan.isSubmittedAndPendingApproval() || loan.isApproved() || loan.isCancelled()) { - return CollectionData.template(); + if (loan.getLoanProduct() != null && !loan.getLoanProduct().isAllowApprovedDisbursedAmountsOverApplied()) { + return collectionData; + } else { + collectionData.setAvailableDisbursementAmountWithOverApplied(calculateAvailableDisbursementAmountWithOverApplied(loan)); + return collectionData; + } } final List<LoanDelinquencyAction> savedDelinquencyList = retrieveLoanDelinquencyActions(loanId); @@ -152,6 +161,7 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo // loans collectionData = loanDelinquencyDomainService.getOverdueCollectionData(loan, effectiveDelinquencyList); collectionData.setAvailableDisbursementAmount(calculateAvailableDisbursementAmount(loan)); + collectionData.setAvailableDisbursementAmountWithOverApplied(calculateAvailableDisbursementAmountWithOverApplied(loan)); collectionData.setNextPaymentDueDate(possibleNextRepaymentDate(nextPaymentDueDateConfig, loan)); PossibleNextRepaymentCalculationService possibleNextRepaymentCalculationService = possibleNextRepaymentCalculationServiceDiscovery .getService(loan); @@ -182,6 +192,54 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo return collectionData; } + @Override + public BigDecimal calculateAvailableDisbursementAmountWithOverApplied(@NonNull final Loan loan) { + final LoanProduct loanProduct = loan.getLoanProduct(); + + // Start with approved principal + BigDecimal approvedWithOverApplied = loan.getApprovedPrincipal(); + + // If over applied amount is enabled, calculate the maximum allowed amount + if (loanProduct.isAllowApprovedDisbursedAmountsOverApplied()) { + if (loanProduct.getOverAppliedCalculationType() != null) { + if ("percentage".equalsIgnoreCase(loanProduct.getOverAppliedCalculationType())) { + final BigDecimal overAppliedNumber = BigDecimal.valueOf(loanProduct.getOverAppliedNumber()); + final BigDecimal totalPercentage = BigDecimal.valueOf(1) + .add(overAppliedNumber.divide(BigDecimal.valueOf(100), MoneyHelper.getMathContext())); + approvedWithOverApplied = loan.getProposedPrincipal().multiply(totalPercentage); + } else { + approvedWithOverApplied = loan.getProposedPrincipal().add(BigDecimal.valueOf(loanProduct.getOverAppliedNumber())); + } + } else { + throw new LoanProductGeneralRuleException("overAppliedCalculationType.must.be.percentage.or.flat", + "Over Applied Calculation Type Must Be 'percentage' or 'flat'"); + } + } + + // Calculate available amount: (approved + over applied) - expected tranches - disbursed - capitalized income + if (loan.isMultiDisburmentLoan() && loan.getDisbursementDetails() != null) { + final BigDecimal expectedDisbursementAmount = loan.getDisbursementDetails().stream() + .filter(detail -> detail.actualDisbursementDate() == null).map(LoanDisbursementDetails::principal) + .reduce(BigDecimal.ZERO, BigDecimal::add); + approvedWithOverApplied = approvedWithOverApplied.subtract(expectedDisbursementAmount); + } + + BigDecimal availableDisbursementAmount = approvedWithOverApplied.subtract(loan.getDisbursedAmount()); + + if (loan.getLoanRepaymentScheduleDetail().isEnableIncomeCapitalization()) { + final LoanSummary loanSummary = loan.getSummary(); + if (loanSummary != null) { + final BigDecimal totalCapitalizedIncome = MathUtil.nullToZero(loanSummary.getTotalCapitalizedIncome()); + final BigDecimal totalCapitalizedIncomeAdjustment = MathUtil.nullToZero(loanSummary.getTotalCapitalizedIncomeAdjustment()); + final BigDecimal netCapitalizedIncome = totalCapitalizedIncome.subtract(totalCapitalizedIncomeAdjustment); + availableDisbursementAmount = availableDisbursementAmount.subtract(netCapitalizedIncome); + } + } + + // Ensure availableDisbursementAmount is never negative + return availableDisbursementAmount.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : availableDisbursementAmount; + } + private BigDecimal calculateAvailableDisbursementAmount(@NonNull final Loan loan) { BigDecimal availableDisbursementAmount = loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()); if (loan.getLoanRepaymentScheduleDetail().isEnableIncomeCapitalization()) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java index aa7c53e700..4d22a3f275 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java @@ -29,6 +29,7 @@ import lombok.Data; public final class CollectionData { private BigDecimal availableDisbursementAmount; + private BigDecimal availableDisbursementAmountWithOverApplied; private Long pastDueDays; private LocalDate nextPaymentDueDate; private BigDecimal nextPaymentAmount; @@ -51,7 +52,7 @@ public final class CollectionData { public static CollectionData template() { final BigDecimal zero = BigDecimal.ZERO; - return new CollectionData(zero, 0L, null, zero, 0L, null, zero, null, zero, null, zero, null, null, zero, zero, zero, zero); + return new CollectionData(zero, zero, 0L, null, zero, 0L, null, zero, null, zero, null, zero, null, null, zero, zero, zero, zero); } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanApprovalData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanApprovalData.java index eb2a18a6b6..9f998a9cbf 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanApprovalData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanApprovalData.java @@ -32,6 +32,7 @@ public class LoanApprovalData { private final LocalDate approvalDate; private final BigDecimal approvalAmount; private final BigDecimal netDisbursalAmount; + private final BigDecimal availableDisbursementAmountWithOverApplied; // import fields private LocalDate approvedOnDate; @@ -54,14 +55,16 @@ public class LoanApprovalData { this.approvalAmount = null; this.approvalDate = null; this.netDisbursalAmount = null; + this.availableDisbursementAmountWithOverApplied = null; } public LoanApprovalData(final BigDecimal approvalAmount, final LocalDate approvalDate, final BigDecimal netDisbursalAmount, - final CurrencyData currency) { + final CurrencyData currency, final BigDecimal availableDisbursementAmountWithOverApplied) { this.approvalDate = approvalDate; this.approvalAmount = approvalAmount; this.netDisbursalAmount = netDisbursalAmount; this.currency = currency; + this.availableDisbursementAmountWithOverApplied = availableDisbursementAmountWithOverApplied; } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java index abcf285169..9f9968f414 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java @@ -25,6 +25,7 @@ import java.time.LocalDate; import java.util.Collection; import java.util.List; import lombok.Getter; +import lombok.Setter; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.organisation.monetary.data.CurrencyData; @@ -70,6 +71,7 @@ public class LoanTransactionData implements Serializable { private final LocalDate submittedOnDate; private final boolean manuallyReversed; private final LocalDate possibleNextRepaymentDate; + private final BigDecimal availableDisbursementAmountWithOverApplied; private Collection<LoanChargePaidByData> loanChargePaidByList; @@ -78,6 +80,7 @@ public class LoanTransactionData implements Serializable { private Collection<CodeValueData> writeOffReasonOptions = null; + @Setter private Integer numberOfRepayments = 0; // import fields @@ -94,6 +97,7 @@ public class LoanTransactionData implements Serializable { private Integer bankNumber; private transient Long accountId; private transient String transactionType; + @Setter private List<LoanRepaymentScheduleInstallmentData> loanRepaymentScheduleInstallments; // Reverse Data @@ -144,6 +148,7 @@ public class LoanTransactionData implements Serializable { this.paymentTypeOptions = null; this.writeOffReasonOptions = null; this.reversalExternalId = ExternalId.empty(); + this.availableDisbursementAmountWithOverApplied = null; } public static LoanTransactionData importInstance(BigDecimal repaymentAmount, LocalDate repaymentDate, Long repaymentTypeId, @@ -196,14 +201,7 @@ public class LoanTransactionData implements Serializable { this.paymentTypeOptions = null; this.writeOffReasonOptions = null; this.reversalExternalId = ExternalId.empty(); - } - - public void setNumberOfRepayments(Integer numberOfRepayments) { - this.numberOfRepayments = numberOfRepayments; - } - - public void setLoanRepaymentScheduleInstallments(final List<LoanRepaymentScheduleInstallmentData> loanRepaymentScheduleInstallments) { - this.loanRepaymentScheduleInstallments = loanRepaymentScheduleInstallments; + this.availableDisbursementAmountWithOverApplied = null; } public static LoanTransactionData templateOnTop(final LoanTransactionData loanTransactionData, @@ -301,6 +299,7 @@ public class LoanTransactionData implements Serializable { this.possibleNextRepaymentDate = null; this.reversalExternalId = reversalExternalId; this.reversedOnDate = reversedOnDate; + this.availableDisbursementAmountWithOverApplied = null; } public LoanTransactionData(Long id, LoanTransactionEnumData transactionType, LocalDate date, BigDecimal totalAmount, @@ -336,17 +335,20 @@ public class LoanTransactionData implements Serializable { final LocalDate submittedOnDate = null; final LocalDate possibleNextRepaymentDate = null; final boolean manuallyReversed = false; + final BigDecimal availableDisbursementAmountWithOverApplied = null; return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currency, transactionDate, transactionAmount, netDisbursalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, paymentOptions, transfer, externalId, fixedEmiAmount, outstandingLoanBalance, - submittedOnDate, manuallyReversed, possibleNextRepaymentDate, loanId, externalLoanId); + submittedOnDate, manuallyReversed, possibleNextRepaymentDate, loanId, externalLoanId, + availableDisbursementAmountWithOverApplied); } public static LoanTransactionData loanTransactionDataForDisbursalTemplate(final LoanTransactionEnumData transactionType, final LocalDate expectedDisbursedOnLocalDateForTemplate, final BigDecimal disburseAmountForTemplate, final BigDecimal netDisbursalAmount, final Collection<PaymentTypeData> paymentOptions, final BigDecimal retriveLastEmiAmount, - final LocalDate possibleNextRepaymentDate, final CurrencyData currency) { + final LocalDate possibleNextRepaymentDate, final CurrencyData currency, + final BigDecimal availableDisbursementAmountWithOverApplied) { final Long id = null; final Long loanId = null; final ExternalId externalLoanId = ExternalId.empty(); @@ -368,7 +370,7 @@ public class LoanTransactionData implements Serializable { expectedDisbursedOnLocalDateForTemplate, disburseAmountForTemplate, netDisbursalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, paymentOptions, transfer, externalId, retriveLastEmiAmount, outstandingLoanBalance, submittedOnDate, manuallyReversed, possibleNextRepaymentDate, - loanId, externalLoanId); + loanId, externalLoanId, availableDisbursementAmountWithOverApplied); } @@ -379,7 +381,7 @@ public class LoanTransactionData implements Serializable { BigDecimal unrecognizedIncomePortion, Collection<PaymentTypeData> paymentOptions, final AccountTransferData transfer, final ExternalId externalId, final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, final LocalDate submittedOnDate, final boolean manuallyReversed, final LocalDate possibleNextRepaymentDate, Long loanId, - ExternalId externalLoanId) { + ExternalId externalLoanId, final BigDecimal availableDisbursementAmountWithOverApplied) { this.id = id; this.loanId = loanId; this.externalLoanId = externalLoanId; @@ -406,6 +408,7 @@ public class LoanTransactionData implements Serializable { this.manuallyReversed = manuallyReversed; this.possibleNextRepaymentDate = possibleNextRepaymentDate; this.reversalExternalId = ExternalId.empty(); + this.availableDisbursementAmountWithOverApplied = availableDisbursementAmountWithOverApplied; } public boolean isNotDisbursement() { 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 447a525776..03b1794faf 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 @@ -814,7 +814,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> { } return principal; } else { - return getNetDisbursalAmount(); + if (this.actualDisbursementDate == null) { + return BigDecimal.ZERO; + } else { + return getNetDisbursalAmount(); + } } } 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 e46b93e95a..3ca327b710 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 @@ -1020,6 +1020,8 @@ final class LoansApiResourceSwagger { @Schema(example = "100.000000") public BigDecimal availableDisbursementAmount; + @Schema(example = "150.000000") + public BigDecimal availableDisbursementAmountWithOverApplied; @Schema(example = "12") public Integer pastDueDays; @Schema(example = "[2022, 07, 01]") 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 7ab92d157c..2332a4782b 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,8 +643,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa public LoanApprovalData retrieveApprovalTemplate(final Long loanId) { final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true); final ApplicationCurrency appCurrency = applicationCurrencyRepository.findOneWithNotFoundDetection(loan.getCurrency()); + final BigDecimal availableDisbursementAmountWithOverApplied = delinquencyReadPlatformService + .calculateAvailableDisbursementAmountWithOverApplied(loan); return new LoanApprovalData(loan.getProposedPrincipal(), DateUtils.getBusinessLocalDate(), loan.getNetDisbursalAmount(), - appCurrency.toData()); + appCurrency.toData(), availableDisbursementAmountWithOverApplied); } @Override @@ -656,9 +658,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes(); } final ApplicationCurrency appCurrency = applicationCurrencyRepository.findOneWithNotFoundDetection(loan.getCurrency()); + final BigDecimal availableDisbursementAmountWithOverApplied = delinquencyReadPlatformService + .calculateAvailableDisbursementAmountWithOverApplied(loan); return LoanTransactionData.loanTransactionDataForDisbursalTemplate(transactionType, loan.getExpectedDisbursedOnLocalDateForTemplate(), loan.getDisburseAmountForTemplate(), loan.getNetDisbursalAmount(), - paymentOptions, loan.retriveLastEmiAmount(), loan.getNextPossibleRepaymentDateForRescheduling(), appCurrency.toData()); + paymentOptions, loan.retriveLastEmiAmount(), loan.getNextPossibleRepaymentDateForRescheduling(), appCurrency.toData(), + availableDisbursementAmountWithOverApplied); } @Override diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java index e388a847a9..5e05029bcb 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java @@ -186,8 +186,8 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest { LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L, loanForProcessing); final BigDecimal zero = BigDecimal.ZERO; - CollectionData collectionData = new CollectionData(zero, 2L, null, zero, 2L, overDueSinceDate, zero, null, null, null, null, null, - null, zero, zero, zero, zero); + CollectionData collectionData = new CollectionData(zero, zero, 2L, null, zero, 2L, overDueSinceDate, zero, null, null, null, null, + null, null, zero, zero, zero, zero); Map<Long, CollectionData> installmentsCollection = new HashMap<>(); @@ -240,10 +240,10 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest { LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate().minusDays(2); LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L, loanForProcessing); - CollectionData collectionData = new CollectionData(zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, zeroAmount, null, null, - null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); + CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, zeroAmount, + null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); - CollectionData installmentCollectionData = new CollectionData(zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, + CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); Map<Long, CollectionData> installmentsCollection = new HashMap<>(); @@ -369,10 +369,10 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest { LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L, loanForProcessing); - CollectionData collectionData = new CollectionData(zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, zeroAmount, null, null, - null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); + CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, zeroAmount, + null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); - CollectionData installmentCollectionData = new CollectionData(zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, + CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); Map<Long, CollectionData> installmentsCollection = new HashMap<>(); @@ -446,10 +446,10 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest { LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate().minusDays(29); LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L, loanForProcessing); - CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 29L, null, zeroAmount, 29L, overDueSinceDate, zeroAmount, null, - null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); + CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate, zeroAmount, + null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); - CollectionData installmentCollectionData = new CollectionData(zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate, + CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); Map<Long, CollectionData> installmentsCollection = new HashMap<>(); @@ -534,13 +534,14 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest { LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate().minusDays(29); LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L, loanForProcessing); - CollectionData collectionData = new CollectionData(zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate, zeroAmount, null, null, - null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); + CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate, zeroAmount, + null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); - CollectionData installmentCollectionData_1 = new CollectionData(zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate, - installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); + CollectionData installmentCollectionData_1 = new CollectionData(zeroAmount, zeroAmount, 29L, null, zeroAmount, 29L, + overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, + zeroAmount); - CollectionData installmentCollectionData_2 = new CollectionData(zeroAmount, 0L, null, zeroAmount, 0L, null, + CollectionData installmentCollectionData_2 = new CollectionData(zeroAmount, zeroAmount, 0L, null, zeroAmount, 0L, null, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount); Map<Long, CollectionData> installmentsCollection = new HashMap<>();
