From 2bfc83722fd8ba394fd66558bd05a28281a8e4fe Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Sun, 5 Apr 2015 23:27:18 +0530 Subject: [PATCH] MIFOSX-1918 : added Pre-closure interest calculation strategy --- api-docs/apiLive.htm | 33 +- .../ClientLoanIntegrationTest.java | 504 +++++++++++++----- .../common/loans/LoanProductTestBuilder.java | 8 +- .../portfolio/loanaccount/domain/Loan.java | 36 +- .../domain/AbstractLoanScheduleGenerator.java | 425 ++++++++++----- .../domain/LoanApplicationTerms.java | 29 +- .../LoanScheduleModelDisbursementPeriod.java | 5 + .../domain/LoanScheduleModelPeriod.java | 2 + .../LoanScheduleModelRepaymentPeriod.java | 8 +- .../service/LoanScheduleAssembler.java | 3 +- ...cheduleCalculationPlatformServiceImpl.java | 15 +- .../loanproduct/LoanProductConstants.java | 1 + .../api/LoanProductsApiResource.java | 5 +- .../loanproduct/data/LoanProductData.java | 7 +- .../LoanProductInterestRecalculationData.java | 11 +- ...anPreCloseInterestCalculationStrategy.java | 76 +++ .../loanproduct/domain/LoanProduct.java | 8 + ...anProductInterestRecalculationDetails.java | 31 +- .../LoanProductDataValidator.java | 85 +-- .../LoanDropdownReadPlatformService.java | 2 + .../LoanDropdownReadPlatformServiceImpl.java | 39 +- .../loanproduct/service/LoanEnumerations.java | 21 + .../LoanProductReadPlatformServiceImpl.java | 8 +- ...V255__pre_close_interest_period_config.sql | 2 + 24 files changed, 987 insertions(+), 377 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanPreCloseInterestCalculationStrategy.java create mode 100644 mifosng-provider/src/main/resources/sql/migrations/core_db/V255__pre_close_interest_period_config.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 06003c97c..93ed9ff9f 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -11566,6 +11566,13 @@ An enumeration that indicates the type of transaction processing strategy to be Specifies rest frequency start date for interest recalculation.
+ + preCloseInterestCalculationStrategy + + + Specifies applicable days for interest calculation on pre close of a loan.
+ Example Values:1=Calculate till pre closure date, 2=Calculate till rest frequency date + isArrearsBasedOnOriginalSchedule @@ -11687,6 +11694,11 @@ GET https://DomainName/api/v1/loanproducts "code":"interestRecalculationFrequencyType.same.as.repayment.period", "value":"Same as repayment period" }, + "preCloseInterestCalculationStrategy": { + "id":1, + "code":"loanPreCloseInterestCalculationStrategy.tillPreClosureDate", + "value":"Till preclose Date" + }, "isArrearsBasedOnOriginalSchedule" : true }, "accountingRule": { @@ -11789,7 +11801,11 @@ GET https://DomainName/api/v1/loanproducts/template "id": 3, "code": "loanRescheduleStrategyMethod.reduce.emi.amount", "value": "Reduce EMI amount" - } + },"preCloseInterestCalculationStrategy": { + "id":1, + "code":"loanPreCloseInterestCalculationStrategy.tillPreClosureDate", + "value":"Till preclose Date" + } }, "paymentTypeOptions": [ { @@ -11825,6 +11841,18 @@ GET https://DomainName/api/v1/loanproducts/template "value": "Months" } ], + "preCloseInterestCalculationStrategyOptions": [ + { + "id":1, + "code":"loanPreCloseInterestCalculationStrategy.tillPreClosureDate", + "value":"Till preclose Date" + }, + { + "id":2, + "code":"loanPreCloseInterestCalculationStrategy.tillRestFrequencyDate", + "value":"Till rest Frequency Date" + } + ], "interestRateFrequencyTypeOptions": [ { "id": 2, @@ -12855,7 +12883,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
Additional Optional Fields if interest recalculation is enabled(true)
- isArrearsBasedOnOriginalSchedule + isArrearsBasedOnOriginalSchedule, preCloseInterestCalculationStrategy @@ -12981,6 +13009,7 @@ Request Body: "interestRecalculationCompoundingMethod":"0", "rescheduleStrategyMethod":"3", "recalculationRestFrequencyType":"1", + "preCloseInterestCalculationStrategy":1, "accountingRule":"2", "fundSourceAccountId":"4", "loanPortfolioAccountId":"8", diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientLoanIntegrationTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientLoanIntegrationTest.java index bade7d93c..7e147ed64 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientLoanIntegrationTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientLoanIntegrationTest.java @@ -1722,9 +1722,8 @@ public class ClientLoanIntegrationTest { System.out.println("----------Make repayment 3 advance------------"); this.loanTransactionHelper.makeRepayment("20 November 2011", Float.valueOf("3303"), loanID); - this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 November 2011", - new JournalEntry(Float.valueOf("3303"), JournalEntry.TransactionType.DEBIT), new JournalEntry(Float.valueOf("3149.11"), - JournalEntry.TransactionType.CREDIT)); + this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 November 2011", new JournalEntry(Float.valueOf("3303"), + JournalEntry.TransactionType.DEBIT), new JournalEntry(Float.valueOf("3149.11"), JournalEntry.TransactionType.CREDIT)); this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "20 November 2011", new JournalEntry(Float.valueOf("31.51"), JournalEntry.TransactionType.CREDIT), new JournalEntry(Float.valueOf("122.38"), JournalEntry.TransactionType.CREDIT)); @@ -2327,9 +2326,8 @@ public class ClientLoanIntegrationTest { System.out.println("----------Make repayment 3 advance------------"); this.loanTransactionHelper.makeRepayment("20 November 2011", Float.valueOf("3303"), loanID); - this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 November 2011", - new JournalEntry(Float.valueOf("3303"), JournalEntry.TransactionType.DEBIT), new JournalEntry(Float.valueOf("31.51"), - JournalEntry.TransactionType.CREDIT), + this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 November 2011", new JournalEntry(Float.valueOf("3303"), + JournalEntry.TransactionType.DEBIT), new JournalEntry(Float.valueOf("31.51"), JournalEntry.TransactionType.CREDIT), new JournalEntry(Float.valueOf("122.38"), JournalEntry.TransactionType.CREDIT), new JournalEntry(Float.valueOf("3149.11"), JournalEntry.TransactionType.CREDIT)); this.loanTransactionHelper.addChargesForLoan( @@ -2948,9 +2946,8 @@ public class ClientLoanIntegrationTest { System.out.println("----------Make repayment 3 advance------------"); this.loanTransactionHelper.makeRepayment("20 November 2011", Float.valueOf("3303"), loanID); - this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 November 2011", - new JournalEntry(Float.valueOf("3303"), JournalEntry.TransactionType.DEBIT), new JournalEntry(Float.valueOf("31.51"), - JournalEntry.TransactionType.CREDIT), + this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 November 2011", new JournalEntry(Float.valueOf("3303"), + JournalEntry.TransactionType.DEBIT), new JournalEntry(Float.valueOf("31.51"), JournalEntry.TransactionType.CREDIT), new JournalEntry(Float.valueOf("122.38"), JournalEntry.TransactionType.CREDIT), new JournalEntry(Float.valueOf("3149.11"), JournalEntry.TransactionType.CREDIT)); @@ -3173,7 +3170,8 @@ public class ClientLoanIntegrationTest { final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY, LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE, LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_EMI_AMOUN, - LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null, null); + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null); final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, null, LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, new ArrayList(0)); @@ -3185,10 +3183,10 @@ public class ClientLoanIntegrationTest { ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); List> expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------"); @@ -3203,10 +3201,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.67", "23.24", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2528.81", "11.67", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.67", "23.24", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2528.81", "11.67", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); @@ -3219,10 +3217,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); Float earlyPayment = new Float("4000"); @@ -3233,14 +3231,107 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "3965.31", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "1771.88", "16.39", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "1780.05", "8.22", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "3965.31", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "1771.88", "16.39", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "1780.05", "8.22", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); HashMap prepayDetail = this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, loanID); String prepayAmount = String.valueOf(prepayDetail.get("amount")); + validateNumberForEqualWithMsg("verify pre-close amount", "3551.93", prepayAmount); + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + String LOAN_REPAYMENT_DATE = dateFormat.format(todaysDate.getTime()); + this.loanTransactionHelper.makeRepayment(LOAN_REPAYMENT_DATE, new Float(prepayAmount), loanID); + loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + LoanStatusChecker.verifyLoanAccountIsClosed(loanStatusHashMap); + } + + @Test + public void testLoanScheduleWithInterestRecalculation_WITH_REST_SAME_AS_REPAYMENT_INTEREST_COMPOUND_NONE_STRATEGY_REDUCE_EMI_PRE_CLOSE_INTEREST_PRE_CLOSE_DATE() { + String preCloseInterestStrategy = LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE; + String preCloseAmount = "7561.84"; + testLoanScheduleWithInterestRecalculation_WITH_REST_SAME_AS_REPAYMENT_INTEREST_COMPOUND_NONE_STRATEGY_REDUCE_EMI_PRE_CLOSE_INTEREST( + preCloseInterestStrategy, preCloseAmount); + } + + @Test + public void testLoanScheduleWithInterestRecalculation_WITH_REST_SAME_AS_REPAYMENT_INTEREST_COMPOUND_NONE_STRATEGY_REDUCE_EMI_PRE_CLOSE_INTEREST_REST_DATE() { + String preCloseInterestStrategy = LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_REST_DATE; + String preCloseAmount = "7578.4"; + testLoanScheduleWithInterestRecalculation_WITH_REST_SAME_AS_REPAYMENT_INTEREST_COMPOUND_NONE_STRATEGY_REDUCE_EMI_PRE_CLOSE_INTEREST( + preCloseInterestStrategy, preCloseAmount); + } + + private void testLoanScheduleWithInterestRecalculation_WITH_REST_SAME_AS_REPAYMENT_INTEREST_COMPOUND_NONE_STRATEGY_REDUCE_EMI_PRE_CLOSE_INTEREST( + String preCloseInterestStrategy, String preCloseAmount) { + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); + + Calendar todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + todaysDate.add(Calendar.DAY_OF_MONTH, -16); + final String LOAN_DISBURSEMENT_DATE = dateFormat.format(todaysDate.getTime()); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID); + final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY, + LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE, + LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_EMI_AMOUN, + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null, preCloseInterestStrategy, null); + + final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, null, + LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, new ArrayList(0)); + + Assert.assertNotNull(loanID); + HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); + + ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); + List> expectedvalues = new ArrayList<>(); + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + addRepaymentValues(expectedvalues, todaysDate, -9, true, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); + verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); + + System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------"); + loanStatusHashMap = this.loanTransactionHelper.approveLoan(LOAN_DISBURSEMENT_DATE, loanID); + LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap); + LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap); + + System.out.println("-------------------------------DISBURSE LOAN-------------------------------------------"); + loanStatusHashMap = this.loanTransactionHelper.disburseLoan(LOAN_DISBURSEMENT_DATE, loanID); + LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap); + + loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); + expectedvalues = new ArrayList<>(); + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + addRepaymentValues(expectedvalues, todaysDate, -9, true, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2499.12", "29.79", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2535.36", "11.7", "0.0", "0.0"); + + verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); + + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + todaysDate.add(Calendar.DAY_OF_MONTH, -9); + final String LOAN_FIRST_REPAYMENT_DATE = dateFormat.format(todaysDate.getTime()); + Float totalDueForCurrentPeriod = (Float) loanSchedule.get(1).get("totalDueForPeriod"); + this.loanTransactionHelper.makeRepayment(LOAN_FIRST_REPAYMENT_DATE, totalDueForCurrentPeriod, loanID); + + loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); + expectedvalues = new ArrayList<>(); + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + addRepaymentValues(expectedvalues, todaysDate, -9, true, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2502.44", "26.47", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2520.58", "11.63", "0.0", "0.0"); + verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); + + HashMap prepayDetail = this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, loanID); + String prepayAmount = String.valueOf(prepayDetail.get("amount")); + validateNumberForEqualWithMsg("verify pre-close amount", preCloseAmount, prepayAmount); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); String LOAN_REPAYMENT_DATE = dateFormat.format(todaysDate.getTime()); this.loanTransactionHelper.makeRepayment(LOAN_REPAYMENT_DATE, new Float(prepayAmount), loanID); @@ -3263,7 +3354,8 @@ public class ClientLoanIntegrationTest { final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY, LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE, LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_EMI_AMOUN, - LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null, null); + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null); List charges = new ArrayList<>(); Integer installmentCharge = ChargesHelper.createCharges(requestSpec, responseSpec, @@ -3279,10 +3371,10 @@ public class ClientLoanIntegrationTest { ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); List> expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "4.62", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "3.47", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "2.32", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "1.16", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "4.62", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "3.47", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "2.32", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "1.16", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------"); @@ -3297,10 +3389,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "4.62", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.76", "46.15", "4.62", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.67", "23.24", "2.32", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2528.81", "11.67", "1.17", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "4.62", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.76", "46.15", "4.62", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.67", "23.24", "2.32", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2528.81", "11.67", "1.17", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); @@ -3313,10 +3405,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "4.62", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "3.47", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "2.32", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "1.16", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "4.62", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "3.47", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "2.32", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "1.16", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); Float earlyPayment = new Float("4000"); @@ -3327,10 +3419,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "4.62", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "3961.84", "34.69", "3.47", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "1773.61", "16.41", "1.64", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "1781.79", "8.22", "0.82", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "4.62", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "3961.84", "34.69", "3.47", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "1773.61", "16.41", "1.64", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "1781.79", "8.22", "0.82", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); HashMap prepayDetail = this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, loanID); @@ -3357,7 +3449,8 @@ public class ClientLoanIntegrationTest { final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY, LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST, LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS, - LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", LOAN_DISBURSEMENT_DATE, null); + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", LOAN_DISBURSEMENT_DATE, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null); final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, LOAN_DISBURSEMENT_DATE, LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList(0)); @@ -3369,10 +3462,10 @@ public class ClientLoanIntegrationTest { ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); List> expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); @@ -3388,19 +3481,19 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.54", "46.37", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.67", "23.24", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2529.03", "11.67", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.54", "46.37", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.67", "23.24", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2529.03", "11.67", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); loanSchedule = this.loanTransactionHelper.getLoanFutureRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, 0, "4965.3", "92.52", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.67", "23.24", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2529.03", "11.67", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 0, false, "4965.3", "92.52", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.67", "23.24", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2529.03", "11.67", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues, 0); @@ -3413,10 +3506,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); Float earlyPayment = new Float("4000"); @@ -3434,10 +3527,10 @@ public class ClientLoanIntegrationTest { paymentday.put("interestDue", "9.91"); paymentday.put("feeChargesDue", "0"); paymentday.put("penaltyChargesDue", "0"); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); expectedvalues.add(paymentday); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.28", "11.63", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "1009.87", "4.66", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.28", "11.63", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "1009.87", "4.66", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); HashMap prepayDetail = this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, loanID); @@ -3482,7 +3575,8 @@ public class ClientLoanIntegrationTest { final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY, LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE, LoanProductTestBuilder.RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS, - LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_WEEKLY, "1", REST_START_DATE, null); + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_WEEKLY, "1", REST_START_DATE, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null); final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, REST_START_DATE, LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, charges); @@ -3494,10 +3588,10 @@ public class ClientLoanIntegrationTest { ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); List> expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "100.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2486.04", "42.87", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2497.5", "31.41", "200", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2533.7", "19.93", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "100.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2486.04", "42.87", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2497.5", "31.41", "200", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2533.7", "19.93", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------"); @@ -3512,10 +3606,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "100.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.57", "46.34", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2497.49", "31.42", "200", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2537.18", "19.95", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "100.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.57", "46.34", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2497.49", "31.42", "200", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2537.18", "19.95", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); @@ -3528,10 +3622,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "100.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2486.04", "42.87", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2497.5", "31.41", "200", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2533.7", "19.93", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "100.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2486.04", "42.87", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2497.5", "31.41", "200", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2533.7", "19.93", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); Float earlyPayment = new Float("5100"); @@ -3543,10 +3637,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "100.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "5057.13", "42.87", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "0", "19.55", "200", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2460.11", "11.35", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "100.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "5100.0", "36.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "0", "11.16", "200", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2417.24", "11.16", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); HashMap prepayDetail = this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, loanID); @@ -3559,6 +3653,118 @@ public class ClientLoanIntegrationTest { } + @Test + public void testLoanScheduleWithInterestRecalculation_WITH_REST_WEEKLY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_REDUCE_NEXT_INSTALLMENTS_PRE_CLOSE_INTEREST_PRE_CLOSE_DATE() { + String preCloseInterestStrategy = LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE; + String preCloseAmount = "7766.75"; + testLoanScheduleWithInterestRecalculation_WITH_REST_WEEKLY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_REDUCE_NEXT_INSTALLMENTS_PRE_CLOSE_INTEREST( + preCloseInterestStrategy, preCloseAmount); + + } + + @Test + public void testLoanScheduleWithInterestRecalculation_WITH_REST_WEEKLY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_REDUCE_NEXT_INSTALLMENTS_PRE_CLOSE_INTEREST_REST_DATE() { + String preCloseInterestStrategy = LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_REST_DATE; + String preCloseAmount = "7771.71"; + testLoanScheduleWithInterestRecalculation_WITH_REST_WEEKLY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_REDUCE_NEXT_INSTALLMENTS_PRE_CLOSE_INTEREST( + preCloseInterestStrategy, preCloseAmount); + + } + + private void testLoanScheduleWithInterestRecalculation_WITH_REST_WEEKLY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_REDUCE_NEXT_INSTALLMENTS_PRE_CLOSE_INTEREST( + String preCloseInterestStrategy, String preCloseAmount) { + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + + DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); + + Calendar todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + todaysDate.add(Calendar.DAY_OF_MONTH, -16); + final String LOAN_DISBURSEMENT_DATE = dateFormat.format(todaysDate.getTime()); + todaysDate.add(Calendar.DAY_OF_MONTH, -4); + final String REST_START_DATE = dateFormat.format(todaysDate.getTime()); + + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + todaysDate.add(Calendar.DAY_OF_MONTH, -16); + todaysDate.add(Calendar.DAY_OF_MONTH, 2); + final String LOAN_FLAT_CHARGE_DATE = dateFormat.format(todaysDate.getTime()); + todaysDate.add(Calendar.DAY_OF_MONTH, 14); + final String LOAN_INTEREST_CHARGE_DATE = dateFormat.format(todaysDate.getTime()); + List charges = new ArrayList<>(2); + Integer flat = ChargesHelper.createCharges(requestSpec, responseSpec, + ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "100", false)); + Integer principalPercentage = ChargesHelper.createCharges(requestSpec, responseSpec, + ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT, "2", false)); + + addCharges(charges, flat, "100", LOAN_FLAT_CHARGE_DATE); + addCharges(charges, principalPercentage, "2", LOAN_INTEREST_CHARGE_DATE); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID); + final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY, + LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE, + LoanProductTestBuilder.RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS, + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_WEEKLY, "1", REST_START_DATE, preCloseInterestStrategy, null); + + final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, + REST_START_DATE, LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, charges); + + Assert.assertNotNull(loanID); + HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); + + ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); + List> expectedvalues = new ArrayList<>(); + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + addRepaymentValues(expectedvalues, todaysDate, -9, true, "2482.76", "46.15", "100.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2489.31", "39.6", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2500.78", "28.13", "200", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2527.15", "16.61", "0.0", "0.0"); + verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); + + System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------"); + loanStatusHashMap = this.loanTransactionHelper.approveLoan(LOAN_DISBURSEMENT_DATE, loanID); + LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap); + LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap); + + System.out.println("-------------------------------DISBURSE LOAN-------------------------------------------"); + loanStatusHashMap = this.loanTransactionHelper.disburseLoan(LOAN_DISBURSEMENT_DATE, loanID); + LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap); + + loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); + expectedvalues = new ArrayList<>(); + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + addRepaymentValues(expectedvalues, todaysDate, -9, true, "2482.76", "46.15", "100.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.37", "46.54", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2497.29", "31.62", "200", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2537.58", "16.65", "0.0", "0.0"); + + verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); + + Calendar repaymentDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + repaymentDate.add(Calendar.DAY_OF_MONTH, -9); + final String LOAN_FIRST_REPAYMENT_DATE = dateFormat.format(repaymentDate.getTime()); + Float totalDueForCurrentPeriod = (Float) loanSchedule.get(1).get("totalDueForPeriod"); + this.loanTransactionHelper.makeRepayment(LOAN_FIRST_REPAYMENT_DATE, totalDueForCurrentPeriod, loanID); + + loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); + expectedvalues = new ArrayList<>(); + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + addRepaymentValues(expectedvalues, todaysDate, -9, true, "2482.76", "46.15", "100.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2489.31", "39.6", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2500.78", "28.13", "200", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2527.15", "16.61", "0.0", "0.0"); + verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); + + HashMap prepayDetail = this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, loanID); + String prepayAmount = String.valueOf(prepayDetail.get("amount")); + validateNumberForEqualWithMsg("verify pre-close amount", preCloseAmount, prepayAmount); + todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + String LOAN_REPAYMENT_DATE = dateFormat.format(todaysDate.getTime()); + this.loanTransactionHelper.makeRepayment(LOAN_REPAYMENT_DATE, new Float(prepayAmount), loanID); + loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + LoanStatusChecker.verifyLoanAccountIsClosed(loanStatusHashMap); + } + @Test public void testLoanScheduleWithInterestRecalculation_WITH_REST_DAILY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_WITH_OVERDUE_CHARGE() throws InterruptedException { @@ -3584,7 +3790,8 @@ public class ClientLoanIntegrationTest { final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY, LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE, LoanProductTestBuilder.RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS, - LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", REST_START_DATE, null, overdueFeeChargeId.toString(), false); + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", REST_START_DATE, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null, overdueFeeChargeId.toString(), false); final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, REST_START_DATE, LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, null); @@ -3596,10 +3803,10 @@ public class ClientLoanIntegrationTest { ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); List> expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -2, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------"); @@ -3614,13 +3821,12 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - - - addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.54", "46.37", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.33", "46.58", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2552.37", "11.78", "0.0", "0.0"); - + + addRepaymentValues(expectedvalues, todaysDate, -2, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.54", "46.37", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.33", "46.58", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2552.37", "11.78", "0.0", "0.0"); + verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); String JobName = "Apply penalty to overdue loans"; @@ -3629,10 +3835,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "252.89"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2481.38", "47.53", "0.0", "252.89"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2479.99", "48.92", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2555.87", "11.8", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -2, false, "2482.76", "46.15", "0.0", "252.89"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2481.38", "47.53", "0.0", "252.89"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2479.99", "48.92", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2555.87", "11.8", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); Calendar repaymentDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); @@ -3645,10 +3851,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "252.89"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2493.05", "35.86", "0.0", "252.89"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2491.72", "37.19", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2532.47", "11.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -2, false, "2482.76", "46.15", "0.0", "252.89"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2493.05", "35.86", "0.0", "252.89"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2491.72", "37.19", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2532.47", "11.69", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); repaymentDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); @@ -3660,10 +3866,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "252.89"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2493.05", "35.86", "0.0", "252.89"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2491.72", "37.19", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2532.47", "11.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -2, false, "2482.76", "46.15", "0.0", "252.89"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2493.05", "35.86", "0.0", "252.89"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2497.22", "31.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2526.97", "11.66", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); } @@ -3691,7 +3897,8 @@ public class ClientLoanIntegrationTest { final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY, LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE, LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_EMI_AMOUN, - LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null, accounts); + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, accounts); final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, null, LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, new ArrayList(0)); @@ -3703,10 +3910,10 @@ public class ClientLoanIntegrationTest { ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); List> expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------"); @@ -3721,10 +3928,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.67", "23.24", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2528.81", "11.67", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.67", "23.24", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2528.81", "11.67", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); @@ -3746,10 +3953,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); this.loanTransactionHelper.checkAccrualTransactionForRepayment(Utils.getLocalDateOfTenant().minusDays(7), 46.15f, 0f, 0f, loanID); @@ -3783,7 +3990,8 @@ public class ClientLoanIntegrationTest { final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY, LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST, LoanProductTestBuilder.RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS, - LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", LOAN_DISBURSEMENT_DATE, null); + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", LOAN_DISBURSEMENT_DATE, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null); final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, LOAN_DISBURSEMENT_DATE, LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList(0)); @@ -3795,10 +4003,10 @@ public class ClientLoanIntegrationTest { ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); List> expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); @@ -3814,10 +4022,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.54", "46.37", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.67", "23.24", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2529.03", "11.67", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.54", "46.37", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.67", "23.24", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2529.03", "11.67", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); @@ -3851,7 +4059,7 @@ public class ClientLoanIntegrationTest { DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); Calendar todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - System.out.println("----timeeeeeeeeeeeeee------>"+dateFormat.format(todaysDate.getTime())); + System.out.println("----timeeeeeeeeeeeeee------>" + dateFormat.format(todaysDate.getTime())); todaysDate.add(Calendar.DAY_OF_MONTH, -14); final String LOAN_DISBURSEMENT_DATE = dateFormat.format(todaysDate.getTime()); @@ -3860,7 +4068,8 @@ public class ClientLoanIntegrationTest { final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY, LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST, LoanProductTestBuilder.RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS, - LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", LOAN_DISBURSEMENT_DATE, null, null, true); + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", LOAN_DISBURSEMENT_DATE, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null, null, true); final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, LOAN_DISBURSEMENT_DATE, LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList(0)); @@ -3872,10 +4081,10 @@ public class ClientLoanIntegrationTest { ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); List> expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2494.22", "34.69", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.73", "23.18", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2517.29", "11.62", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); @@ -3891,10 +4100,10 @@ public class ClientLoanIntegrationTest { loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); expectedvalues = new ArrayList<>(); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); - addRepaymentValues(expectedvalues, todaysDate, -1, "2482.76", "46.15", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2482.54", "46.37", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2505.67", "23.24", "0.0", "0.0"); - addRepaymentValues(expectedvalues, todaysDate, 1, "2529.03", "11.67", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, -1, false, "2482.76", "46.15", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2482.54", "46.37", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2505.67", "23.24", "0.0", "0.0"); + addRepaymentValues(expectedvalues, todaysDate, 1, false, "2529.03", "11.67", "0.0", "0.0"); verifyLoanRepaymentSchedule(loanSchedule, expectedvalues); todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant()); @@ -3919,10 +4128,14 @@ public class ClientLoanIntegrationTest { } - private void addRepaymentValues(List> expectedvalues, Calendar todaysDate, int addMonths, String principalDue, - String interestDue, String feeChargesDue, String penaltyChargesDue) { + private void addRepaymentValues(List> expectedvalues, Calendar todaysDate, int addPeriod, boolean isAddDays, + String principalDue, String interestDue, String feeChargesDue, String penaltyChargesDue) { Map values = new HashMap<>(3); - values.put("dueDate", getDateAsArray(todaysDate, addMonths)); + if (isAddDays) { + values.put("dueDate", getDateAsArray(todaysDate, addPeriod)); + } else { + values.put("dueDate", getDateAsArray(todaysDate, addPeriod * 7)); + } values.put("principalDue", principalDue); values.put("interestDue", interestDue); values.put("feeChargesDue", feeChargesDue); @@ -3930,8 +4143,8 @@ public class ClientLoanIntegrationTest { expectedvalues.add(values); } - private List getDateAsArray(Calendar todaysDate, int addWeeks) { - getDateAsArray(todaysDate, addWeeks * 7, Calendar.DAY_OF_MONTH); + private List getDateAsArray(Calendar todaysDate, int addPeriod) { + getDateAsArray(todaysDate, addPeriod, Calendar.DAY_OF_MONTH); return new ArrayList<>(Arrays.asList(todaysDate.get(Calendar.YEAR), todaysDate.get(Calendar.MONTH) + 1, todaysDate.get(Calendar.DAY_OF_MONTH))); } @@ -3945,17 +4158,17 @@ public class ClientLoanIntegrationTest { private Integer createLoanProductWithInterestRecalculation(final String repaymentStrategy, final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod, final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval, - final String recalculationRestFrequencyDate, final Account[] accounts) { + final String recalculationRestFrequencyDate, final String preCloseInterestCalculationStrategy, final Account[] accounts) { return createLoanProductWithInterestRecalculation(repaymentStrategy, interestRecalculationCompoundingMethod, rescheduleStrategyMethod, recalculationRestFrequencyType, recalculationRestFrequencyInterval, - recalculationRestFrequencyDate, accounts, null, false); + recalculationRestFrequencyDate, preCloseInterestCalculationStrategy, accounts, null, false); } private Integer createLoanProductWithInterestRecalculation(final String repaymentStrategy, final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod, final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval, - final String recalculationRestFrequencyDate, final Account[] accounts, final String chargeId, - boolean isArrearsBasedOnOriginalSchedule) { + final String recalculationRestFrequencyDate, final String preCloseInterestCalculationStrategy, final Account[] accounts, + final String chargeId, boolean isArrearsBasedOnOriginalSchedule) { System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); LoanProductTestBuilder builder = new LoanProductTestBuilder() .withPrincipal("10000000.00") @@ -3967,7 +4180,8 @@ public class ClientLoanIntegrationTest { .withRepaymentStrategy(repaymentStrategy) .withAmortizationTypeAsEqualPrincipalPayment() .withInterestTypeAsDecliningBalance() - .withInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod) + .withInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod, + preCloseInterestCalculationStrategy) .withInterestRecalculationRestFrequencyDetails(recalculationRestFrequencyType, recalculationRestFrequencyInterval, recalculationRestFrequencyDate); if (accounts != null) { diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java index b1d095303..3920d2f0e 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java @@ -51,6 +51,9 @@ public class LoanProductTestBuilder { private static final String CASH_BASED = "2"; private static final String ACCRUAL_PERIODIC = "3"; private static final String ACCRUAL_UPFRONT = "4"; + + public static final String INTEREST_APPLICABLE_STRATEGY_REST_DATE = "2"; + public static final String INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE = "1"; private String digitsAfterDecimal = "2"; private String inMultiplesOf = "0"; @@ -83,6 +86,7 @@ public class LoanProductTestBuilder { private String daysInYearType = "1"; private String daysInMonthType = "1"; private String interestRecalculationCompoundingMethod = "0"; + private String preCloseInterestCalculationStrategy = INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE; private String rescheduleStrategyMethod = "1"; private String recalculationRestFrequencyType = "1"; private String recalculationRestFrequencyInterval = "0"; @@ -149,6 +153,7 @@ public class LoanProductTestBuilder { map.put("recalculationRestFrequencyType", recalculationRestFrequencyType); map.put("recalculationRestFrequencyInterval", recalculationRestFrequencyInterval); map.put("recalculationRestFrequencyDate", recalculationRestFrequencyDate); + map.put("preCloseInterestCalculationStrategy", preCloseInterestCalculationStrategy); if (isArrearsBasedOnOriginalSchedule != null) { map.put("isArrearsBasedOnOriginalSchedule", isArrearsBasedOnOriginalSchedule); } @@ -381,10 +386,11 @@ public class LoanProductTestBuilder { } public LoanProductTestBuilder withInterestRecalculationDetails(final String interestRecalculationCompoundingMethod, - final String rescheduleStrategyMethod) { + final String rescheduleStrategyMethod, String preCloseInterestCalculationStrategy) { this.isInterestRecalculationEnabled = true; this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod; this.rescheduleStrategyMethod = rescheduleStrategyMethod; + this.preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy; return this; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 16cbbb16c..f55ca52bc 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -2375,13 +2375,15 @@ public class Loan extends AbstractPersistable { .getInterestRecalculationCompoundingMethod()); } - final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getApplicationCurrency(), - loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(), - getExpectedFirstRepaymentOnDate(), scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(), - this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, - this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(), - this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(), - recalculationFrequencyType, restCalendarInstance, compoundingMethod); + final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms + .assembleFrom(scheduleGeneratorDTO.getApplicationCurrency(), loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, + dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), + scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(), + this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, + this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(), + this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(), + recalculationFrequencyType, restCalendarInstance, compoundingMethod, + this.loanProduct.preCloseInterestCalculationStrategy()); final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(), scheduleGeneratorDTO.getHolidayDetailDTO()); @@ -4729,7 +4731,8 @@ public class Loan extends AbstractPersistable { this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(), this.loanInterestRecalculationDetails, calendarInstanceForInterestRecalculation, recalculationFrequencyType, - this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf()); + this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(), + this.loanProduct.preCloseInterestCalculationStrategy()); return loanApplicationTerms; } @@ -4783,7 +4786,8 @@ public class Loan extends AbstractPersistable { getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(), this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(), - recalculationFrequencyType, restCalendarInstance, compoundingMethod); + recalculationFrequencyType, restCalendarInstance, compoundingMethod, + this.loanProduct.preCloseInterestCalculationStrategy()); installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(), DateUtils.getLocalDateOfTenant(), loanApplicationTerms, mc, charges(), holidayDetailDTO); @@ -4992,12 +4996,14 @@ public class Loan extends AbstractPersistable { .getInterestRecalculationCompoundingMethod()); } - return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, - dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, - inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData, - maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate, - this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(), - recalculationFrequencyType, restCalendarInstance, compoundingMethod); + return LoanApplicationTerms + .assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, + expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, + inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, + disbursementData, maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate, + this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(), + recalculationFrequencyType, restCalendarInstance, compoundingMethod, + this.loanProduct.preCloseInterestCalculationStrategy()); } /** diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index a5544a169..3dedcebdf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -144,6 +144,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // interest calculation final Map latePaymentMap = new HashMap<>(); final List installments = new ArrayList<>(); + LocalDate currentDate = DateUtils.getLocalDateOfTenant(); while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) { actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms, @@ -202,126 +203,213 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // transaction processor configuration // will be added only if there is a loan repayment between the // period for interest first repayment strategies - if (isInterestRecalculationRequired(loanApplicationTerms, transactions) && loanRepaymentScheduleTransactionProcessor != null - && loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) { + Money earlyPaidAmount = Money.zero(currency); + LoanScheduleModelPeriod lastInstallment = null; + if (isInterestRecalculationRequired(loanApplicationTerms, transactions)) { + boolean checkForOutstanding = true; + List unprocessedTransactions = new ArrayList<>(); LoanScheduleModelPeriod installment = null; for (RecalculationDetail detail : applicableTransactions) { if (detail.isProcessed()) { continue; } if (detail.getTransactionDate().isBefore(scheduledDueDate)) { - List currentTransactions = createCurrentTransactionList(detail); - if (!detail.getTransactionDate().isEqual(periodStartDate)) { - int periodDays = Days.daysBetween(periodStartDate, detail.getTransactionDate()).getDays(); - // calculates period start date for interest - // calculation as per the configuration - periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, - periodStartDate, idealDisbursementDate, periodStartDateApplicableForInterest); + if (loanRepaymentScheduleTransactionProcessor != null + && loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) { + List currentTransactions = createCurrentTransactionList(detail); + if (!detail.getTransactionDate().isEqual(periodStartDate)) { + int periodDays = Days.daysBetween(periodStartDate, detail.getTransactionDate()).getDays(); + // calculates period start date for interest + // calculation as per the configuration + periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, + periodStartDate, idealDisbursementDate, periodStartDateApplicableForInterest); - int daysInPeriodApplicable = Days - .daysBetween(periodStartDateApplicableForInterest, detail.getTransactionDate()).getDays(); - Money interestForThisinstallment = Money.zero(currency); - if (daysInPeriodApplicable > 0) { - // 5 determine interest till the transaction - // date - PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( - this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, - totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest, - totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, - outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc, - mergeLateAndPaymentMaps(principalPortionMap, latePaymentMap, disburseDetailMap), - periodStartDateApplicableForInterest, detail.getTransactionDate(), - daysInPeriodApplicableForInterest); - interestForThisinstallment = principalInterestForThisPeriod.interest(); + int daysInPeriodApplicable = Days.daysBetween(periodStartDateApplicableForInterest, + detail.getTransactionDate()).getDays(); + Money interestForThisinstallment = Money.zero(currency); + if (daysInPeriodApplicable > 0) { + // 5 determine interest till the transaction + // date + PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( + this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, + totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest, + totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, + outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc, + mergeLateAndPaymentMaps(principalPortionMap, latePaymentMap, disburseDetailMap), + periodStartDateApplicableForInterest, detail.getTransactionDate(), + daysInPeriodApplicableForInterest); + interestForThisinstallment = principalInterestForThisPeriod.interest(); - totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace(); + totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace(); + } + + Money principalForThisPeriod = principalDisbursed.zero(); + + // applies all the applicable charges to the + // newly + // created installment + PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod, + interestForThisinstallment, null); + Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, + detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed, + totalCumulativeInterest, numberOfRepayments, true, lastTransactionDate); + Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate, + detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed, + totalCumulativeInterest, numberOfRepayments, true, lastTransactionDate); + + // 8. sum up real totalInstallmentDue from + // components + final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment) + .plus(feeChargesForInstallment).plus(penaltyChargesForInstallment); + // 9. create repayment period from parts + installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, + detail.getTransactionDate(), principalForThisPeriod, outstandingBalance, + interestForThisinstallment, feeChargesForInstallment, penaltyChargesForInstallment, + totalInstallmentDue, true); + periods.add(installment); + + // update outstanding balance for interest + // calculation as per the rest + outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, + detail.getTransactionDate(), outstandingBalanceAsPerRest, false); + outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, + detail.getTransactionDate(), outstandingBalanceAsPerRest, true); + + // handle cumulative fields + loanTermInDays += periodDays; + totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount()); + totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment); + totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); + totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount()); + + periodStartDate = detail.getTransactionDate(); + periodStartDateApplicableForInterest = periodStartDate; + instalmentNumber++; + // creates and insert Loan repayment schedule + // for + // the period + addLoanRepaymentScheduleInstallment(installments, installment); + } else if (installment == null) { + installment = ((List) periods).get(periods.size() - 1); + } + // applies the transaction as per transaction + // strategy + // on scheduled installments to identify the + // unprocessed(early payment ) amounts + Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, + currency, installments); + if (unprocessed.isGreaterThanZero()) { + + if (loanApplicationTerms.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) { + LocalDate applicableDate = getNextRestScheduleDate(detail.getTransactionDate().minusDays(1), + loanApplicationTerms, holidayDetailDTO); + checkForOutstanding = detail.getTransactionDate().isEqual(applicableDate); + + } + // reduces actual outstanding balance + outstandingBalance = outstandingBalance.minus(unprocessed); + // if outstanding balance becomes less than zero + // then adjusts the princiapal + Money addToPrinciapal = Money.zero(currency); + if (!outstandingBalance.isGreaterThanZero()) { + addToPrinciapal = addToPrinciapal.plus(outstandingBalance); + outstandingBalance = outstandingBalance.zero(); + lastInstallment = installment; + } + // updates principal portion map with the early + // payment amounts and applicable date as per + // rest + updatePrincipalPaidPortionToMap(loanApplicationTerms, holidayDetailDTO, principalPortionMap, installment, + detail, unprocessed.plus(addToPrinciapal), installments); + totalRepaymentExpected = totalRepaymentExpected.add(unprocessed.plus(addToPrinciapal).getAmount()); + totalCumulativePrincipal = totalCumulativePrincipal.plus(unprocessed.plus(addToPrinciapal)); + + // method applies early payment strategy + reducePrincipal = reducePrincipal.plus(unprocessed); + reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal); + } + // identify late payments and add compounding + // details to + // map for interest calculation + if (!reducePrincipal.isZero() || !installment.periodDueDate().isBefore(currentDate)) { + latePaymentMap.clear(); + } else { + updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, scheduledDueDate, + installments, true); + } + } else if (loanRepaymentScheduleTransactionProcessor != null) { + LocalDate applicableDate = getNextRestScheduleDate(detail.getTransactionDate().minusDays(1), + loanApplicationTerms, holidayDetailDTO); + if (applicableDate.isBefore(scheduledDueDate)) { + List currentTransactions = createCurrentTransactionList(detail); + Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, + currency, installments); + Money arrears = detail.getTransaction().getAmount(currency); + if (unprocessed.isGreaterThanZero()) { + arrears = getTotalAmount(latePaymentMap, currency); + updateMapWithAmount(principalPortionMap, unprocessed, applicableDate); + earlyPaidAmount = earlyPaidAmount.plus(unprocessed); + + // this check is to identify pre-closure and + // apply interest calculation as per + // configuration + if (!outstandingBalance.isGreaterThan(unprocessed) + && !loanApplicationTerms.getPreCloseInterestCalculationStrategy() + .calculateTillRestFrequencyEnabled()) { + + LocalDate calculateTill = detail.getTransactionDate(); + PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( + this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, + totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest, + totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, + outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc, + mergeLateAndPaymentMaps(principalPortionMap, latePaymentMap, disburseDetailMap), + periodStartDateApplicableForInterest, calculateTill, daysInPeriodApplicableForInterest); + if (!principalInterestForThisPeriod.interest() + .plus(principalInterestForThisPeriod.interestPaymentDueToGrace()).plus(outstandingBalance) + .isGreaterThan(unprocessed)) { + earlyPaidAmount = earlyPaidAmount.minus(unprocessed); + updateMapWithAmount(principalPortionMap, unprocessed.negated(), applicableDate); + LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null, + detail.getTransactionDate(), null, DateUtils.getLocalDateTimeOfTenant(), null); + RecalculationDetail recalculationDetail = new RecalculationDetail(detail.getTransactionDate(), + loanTransaction); + unprocessedTransactions.add(recalculationDetail); + break; + } + } + LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null, scheduledDueDate, + null, DateUtils.getLocalDateTimeOfTenant(), null); + RecalculationDetail recalculationDetail = new RecalculationDetail(scheduledDueDate, loanTransaction); + unprocessedTransactions.add(recalculationDetail); + checkForOutstanding = false; + + outstandingBalance = outstandingBalance.minus(unprocessed); + // if outstanding balance becomes less than + // zero + // then adjusts the princiapal + Money addToPrinciapal = Money.zero(currency); + if (outstandingBalance.isLessThanZero()) { + addToPrinciapal = addToPrinciapal.plus(outstandingBalance); + outstandingBalance = outstandingBalance.zero(); + updateMapWithAmount(principalPortionMap, addToPrinciapal, applicableDate); + earlyPaidAmount = earlyPaidAmount.plus(addToPrinciapal); + } + + } + if (arrears.isGreaterThanZero() && applicableDate.isBefore(currentDate)) { + updateMapWithAmount(latePaymentMap, arrears.negated(), applicableDate); + updateMapWithAmount(latePaymentMap, arrears, currentDate); + } } - Money principalForThisPeriod = principalDisbursed.zero(); - - // applies all the applicable charges to the newly - // created installment - PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod, interestForThisinstallment, - null); - Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, detail.getTransactionDate(), - loanCharges, currency, principalInterest, principalDisbursed, totalCumulativeInterest, - numberOfRepayments, true, lastTransactionDate); - Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate, - detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed, - totalCumulativeInterest, numberOfRepayments, true, lastTransactionDate); - - // 8. sum up real totalInstallmentDue from - // components - final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment) - .plus(feeChargesForInstallment).plus(penaltyChargesForInstallment); - // 9. create repayment period from parts - installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, - detail.getTransactionDate(), principalForThisPeriod, outstandingBalance, interestForThisinstallment, - feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue, true); - periods.add(installment); - - // update outstanding balance for interest - // calculation as per the rest - outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, - detail.getTransactionDate(), outstandingBalanceAsPerRest, false); - outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, - detail.getTransactionDate(), outstandingBalanceAsPerRest, true); - - // handle cumulative fields - loanTermInDays += periodDays; - totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount()); - totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment); - totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); - totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount()); - - periodStartDate = detail.getTransactionDate(); - periodStartDateApplicableForInterest = periodStartDate; - instalmentNumber++; - // creates and insert Loan repayment schedule for - // the period - addLoanRepaymentScheduleInstallment(installments, installment); - } else if (installment == null) { - installment = ((List) periods).get(periods.size() - 1); - } - // applies the transaction as per transaction strategy - // on scheduled installments to identify the - // unprocessed(early payment ) amounts - Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, - currency, installments); - if (unprocessed.isGreaterThanZero()) { - // reduces actual outstanding balance - outstandingBalance = outstandingBalance.minus(unprocessed); - // if outstanding balance becomes less than zero - // then adjusts the princiapal - Money addToPrinciapal = Money.zero(currency); - if (outstandingBalance.isLessThanZero()) { - addToPrinciapal = addToPrinciapal.plus(outstandingBalance); - outstandingBalance = outstandingBalance.zero(); - } - // updates principal portion map with the early - // payment amounts and applicable date as per rest - updatePrincipalPaidPortionToMap(loanApplicationTerms, holidayDetailDTO, principalPortionMap, installment, - detail, unprocessed.plus(addToPrinciapal), installments); - totalRepaymentExpected = totalRepaymentExpected.add(unprocessed.plus(addToPrinciapal).getAmount()); - totalCumulativePrincipal = totalCumulativePrincipal.plus(unprocessed.plus(addToPrinciapal)); - - // method applies early payment strategy - reducePrincipal = reducePrincipal.plus(unprocessed); - reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal); - } - // identify late payments and add compounding details to - // map for interest calculation - if (!reducePrincipal.isZero() || !installment.periodDueDate().isBefore(DateUtils.getLocalDateOfTenant())) { - latePaymentMap.clear(); - } else { - updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, scheduledDueDate, - installments, true); } } } - if (outstandingBalance.isZero() && disburseDetailMap.isEmpty()) { + applicableTransactions.addAll(unprocessedTransactions); + if (checkForOutstanding && outstandingBalance.isZero() && disburseDetailMap.isEmpty()) { continue; } } @@ -364,9 +452,19 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener principalForThisPeriod = principalForThisPeriod.zero(); } + // earlyPaidAmount is already subtracted from balancereducePrincipal + // reducePrincipal.plus(unprocessed); + Money reducedBalance = earlyPaidAmount; + earlyPaidAmount = earlyPaidAmount.minus(principalForThisPeriod); + if (earlyPaidAmount.isGreaterThanZero()) { + reducePrincipal = reducePrincipal.plus(earlyPaidAmount); + reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal); + principalForThisPeriod = principalForThisPeriod.plus(earlyPaidAmount); + } + // 6. update outstandingLoanBlance using current period // 'principalDue' - outstandingBalance = outstandingBalance.minus(principalForThisPeriod); + outstandingBalance = outstandingBalance.minus(principalForThisPeriod.minus(reducedBalance)); if (outstandingBalance.isLessThanZero()) { principalForThisPeriod = principalForThisPeriod.plus(outstandingBalance); @@ -386,6 +484,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment).plus(feeChargesForInstallment) .plus(penaltyChargesForInstallment); + // if previous installment is last then add interest to same + // installment + if (lastInstallment != null && principalForThisPeriod.isZero()) { + lastInstallment.addInterestAmount(interestForThisinstallment); + continue; + } + // 9. create repayment period from parts LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, scheduledDueDate, principalForThisPeriod, outstandingBalance, interestForThisinstallment, feeChargesForInstallment, @@ -393,9 +498,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // apply loan transactions on installments to identify early/late // payments for interest recalculation - if (isInterestRecalculationRequired(loanApplicationTerms, transactions)) { + if (isInterestRecalculationRequired(loanApplicationTerms, transactions) && loanRepaymentScheduleTransactionProcessor != null) { addLoanRepaymentScheduleInstallment(installments, installment); - for (RecalculationDetail detail : applicableTransactions) { if (!detail.isProcessed()) { List currentTransactions = new ArrayList<>(2); @@ -405,18 +509,23 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // unprocessed(early payment ) amounts Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency, installments); + if (unprocessed.isGreaterThanZero()) { outstandingBalance = outstandingBalance.minus(unprocessed); // pre closure check and processing if (outstandingBalance.isLessThan(interestForThisinstallment) && !scheduledDueDate.equals(detail.getTransactionDate())) { + LocalDate calculateTill = detail.getTransactionDate(); + if (loanApplicationTerms.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) { + calculateTill = getNextRestScheduleDate(calculateTill.minusDays(1), loanApplicationTerms, + holidayDetailDTO); + } PrincipalInterest interestTillDate = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc, mergeLateAndPaymentMaps(principalPortionMap, latePaymentMap, disburseDetailMap), - periodStartDateApplicableForInterest, detail.getTransactionDate(), - daysInPeriodApplicableForInterest); + periodStartDateApplicableForInterest, calculateTill, daysInPeriodApplicableForInterest); Money diff = interestForThisinstallment.minus(interestTillDate.interest()); if (!outstandingBalance.minus(diff).isGreaterThanZero()) { outstandingBalance = outstandingBalance.minus(diff); @@ -455,7 +564,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener } // identify late payments and adds compounding details to map - if (!reducePrincipal.isZero() || !installment.periodDueDate().isBefore(DateUtils.getLocalDateOfTenant())) { + if (!reducePrincipal.isZero() || !installment.periodDueDate().isBefore(currentDate)) { latePaymentMap.clear(); } else { updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, scheduledDueDate, @@ -472,7 +581,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener amountApplicableDate = getNextRestScheduleDate(installment.periodDueDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); } - updateMapWithAmount(principalPortionMap, principalForThisPeriod, amountApplicableDate); + updateMapWithAmount(principalPortionMap, principalForThisPeriod.minus(reducedBalance), amountApplicableDate); // update outstanding balance for interest calculation outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, scheduledDueDate, @@ -515,7 +624,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // this block is to add extra re-payment schedules with interest portion // if the loan not paid with in loan term - LocalDate currentDate = DateUtils.getLocalDateOfTenant(); + if (isInterestRecalculationRequired(loanApplicationTerms, transactions) && latePaymentMap.size() > 0 && currentDate.isAfter(periodStartDate)) { Money totalInterest = addInterestOnlyRepaymentScheduleForCurrentdate(mc, loanApplicationTerms, holidayDetailDTO, currency, @@ -743,9 +852,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener principalEffectiveDate = getNextRestScheduleDate(loanRepaymentScheduleInstallment.getDueDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); } - updateMapWithAmount(latePaymentMap, loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency), - principalEffectiveDate); - totalCompoundingAmount = totalCompoundingAmount.plus(loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency)); + if (principalEffectiveDate.isBefore(DateUtils.getLocalDateOfTenant())) { + updateMapWithAmount(latePaymentMap, loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency), + principalEffectiveDate); + totalCompoundingAmount = totalCompoundingAmount + .plus(loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency)); + } totalCompoundingAmount = updateMapWithCompoundingDetails(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, totalCompoundingAmount, loanRepaymentScheduleInstallment); } @@ -762,25 +874,27 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener if (loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) { LocalDate compoundingEffectiveDate = getNextCompoundScheduleDate(loanRepaymentScheduleInstallment.getDueDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); - Money amount = Money.zero(currency); - switch (loanApplicationTerms.getInterestRecalculationCompoundingMethod()) { - case INTEREST: - amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency)); - break; - case FEE: - amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)); - amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency)); - break; - case INTEREST_AND_FEE: - amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency)); - amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)); - amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency)); - break; - default: - break; + if (compoundingEffectiveDate.isBefore(DateUtils.getLocalDateOfTenant())) { + Money amount = Money.zero(currency); + switch (loanApplicationTerms.getInterestRecalculationCompoundingMethod()) { + case INTEREST: + amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency)); + break; + case FEE: + amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)); + amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency)); + break; + case INTEREST_AND_FEE: + amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency)); + amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)); + amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency)); + break; + default: + break; + } + updateMapWithAmount(latePaymentMap, amount, compoundingEffectiveDate); + totalCompoundingAmount = totalCompoundingAmount.plus(amount); } - updateMapWithAmount(latePaymentMap, amount, compoundingEffectiveDate); - totalCompoundingAmount = totalCompoundingAmount.plus(amount); } return totalCompoundingAmount; } @@ -852,6 +966,16 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener map.put(amountApplicableDate, principalPaid); } + private Money getTotalAmount(final Map map, final MonetaryCurrency currency) { + Money total = Money.zero(currency); + for (Map.Entry mapEntry : map.entrySet()) { + if (mapEntry.getKey().isBefore(DateUtils.getLocalDateOfTenant())) { + total = total.plus(mapEntry.getValue()); + } + } + return total; + } + @Override public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, @@ -1381,16 +1505,20 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener Set charges, final HolidayDetailDTO holidayDetailDTO) { Money feeCharges = Money.zero(currency); Money penaltyCharges = Money.zero(currency); + Money totalCompoundingAmount = Money.zero(currency); Money totalPrincipal = Money.zero(currency); Money totalInterest = Money.zero(currency); + Money reducePrincipalAsperRest = Money.zero(currency); + LocalDate reducePrincipalRestDate = onDate; LocalDate calculateInterestFrom = onDate; LocalDate periodStartDate = onDate; + LocalDate currentDate = DateUtils.getLocalDateOfTenant(); Money principalOutstanding = Money.zero(currency); Integer periodNumber = 1; // for multi disburse loan to identify all disbursed principal for // interest calculation - Map differenceMap = new HashMap<>(); - Map disburseAmtMap = new HashMap<>(); + Map differenceMap = new TreeMap<>(); + Map disburseAmtMap = new TreeMap<>(); if (loanApplicationTerms.isMultiDisburseLoan()) { for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) { if (disbursementData.isDisbursed()) { @@ -1412,10 +1540,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener feeCharges = feeCharges.plus(currentInstallment.getFeeChargesOutstanding(currency)); penaltyCharges = penaltyCharges.plus(currentInstallment.getPenaltyChargesOutstanding(currency)); principalOutstanding = principalOutstanding.minus(currentInstallment.getPrincipal(currency)); + LocalDate restDate = getNextRestScheduleDate(currentInstallment.getDueDate().minusDays(1), loanApplicationTerms, + holidayDetailDTO); + if (restDate.isAfter(currentDate)) { + reducePrincipalRestDate = restDate; + reducePrincipalAsperRest = reducePrincipalAsperRest.plus(currentInstallment.getPrincipalOutstanding(currency)); + } + updateMapWithAmount(differenceMap, currentInstallment.getPrincipalOutstanding(currency), currentInstallment.getDueDate()); - updateMapWithCompoundingDetails(loanApplicationTerms, holidayDetailDTO, currency, differenceMap, Money.zero(currency), - currentInstallment); + totalCompoundingAmount = updateMapWithCompoundingDetails(loanApplicationTerms, holidayDetailDTO, currency, + differenceMap, totalCompoundingAmount, currentInstallment); } else { totalPrincipal = totalPrincipal.minus(currentInstallment.getPrincipalCompleted(currency)); totalInterest = totalInterest.minus(currentInstallment.getInterestPaid(currency)).minus( @@ -1433,7 +1568,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener principalOutstanding = principalOutstanding.minus(currentInstallment.getPrincipal(currency)); } } - + Money arrears = totalPrincipal; totalPrincipal = totalPrincipal.plus(principalOutstanding); for (Map.Entry disburseDetail : disburseAmtMap.entrySet()) { @@ -1467,12 +1602,22 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener if (calculateInterestFrom.isBefore(periodStartDateApplicableForInterest)) { calculateInterestFrom = periodStartDateApplicableForInterest; } + LocalDate calculateTill = onDate; + if (loanApplicationTerms.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) { + LocalDate applicableDate = getNextRestScheduleDate(onDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); + calculateTill = applicableDate; + } + if (arrears.isGreaterThanZero()) { + updateMapWithAmount(differenceMap, arrears.minus(reducePrincipalAsperRest).plus(totalCompoundingAmount).negated(), + currentDate); + updateMapWithAmount(differenceMap, reducePrincipalAsperRest.negated(), reducePrincipalRestDate); + } if (calculateInterestFrom.isBefore(onDate)) { double interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf(0); PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), principalOutstanding, loanApplicationTerms, - periodNumber, mc, differenceMap, calculateInterestFrom, onDate, daysInPeriodApplicableForInterest); + periodNumber, mc, differenceMap, calculateInterestFrom, calculateTill, daysInPeriodApplicableForInterest); interest = interest.plus(principalInterestForThisPeriod.interest()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java index d83dfbf48..537b95f9c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java @@ -31,6 +31,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; +import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod; import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType; @@ -137,6 +138,8 @@ public final class LoanApplicationTerms { private final BigDecimal principalThresholdForLastInstalment; private final Integer installmentAmountInMultiplesOf; + private final LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy; + public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType, @@ -151,7 +154,8 @@ public final class LoanApplicationTerms { final List emiAmountVariations, final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled, final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance, - final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf) { + final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf, + final LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy) { final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null; final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null; @@ -162,7 +166,8 @@ public final class LoanApplicationTerms { graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, - recalculationFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf); + recalculationFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, + preCloseInterestCalculationStrategy); } public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency, @@ -174,7 +179,8 @@ public final class LoanApplicationTerms { final List emiAmountVariations, final LocalDate interestChargedFromDate, final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf, final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance, - final InterestRecalculationCompoundingMethod compoundingMethod) { + final InterestRecalculationCompoundingMethod compoundingMethod, + final LoanPreCloseInterestCalculationStrategy loanPreCloseInterestCalculationStrategy) { final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments(); final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery(); @@ -207,7 +213,7 @@ public final class LoanApplicationTerms { multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType, - principalThresholdForLastInstalment, installmentAmountInMultiplesOf); + principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanPreCloseInterestCalculationStrategy); } public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency, @@ -218,7 +224,7 @@ public final class LoanApplicationTerms { final List emiAmountVariations, final LocalDate interestChargedFromDate, final LoanInterestRecalculationDetails interestRecalculationDetails, final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType, final BigDecimal principalThresholdForLastInstalment, - Integer installmentAmountInMultiplesOf) { + Integer installmentAmountInMultiplesOf, final LoanPreCloseInterestCalculationStrategy loanPreCloseInterestCalculationStrategy) { final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments(); final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery(); @@ -256,7 +262,7 @@ public final class LoanApplicationTerms { disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, - principalThresholdForLastInstalment, installmentAmountInMultiplesOf); + principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanPreCloseInterestCalculationStrategy); } public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, @@ -275,7 +281,7 @@ public final class LoanApplicationTerms { applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod, applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance, applicationTerms.recalculationFrequencyType, applicationTerms.principalThresholdForLastInstalment, - applicationTerms.installmentAmountInMultiplesOf); + applicationTerms.installmentAmountInMultiplesOf, applicationTerms.preCloseInterestCalculationStrategy); } private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency, @@ -293,7 +299,8 @@ public final class LoanApplicationTerms { final boolean isInterestRecalculationEnabled, final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod, final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType, - final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf) { + final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf, + final LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy) { this.currency = currency; this.loanTermFrequency = loanTermFrequency; this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType; @@ -337,6 +344,7 @@ public final class LoanApplicationTerms { this.recalculationFrequencyType = recalculationFrequencyType; this.principalThresholdForLastInstalment = principalThresholdForLastInstalment; this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; + this.preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy; } public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate, @@ -1170,4 +1178,9 @@ public final class LoanApplicationTerms { public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() { return this.interestCalculationPeriodMethod; } + + + public LoanPreCloseInterestCalculationStrategy getPreCloseInterestCalculationStrategy() { + return this.preCloseInterestCalculationStrategy; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelDisbursementPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelDisbursementPeriod.java index dad390711..6a0461373 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelDisbursementPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelDisbursementPeriod.java @@ -104,4 +104,9 @@ public final class LoanScheduleModelDisbursementPeriod implements LoanScheduleMo public void addPrincipalAmount(@SuppressWarnings("unused") Money principalDue) { return; } + + @Override + public void addInterestAmount(@SuppressWarnings("unused") Money principalDue) { + return; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelPeriod.java index 1b8278b2c..6106e2e13 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelPeriod.java @@ -36,4 +36,6 @@ public interface LoanScheduleModelPeriod { boolean isRecalculatedInterestComponent(); void addPrincipalAmount(Money principalDue); + + void addInterestAmount(Money interestDue); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelRepaymentPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelRepaymentPeriod.java index 876e929b0..df7dd830d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelRepaymentPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelRepaymentPeriod.java @@ -22,7 +22,7 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel private final LocalDate dueDate; private Money principalDue; private final Money outstandingLoanBalance; - private final Money interestDue; + private Money interestDue; private Money feeChargesDue; private Money penaltyChargesDue; private Money totalDue; @@ -135,4 +135,10 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel public boolean isRecalculatedInterestComponent() { return this.recalculatedInterestComponent; } + + @Override + public void addInterestAmount(Money interestDue) { + this.interestDue = this.interestDue.plus(interestDue); + this.totalDue = this.totalDue.plus(principalDue); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index e29c9d714..5af525b58 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -281,7 +281,8 @@ public class LoanScheduleAssembler { graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsToleranceMoney, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, loanVariationTermsData, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, recalculationFrequencyType, - restCalendarInstance, principalThresholdForLastInstalment, installmentAmountInMultiplesOf); + restCalendarInstance, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, + loanProduct.preCloseInterestCalculationStrategy()); } private CalendarInstance createInterestRecalculationCalendarInstance(final LocalDate calendarStartDate, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java index 504b39a78..73c6b1ed2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java @@ -132,6 +132,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC final Loan loan = this.loanAssembler.assembleFrom(loanId); + LocalDate today = DateUtils.getLocalDateOfTenant(); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory .determineProcessor(loan.transactionProcessingStrategy()); @@ -149,7 +150,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC final List futureInstallments = new ArrayList<>(); for (final LoanRepaymentScheduleInstallment currentInstallment : loan.fetchRepaymentScheduleInstallments()) { if (currentInstallment.isNotFullyPaidOff()) { - if (!currentInstallment.getDueDate().isAfter(LocalDate.now())) { + if (!currentInstallment.getDueDate().isAfter(today)) { totalPrincipal = totalPrincipal.plus(currentInstallment.getPrincipalOutstanding(currency)); } } @@ -164,8 +165,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance); LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount( - loan.fetchRepaymentScheduleInstallments(), currency, LocalDate.now(), loanApplicationTerms, loan.charges(), - loan.getOfficeId()); + loan.fetchRepaymentScheduleInstallments(), currency, today, loanApplicationTerms, loan.charges(), loan.getOfficeId()); Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus( loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency)); Money interestDue = Money.zero(currency); @@ -180,18 +180,17 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC modifiedTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction)); } if (isNewPaymentRequired) { - LoanTransaction ondayPaymentTransaction = LoanTransaction.repayment(null, totalAmount, null, LocalDate.now(), null, + LoanTransaction ondayPaymentTransaction = LoanTransaction.repayment(null, totalAmount, null, today, null, DateUtils.getLocalDateTimeOfTenant(), null); modifiedTransactions.add(ondayPaymentTransaction); } LoanScheduleModel model = this.loanScheduleAssembler.assembleForInterestRecalculation(loanApplicationTerms, loan.getOfficeId(), - modifiedTransactions, loan.charges(), loanRepaymentScheduleTransactionProcessor, LocalDate.now()); + modifiedTransactions, loan.charges(), loanRepaymentScheduleTransactionProcessor, today); LoanScheduleData scheduleDate = model.toData(); Collection periodDatas = scheduleDate.getPeriods(); for (LoanSchedulePeriodData periodData : periodDatas) { - if ((periodData.periodDueDate().isEqual(LocalDate.now()) || periodData.periodDueDate().isAfter(LocalDate.now())) - && isNewPaymentRequired) { + if ((periodData.periodDueDate().isEqual(today) || periodData.periodDueDate().isAfter(today)) && isNewPaymentRequired) { LoanSchedulePeriodData loanSchedulePeriodData = LoanSchedulePeriodData.repaymentOnlyPeriod(periodData.periodNumber(), periodData.periodFromDate(), periodData.periodDueDate(), totalPrincipal.getAmount(), periodData .principalLoanBalanceOutstanding(), interestDue.getAmount(), loanRepaymentScheduleInstallment @@ -199,7 +198,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC loanRepaymentScheduleInstallment.getPenaltyChargesCharged(currency).getAmount(), totalAmount.getAmount()); futureInstallments.add(loanSchedulePeriodData); isNewPaymentRequired = false; - } else if (periodData.periodDueDate().isAfter(LocalDate.now())) { + } else if (periodData.periodDueDate().isAfter(today)) { futureInstallments.add(periodData); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java index ee44461c5..e613df27c 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java @@ -75,6 +75,7 @@ public interface LoanProductConstants { public static final String recalculationRestFrequencyIntervalParameterName = "recalculationRestFrequencyInterval"; public static final String recalculationRestFrequencyDateParamName = "recalculationRestFrequencyDate"; public static final String isArrearsBasedOnOriginalScheduleParamName = "isArrearsBasedOnOriginalSchedule"; + public static final String preCloseInterestCalculationStrategyParamName = "preCloseInterestCalculationStrategy"; // Guarantee related public static final String holdGuaranteeFundsParamName = "holdGuaranteeFunds"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java index 4436bd1e3..ccc45e65a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java @@ -277,12 +277,15 @@ public class LoanProductsApiResource { final List rescheduleStrategyTypeOptions = dropdownReadPlatformService.retrieveRescheduleStrategyTypeOptions(); final List interestRecalculationFrequencyTypeOptions = dropdownReadPlatformService .retrieveInterestRecalculationFrequencyTypeOptions(); + final List preCloseInterestCalculationStrategyOptions = dropdownReadPlatformService + .retrivePreCloseInterestCalculationStrategyOptions(); return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions, loanCycleValueConditionTypeOptions, daysInMonthTypeOptions, daysInYearTypeOptions, - interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions); + interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions, + preCloseInterestCalculationStrategyOptions); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java index d21a27011..90266fc53 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java @@ -129,6 +129,8 @@ public class LoanProductData { private final List interestRecalculationCompoundingTypeOptions; @SuppressWarnings("unused") private final List rescheduleStrategyTypeOptions; + @SuppressWarnings("unused") + private final List preCloseInterestCalculationStrategyOptions; @SuppressWarnings("unused") private final List interestRecalculationFrequencyTypeOptions; @@ -479,6 +481,7 @@ public class LoanProductData { this.canDefineInstallmentAmount = canDefineInstallmentAmount; this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; + this.preCloseInterestCalculationStrategyOptions = null; } @@ -491,7 +494,8 @@ public class LoanProductData { final Map> accountingMappingOptions, final List accountingRuleOptions, final List valueConditionTypeOptions, final List daysInMonthTypeOptions, final List daysInYearTypeOptions, final List interestRecalculationCompoundingTypeOptions, - final List rescheduleStrategyTypeOptions, final List interestRecalculationFrequencyTypeOptions) { + final List rescheduleStrategyTypeOptions, final List interestRecalculationFrequencyTypeOptions, + final List preCloseInterestCalculationStrategyOptions) { this.id = productData.id; this.name = productData.name; this.shortName = productData.shortName; @@ -592,6 +596,7 @@ public class LoanProductData { this.canDefineInstallmentAmount = productData.canDefineInstallmentAmount; this.installmentAmountInMultiplesOf = productData.installmentAmountInMultiplesOf; + this.preCloseInterestCalculationStrategyOptions = preCloseInterestCalculationStrategyOptions; } private Collection nullIfEmpty(final Collection charges) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductInterestRecalculationData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductInterestRecalculationData.java index 400f32f68..39dec8f75 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductInterestRecalculationData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductInterestRecalculationData.java @@ -6,11 +6,13 @@ package org.mifosplatform.portfolio.loanproduct.data; import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestRecalculationCompoundingType; +import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.preCloseInterestCalculationStrategy; import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.rescheduleStrategyType; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; +import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy; import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod; public class LoanProductInterestRecalculationData { @@ -26,11 +28,14 @@ public class LoanProductInterestRecalculationData { private final LocalDate recalculationRestFrequencyDate; @SuppressWarnings("unused") private final boolean isArrearsBasedOnOriginalSchedule; + @SuppressWarnings("unused") + private final EnumOptionData preCloseInterestCalculationStrategy; public LoanProductInterestRecalculationData(final Long id, final Long productId, final EnumOptionData interestRecalculationCompoundingType, final EnumOptionData rescheduleStrategyType, final EnumOptionData recalculationRestFrequencyType, final Integer recalculationRestFrequencyInterval, - final LocalDate recalculationRestFrequencyDate, boolean isArrearsBasedOnOriginalSchedule) { + final LocalDate recalculationRestFrequencyDate, boolean isArrearsBasedOnOriginalSchedule, + final EnumOptionData preCloseInterestCalculationStrategy) { this.id = id; this.productId = productId; this.interestRecalculationCompoundingType = interestRecalculationCompoundingType; @@ -39,6 +44,7 @@ public class LoanProductInterestRecalculationData { this.recalculationRestFrequencyInterval = recalculationRestFrequencyInterval; this.recalculationRestFrequencyDate = recalculationRestFrequencyDate; this.isArrearsBasedOnOriginalSchedule = isArrearsBasedOnOriginalSchedule; + this.preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy; } public static LoanProductInterestRecalculationData sensibleDefaultsForNewLoanProductCreation() { @@ -50,9 +56,10 @@ public class LoanProductInterestRecalculationData { final Integer recalculationRestFrequencyInterval = null; final LocalDate recalculationRestFrequencyDate = null; final boolean isArrearsBasedOnOriginalSchedule = false; + final EnumOptionData preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy(LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE); return new LoanProductInterestRecalculationData(id, productId, interestRecalculationCompoundingType, rescheduleStrategyType, recalculationRestFrequencyType, recalculationRestFrequencyInterval, recalculationRestFrequencyDate, - isArrearsBasedOnOriginalSchedule); + isArrearsBasedOnOriginalSchedule, preCloseInterestCalculationStrategy); } public EnumOptionData getInterestRecalculationCompoundingType() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanPreCloseInterestCalculationStrategy.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanPreCloseInterestCalculationStrategy.java new file mode 100644 index 000000000..35e8c2b58 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanPreCloseInterestCalculationStrategy.java @@ -0,0 +1,76 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanproduct.domain; + +import java.util.HashMap; +import java.util.Map; + +public enum LoanPreCloseInterestCalculationStrategy { + + NONE(0, "loanPreCloseInterestCalculationStrategy.none"), // + TILL_PRE_CLOSURE_DATE(1, "loanPreCloseInterestCalculationStrategy.tillPreClosureDate"), // + TILL_REST_FREQUENCY_DATE(2, "loanPreCloseInterestCalculationStrategy.tillRestFrequencyDate"); + + // REPAYMENT_PERIOD_DATE(3, + // "loanPreCloseInterestCalculationStrategy.repaymentPeriodDate") + + private Integer value; + private String code; + + private static final Map intToEnumMap = new HashMap<>(); + private static int minValue; + private static int maxValue; + static { + int i = 0; + for (final LoanPreCloseInterestCalculationStrategy type : LoanPreCloseInterestCalculationStrategy.values()) { + if (i == 0) { + minValue = type.value; + } + intToEnumMap.put(type.value, type); + if (minValue >= type.value) { + minValue = type.value; + } + if (maxValue < type.value) { + maxValue = type.value; + } + i = i + 1; + } + } + + public static LoanPreCloseInterestCalculationStrategy fromInt(final Integer ruleTypeValue) { + final LoanPreCloseInterestCalculationStrategy type = intToEnumMap.get(ruleTypeValue); + return type; + } + + private LoanPreCloseInterestCalculationStrategy(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static int getMinValue() { + return minValue; + } + + public static int getMaxValue() { + return maxValue; + } + + public boolean calculateTillRestFrequencyEnabled() { + return this.getValue().equals(LoanPreCloseInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE.getValue()); + } + + public boolean calculateTillPreClosureDateEnabled() { + return this.getValue().equals(LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getValue()); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 69c7dc542..8f7205957 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -1069,4 +1069,12 @@ public class LoanProduct extends AbstractPersistable { return this.installmentAmountInMultiplesOf; } + public LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy() { + LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy = LoanPreCloseInterestCalculationStrategy.NONE; + if (this.isInterestRecalculationEnabled()) { + preCloseInterestCalculationStrategy = getProductInterestRecalculationDetails().preCloseInterestCalculationStrategy(); + } + return preCloseInterestCalculationStrategy; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductInterestRecalculationDetails.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductInterestRecalculationDetails.java index 870037252..399869207 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductInterestRecalculationDetails.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductInterestRecalculationDetails.java @@ -31,6 +31,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_product_loan_recalculation_details") public class LoanProductInterestRecalculationDetails extends AbstractPersistable { + @SuppressWarnings("unused") @OneToOne @JoinColumn(name = "product_id", nullable = false) private LoanProduct loanProduct; @@ -60,6 +61,9 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable @Column(name = "arrears_based_on_original_schedule") private boolean isArrearsBasedOnOriginalSchedule; + @Column(name = "pre_close_interest_calculation_strategy") + private Integer preCloseInterestCalculationStrategy; + protected LoanProductInterestRecalculationDetails() { // } @@ -91,19 +95,27 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable recurrenceInterval = 0; } + Integer preCloseInterestCalculationStrategy = command + .integerValueOfParameterNamed(LoanProductConstants.preCloseInterestCalculationStrategyParamName); + if (preCloseInterestCalculationStrategy == null) { + preCloseInterestCalculationStrategy = LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getValue(); + } + return new LoanProductInterestRecalculationDetails(interestRecalculationCompoundingMethod, loanRescheduleStrategyMethod, - recurrenceFrequency, recurrenceInterval, recurrenceOnDate, isArrearsBasedOnOriginalSchedule); + recurrenceFrequency, recurrenceInterval, recurrenceOnDate, isArrearsBasedOnOriginalSchedule, + preCloseInterestCalculationStrategy); } private LoanProductInterestRecalculationDetails(final Integer interestRecalculationCompoundingMethod, final Integer rescheduleStrategyMethod, final Integer restFrequencyType, final Integer restInterval, - final Date restFrequencyDate, final boolean isArrearsBasedOnOriginalSchedule) { + final Date restFrequencyDate, final boolean isArrearsBasedOnOriginalSchedule, final Integer preCloseInterestCalculationStrategy) { this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod; this.rescheduleStrategyMethod = rescheduleStrategyMethod; this.restFrequencyType = restFrequencyType; this.restInterval = restInterval; this.restFrequencyDate = restFrequencyDate; this.isArrearsBasedOnOriginalSchedule = isArrearsBasedOnOriginalSchedule; + this.preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy; } public void updateProduct(final LoanProduct loanProduct) { @@ -183,6 +195,16 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable this.isArrearsBasedOnOriginalSchedule = newValue; } + if (command.isChangeInIntegerParameterNamed(LoanProductConstants.preCloseInterestCalculationStrategyParamName, + this.preCloseInterestCalculationStrategy)) { + Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.preCloseInterestCalculationStrategyParamName); + if (newValue == null) { + newValue = LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getValue(); + } + actualChanges.put(LoanProductConstants.preCloseInterestCalculationStrategyParamName, newValue); + this.preCloseInterestCalculationStrategy = newValue; + } + } public LocalDate getRestFrequencyLocalDate() { @@ -201,8 +223,11 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable return this.restInterval; } - public boolean isArrearsBasedOnOriginalSchedule() { return this.isArrearsBasedOnOriginalSchedule; } + + public LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy() { + return LoanPreCloseInterestCalculationStrategy.fromInt(this.preCloseInterestCalculationStrategy); + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java index eb43e2b6e..8509e0fce 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -26,6 +26,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.portfolio.loanproduct.LoanProductConstants; import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod; +import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy; import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType; import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType; @@ -43,40 +44,47 @@ public final class LoanProductDataValidator { /** * The parameters supported for this command. */ - private final Set supportedParameters = new HashSet<>(Arrays.asList("locale", "dateFormat", "name", "description", "fundId", - "currencyCode", "digitsAfterDecimal", "inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", - "numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", - "minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", - "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment", - "graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule", "includeInBorrowerCycle", "startDate", - "closeDate", "externalId", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), LoanProductConstants.useBorrowerCycleParameterName, - LoanProductConstants.principalVariationsForBorrowerCycleParameterName, - LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName, - LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName, LoanProductConstants.shortName, - LoanProductConstants.multiDisburseLoanParameterName, LoanProductConstants.outstandingLoanBalanceParameterName, - LoanProductConstants.maxTrancheCountParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName, - LoanProductConstants.overdueDaysForNPAParameterName, LoanProductConstants.isInterestRecalculationEnabledParameterName, - LoanProductConstants.daysInYearTypeParameterName, LoanProductConstants.daysInMonthTypeParameterName, - LoanProductConstants.rescheduleStrategyMethodParameterName, - LoanProductConstants.interestRecalculationCompoundingMethodParameterName, - LoanProductConstants.recalculationRestFrequencyDateParamName, - LoanProductConstants.recalculationRestFrequencyIntervalParameterName, - LoanProductConstants.recalculationRestFrequencyTypeParameterName, - LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName, - LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, LoanProductConstants.mandatoryGuaranteeParamName, - LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName, - LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstallmentParamName, - LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName, LoanProductConstants.canDefineEmiAmountParamName, - LoanProductConstants.installmentAmountInMultiplesOfParamName)); + private final Set supportedParameters = new HashSet<>( + Arrays.asList("locale", "dateFormat", "name", "description", "fundId", "currencyCode", "digitsAfterDecimal", "inMultiplesOf", + "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", "minNumberOfRepayments", + "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod", + "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", + "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment", + "graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule", "includeInBorrowerCycle", "startDate", + "closeDate", "externalId", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), + LoanProductConstants.useBorrowerCycleParameterName, + LoanProductConstants.principalVariationsForBorrowerCycleParameterName, + LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName, + LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName, LoanProductConstants.shortName, + LoanProductConstants.multiDisburseLoanParameterName, LoanProductConstants.outstandingLoanBalanceParameterName, + LoanProductConstants.maxTrancheCountParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName, + LoanProductConstants.overdueDaysForNPAParameterName, LoanProductConstants.isInterestRecalculationEnabledParameterName, + LoanProductConstants.daysInYearTypeParameterName, LoanProductConstants.daysInMonthTypeParameterName, + LoanProductConstants.rescheduleStrategyMethodParameterName, + LoanProductConstants.interestRecalculationCompoundingMethodParameterName, + LoanProductConstants.recalculationRestFrequencyDateParamName, + LoanProductConstants.recalculationRestFrequencyIntervalParameterName, + LoanProductConstants.recalculationRestFrequencyTypeParameterName, + LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName, + LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, LoanProductConstants.mandatoryGuaranteeParamName, + LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName, + LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, + LoanProductConstants.principalThresholdForLastInstallmentParamName, + LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName, + LoanProductConstants.canDefineEmiAmountParamName, LoanProductConstants.installmentAmountInMultiplesOfParamName, + LoanProductConstants.preCloseInterestCalculationStrategyParamName)); private final FromJsonHelper fromApiJsonHelper; @@ -537,6 +545,15 @@ public final class LoanProductDataValidator { baseDataValidator.reset().parameter(LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName) .value(isArrearsBasedOnOriginalSchedule).notNull().isOneOfTheseValues(true, false); } + + final Integer preCloseInterestCalculationStrategy = this.fromApiJsonHelper.extractIntegerWithLocaleNamed( + LoanProductConstants.preCloseInterestCalculationStrategyParamName, element); + baseDataValidator + .reset() + .parameter(LoanProductConstants.preCloseInterestCalculationStrategyParamName) + .value(preCloseInterestCalculationStrategy) + .ignoreIfNull() + .inMinMaxRange(LoanPreCloseInterestCalculationStrategy.getMinValue(), LoanPreCloseInterestCalculationStrategy.getMaxValue()); } public void validateForUpdate(final String json, final LoanProduct loanProduct) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java index 632a62554..6ebb3db22 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java @@ -38,5 +38,7 @@ public interface LoanDropdownReadPlatformService { List retrieveRescheduleStrategyTypeOptions(); List retrieveInterestRecalculationFrequencyTypeOptions(); + + List retrivePreCloseInterestCalculationStrategyOptions(); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java index 67c35c405..6531dcc1c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java @@ -17,7 +17,7 @@ import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.r import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyNthDayType; import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType; import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.rescheduleStrategyType; - +import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.preCloseInterestCalculationStrategy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -33,6 +33,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; +import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType; import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod; import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy; @@ -93,21 +94,22 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat } @Override - public List retrieveRepaymentFrequencyOptionsForNthDayOfMonth() { - final List repaymentFrequencyOptions = Arrays.asList(repaymentFrequencyNthDayType(NthDayType.ONE), - repaymentFrequencyNthDayType(NthDayType.TWO), repaymentFrequencyNthDayType(NthDayType.THREE), - repaymentFrequencyNthDayType(NthDayType.FOUR)); - return repaymentFrequencyOptions; + public List retrieveRepaymentFrequencyOptionsForNthDayOfMonth() { + final List repaymentFrequencyOptions = Arrays.asList(repaymentFrequencyNthDayType(NthDayType.ONE), + repaymentFrequencyNthDayType(NthDayType.TWO), repaymentFrequencyNthDayType(NthDayType.THREE), + repaymentFrequencyNthDayType(NthDayType.FOUR)); + return repaymentFrequencyOptions; } - @Override - public List retrieveRepaymentFrequencyOptionsForDaysOfWeek() { - - final List repaymentFrequencyOptions = Arrays.asList(repaymentFrequencyDayOfWeekType(DayOfWeekType.SUNDAY), - repaymentFrequencyDayOfWeekType(DayOfWeekType.MONDAY),repaymentFrequencyDayOfWeekType(DayOfWeekType.TUESDAY),repaymentFrequencyDayOfWeekType(DayOfWeekType.WEDNESDAY), - repaymentFrequencyDayOfWeekType(DayOfWeekType.THURSDAY),repaymentFrequencyDayOfWeekType(DayOfWeekType.FRIDAY), repaymentFrequencyDayOfWeekType(DayOfWeekType.SATURDAY)); - return repaymentFrequencyOptions; - } + @Override + public List retrieveRepaymentFrequencyOptionsForDaysOfWeek() { + + final List repaymentFrequencyOptions = Arrays.asList(repaymentFrequencyDayOfWeekType(DayOfWeekType.SUNDAY), + repaymentFrequencyDayOfWeekType(DayOfWeekType.MONDAY), repaymentFrequencyDayOfWeekType(DayOfWeekType.TUESDAY), + repaymentFrequencyDayOfWeekType(DayOfWeekType.WEDNESDAY), repaymentFrequencyDayOfWeekType(DayOfWeekType.THURSDAY), + repaymentFrequencyDayOfWeekType(DayOfWeekType.FRIDAY), repaymentFrequencyDayOfWeekType(DayOfWeekType.SATURDAY)); + return repaymentFrequencyOptions; + } @Override public List retrieveInterestRateFrequencyTypeOptions() { @@ -170,4 +172,13 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat interestRecalculationFrequencyType(RecalculationFrequencyType.MONTHLY)); return interestRecalculationFrequencyTypeOptions; } + + @Override + public List retrivePreCloseInterestCalculationStrategyOptions() { + + final List preCloseInterestCalculationStrategyOptions = Arrays.asList( + preCloseInterestCalculationStrategy(LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE), + preCloseInterestCalculationStrategy(LoanPreCloseInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE)); + return preCloseInterestCalculationStrategyOptions; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java index 9b6af7069..5dc83893a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java @@ -20,6 +20,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; +import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductParamType; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType; import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod; @@ -595,4 +596,24 @@ public class LoanEnumerations { } return optionData; } + + public static EnumOptionData preCloseInterestCalculationStrategy(final int id) { + return preCloseInterestCalculationStrategy(LoanPreCloseInterestCalculationStrategy.fromInt(id)); + } + + public static EnumOptionData preCloseInterestCalculationStrategy(final LoanPreCloseInterestCalculationStrategy type) { + EnumOptionData optionData = null; + switch (type) { + case TILL_PRE_CLOSURE_DATE: + optionData = new EnumOptionData(LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getValue().longValue(), + LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getCode(), "Till preclose Date"); + break; + case TILL_REST_FREQUENCY_DATE: + optionData = new EnumOptionData(LoanPreCloseInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE.getValue().longValue(), + LoanPreCloseInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE.getCode(), "Till rest Frequency Date"); + break; + } + return optionData; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index f0cd945ea..745c11818 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -161,6 +161,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo + "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, " + "lp.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, " + + "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, " + "lpr.rest_freqency_date as restFrequencyDate, lpr.arrears_based_on_original_schedule as isArrearsBasedOnOriginalSchedule, " @@ -281,6 +282,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final boolean canDefineInstallmentAmount = rs.getBoolean("canDefineInstallmentAmount"); final boolean isInterestRecalculationEnabled = rs.getBoolean("isInterestRecalculationEnabled"); + LoanProductInterestRecalculationData interestRecalculationData = null; if (isInterestRecalculationEnabled) { @@ -296,9 +298,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final int restFrequencyInterval = JdbcSupport.getInteger(rs, "restFrequencyInterval"); final LocalDate restFrequencyDate = JdbcSupport.getLocalDate(rs, "restFrequencyDate"); final boolean isArrearsBasedOnOriginalSchedule = rs.getBoolean("isArrearsBasedOnOriginalSchedule"); + final int preCloseInterestCalculationStrategyEnumValue = JdbcSupport.getInteger(rs, "preCloseInterestCalculationStrategy"); + final EnumOptionData preCloseInterestCalculationStrategy = LoanEnumerations + .preCloseInterestCalculationStrategy(preCloseInterestCalculationStrategyEnumValue); + interestRecalculationData = new LoanProductInterestRecalculationData(lprId, productId, interestRecalculationCompoundingType, rescheduleStrategyType, restFrequencyType, restFrequencyInterval, - restFrequencyDate, isArrearsBasedOnOriginalSchedule); + restFrequencyDate, isArrearsBasedOnOriginalSchedule, preCloseInterestCalculationStrategy); } final boolean holdGuaranteeFunds = rs.getBoolean("holdGuaranteeFunds"); diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V255__pre_close_interest_period_config.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V255__pre_close_interest_period_config.sql new file mode 100644 index 000000000..0399c0d4b --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V255__pre_close_interest_period_config.sql @@ -0,0 +1,2 @@ +ALTER TABLE `m_product_loan_recalculation_details` + ADD COLUMN `pre_close_interest_calculation_strategy` SMALLINT(3) NOT NULL DEFAULT '1'; \ No newline at end of file