From 37dfe1a0bf1fc65d829cc48b57a69fea616cc093 Mon Sep 17 00:00:00 2001 From: pramod Date: Fri, 4 Mar 2016 15:57:27 +0530 Subject: [PATCH] MIFOSX-2426 : added support for Interest Rate Charts based on Amount --- .../AccountingScenarioIntegrationTest.java | 6 +- .../integrationtests/FixedDepositTest.java | 331 ++++++++++++++---- .../RecurringDepositTest.java | 206 ++++++++++- .../SchedulerJobsTestResults.java | 3 +- .../FixedDepositAccountHelper.java | 85 ++--- .../FixedDepositProductHelper.java | 270 +++++++++++--- .../RecurringDepositAccountHelper.java | 74 +--- .../RecurringDepositProductHelper.java | 262 ++++++++++++-- .../portfolio/calendar/domain/Calendar.java | 11 +- .../InterestRateChartApiConstants.java | 10 +- .../data/InterestRateChartData.java | 65 ++-- .../data/InterestRateChartDataValidator.java | 21 +- .../InterestRateChartSlabDataValidator.java | 69 ++-- .../domain/InterestRateChart.java | 83 ++++- .../domain/InterestRateChartFields.java | 34 +- .../InterestRateChartSlabComparator.java | 92 +++++ .../domain/InterestRateChartSlabFields.java | 148 +++++++- .../service/InterestRateChartAssembler.java | 30 +- ...erestRateChartReadPlatformServiceImpl.java | 27 +- .../DepositAccountInterestRateChartData.java | 67 ++-- .../DepositAccountDomainServiceJpa.java | 60 +++- .../DepositAccountInterestRateChart.java | 5 + .../domain/DepositProductAssembler.java | 28 +- .../savings/domain/FixedDepositProduct.java | 2 +- .../domain/RecurringDepositAccount.java | 34 +- ...erestRateChartReadPlatformServiceImpl.java | 40 ++- ...nterest_rate_chart_support_for_amounts.sql | 13 + 27 files changed, 1645 insertions(+), 431 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartSlabComparator.java create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V293__interest_rate_chart_support_for_amounts.sql diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/AccountingScenarioIntegrationTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/AccountingScenarioIntegrationTest.java index ef020bcbe..0e40d7478 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/AccountingScenarioIntegrationTest.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/AccountingScenarioIntegrationTest.java @@ -521,6 +521,7 @@ public class AccountingScenarioIntegrationTest { System.out.println("------------------------------CREATING NEW FIXED DEPOSIT PRODUCT ---------------------------------------"); FixedDepositProductHelper fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec); final String fixedDepositProductJSON = fixedDepositProductHelper // + .withPeriodRangeChart()// .withAccountingRuleAsCashBased(accounts).build(validFrom, validTo); return FixedDepositProductHelper.createFixedDepositProduct(fixedDepositProductJSON, requestSpec, responseSpec); } @@ -529,7 +530,7 @@ public class AccountingScenarioIntegrationTest { final String validTo, final String submittedOnDate, final String penalInterestType) { System.out.println("--------------------------------APPLYING FOR FIXED DEPOSIT ACCOUNT --------------------------------"); final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec) // - .withSubmittedOnDate(submittedOnDate).build(clientID, productID, validFrom, validTo, penalInterestType); + .withSubmittedOnDate(submittedOnDate).build(clientID, productID, penalInterestType); return this.fixedDepositAccountHelper .applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec); } @@ -538,6 +539,7 @@ public class AccountingScenarioIntegrationTest { System.out.println("------------------------------CREATING NEW RECURRING DEPOSIT PRODUCT ---------------------------------------"); RecurringDepositProductHelper recurringDepositProductHelper = new RecurringDepositProductHelper(this.requestSpec, this.responseSpec); final String recurringDepositProductJSON = recurringDepositProductHelper // + .withPeriodRangeChart()// .withAccountingRuleAsCashBased(accounts).build(validFrom, validTo); return RecurringDepositProductHelper.createRecurringDepositProduct(recurringDepositProductJSON, requestSpec, responseSpec); } @@ -548,7 +550,7 @@ public class AccountingScenarioIntegrationTest { final String recurringDepositApplicationJSON = new RecurringDepositAccountHelper(this.requestSpec, this.responseSpec) // .withSubmittedOnDate(submittedOnDate).withExpectedFirstDepositOnDate(expectedFirstDepositOnDate) - .build(clientID, productID, validFrom, validTo, penalInterestType); + .build(clientID, productID, penalInterestType); return this.recurringDepositAccountHelper.applyRecurringDepositApplication(recurringDepositApplicationJSON, this.requestSpec, this.responseSpec); } diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/FixedDepositTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/FixedDepositTest.java index 3d6a4ef0e..fa638bc34 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/FixedDepositTest.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/FixedDepositTest.java @@ -114,8 +114,8 @@ public class FixedDepositTest { } /*** - * Test case for Fixed Deposit Account premature closure with - * transaction type withdrawal and Cash Based accounting enabled + * Test case for Fixed Deposit Account premature closure with transaction + * type withdrawal and Cash Based accounting enabled */ @Test public void testFixedDepositAccountWithPrematureClosureTypeWithdrawal() { @@ -123,7 +123,7 @@ public class FixedDepositTest { this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec); this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec); - + /*** * Create GL Accounts for product account mapping */ @@ -170,8 +170,8 @@ public class FixedDepositTest { /*** * Apply for FD account with created product and verify status */ - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -189,11 +189,11 @@ public class FixedDepositTest { */ fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE); FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap); - + HashMap accountSummary = this.fixedDepositAccountHelper.getFixedDepositSummary(fixedDepositAccountId); Float depositAmount = (Float) accountSummary.get("totalDeposits"); - + /*** * Verify journal entries posted for initial deposit transaction which * happened at activation time @@ -214,7 +214,7 @@ public class FixedDepositTest { */ Integer transactionIdForPostInterest = this.fixedDepositAccountHelper.postInterestForFixedDeposit(fixedDepositAccountId); Assert.assertNotNull(transactionIdForPostInterest); - + accountSummary = this.fixedDepositAccountHelper.getFixedDepositSummary(fixedDepositAccountId); Float totalInterestPosted = (Float) accountSummary.get("totalInterestPosted"); @@ -252,6 +252,165 @@ public class FixedDepositTest { JournalEntry.TransactionType.DEBIT)); } + + @Test + public void testFixedDepositAccountWithPeriodInterestRateChart() { + final String chartToUse = "period"; + final String depositAmount = "10000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(6.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithPeriodInterestRateChart_AMOUNT_VARIATION() { + final String chartToUse = "period"; + final String depositAmount = "2000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(6.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithPeriodInterestRateChart_PERIOD_VARIATION() { + final String chartToUse = "period"; + final String depositAmount = "10000"; + final String depositPeriod = "18"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithAmountInterestRateChart() { + final String chartToUse = "amount"; + final String depositAmount = "10000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithAmountInterestRateChart_AMOUNT_VARIATION() { + final String chartToUse = "amount"; + final String depositAmount = "5000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(5.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithAmountInterestRateChart_PERIOD_VARIATION() { + final String chartToUse = "amount"; + final String depositAmount = "10000"; + final String depositPeriod = "26"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithPeriodAndAmountInterestRateChart() { + final String chartToUse = "period_amount"; + final String depositAmount = "10000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithPeriodAndAmountInterestRateChart_AMOUNT_VARIATION() { + final String chartToUse = "period_amount"; + final String depositAmount = "5000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(6.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithPeriodAndAmountInterestRateChart_PERIOD_VARIATION() { + final String chartToUse = "period_amount"; + final String depositAmount = "10000"; + final String depositPeriod = "20"; + final Float interestRate = new Float(9.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithAmountAndPeriodInterestRateChart() { + final String chartToUse = "amount_period"; + final String depositAmount = "10000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(8.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithAmountAndPeriodInterestRateChart_AMOUNT_VARIATION() { + final String chartToUse = "amount_period"; + final String depositAmount = "5000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(6.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testFixedDepositAccountWithAmountAndPeriodInterestRateChart_PERIOD_VARIATION() { + final String chartToUse = "amount_period"; + final String depositAmount = "10000"; + final String depositPeriod = "6"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + private void testFixedDepositAccountForInterestRate(final String chartToUse, final String depositAmount, final String depositPeriod, + final Float interestRate) { + this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec); + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec); + + final String VALID_FROM = "01 March 2014"; + final String VALID_TO = "01 March 2016"; + + final String SUBMITTED_ON_DATE = "01 March 2015"; + final String APPROVED_ON_DATE = "01 March 2015"; + final String ACTIVATION_DATE = "01 March 2015"; + + Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); + Assert.assertNotNull(clientId); + + /*** + * Create FD product with CashBased accounting enabled + */ + final String accountingRule = NONE; + Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule, chartToUse); + Assert.assertNotNull(fixedDepositProductId); + + /*** + * Apply for FD account with created product and verify status + */ + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM, depositAmount, depositPeriod); + Assert.assertNotNull(fixedDepositAccountId); + + HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, + this.responseSpec, fixedDepositAccountId.toString()); + FixedDepositAccountStatusChecker.verifyFixedDepositIsPending(fixedDepositAccountStatusHashMap); + + /*** + * Approve the FD account and verify whether account is approved + */ + fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId, APPROVED_ON_DATE); + FixedDepositAccountStatusChecker.verifyFixedDepositIsApproved(fixedDepositAccountStatusHashMap); + + /*** + * Activate the FD Account and verify whether account is activated + */ + fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE); + FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap); + + HashMap accountSummary = this.fixedDepositAccountHelper.getFixedDepositDetails(fixedDepositAccountId); + + Assert.assertEquals(interestRate, accountSummary.get("nominalAnnualInterestRate")); + } /*** * Test case for FD Account premature closure with transaction transfers to @@ -263,7 +422,7 @@ public class FixedDepositTest { this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec); this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec); - + /*** * Create GL Accounts for product account mapping */ @@ -295,7 +454,7 @@ public class FixedDepositTest { todaysDate.add(Calendar.DATE, numberOfDaysLeft); final String INTEREST_POSTED_DATE = dateFormat.format(todaysDate.getTime()); final String CLOSED_ON_DATE = dateFormat.format(Calendar.getInstance().getTime()); - + Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); Assert.assertNotNull(clientId); @@ -326,8 +485,8 @@ public class FixedDepositTest { incomeAccount, expenseAccount); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -343,7 +502,7 @@ public class FixedDepositTest { HashMap accountSummary = this.fixedDepositAccountHelper.getFixedDepositSummary(fixedDepositAccountId); Float depositAmount = (Float) accountSummary.get("totalDeposits"); - + /*** * Verify journal entries posted for initial deposit transaction which * happened at activation time @@ -375,10 +534,10 @@ public class FixedDepositTest { final JournalEntry[] liablilityAccountEntry = { new JournalEntry(totalInterestPosted, JournalEntry.TransactionType.CREDIT) }; this.journalEntryHelper.checkJournalEntryForAssetAccount(expenseAccount, INTEREST_POSTED_DATE, expenseAccountEntry); this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, INTEREST_POSTED_DATE, liablilityAccountEntry); - + HashMap savingsSummaryBefore = this.savingsAccountHelper.getSavingsSummary(savingsId); Float balanceBefore = (Float) savingsSummaryBefore.get("accountBalance"); - + /*** * Retrieve mapped financial account for liability transfer */ @@ -412,7 +571,7 @@ public class FixedDepositTest { this.journalEntryHelper.checkJournalEntryForAssetAccount(financialAccount, CLOSED_ON_DATE, new JournalEntry(prematurityAmount, JournalEntry.TransactionType.DEBIT), new JournalEntry(prematurityAmount, JournalEntry.TransactionType.CREDIT)); - + HashMap savingsSummaryAfter = this.savingsAccountHelper.getSavingsSummary(savingsId); Float balanceAfter = (Float) savingsSummaryAfter.get("accountBalance"); Float expectedSavingsBalance = balanceBefore + prematurityAmount; @@ -422,8 +581,8 @@ public class FixedDepositTest { } /*** - * Test case for Fixed Deposit Account premature closure with - * transaction type ReInvest and Cash Based accounting enabled + * Test case for Fixed Deposit Account premature closure with transaction + * type ReInvest and Cash Based accounting enabled */ @Test public void testFixedDepositAccountWithPrematureClosureTypeReinvest() { @@ -459,7 +618,7 @@ public class FixedDepositTest { final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime()); final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime()); final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime()); - + Integer currentDate = new Integer(currentDateFormat.format(todaysDate.getTime())); Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE); Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1; @@ -478,8 +637,8 @@ public class FixedDepositTest { incomeAccount, expenseAccount); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -491,11 +650,11 @@ public class FixedDepositTest { fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE); FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap); - + HashMap accountSummary = this.fixedDepositAccountHelper.getFixedDepositSummary(fixedDepositAccountId); Float depositAmount = (Float) accountSummary.get("totalDeposits"); - + /*** * Verify journal entries posted for initial deposit transaction which * happened at activation time @@ -564,8 +723,8 @@ public class FixedDepositTest { HashMap fixedDepositProductData = this.fixedDepositProductHelper.retrieveFixedDepositProductById(this.requestSpec, this.responseSpec, fixedDepositProductId.toString()); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); todaysDate.add(Calendar.DATE, -1); @@ -603,8 +762,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -646,8 +805,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -687,8 +846,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -726,8 +885,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -769,8 +928,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -839,8 +998,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -916,8 +1075,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -1020,8 +1179,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1128,8 +1287,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, TILL_PREMATURE_WITHDRAWAL); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + TILL_PREMATURE_WITHDRAWAL); Assert.assertNotNull(fixedDepositAccountId); HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, @@ -1240,8 +1399,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, TILL_PREMATURE_WITHDRAWAL); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + TILL_PREMATURE_WITHDRAWAL); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1352,8 +1511,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1421,7 +1580,7 @@ public class FixedDepositTest { final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime()); final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime()); - System.out.println("Submitted Date:"+SUBMITTED_ON_DATE); + System.out.println("Submitted Date:" + SUBMITTED_ON_DATE); Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); Assert.assertNotNull(clientId); @@ -1429,8 +1588,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1509,8 +1668,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1593,8 +1752,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1678,8 +1837,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1760,8 +1919,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1842,8 +2001,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1923,8 +2082,8 @@ public class FixedDepositTest { Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule); Assert.assertNotNull(fixedDepositProductId); - Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM, - VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM); + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM); Assert.assertNotNull(fixedDepositAccountId); HashMap modificationsHashMap = this.fixedDepositAccountHelper.updateInterestCalculationConfigForFixedDeposit(clientId.toString(), @@ -1975,16 +2134,58 @@ public class FixedDepositTest { } else if (accountingRule.equals(NONE)) { fixedDepositProductHelper = fixedDepositProductHelper.withAccountingRuleAsNone(); } + final String fixedDepositProductJSON = fixedDepositProductHelper.withPeriodRangeChart() // + .build(validFrom, validTo); + return FixedDepositProductHelper.createFixedDepositProduct(fixedDepositProductJSON, requestSpec, responseSpec); + } + + private Integer createFixedDepositProduct(final String validFrom, final String validTo, final String accountingRule, + final String chartToBePicked, Account... accounts) { + System.out.println("------------------------------CREATING NEW FIXED DEPOSIT PRODUCT ---------------------------------------"); + FixedDepositProductHelper fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec); + if (accountingRule.equals(CASH_BASED)) { + fixedDepositProductHelper = fixedDepositProductHelper.withAccountingRuleAsCashBased(accounts); + } else if (accountingRule.equals(NONE)) { + fixedDepositProductHelper = fixedDepositProductHelper.withAccountingRuleAsNone(); + } + switch (chartToBePicked) { + case "period": + fixedDepositProductHelper = fixedDepositProductHelper.withPeriodRangeChart(); + break; + case "amount": + fixedDepositProductHelper = fixedDepositProductHelper.withAmountRangeChart(); + break; + case "period_amount": + fixedDepositProductHelper = fixedDepositProductHelper.withPeriodAndAmountRangeChart(); + break; + case "amount_period": + fixedDepositProductHelper = fixedDepositProductHelper.withAmountAndPeriodRangeChart(); + break; + default: + break; + } + final String fixedDepositProductJSON = fixedDepositProductHelper // .build(validFrom, validTo); return FixedDepositProductHelper.createFixedDepositProduct(fixedDepositProductJSON, requestSpec, responseSpec); } - private Integer applyForFixedDepositApplication(final String clientID, final String productID, final String validFrom, - final String validTo, final String submittedOnDate, final String penalInterestType) { + private Integer applyForFixedDepositApplication(final String clientID, final String productID, final String submittedOnDate, + final String penalInterestType) { System.out.println("--------------------------------APPLYING FOR FIXED DEPOSIT ACCOUNT --------------------------------"); final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec) // - .withSubmittedOnDate(submittedOnDate).build(clientID, productID, validFrom, validTo, penalInterestType); + .withSubmittedOnDate(submittedOnDate).build(clientID, productID, penalInterestType); + return this.fixedDepositAccountHelper + .applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec); + } + + private Integer applyForFixedDepositApplication(final String clientID, final String productID, final String submittedOnDate, + final String penalInterestType, final String depositAmount, final String depositPeriod) { + System.out.println("--------------------------------APPLYING FOR FIXED DEPOSIT ACCOUNT --------------------------------"); + final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec) + // + .withSubmittedOnDate(submittedOnDate).withDepositPeriod(depositPeriod).withDepositAmount(depositAmount) + .build(clientID, productID, penalInterestType); return this.fixedDepositAccountHelper .applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec); } diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/RecurringDepositTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/RecurringDepositTest.java index b9fd15249..1d52aec36 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/RecurringDepositTest.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/RecurringDepositTest.java @@ -2496,6 +2496,169 @@ public class RecurringDepositTest { } + @Test + public void testRecurringDepositAccountWithPeriodInterestRateChart() { + final String chartToUse = "period"; + final String depositAmount = "1000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(6.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithPeriodInterestRateChart_AMOUNT_VARIATION() { + final String chartToUse = "period"; + final String depositAmount = "10000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(6.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithPeriodInterestRateChart_PERIOD_VARIATION() { + final String chartToUse = "period"; + final String depositAmount = "1000"; + final String depositPeriod = "18"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithAmountInterestRateChart() { + final String chartToUse = "amount"; + final String depositAmount = "1000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(8.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithAmountInterestRateChart_AMOUNT_VARIATION() { + final String chartToUse = "amount"; + final String depositAmount = "500"; + final String depositPeriod = "12"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithAmountInterestRateChart_PERIOD_VARIATION() { + final String chartToUse = "amount"; + final String depositAmount = "500"; + final String depositPeriod = "10"; + final Float interestRate = new Float(5.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithPeriodAndAmountInterestRateChart() { + final String chartToUse = "period_amount"; + final String depositAmount = "1000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithPeriodAndAmountInterestRateChart_AMOUNT_VARIATION() { + final String chartToUse = "period_amount"; + final String depositAmount = "400"; + final String depositPeriod = "12"; + final Float interestRate = new Float(6.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithPeriodAndAmountInterestRateChart_PERIOD_VARIATION() { + final String chartToUse = "period_amount"; + final String depositAmount = "1000"; + final String depositPeriod = "14"; + final Float interestRate = new Float(8.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithAmountAndPeriodInterestRateChart() { + final String chartToUse = "amount_period"; + final String depositAmount = "1000"; + final String depositPeriod = "12"; + final Float interestRate = new Float(8.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithAmountAndPeriodInterestRateChart_AMOUNT_VARIATION() { + final String chartToUse = "amount_period"; + final String depositAmount = "100"; + final String depositPeriod = "12"; + final Float interestRate = new Float(6.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + @Test + public void testRecurringDepositAccountWithAmountAndPeriodInterestRateChart_PERIOD_VARIATION() { + final String chartToUse = "amount_period"; + final String depositAmount = "1000"; + final String depositPeriod = "6"; + final Float interestRate = new Float(7.0); + testFixedDepositAccountForInterestRate(chartToUse, depositAmount, depositPeriod, interestRate); + } + + private void testFixedDepositAccountForInterestRate(final String chartToUse, final String depositAmount, final String depositPeriod, + final Float interestRate) { + this.recurringDepositProductHelper = new RecurringDepositProductHelper(this.requestSpec, this.responseSpec); + this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec); + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.recurringDepositAccountHelper = new RecurringDepositAccountHelper(this.requestSpec, this.responseSpec); + + final String VALID_FROM = "01 March 2014"; + final String VALID_TO = "01 March 2016"; + + final String SUBMITTED_ON_DATE = "01 March 2015"; + final String APPROVED_ON_DATE = "01 March 2015"; + final String ACTIVATION_DATE = "01 March 2015"; + + Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); + Assert.assertNotNull(clientId); + + /*** + * Create FD product with CashBased accounting enabled + */ + final String accountingRule = NONE; + Integer recurringDepositProductId = createRecurringDepositProduct(VALID_FROM, VALID_TO, accountingRule, chartToUse); + Assert.assertNotNull(recurringDepositProductId); + + /*** + * Apply for FD account with created product and verify status + */ + Integer recurringDepositAccountId = applyForRecurringDepositApplication(clientId.toString(), recurringDepositProductId.toString(), + VALID_FROM, VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM, SUBMITTED_ON_DATE, depositAmount, depositPeriod); + Assert.assertNotNull(recurringDepositAccountId); + + HashMap recurringDepositAccountStatusHashMap = RecurringDepositAccountStatusChecker.getStatusOfRecurringDepositAccount( + this.requestSpec, this.responseSpec, recurringDepositAccountId.toString()); + RecurringDepositAccountStatusChecker.verifyRecurringDepositIsPending(recurringDepositAccountStatusHashMap); + + /*** + * Approve the RD account and verify whether account is approved + */ + recurringDepositAccountStatusHashMap = this.recurringDepositAccountHelper.approveRecurringDeposit(recurringDepositAccountId, + APPROVED_ON_DATE); + RecurringDepositAccountStatusChecker.verifyRecurringDepositIsApproved(recurringDepositAccountStatusHashMap); + + /*** + * Activate the RD Account and verify whether account is activated + */ + recurringDepositAccountStatusHashMap = this.recurringDepositAccountHelper.activateRecurringDeposit(recurringDepositAccountId, + ACTIVATION_DATE); + RecurringDepositAccountStatusChecker.verifyRecurringDepositIsActive(recurringDepositAccountStatusHashMap); + + HashMap recurringDepositAccountData = this.recurringDepositAccountHelper.getRecurringDepositAccountById(this.requestSpec, + this.responseSpec, recurringDepositAccountId); + + Assert.assertEquals(interestRate, recurringDepositAccountData.get("nominalAnnualInterestRate")); + } + private Integer createRecurringDepositProduct(final String validFrom, final String validTo, final String accountingRule, Account... accounts) { System.out.println("------------------------------CREATING NEW RECURRING DEPOSIT PRODUCT ---------------------------------------"); @@ -2505,6 +2668,36 @@ public class RecurringDepositTest { } else if (accountingRule.equals(NONE)) { recurringDepositProductHelper = recurringDepositProductHelper.withAccountingRuleAsNone(); } + final String recurringDepositProductJSON = recurringDepositProductHelper.withPeriodRangeChart().build(validFrom, validTo); + return RecurringDepositProductHelper.createRecurringDepositProduct(recurringDepositProductJSON, requestSpec, responseSpec); + } + + private Integer createRecurringDepositProduct(final String validFrom, final String validTo, final String accountingRule, + final String chartToBePicked, Account... accounts) { + System.out.println("------------------------------CREATING NEW RECURRING DEPOSIT PRODUCT ---------------------------------------"); + RecurringDepositProductHelper recurringDepositProductHelper = new RecurringDepositProductHelper(this.requestSpec, this.responseSpec); + if (accountingRule.equals(CASH_BASED)) { + recurringDepositProductHelper = recurringDepositProductHelper.withAccountingRuleAsCashBased(accounts); + } else if (accountingRule.equals(NONE)) { + recurringDepositProductHelper = recurringDepositProductHelper.withAccountingRuleAsNone(); + } + + switch (chartToBePicked) { + case "period": + recurringDepositProductHelper = recurringDepositProductHelper.withPeriodRangeChart(); + break; + case "amount": + recurringDepositProductHelper = recurringDepositProductHelper.withAmountRangeChart(); + break; + case "period_amount": + recurringDepositProductHelper = recurringDepositProductHelper.withPeriodAndAmountRangeChart(); + break; + case "amount_period": + recurringDepositProductHelper = recurringDepositProductHelper.withAmountAndPeriodRangeChart(); + break; + default: + break; + } final String recurringDepositProductJSON = recurringDepositProductHelper.build(validFrom, validTo); return RecurringDepositProductHelper.createRecurringDepositProduct(recurringDepositProductJSON, requestSpec, responseSpec); } @@ -2514,7 +2707,18 @@ public class RecurringDepositTest { System.out.println("--------------------------------APPLYING FOR RECURRING DEPOSIT ACCOUNT --------------------------------"); final String recurringDepositApplicationJSON = new RecurringDepositAccountHelper(this.requestSpec, this.responseSpec) .withSubmittedOnDate(submittedOnDate).withExpectedFirstDepositOnDate(expectedFirstDepositOnDate) - .build(clientID, productID, validFrom, validTo, penalInterestType); + .build(clientID, productID, penalInterestType); + return this.recurringDepositAccountHelper.applyRecurringDepositApplication(recurringDepositApplicationJSON, this.requestSpec, + this.responseSpec); + } + + private Integer applyForRecurringDepositApplication(final String clientID, final String productID, final String validFrom, + final String validTo, final String submittedOnDate, final String penalInterestType, final String expectedFirstDepositOnDate, + final String depositAmount, final String depositPeriod) { + System.out.println("--------------------------------APPLYING FOR RECURRING DEPOSIT ACCOUNT --------------------------------"); + final String recurringDepositApplicationJSON = new RecurringDepositAccountHelper(this.requestSpec, this.responseSpec) + .withSubmittedOnDate(submittedOnDate).withExpectedFirstDepositOnDate(expectedFirstDepositOnDate) + .withDepositPeriod(depositPeriod).withMandatoryDepositAmount(depositAmount).build(clientID, productID, penalInterestType); return this.recurringDepositAccountHelper.applyRecurringDepositApplication(recurringDepositApplicationJSON, this.requestSpec, this.responseSpec); } diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java index 985889a53..cc6b3c851 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java @@ -892,6 +892,7 @@ public class SchedulerJobsTestResults { FixedDepositProductHelper fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec); final String fixedDepositProductJSON = fixedDepositProductHelper // // .withAccountingRuleAsCashBased(accounts) + .withPeriodRangeChart()// .build(validFrom, validTo); return FixedDepositProductHelper.createFixedDepositProduct(fixedDepositProductJSON, requestSpec, responseSpec); } @@ -904,7 +905,7 @@ public class SchedulerJobsTestResults { // .withSubmittedOnDate(submittedOnDate).withSavings(savingsId).transferInterest(true) .withLockinPeriodFrequency("1", FixedDepositAccountHelper.DAYS) - .build(clientID, productID, validFrom, validTo, penalInterestType); + .build(clientID, productID, penalInterestType); return fixedDepositAccountHelper.applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec); } diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java index fa4871726..64bd4c878 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java @@ -72,6 +72,7 @@ public class FixedDepositAccountHelper { private static final String DAYS_360 = "360"; private static final String DAYS_365 = "365"; public final static String depositAmount = "100000"; + private String newDepositAmount = null; private String interestCompoundingPeriodType = MONTHLY; private String interestPostingPeriodType = MONTHLY; @@ -90,64 +91,15 @@ public class FixedDepositAccountHelper { private final boolean isActiveChart = true; private final String currencyCode = USD; - private final String depositPeriod = "14"; + private String depositPeriod = "14"; private final String depositPeriodFrequencyId = MONTHS; private String submittedOnDate = ""; private String savingsId = null; private boolean transferInterest = false; - public String build(final String clientId, final String productId, final String validFrom, final String validTo, - final String penalInterestType) { + public String build(final String clientId, final String productId, final String penalInterestType) { final HashMap map = new HashMap<>(); - List> chartSlabs = new ArrayList>(); - HashMap chartSlabsMap1 = new HashMap<>(); - chartSlabsMap1.put("description", "First"); - chartSlabsMap1.put("periodType", MONTHS); - chartSlabsMap1.put("fromPeriod", "1"); - chartSlabsMap1.put("toPeriod", "6"); - chartSlabsMap1.put("annualInterestRate", "5"); - chartSlabsMap1.put("locale", LOCALE); - chartSlabs.add(0, chartSlabsMap1); - - HashMap chartSlabsMap2 = new HashMap<>(); - chartSlabsMap2.put("description", "Second"); - chartSlabsMap2.put("periodType", MONTHS); - chartSlabsMap2.put("fromPeriod", "7"); - chartSlabsMap2.put("toPeriod", "12"); - chartSlabsMap2.put("annualInterestRate", "6"); - chartSlabsMap2.put("locale", LOCALE); - chartSlabs.add(1, chartSlabsMap2); - - HashMap chartSlabsMap3 = new HashMap<>(); - chartSlabsMap3.put("description", "Third"); - chartSlabsMap3.put("periodType", MONTHS); - chartSlabsMap3.put("fromPeriod", "13"); - chartSlabsMap3.put("toPeriod", "18"); - chartSlabsMap3.put("annualInterestRate", "7"); - chartSlabsMap3.put("locale", LOCALE); - chartSlabs.add(2, chartSlabsMap3); - - HashMap chartSlabsMap4 = new HashMap<>(); - chartSlabsMap4.put("description", "Fourth"); - chartSlabsMap4.put("periodType", MONTHS); - chartSlabsMap4.put("fromPeriod", "19"); - chartSlabsMap4.put("toPeriod", "24"); - chartSlabsMap4.put("annualInterestRate", "8"); - chartSlabsMap4.put("locale", LOCALE); - chartSlabs.add(3, chartSlabsMap4); - - List> charts = new ArrayList>(); - HashMap chartsMap = new HashMap<>(); - chartsMap.put("fromDate", validFrom); - chartsMap.put("endDate", validTo); - chartsMap.put("dateFormat", "dd MMMM yyyy"); - chartsMap.put("locale", LOCALE); - chartsMap.put("isActiveChart", this.isActiveChart); - chartsMap.put("chartSlabs", chartSlabs); - charts.add(chartsMap); - - map.put("charts", charts); map.put("productId", productId); map.put("clientId", clientId); map.put("interestCalculationDaysInYearType", this.interestCalculationDaysInYearType); @@ -169,7 +121,7 @@ public class FixedDepositAccountHelper { map.put("inMultiplesOfDepositTermTypeId", this.inMultiplesOfDepositTermTypeId); map.put("preClosurePenalInterest", this.preClosurePenalInterest); map.put("preClosurePenalInterestOnTypeId", penalInterestType); - map.put("depositAmount", depositAmount); + map.put("depositAmount", getDepositAmount()); map.put("depositPeriod", this.depositPeriod); map.put("depositPeriodFrequencyId", this.depositPeriodFrequencyId); map.put("submittedOnDate", this.submittedOnDate); @@ -196,8 +148,16 @@ public class FixedDepositAccountHelper { } public HashMap getFixedDepositSummary(final Integer accountID) { + return getFixedDepositDetails(accountID, "summary"); + } + + public HashMap getFixedDepositDetails(final Integer accountID) { + return getFixedDepositDetails(accountID, ""); + } + + private HashMap getFixedDepositDetails(final Integer accountID, final String jsonAttributeToGetBack) { final String URL = FIXED_DEPOSIT_ACCOUNT_URL + "/" + accountID + "?" + Utils.TENANT_IDENTIFIER; - final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, "summary"); + final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, jsonAttributeToGetBack); return response; } @@ -251,7 +211,7 @@ public class FixedDepositAccountHelper { final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec) // .withSubmittedOnDate(submittedOnDate) // - .build(clientID, productID, validFrom, validTo, penalInterestType); + .build(clientID, productID, penalInterestType); return Utils.performServerPut(this.requestSpec, this.responseSpec, FIXED_DEPOSIT_ACCOUNT_URL + "/" + accountID + "?" + Utils.TENANT_IDENTIFIER, fixedDepositApplicationJSON, CommonConstants.RESPONSE_CHANGES); @@ -268,7 +228,7 @@ public class FixedDepositAccountHelper { .withInterestCalculationPeriodType(interestCalculationType) // .withInterestCompoundingPeriodType(interestCompoundingPeriodType) // .withInterestPostingPeriodType(interestPostingPeriodType) // - .build(clientID, productID, validFrom, validTo, penalInterestType); + .build(clientID, productID, penalInterestType); return Utils.performServerPut(this.requestSpec, this.responseSpec, FIXED_DEPOSIT_ACCOUNT_URL + "/" + accountID + "?" + Utils.TENANT_IDENTIFIER, fixedDepositApplicationJSON, CommonConstants.RESPONSE_CHANGES); @@ -487,4 +447,19 @@ public class FixedDepositAccountHelper { this.lockinPeriodFrequency = lockinPeriodFrequency; return this; } + + public FixedDepositAccountHelper withDepositPeriod(final String depositPeriod) { + this.depositPeriod = depositPeriod; + return this; + } + + public FixedDepositAccountHelper withDepositAmount(final String depositAmount) { + this.newDepositAmount = depositAmount; + return this; + } + + private String getDepositAmount() { + if (this.newDepositAmount == null) { return depositAmount; } + return this.newDepositAmount; + } } \ No newline at end of file diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositProductHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositProductHelper.java index 270334030..b20b6f96e 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositProductHelper.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositProductHelper.java @@ -97,10 +97,60 @@ public class FixedDepositProductHelper { private final String currencyCode = USD; private final String interestCalculationDaysInYearType = DAYS_365; private Account[] accountList = null; + private List> chartSlabs = null; + private boolean isPrimaryGroupingByAmount = false; public String build(final String validFrom, final String validTo) { final HashMap map = new HashMap<>(); + List> charts = new ArrayList>(); + HashMap chartsMap = new HashMap<>(); + chartsMap.put("fromDate", validFrom); + chartsMap.put("endDate", validTo); + chartsMap.put("dateFormat", "dd MMMM yyyy"); + chartsMap.put("locale", LOCALE); + chartsMap.put("chartSlabs", this.chartSlabs); + chartsMap.put("isPrimaryGroupingByAmount", this.isPrimaryGroupingByAmount); + charts.add(chartsMap); + + map.put("charts", charts); + map.put("name", this.name); + map.put("shortName", this.shortName); + map.put("description", this.description); + map.put("currencyCode", this.currencyCode); + map.put("interestCalculationDaysInYearType", this.interestCalculationDaysInYearType); + map.put("locale", LOCALE); + map.put("digitsAfterDecimal", DIGITS_AFTER_DECIMAL); + map.put("inMultiplesOf", IN_MULTIPLES_OF); + map.put("interestCalculationType", this.interestCalculationType); + map.put("interestCompoundingPeriodType", this.interestCompoundingPeriodType); + map.put("interestPostingPeriodType", this.interestPostingPeriodType); + map.put("accountingRule", this.accountingRule); + map.put("lockinPeriodFrequency", this.lockinPeriodFrequency); + map.put("lockinPeriodFrequencyType", this.lockingPeriodFrequencyType); + map.put("preClosurePenalApplicable", "true"); + map.put("minDepositTermTypeId", this.minDepositTermTypeId); + map.put("minDepositTerm", this.minDepositTerm); + map.put("maxDepositTermTypeId", this.maxDepositTermTypeId); + map.put("maxDepositTerm", this.maxDepositTerm); + map.put("depositAmount", this.depositAmount); + map.put("preClosurePenalApplicable", this.preClosurePenalApplicable); + map.put("inMultiplesOfDepositTerm", this.inMultiplesOfDepositTerm); + map.put("inMultiplesOfDepositTermTypeId", this.inMultiplesOfDepositTermTypeId); + map.put("preClosurePenalInterest", this.preClosurePenalInterest); + map.put("preClosurePenalInterestOnTypeId", this.preClosurePenalInterestOnTypeId); + + + if (this.accountingRule.equals(CASH_BASED)) { + map.putAll(getAccountMappingForCashBased()); + } + + String FixedDepositProductCreateJson = new Gson().toJson(map); + System.out.println(FixedDepositProductCreateJson); + return FixedDepositProductCreateJson; + } + + public List> constructChartSlabWithPeriodRange() { List> chartSlabs = new ArrayList>(); HashMap chartSlabsMap1 = new HashMap<>(); chartSlabsMap1.put("description", "First"); @@ -133,54 +183,172 @@ public class FixedDepositProductHelper { chartSlabsMap4.put("description", "Fourth"); chartSlabsMap4.put("periodType", MONTHS); chartSlabsMap4.put("fromPeriod", "19"); - chartSlabsMap4.put("toPeriod", "24"); + chartSlabsMap4.put("annualInterestRate", "8"); + chartSlabsMap4.put("locale", LOCALE); + chartSlabs.add(3, chartSlabsMap4); + return chartSlabs; + } + + public List> constructChartSlabWithPeriodAndAmountRange() { + List> chartSlabs = new ArrayList>(); + HashMap chartSlabsMap1 = new HashMap<>(); + chartSlabsMap1.put("description", "First"); + chartSlabsMap1.put("periodType", MONTHS); + chartSlabsMap1.put("fromPeriod", "1"); + chartSlabsMap1.put("toPeriod", "6"); + chartSlabsMap1.put("amountRangeFrom", "1"); + chartSlabsMap1.put("amountRangeTo", "5000"); + chartSlabsMap1.put("annualInterestRate", "5"); + chartSlabsMap1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1); + + HashMap chartSlabsMap1_1 = new HashMap<>(); + chartSlabsMap1_1.put("description", "First"); + chartSlabsMap1_1.put("periodType", MONTHS); + chartSlabsMap1_1.put("fromPeriod", "1"); + chartSlabsMap1_1.put("toPeriod", "6"); + chartSlabsMap1_1.put("amountRangeFrom", "5001"); + chartSlabsMap1_1.put("annualInterestRate", "6"); + chartSlabsMap1_1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1_1); + + HashMap chartSlabsMap2 = new HashMap<>(); + chartSlabsMap2.put("description", "Second"); + chartSlabsMap2.put("periodType", MONTHS); + chartSlabsMap2.put("fromPeriod", "7"); + chartSlabsMap2.put("toPeriod", "12"); + chartSlabsMap2.put("amountRangeFrom", "1"); + chartSlabsMap2.put("amountRangeTo", "5000"); + chartSlabsMap2.put("annualInterestRate", "6"); + chartSlabsMap2.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap2); + + HashMap chartSlabsMap2_2 = new HashMap<>(); + chartSlabsMap2_2.put("description", "Second"); + chartSlabsMap2_2.put("periodType", MONTHS); + chartSlabsMap2_2.put("fromPeriod", "7"); + chartSlabsMap2_2.put("toPeriod", "12"); + chartSlabsMap2_2.put("amountRangeFrom", "5001"); + chartSlabsMap2_2.put("annualInterestRate", "7"); + chartSlabsMap2_2.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap2_2); + + HashMap chartSlabsMap3 = new HashMap<>(); + chartSlabsMap3.put("description", "Third"); + chartSlabsMap3.put("periodType", MONTHS); + chartSlabsMap3.put("fromPeriod", "13"); + chartSlabsMap3.put("toPeriod", "18"); + chartSlabsMap3.put("amountRangeFrom", "1"); + chartSlabsMap3.put("amountRangeTo", "5000"); + chartSlabsMap3.put("annualInterestRate", "7"); + chartSlabsMap3.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap3); + + HashMap chartSlabsMap3_1 = new HashMap<>(); + chartSlabsMap3_1.put("description", "Third"); + chartSlabsMap3_1.put("periodType", MONTHS); + chartSlabsMap3_1.put("fromPeriod", "13"); + chartSlabsMap3_1.put("toPeriod", "18"); + chartSlabsMap3_1.put("amountRangeFrom", "5001"); + chartSlabsMap3_1.put("annualInterestRate", "8"); + chartSlabsMap3_1.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap3_1); + + HashMap chartSlabsMap4 = new HashMap<>(); + chartSlabsMap4.put("description", "Fourth"); + chartSlabsMap4.put("periodType", MONTHS); + chartSlabsMap4.put("fromPeriod", "19"); + chartSlabsMap4.put("amountRangeFrom", "1"); + chartSlabsMap4.put("amountRangeTo", "5000"); chartSlabsMap4.put("annualInterestRate", "8"); chartSlabsMap4.put("locale", LOCALE); chartSlabs.add(3, chartSlabsMap4); - List> charts = new ArrayList>(); - HashMap chartsMap = new HashMap<>(); - chartsMap.put("fromDate", validFrom); - chartsMap.put("endDate", validTo); - chartsMap.put("dateFormat", "dd MMMM yyyy"); - chartsMap.put("locale", LOCALE); - chartsMap.put("chartSlabs", chartSlabs); - charts.add(chartsMap); + HashMap chartSlabsMap4_1 = new HashMap<>(); + chartSlabsMap4_1.put("description", "Fourth"); + chartSlabsMap4_1.put("periodType", MONTHS); + chartSlabsMap4_1.put("fromPeriod", "19"); + chartSlabsMap4_1.put("amountRangeFrom", "5001"); + chartSlabsMap4_1.put("annualInterestRate", "9"); + chartSlabsMap4_1.put("locale", LOCALE); + chartSlabs.add(3, chartSlabsMap4_1); - map.put("charts", charts); - map.put("name", this.name); - map.put("shortName", this.shortName); - map.put("description", this.description); - map.put("currencyCode", this.currencyCode); - map.put("interestCalculationDaysInYearType", this.interestCalculationDaysInYearType); - map.put("locale", LOCALE); - map.put("digitsAfterDecimal", DIGITS_AFTER_DECIMAL); - map.put("inMultiplesOf", IN_MULTIPLES_OF); - map.put("interestCalculationType", this.interestCalculationType); - map.put("interestCompoundingPeriodType", this.interestCompoundingPeriodType); - map.put("interestPostingPeriodType", this.interestPostingPeriodType); - map.put("accountingRule", this.accountingRule); - map.put("lockinPeriodFrequency", this.lockinPeriodFrequency); - map.put("lockinPeriodFrequencyType", this.lockingPeriodFrequencyType); - map.put("preClosurePenalApplicable", "true"); - map.put("minDepositTermTypeId", this.minDepositTermTypeId); - map.put("minDepositTerm", this.minDepositTerm); - map.put("maxDepositTermTypeId", this.maxDepositTermTypeId); - map.put("maxDepositTerm", this.maxDepositTerm); - map.put("depositAmount", this.depositAmount); - map.put("preClosurePenalApplicable", this.preClosurePenalApplicable); - map.put("inMultiplesOfDepositTerm", this.inMultiplesOfDepositTerm); - map.put("inMultiplesOfDepositTermTypeId", this.inMultiplesOfDepositTermTypeId); - map.put("preClosurePenalInterest", this.preClosurePenalInterest); - map.put("preClosurePenalInterestOnTypeId", this.preClosurePenalInterestOnTypeId); + return chartSlabs; + } - if (this.accountingRule.equals(CASH_BASED)) { - map.putAll(getAccountMappingForCashBased()); - } + public List> constructChartSlabWithAmountAndPeriodRange() { + this.isPrimaryGroupingByAmount = true; + List> chartSlabs = new ArrayList>(); + HashMap chartSlabsMap1 = new HashMap<>(); + chartSlabsMap1.put("description", "First"); + chartSlabsMap1.put("periodType", MONTHS); + chartSlabsMap1.put("amountRangeFrom", "1"); + chartSlabsMap1.put("amountRangeTo", "5000"); + chartSlabsMap1.put("fromPeriod", "1"); + chartSlabsMap1.put("toPeriod", "6"); + chartSlabsMap1.put("annualInterestRate", "5"); + chartSlabsMap1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1); - String FixedDepositProductCreateJson = new Gson().toJson(map); - System.out.println(FixedDepositProductCreateJson); - return FixedDepositProductCreateJson; + HashMap chartSlabsMap2 = new HashMap<>(); + chartSlabsMap2.put("description", "Second"); + chartSlabsMap2.put("periodType", MONTHS); + chartSlabsMap2.put("fromPeriod", "7"); + chartSlabsMap2.put("amountRangeFrom", "1"); + chartSlabsMap2.put("amountRangeTo", "5000"); + chartSlabsMap2.put("annualInterestRate", "6"); + chartSlabsMap2.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap2); + + HashMap chartSlabsMap3 = new HashMap<>(); + chartSlabsMap3.put("description", "Third"); + chartSlabsMap3.put("periodType", MONTHS); + chartSlabsMap3.put("fromPeriod", "1"); + chartSlabsMap3.put("toPeriod", "6"); + chartSlabsMap3.put("amountRangeFrom", "5001"); + chartSlabsMap3.put("annualInterestRate", "7"); + chartSlabsMap3.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap3); + + HashMap chartSlabsMap4 = new HashMap<>(); + chartSlabsMap4.put("description", "Fourth"); + chartSlabsMap4.put("periodType", MONTHS); + chartSlabsMap4.put("fromPeriod", "7"); + chartSlabsMap4.put("amountRangeFrom", "5001"); + chartSlabsMap4.put("annualInterestRate", "8"); + chartSlabsMap4.put("locale", LOCALE); + chartSlabs.add(3, chartSlabsMap4); + + return chartSlabs; + } + + public List> constructChartSlabWithAmountRange() { + this.isPrimaryGroupingByAmount = true; + List> chartSlabs = new ArrayList>(); + HashMap chartSlabsMap1 = new HashMap<>(); + chartSlabsMap1.put("description", "First"); + chartSlabsMap1.put("amountRangeFrom", "1"); + chartSlabsMap1.put("amountRangeTo", "5000"); + chartSlabsMap1.put("annualInterestRate", "5"); + chartSlabsMap1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1); + + HashMap chartSlabsMap3 = new HashMap<>(); + chartSlabsMap3.put("description", "Third"); + chartSlabsMap3.put("amountRangeFrom", "5001"); + chartSlabsMap3.put("amountRangeTo", "10000"); + chartSlabsMap3.put("annualInterestRate", "7"); + chartSlabsMap3.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap3); + + HashMap chartSlabsMap4 = new HashMap<>(); + chartSlabsMap4.put("description", "Fourth"); + chartSlabsMap4.put("amountRangeFrom", "10001"); + chartSlabsMap4.put("annualInterestRate", "8"); + chartSlabsMap4.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap4); + + return chartSlabs; } public FixedDepositProductHelper withAccountingRuleAsNone() { @@ -194,6 +362,26 @@ public class FixedDepositProductHelper { return this; } + public FixedDepositProductHelper withPeriodRangeChart() { + this.chartSlabs = constructChartSlabWithPeriodRange(); + return this; + } + + public FixedDepositProductHelper withPeriodAndAmountRangeChart() { + this.chartSlabs = constructChartSlabWithPeriodAndAmountRange(); + return this; + } + + public FixedDepositProductHelper withAmountRangeChart() { + this.chartSlabs = constructChartSlabWithAmountRange(); + return this; + } + + public FixedDepositProductHelper withAmountAndPeriodRangeChart() { + this.chartSlabs = constructChartSlabWithAmountAndPeriodRange(); + return this; + } + private Map getAccountMappingForCashBased() { final Map map = new HashMap<>(); if (accountList != null) { diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/recurringdeposit/RecurringDepositAccountHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/recurringdeposit/RecurringDepositAccountHelper.java index 5ec7cfd2c..d556cd873 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/recurringdeposit/RecurringDepositAccountHelper.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/recurringdeposit/RecurringDepositAccountHelper.java @@ -28,6 +28,7 @@ import java.util.Locale; import org.apache.fineract.integrationtests.common.CommonConstants; import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.fixeddeposit.FixedDepositAccountHelper; import com.google.gson.Gson; import com.jayway.restassured.specification.RequestSpecification; @@ -99,67 +100,18 @@ public class RecurringDepositAccountHelper { private final String currencyCode = USD; private String interestCalculationDaysInYearType = DAYS_365; private final String depositAmount = "2000"; - private final String depositPeriod = "14"; + private String depositPeriod = "14"; private final String depositPeriodFrequencyId = MONTHS; private final String recurringFrequency = "1"; private final String recurringFrequencyType = MONTHS; - private final String mandatoryRecommendedDepositAmount = "2000"; + private String mandatoryRecommendedDepositAmount = "2000"; private String submittedOnDate = ""; private String expectedFirstDepositOnDate = ""; private boolean isCalendarInherited = false; - public String build(final String clientId, final String productId, final String validFrom, final String validTo, - final String penalInterestType) { + public String build(final String clientId, final String productId, final String penalInterestType) { final HashMap map = new HashMap<>(); - List> chartSlabs = new ArrayList>(); - HashMap chartSlabsMap1 = new HashMap<>(); - chartSlabsMap1.put("description", "First"); - chartSlabsMap1.put("periodType", MONTHS); - chartSlabsMap1.put("fromPeriod", "1"); - chartSlabsMap1.put("toPeriod", "6"); - chartSlabsMap1.put("annualInterestRate", "5"); - chartSlabsMap1.put("locale", LOCALE); - chartSlabs.add(0, chartSlabsMap1); - - HashMap chartSlabsMap2 = new HashMap<>(); - chartSlabsMap2.put("description", "Second"); - chartSlabsMap2.put("periodType", MONTHS); - chartSlabsMap2.put("fromPeriod", "7"); - chartSlabsMap2.put("toPeriod", "12"); - chartSlabsMap2.put("annualInterestRate", "6"); - chartSlabsMap2.put("locale", LOCALE); - chartSlabs.add(1, chartSlabsMap2); - - HashMap chartSlabsMap3 = new HashMap<>(); - chartSlabsMap3.put("description", "Third"); - chartSlabsMap3.put("periodType", MONTHS); - chartSlabsMap3.put("fromPeriod", "13"); - chartSlabsMap3.put("toPeriod", "18"); - chartSlabsMap3.put("annualInterestRate", "7"); - chartSlabsMap3.put("locale", LOCALE); - chartSlabs.add(2, chartSlabsMap3); - - HashMap chartSlabsMap4 = new HashMap<>(); - chartSlabsMap4.put("description", "Fourth"); - chartSlabsMap4.put("periodType", MONTHS); - chartSlabsMap4.put("fromPeriod", "19"); - chartSlabsMap4.put("toPeriod", "24"); - chartSlabsMap4.put("annualInterestRate", "8"); - chartSlabsMap4.put("locale", LOCALE); - chartSlabs.add(3, chartSlabsMap4); - - List> charts = new ArrayList>(); - HashMap chartsMap = new HashMap<>(); - chartsMap.put("fromDate", validFrom); - chartsMap.put("endDate", validTo); - chartsMap.put("dateFormat", "dd MMMM yyyy"); - chartsMap.put("locale", LOCALE); - chartsMap.put("isActiveChart", this.isActiveChart); - chartsMap.put("chartSlabs", chartSlabs); - charts.add(chartsMap); - - map.put("charts", charts); map.put("productId", productId); map.put("clientId", clientId); map.put("interestCalculationDaysInYearType", this.interestCalculationDaysInYearType); @@ -231,8 +183,8 @@ public class RecurringDepositAccountHelper { return annualInterestRate; } - public static Float getPrincipalAfterCompoundingInterest(Calendar currentDate, Float principal, Float depositAmount, Integer depositPeriod, - double interestPerDay, Integer compoundingInterval, Integer postingInterval) { + public static Float getPrincipalAfterCompoundingInterest(Calendar currentDate, Float principal, Float depositAmount, + Integer depositPeriod, double interestPerDay, Integer compoundingInterval, Integer postingInterval) { Float totalInterest = 0.0f; Float interestEarned = 0.0f; @@ -274,7 +226,7 @@ public class RecurringDepositAccountHelper { final String EXPECTED_FIRST_DEPOSIT_ON_ON_DATE = SUBMITTED_ON_DATE; final String recurringDepositApplicationJSON = new RecurringDepositAccountHelper(this.requestSpec, this.responseSpec) .withSubmittedOnDate(SUBMITTED_ON_DATE).withExpectedFirstDepositOnDate(EXPECTED_FIRST_DEPOSIT_ON_ON_DATE) - .build(clientID, productID, validFrom, validTo, penalInterestType); + .build(clientID, productID, penalInterestType); return Utils.performServerPut(this.requestSpec, this.responseSpec, RECURRING_DEPOSIT_ACCOUNT_URL + "/" + accountID + "?" + Utils.TENANT_IDENTIFIER, recurringDepositApplicationJSON, CommonConstants.RESPONSE_CHANGES); @@ -292,7 +244,7 @@ public class RecurringDepositAccountHelper { .withInterestCompoundingPeriodType(interestCompoundingPeriodType) // .withInterestPostingPeriodType(interestPostingPeriodType) // .withExpectedFirstDepositOnDate(expectedFirstDepositOnDate) // - .build(clientID, productID, validFrom, validTo, penalInterestType); + .build(clientID, productID, penalInterestType); return Utils.performServerPut(this.requestSpec, this.responseSpec, RECURRING_DEPOSIT_ACCOUNT_URL + "/" + accountID + "?" + Utils.TENANT_IDENTIFIER, recurringDepositApplicationJSON, CommonConstants.RESPONSE_CHANGES); @@ -552,5 +504,15 @@ public class RecurringDepositAccountHelper { this.interestPostingPeriodType = interestPostingPeriodTypeId; return this; } + + public RecurringDepositAccountHelper withDepositPeriod(final String depositPeriod) { + this.depositPeriod = depositPeriod; + return this; + } + + public RecurringDepositAccountHelper withMandatoryDepositAmount(final String depositAmount) { + this.mandatoryRecommendedDepositAmount = depositAmount; + return this; + } } \ No newline at end of file diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/recurringdeposit/RecurringDepositProductHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/recurringdeposit/RecurringDepositProductHelper.java index 71a92f840..16dc1d278 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/recurringdeposit/RecurringDepositProductHelper.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/recurringdeposit/RecurringDepositProductHelper.java @@ -25,6 +25,7 @@ import java.util.Map; import org.apache.fineract.integrationtests.common.Utils; import org.apache.fineract.integrationtests.common.accounting.Account; +import org.apache.fineract.integrationtests.common.fixeddeposit.FixedDepositProductHelper; import com.google.gson.Gson; import com.jayway.restassured.specification.RequestSpecification; @@ -95,47 +96,12 @@ public class RecurringDepositProductHelper { private final String minDepositAmount = "100"; private final String maxDepositAmount = "1000000"; private Account[] accountList = null; + private List> chartSlabs = null; + private boolean isPrimaryGroupingByAmount = false; public String build(final String validFrom, final String validTo) { final HashMap map = new HashMap<>(); - List> chartSlabs = new ArrayList>(); - HashMap chartSlabsMap1 = new HashMap<>(); - chartSlabsMap1.put("description", "First"); - chartSlabsMap1.put("periodType", MONTHS); - chartSlabsMap1.put("fromPeriod", "1"); - chartSlabsMap1.put("toPeriod", "6"); - chartSlabsMap1.put("annualInterestRate", "5"); - chartSlabsMap1.put("locale", LOCALE); - chartSlabs.add(0, chartSlabsMap1); - - HashMap chartSlabsMap2 = new HashMap<>(); - chartSlabsMap2.put("description", "Second"); - chartSlabsMap2.put("periodType", MONTHS); - chartSlabsMap2.put("fromPeriod", "7"); - chartSlabsMap2.put("toPeriod", "12"); - chartSlabsMap2.put("annualInterestRate", "6"); - chartSlabsMap2.put("locale", LOCALE); - chartSlabs.add(1, chartSlabsMap2); - - HashMap chartSlabsMap3 = new HashMap<>(); - chartSlabsMap3.put("description", "Third"); - chartSlabsMap3.put("periodType", MONTHS); - chartSlabsMap3.put("fromPeriod", "13"); - chartSlabsMap3.put("toPeriod", "18"); - chartSlabsMap3.put("annualInterestRate", "7"); - chartSlabsMap3.put("locale", LOCALE); - chartSlabs.add(2, chartSlabsMap3); - - HashMap chartSlabsMap4 = new HashMap<>(); - chartSlabsMap4.put("description", "Fourth"); - chartSlabsMap4.put("periodType", MONTHS); - chartSlabsMap4.put("fromPeriod", "19"); - chartSlabsMap4.put("toPeriod", "24"); - chartSlabsMap4.put("annualInterestRate", "8"); - chartSlabsMap4.put("locale", LOCALE); - chartSlabs.add(3, chartSlabsMap4); - List> charts = new ArrayList>(); HashMap chartsMap = new HashMap<>(); chartsMap.put("fromDate", validFrom); @@ -143,6 +109,7 @@ public class RecurringDepositProductHelper { chartsMap.put("dateFormat", "dd MMMM yyyy"); chartsMap.put("locale", LOCALE); chartsMap.put("chartSlabs", chartSlabs); + chartsMap.put("isPrimaryGroupingByAmount", this.isPrimaryGroupingByAmount); charts.add(chartsMap); map.put("charts", charts); @@ -198,6 +165,26 @@ public class RecurringDepositProductHelper { return this; } + public RecurringDepositProductHelper withPeriodRangeChart() { + this.chartSlabs = constructChartSlabWithPeriodRange(); + return this; + } + + public RecurringDepositProductHelper withPeriodAndAmountRangeChart() { + this.chartSlabs = constructChartSlabWithPeriodAndAmountRange(); + return this; + } + + public RecurringDepositProductHelper withAmountRangeChart() { + this.chartSlabs = constructChartSlabWithAmountRange(); + return this; + } + + public RecurringDepositProductHelper withAmountAndPeriodRangeChart() { + this.chartSlabs = constructChartSlabWithAmountAndPeriodRange(); + return this; + } + private Map getAccountMappingForCashBased() { final Map map = new HashMap<>(); if (accountList != null) { @@ -256,4 +243,205 @@ public class RecurringDepositProductHelper { return response; } + public List> constructChartSlabWithPeriodRange() { + List> chartSlabs = new ArrayList>(); + HashMap chartSlabsMap1 = new HashMap<>(); + chartSlabsMap1.put("description", "First"); + chartSlabsMap1.put("periodType", MONTHS); + chartSlabsMap1.put("fromPeriod", "1"); + chartSlabsMap1.put("toPeriod", "6"); + chartSlabsMap1.put("annualInterestRate", "5"); + chartSlabsMap1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1); + + HashMap chartSlabsMap2 = new HashMap<>(); + chartSlabsMap2.put("description", "Second"); + chartSlabsMap2.put("periodType", MONTHS); + chartSlabsMap2.put("fromPeriod", "7"); + chartSlabsMap2.put("toPeriod", "12"); + chartSlabsMap2.put("annualInterestRate", "6"); + chartSlabsMap2.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap2); + + HashMap chartSlabsMap3 = new HashMap<>(); + chartSlabsMap3.put("description", "Third"); + chartSlabsMap3.put("periodType", MONTHS); + chartSlabsMap3.put("fromPeriod", "13"); + chartSlabsMap3.put("toPeriod", "18"); + chartSlabsMap3.put("annualInterestRate", "7"); + chartSlabsMap3.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap3); + + HashMap chartSlabsMap4 = new HashMap<>(); + chartSlabsMap4.put("description", "Fourth"); + chartSlabsMap4.put("periodType", MONTHS); + chartSlabsMap4.put("fromPeriod", "19"); + chartSlabsMap4.put("annualInterestRate", "8"); + chartSlabsMap4.put("locale", LOCALE); + chartSlabs.add(3, chartSlabsMap4); + return chartSlabs; + } + + public List> constructChartSlabWithPeriodAndAmountRange() { + List> chartSlabs = new ArrayList>(); + HashMap chartSlabsMap1 = new HashMap<>(); + chartSlabsMap1.put("description", "First"); + chartSlabsMap1.put("periodType", MONTHS); + chartSlabsMap1.put("fromPeriod", "1"); + chartSlabsMap1.put("toPeriod", "6"); + chartSlabsMap1.put("amountRangeFrom", "1"); + chartSlabsMap1.put("amountRangeTo", "5000"); + chartSlabsMap1.put("annualInterestRate", "5"); + chartSlabsMap1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1); + + HashMap chartSlabsMap1_1 = new HashMap<>(); + chartSlabsMap1_1.put("description", "First"); + chartSlabsMap1_1.put("periodType", MONTHS); + chartSlabsMap1_1.put("fromPeriod", "1"); + chartSlabsMap1_1.put("toPeriod", "6"); + chartSlabsMap1_1.put("amountRangeFrom", "5001"); + chartSlabsMap1_1.put("annualInterestRate", "6"); + chartSlabsMap1_1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1_1); + + HashMap chartSlabsMap2 = new HashMap<>(); + chartSlabsMap2.put("description", "Second"); + chartSlabsMap2.put("periodType", MONTHS); + chartSlabsMap2.put("fromPeriod", "7"); + chartSlabsMap2.put("toPeriod", "12"); + chartSlabsMap2.put("amountRangeFrom", "1"); + chartSlabsMap2.put("amountRangeTo", "5000"); + chartSlabsMap2.put("annualInterestRate", "6"); + chartSlabsMap2.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap2); + + HashMap chartSlabsMap2_2 = new HashMap<>(); + chartSlabsMap2_2.put("description", "Second"); + chartSlabsMap2_2.put("periodType", MONTHS); + chartSlabsMap2_2.put("fromPeriod", "7"); + chartSlabsMap2_2.put("toPeriod", "12"); + chartSlabsMap2_2.put("amountRangeFrom", "5001"); + chartSlabsMap2_2.put("annualInterestRate", "7"); + chartSlabsMap2_2.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap2_2); + + HashMap chartSlabsMap3 = new HashMap<>(); + chartSlabsMap3.put("description", "Third"); + chartSlabsMap3.put("periodType", MONTHS); + chartSlabsMap3.put("fromPeriod", "13"); + chartSlabsMap3.put("toPeriod", "18"); + chartSlabsMap3.put("amountRangeFrom", "1"); + chartSlabsMap3.put("amountRangeTo", "5000"); + chartSlabsMap3.put("annualInterestRate", "7"); + chartSlabsMap3.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap3); + + HashMap chartSlabsMap3_1 = new HashMap<>(); + chartSlabsMap3_1.put("description", "Third"); + chartSlabsMap3_1.put("periodType", MONTHS); + chartSlabsMap3_1.put("fromPeriod", "13"); + chartSlabsMap3_1.put("toPeriod", "18"); + chartSlabsMap3_1.put("amountRangeFrom", "5001"); + chartSlabsMap3_1.put("annualInterestRate", "8"); + chartSlabsMap3_1.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap3_1); + + HashMap chartSlabsMap4 = new HashMap<>(); + chartSlabsMap4.put("description", "Fourth"); + chartSlabsMap4.put("periodType", MONTHS); + chartSlabsMap4.put("fromPeriod", "19"); + chartSlabsMap4.put("amountRangeFrom", "1"); + chartSlabsMap4.put("amountRangeTo", "5000"); + chartSlabsMap4.put("annualInterestRate", "8"); + chartSlabsMap4.put("locale", LOCALE); + chartSlabs.add(3, chartSlabsMap4); + + HashMap chartSlabsMap4_1 = new HashMap<>(); + chartSlabsMap4_1.put("description", "Fourth"); + chartSlabsMap4_1.put("periodType", MONTHS); + chartSlabsMap4_1.put("fromPeriod", "19"); + chartSlabsMap4_1.put("amountRangeFrom", "5001"); + chartSlabsMap4_1.put("annualInterestRate", "9"); + chartSlabsMap4_1.put("locale", LOCALE); + chartSlabs.add(3, chartSlabsMap4_1); + + return chartSlabs; + } + + public List> constructChartSlabWithAmountAndPeriodRange() { + this.isPrimaryGroupingByAmount = true; + List> chartSlabs = new ArrayList>(); + HashMap chartSlabsMap1 = new HashMap<>(); + chartSlabsMap1.put("description", "First"); + chartSlabsMap1.put("periodType", MONTHS); + chartSlabsMap1.put("amountRangeFrom", "1"); + chartSlabsMap1.put("amountRangeTo", "5000"); + chartSlabsMap1.put("fromPeriod", "1"); + chartSlabsMap1.put("toPeriod", "6"); + chartSlabsMap1.put("annualInterestRate", "5"); + chartSlabsMap1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1); + + HashMap chartSlabsMap2 = new HashMap<>(); + chartSlabsMap2.put("description", "Second"); + chartSlabsMap2.put("periodType", MONTHS); + chartSlabsMap2.put("fromPeriod", "7"); + chartSlabsMap2.put("amountRangeFrom", "1"); + chartSlabsMap2.put("amountRangeTo", "5000"); + chartSlabsMap2.put("annualInterestRate", "6"); + chartSlabsMap2.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap2); + + HashMap chartSlabsMap3 = new HashMap<>(); + chartSlabsMap3.put("description", "Third"); + chartSlabsMap3.put("periodType", MONTHS); + chartSlabsMap3.put("fromPeriod", "1"); + chartSlabsMap3.put("toPeriod", "6"); + chartSlabsMap3.put("amountRangeFrom", "5001"); + chartSlabsMap3.put("annualInterestRate", "7"); + chartSlabsMap3.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap3); + + HashMap chartSlabsMap4 = new HashMap<>(); + chartSlabsMap4.put("description", "Fourth"); + chartSlabsMap4.put("periodType", MONTHS); + chartSlabsMap4.put("fromPeriod", "7"); + chartSlabsMap4.put("amountRangeFrom", "5001"); + chartSlabsMap4.put("annualInterestRate", "8"); + chartSlabsMap4.put("locale", LOCALE); + chartSlabs.add(3, chartSlabsMap4); + + return chartSlabs; + } + + public List> constructChartSlabWithAmountRange() { + this.isPrimaryGroupingByAmount = true; + List> chartSlabs = new ArrayList>(); + HashMap chartSlabsMap1 = new HashMap<>(); + chartSlabsMap1.put("description", "First"); + chartSlabsMap1.put("amountRangeFrom", "1"); + chartSlabsMap1.put("amountRangeTo", "5000"); + chartSlabsMap1.put("annualInterestRate", "5"); + chartSlabsMap1.put("locale", LOCALE); + chartSlabs.add(0, chartSlabsMap1); + + HashMap chartSlabsMap3 = new HashMap<>(); + chartSlabsMap3.put("description", "Third"); + chartSlabsMap3.put("amountRangeFrom", "5001"); + chartSlabsMap3.put("amountRangeTo", "10000"); + chartSlabsMap3.put("annualInterestRate", "7"); + chartSlabsMap3.put("locale", LOCALE); + chartSlabs.add(1, chartSlabsMap3); + + HashMap chartSlabsMap4 = new HashMap<>(); + chartSlabsMap4.put("description", "Fourth"); + chartSlabsMap4.put("amountRangeFrom", "10001"); + chartSlabsMap4.put("annualInterestRate", "8"); + chartSlabsMap4.put("locale", LOCALE); + chartSlabs.add(2, chartSlabsMap4); + + return chartSlabs; + } + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java index 98e80967e..52d072d07 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java @@ -142,7 +142,12 @@ public class Calendar extends AbstractAuditableCustom { public static Calendar createRepeatingCalendar(final String title, final LocalDate startDate, final Integer typeId, final CalendarFrequencyType frequencyType, final Integer interval, final Integer repeatsOnDay) { + final String recurrence = constructRecurrence(frequencyType, interval, repeatsOnDay); + return createRepeatingCalendar(title, startDate, typeId, recurrence); + } + public static Calendar createRepeatingCalendar(final String title, final LocalDate startDate, final Integer typeId, + final String recurrence) { final String description = null; final String location = null; final LocalDate endDate = null; @@ -151,8 +156,6 @@ public class Calendar extends AbstractAuditableCustom { final Integer remindById = null; final Integer firstReminder = null; final Integer secondReminder = null; - final String recurrence = constructRecurrence(frequencyType, interval, repeatsOnDay); - return new Calendar(title, description, location, startDate, endDate, duration, typeId, repeating, recurrence, remindById, firstReminder, secondReminder); } @@ -233,7 +236,7 @@ public class Calendar extends AbstractAuditableCustom { } - public Map update(final JsonCommand command, final Boolean areActiveEntitiesSynced ) { + public Map update(final JsonCommand command, final Boolean areActiveEntitiesSynced) { final Map actualChanges = new LinkedHashMap<>(9); @@ -345,7 +348,7 @@ public class Calendar extends AbstractAuditableCustom { * calendar then do not allow to change meeting frequency */ - if ( areActiveEntitiesSynced && !CalendarUtils.isFrequencySame(this.recurrence, newRecurrence)) { + if (areActiveEntitiesSynced && !CalendarUtils.isFrequencySame(this.recurrence, newRecurrence)) { final String defaultUserMessage = "Update of meeting frequency is not supported"; throw new CalendarParameterUpdateNotSupportedException("meeting.frequency", defaultUserMessage); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/InterestRateChartApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/InterestRateChartApiConstants.java index 7bce4eeda..20f760b4f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/InterestRateChartApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/InterestRateChartApiConstants.java @@ -44,6 +44,7 @@ public class InterestRateChartApiConstants { public static final String endDateParamName = "endDate"; public static final String productIdParamName = "productId"; public static final String productNameParamName = "productName"; + public static final String isPrimaryGroupingByAmountParamName = "isPrimaryGroupingByAmount"; // interest rate chart Slabs parameters // public static final String periodTypeParamName = "periodType"; @@ -63,13 +64,14 @@ public class InterestRateChartApiConstants { public static final String deleteParamName = "delete"; public static final Set INTERESTRATE_CHART_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, - dateFormatParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, productIdParamName, - chartSlabs)); + dateFormatParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, productIdParamName, chartSlabs, + isPrimaryGroupingByAmountParamName)); public static final Set INTERESTRATE_CHART_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, dateFormatParamName, idParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, chartSlabs, - deleteParamName)); + deleteParamName, isPrimaryGroupingByAmountParamName)); public static final Set INTERESTRATE_CHART_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, - dateFormatParamName, idParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, chartSlabs)); + dateFormatParamName, idParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, chartSlabs, + isPrimaryGroupingByAmountParamName)); } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartData.java index f7aef9691..1b3128791 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartData.java @@ -18,9 +18,8 @@ */ package org.apache.fineract.portfolio.interestratechart.data; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.core.data.EnumOptionData; @@ -38,9 +37,10 @@ public class InterestRateChartData { private final LocalDate endDate; private final Long productId; private final String productName; + private final boolean isPrimaryGroupingByAmount; // associations - private Set chartSlabs; + private Collection chartSlabs; // template private final Collection periodTypes; @@ -53,9 +53,9 @@ public class InterestRateChartData { private final Collection clientClassificationOptions; public static InterestRateChartData instance(Long id, String name, String description, LocalDate fromDate, LocalDate endDate, - Long savingsProductId, String savingsProductName) { + boolean isPrimaryGroupingByAmount, Long savingsProductId, String savingsProductName) { Collection periodTypes = null; - Set chartSlabs = null; + Collection chartSlabs = null; final Collection entityTypeOptions = null; final Collection attributeNameOptions = null; final Collection conditionTypeOptions = null; @@ -63,17 +63,18 @@ public class InterestRateChartData { final Collection genderOptions = null; final Collection clientTypeOptions = null; final Collection clientClassificationOptions = null; - return new InterestRateChartData(id, name, description, fromDate, endDate, savingsProductId, savingsProductName, chartSlabs, - periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, - clientTypeOptions, clientClassificationOptions); + return new InterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, savingsProductId, + savingsProductName, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, + incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions); } - public static InterestRateChartData withSlabs(InterestRateChartData interestRateChartData, Set chartSlabs) { + public static InterestRateChartData withSlabs(InterestRateChartData interestRateChartData, Collection chartSlabs) { return new InterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description, - interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.productId, - interestRateChartData.productName, chartSlabs, interestRateChartData.periodTypes, interestRateChartData.entityTypeOptions, - interestRateChartData.attributeNameOptions, interestRateChartData.conditionTypeOptions, - interestRateChartData.incentiveTypeOptions, interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions, + interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.isPrimaryGroupingByAmount, + interestRateChartData.productId, interestRateChartData.productName, chartSlabs, interestRateChartData.periodTypes, + interestRateChartData.entityTypeOptions, interestRateChartData.attributeNameOptions, + interestRateChartData.conditionTypeOptions, interestRateChartData.incentiveTypeOptions, + interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions, interestRateChartData.clientClassificationOptions); } @@ -83,9 +84,10 @@ public class InterestRateChartData { final Collection genderOptions, final Collection clientTypeOptions, final Collection clientClassificationOptions) { return new InterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description, - interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.productId, - interestRateChartData.productName, interestRateChartData.chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, - conditionTypeOptions, incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions); + interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.isPrimaryGroupingByAmount, + interestRateChartData.productId, interestRateChartData.productName, interestRateChartData.chartSlabs, periodTypes, + entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, clientTypeOptions, + clientClassificationOptions); } public static InterestRateChartData template(Collection periodTypes, @@ -98,26 +100,28 @@ public class InterestRateChartData { final String description = null; final LocalDate fromDate = null; final LocalDate endDate = null; + final boolean isPrimaryGroupingByAmount = false; final Long savingsProductId = null; final String savingsProductName = null; - final Set chartSlabs = null; + final Collection chartSlabs = null; - return new InterestRateChartData(id, name, description, fromDate, endDate, savingsProductId, savingsProductName, chartSlabs, - periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, - clientTypeOptions, clientClassificationOptions); + return new InterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, savingsProductId, + savingsProductName, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, + incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions); } - private InterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate, Long savingsProductId, - String savingsProductName, Set chartSlabs, Collection periodTypes, - final Collection entityTypeOptions, final Collection attributeNameOptions, - final Collection conditionTypeOptions, final Collection incentiveTypeOptions, - final Collection genderOptions, final Collection clientTypeOptions, - final Collection clientClassificationOptions) { + private InterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate, + boolean isPrimaryGroupingByAmount, Long savingsProductId, String savingsProductName, Collection chartSlabs, + Collection periodTypes, final Collection entityTypeOptions, + final Collection attributeNameOptions, final Collection conditionTypeOptions, + final Collection incentiveTypeOptions, final Collection genderOptions, + final Collection clientTypeOptions, final Collection clientClassificationOptions) { this.id = id; this.name = name; this.description = description; this.fromDate = fromDate; this.endDate = endDate; + this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount; this.chartSlabs = chartSlabs; this.productId = savingsProductId; this.productName = savingsProductName; @@ -133,7 +137,7 @@ public class InterestRateChartData { public void addChartSlab(final InterestRateChartSlabData chartSlab) { if (this.chartSlabs == null) { - this.chartSlabs = new HashSet<>(); + this.chartSlabs = new ArrayList<>(); } this.chartSlabs.add(chartSlab); @@ -159,7 +163,7 @@ public class InterestRateChartData { return this.description; } - public Set chartSlabs() { + public Collection chartSlabs() { return this.chartSlabs; } @@ -195,4 +199,9 @@ public class InterestRateChartData { return this.clientClassificationOptions; } + + public boolean isPrimaryGroupingByAmount() { + return this.isPrimaryGroupingByAmount; + } + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartDataValidator.java index 04c4d222a..a3251755f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartDataValidator.java @@ -26,6 +26,7 @@ import static org.apache.fineract.portfolio.interestratechart.InterestRateChartA import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.idParamName; +import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.isPrimaryGroupingByAmountParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.productIdParamName; @@ -98,6 +99,11 @@ public class InterestRateChartDataValidator { toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element); baseDataValidator.reset().parameter(endDateParamName).value(toDate).notNull(); } + + Boolean isPrimaryGroupingByAmount = this.fromApiJsonHelper.extractBooleanNamed(isPrimaryGroupingByAmountParamName, element); + if (isPrimaryGroupingByAmount == null) { + isPrimaryGroupingByAmount = false; + } if (fromDate != null && toDate != null) { if (fromDate.isAfter(toDate)) { @@ -106,7 +112,7 @@ public class InterestRateChartDataValidator { } // validate chart Slabs - validateChartSlabs(element, baseDataValidator); + validateChartSlabs(element, baseDataValidator, isPrimaryGroupingByAmount); } public void validateUpdate(final String json) { @@ -151,6 +157,11 @@ public class InterestRateChartDataValidator { toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element); baseDataValidator.reset().parameter(endDateParamName).value(toDate).notNull(); } + + Boolean isPrimaryGroupingByAmount = this.fromApiJsonHelper.extractBooleanNamed(isPrimaryGroupingByAmountParamName, element); + if (isPrimaryGroupingByAmount == null) { + isPrimaryGroupingByAmount = false; + } if (fromDate != null && toDate != null) { if (fromDate.isAfter(toDate)) { @@ -158,7 +169,7 @@ public class InterestRateChartDataValidator { } } - validateChartSlabs(element, baseDataValidator); + validateChartSlabs(element, baseDataValidator, isPrimaryGroupingByAmount); } @@ -166,7 +177,7 @@ public class InterestRateChartDataValidator { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - private void validateChartSlabs(JsonElement element, DataValidatorBuilder baseDataValidator) { + private void validateChartSlabs(JsonElement element, DataValidatorBuilder baseDataValidator,final boolean isPrimaryGroupingByAmount) { if (element.isJsonObject()) { final JsonObject topLevelJsonElement = element.getAsJsonObject(); @@ -178,9 +189,9 @@ public class InterestRateChartDataValidator { if (this.fromApiJsonHelper.parameterExists(idParamName, interstRateChartElement)) { final Long id = this.fromApiJsonHelper.extractLongNamed(idParamName, interstRateChartElement); baseDataValidator.reset().parameter(idParamName).value(id).notNull().integerGreaterThanZero(); - this.chartSlabDataValidator.validateChartSlabsUpdate(interstRateChartElement, baseDataValidator, locale); + this.chartSlabDataValidator.validateChartSlabsUpdate(interstRateChartElement, baseDataValidator, locale, isPrimaryGroupingByAmount); } else { - this.chartSlabDataValidator.validateChartSlabsCreate(interstRateChartElement, baseDataValidator, locale); + this.chartSlabDataValidator.validateChartSlabsCreate(interstRateChartElement, baseDataValidator, locale, isPrimaryGroupingByAmount); } } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartSlabDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartSlabDataValidator.java index 45b0ba4ba..fa7f26a90 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartSlabDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/data/InterestRateChartSlabDataValidator.java @@ -84,12 +84,14 @@ public class InterestRateChartSlabDataValidator { final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element); baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank().notExceedingLengthOf(3); - validateChartSlabsCreate(element, baseDataValidator, locale); + final Boolean isPrimaryGroupingByAmount = null; + validateChartSlabsCreate(element, baseDataValidator, locale, isPrimaryGroupingByAmount); throwExceptionIfValidationWarningsExist(dataValidationErrors); } - public void validateChartSlabsCreate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale) { + public void validateChartSlabsCreate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale, + final Boolean isPrimaryGroupingByAmount) { if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) { final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); @@ -97,17 +99,25 @@ public class InterestRateChartSlabDataValidator { } final Integer periodType = this.fromApiJsonHelper.extractIntegerNamed(periodTypeParamName, element, locale); - baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull() - .isOneOfTheseValues(PeriodFrequencyType.integerValues()); - + if (this.fromApiJsonHelper.parameterExists(periodTypeParamName, element)) { + baseDataValidator.reset().parameter(periodTypeParamName).value(periodType) + .isOneOfTheseValues(PeriodFrequencyType.integerValues()); + } Integer toPeriod = null; final Integer fromPeriod = this.fromApiJsonHelper.extractIntegerNamed(fromPeriodParamName, element, locale); - baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull().integerZeroOrGreater(); + if (this.fromApiJsonHelper.parameterExists(fromPeriodParamName, element)) { + baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).integerZeroOrGreater(); + } + + if ((isPrimaryGroupingByAmount != null && !isPrimaryGroupingByAmount) || (periodType != null || fromPeriod != null)) { + baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull(); + baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull(); + } if (this.fromApiJsonHelper.parameterExists(toPeriodParamName, element)) { toPeriod = this.fromApiJsonHelper.extractIntegerNamed(toPeriodParamName, element, locale); - baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).notNull().integerZeroOrGreater(); + baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).integerGreaterThanZero(); } if (fromPeriod != null && toPeriod != null) { @@ -115,21 +125,26 @@ public class InterestRateChartSlabDataValidator { baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("fromperiod.greater.than.to.period"); } } - BigDecimal amountRangeFrom = null; - BigDecimal amountRangeTo = null; + final BigDecimal amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale); + final BigDecimal amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale); if (this.fromApiJsonHelper.parameterExists(amountRangeFromParamName, element)) { - amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale); - baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull().positiveAmount(); + baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).zeroOrPositiveAmount(); + if (isPrimaryGroupingByAmount != null && isPrimaryGroupingByAmount) { + baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull(); + } } if (this.fromApiJsonHelper.parameterExists(amountRangeToParamName, element)) { - amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale); - baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).notNull().positiveAmount(); + baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).positiveAmount(); + } + + if (amountRangeFrom == null && fromPeriod == null) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("fromperiod.or.amountRangeFrom.required"); } if (amountRangeFrom != null && amountRangeTo != null) { if (amountRangeFrom.compareTo(amountRangeTo) > 1) { - baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("fromperiod.greater.than.toperiod"); + baseDataValidator.parameter(amountRangeFromParamName).value(fromPeriod).failWithCode("from.amount.greater.than.to.amount"); } } @@ -152,12 +167,14 @@ public class InterestRateChartSlabDataValidator { final JsonElement element = this.fromApiJsonHelper.parse(json); final JsonObject objectElement = element.getAsJsonObject(); final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(objectElement); - validateChartSlabsUpdate(element, baseDataValidator, locale); + final Boolean isPrimaryGroupingByAmount = null; + validateChartSlabsUpdate(element, baseDataValidator, locale, isPrimaryGroupingByAmount); throwExceptionIfValidationWarningsExist(dataValidationErrors); } - public void validateChartSlabsUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale) { + public void validateChartSlabsUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale, + final Boolean isPrimaryGroupingByAmount) { if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) { final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); @@ -166,8 +183,12 @@ public class InterestRateChartSlabDataValidator { if (this.fromApiJsonHelper.parameterExists(periodTypeParamName, element)) { final Integer periodType = this.fromApiJsonHelper.extractIntegerNamed(periodTypeParamName, element, locale); - baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull() + baseDataValidator.reset().parameter(periodTypeParamName).value(periodType) .isOneOfTheseValues(PeriodFrequencyType.integerValues()); + if (isPrimaryGroupingByAmount != null && !isPrimaryGroupingByAmount) { + baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull(); + } + } Integer fromPeriod = null; @@ -175,12 +196,15 @@ public class InterestRateChartSlabDataValidator { if (this.fromApiJsonHelper.parameterExists(fromPeriodParamName, element)) { fromPeriod = this.fromApiJsonHelper.extractIntegerNamed(fromPeriodParamName, element, locale); - baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull().integerGreaterThanNumber(-1); + baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).integerZeroOrGreater(); + if (isPrimaryGroupingByAmount != null && !isPrimaryGroupingByAmount) { + baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull(); + } } if (this.fromApiJsonHelper.parameterExists(toPeriodParamName, element)) { toPeriod = this.fromApiJsonHelper.extractIntegerNamed(toPeriodParamName, element, locale); - baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).notNull().integerGreaterThanNumber(-1); + baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).integerGreaterThanZero(); } if (fromPeriod != null && toPeriod != null) { @@ -192,12 +216,15 @@ public class InterestRateChartSlabDataValidator { BigDecimal amountRangeTo = null; if (this.fromApiJsonHelper.parameterExists(amountRangeFromParamName, element)) { amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale); - baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull().positiveAmount(); + baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).zeroOrPositiveAmount(); + if (isPrimaryGroupingByAmount != null && isPrimaryGroupingByAmount) { + baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull(); + } } if (this.fromApiJsonHelper.parameterExists(amountRangeToParamName, element)) { amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale); - baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).notNull().positiveAmount(); + baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).positiveAmount(); } if (amountRangeFrom != null && amountRangeTo != null) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChart.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChart.java index 428acb369..3537106bb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChart.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChart.java @@ -51,6 +51,7 @@ import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants; +import org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants; import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType; import org.joda.time.LocalDate; import org.springframework.data.jpa.domain.AbstractPersistable; @@ -82,12 +83,11 @@ public class InterestRateChart extends AbstractPersistable { final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(INTERESTRATE_CHART_RESOURCE_NAME); - + this.chartFields = chartFields; // validate before setting the other fields this.validateChartSlabs(baseDataValidator); this.throwExceptionIfValidationWarningsExist(dataValidationErrors); - this.chartFields = chartFields; this.addChartSlabs(interestRateChartSlabs); } @@ -97,22 +97,85 @@ public class InterestRateChart extends AbstractPersistable { Integer tmpPeriodType = null; List chartSlabsList = new ArrayList<>(chartSlabs); + boolean isPrimaryGroupingByAmount = this.chartFields.isPrimaryGroupingByAmount(); + chartSlabsList.sort(new InterestRateChartSlabComparator(isPrimaryGroupingByAmount)); + boolean isPeriodChart = !isPrimaryGroupingByAmount; + boolean isAmountChart = isPrimaryGroupingByAmount; for (int i = 0; i < chartSlabsList.size(); i++) { InterestRateChartSlab iSlabs = chartSlabsList.get(i); - if (tmpPeriodType == null) { + if (!iSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount)) { + if (isPrimaryGroupingByAmount) { + baseDataValidator.parameter(InterestRateChartSlabApiConstants.amountRangeFromParamName).failWithCode("cannot.be.blank"); + } else { + baseDataValidator.parameter(InterestRateChartSlabApiConstants.fromPeriodParamName).failWithCode("cannot.be.blank"); + } + + } else if (i > 0) { + if (isPeriodChart ^ iSlabs.slabFields().fromPeriod() != null) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.incomplete"); + isPeriodChart = isPeriodChart || iSlabs.slabFields().fromPeriod() != null; + } + if (isAmountChart ^ iSlabs.slabFields().getAmountRangeFrom() != null) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.incomplete"); + isAmountChart = isAmountChart || iSlabs.slabFields().getAmountRangeFrom() != null; + } + } + + if (i == 0) { tmpPeriodType = iSlabs.slabFields().periodType(); - } else if (!iSlabs.slabFields().periodType().equals(tmpPeriodType)) { + if (iSlabs.slabFields().isNotProperChartStart()) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.start.incorrect", iSlabs.slabFields() + .fromPeriod(), iSlabs.slabFields().getAmountRangeFrom()); + } + isAmountChart = isAmountChart || iSlabs.slabFields().getAmountRangeFrom() != null; + isPeriodChart = isPeriodChart || iSlabs.slabFields().fromPeriod() != null; + } else if (iSlabs.slabFields().periodType() != null && !iSlabs.slabFields().periodType().equals(tmpPeriodType)) { baseDataValidator.parameter(periodTypeParamName).value(iSlabs.slabFields().periodType()) .failWithCode("period.type.is.not.same", tmpPeriodType); } - for (int j = i + 1; j < chartSlabsList.size(); j++) { - InterestRateChartSlab jSlabs = chartSlabsList.get(j); - if (iSlabs.slabFields().isPeriodOverlapping(jSlabs.slabFields())) { - baseDataValidator - .failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.overlapping", iSlabs.slabFields().fromPeriod(), - iSlabs.slabFields().toPeriod(), jSlabs.slabFields().fromPeriod(), jSlabs.slabFields().toPeriod()); + if (i + 1 < chartSlabsList.size()) { + InterestRateChartSlab nextSlabs = chartSlabsList.get(i + 1); + if (iSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount) + && nextSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount)) { + if (iSlabs.slabFields().isRateChartOverlapping(nextSlabs.slabFields(), isPrimaryGroupingByAmount)) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.overlapping", iSlabs.slabFields() + .fromPeriod(), iSlabs.slabFields().toPeriod(), nextSlabs.slabFields().fromPeriod(), nextSlabs.slabFields() + .toPeriod(), iSlabs.slabFields().getAmountRangeFrom(), iSlabs.slabFields().getAmountRangeTo(), nextSlabs + .slabFields().getAmountRangeFrom(), nextSlabs.slabFields().getAmountRangeTo()); + } else if (iSlabs.slabFields().isRateChartHasGap(nextSlabs.slabFields(), isPrimaryGroupingByAmount)) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.has.gap", iSlabs.slabFields() + .fromPeriod(), iSlabs.slabFields().toPeriod(), nextSlabs.slabFields().fromPeriod(), nextSlabs.slabFields() + .toPeriod(), iSlabs.slabFields().getAmountRangeFrom(), iSlabs.slabFields().getAmountRangeTo(), nextSlabs + .slabFields().getAmountRangeFrom(), nextSlabs.slabFields().getAmountRangeTo()); + } + if (isPrimaryGroupingByAmount) { + if (!iSlabs.slabFields().isAmountSame(nextSlabs.slabFields())) { + if (InterestRateChartSlabFields.isNotProperPeriodStart(nextSlabs.slabFields())) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.start.incorrect", + nextSlabs.slabFields().toPeriod()); + } + if (iSlabs.slabFields().toPeriod() != null) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.end.incorrect", iSlabs + .slabFields().toPeriod()); + } + + } + } else if (!iSlabs.slabFields().isPeriodsSame(nextSlabs.slabFields())) { + if (InterestRateChartSlabFields.isNotProperAmountStart(nextSlabs.slabFields())) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.start.incorrect", nextSlabs + .slabFields().getAmountRangeFrom()); + } + if (iSlabs.slabFields().getAmountRangeTo() != null) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.end.incorrect", iSlabs + .slabFields().getAmountRangeTo()); + } + + } } + } else if (iSlabs.slabFields().isNotProperPriodEnd()) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.end.incorrect", + iSlabs.slabFields().toPeriod(), iSlabs.slabFields().getAmountRangeTo()); } } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartFields.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartFields.java index ca1f1b002..2b7b7a831 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartFields.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartFields.java @@ -24,6 +24,7 @@ import static org.apache.fineract.portfolio.interestratechart.InterestRateChartA import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.localeParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName; +import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.isPrimaryGroupingByAmountParamName; import java.util.Date; import java.util.Map; @@ -56,19 +57,24 @@ public class InterestRateChartFields { @Column(name = "end_date", nullable = false) private Date endDate; + @Column(name = "is_primary_grouping_by_amount", nullable = false) + private boolean isPrimaryGroupingByAmount; + protected InterestRateChartFields() { // } - public static InterestRateChartFields createNew(String name, String description, LocalDate fromDate, LocalDate toDate) { - return new InterestRateChartFields(name, description, fromDate, toDate); + public static InterestRateChartFields createNew(String name, String description, LocalDate fromDate, LocalDate toDate, + boolean isPrimaryGroupingByAmount) { + return new InterestRateChartFields(name, description, fromDate, toDate, isPrimaryGroupingByAmount); } - private InterestRateChartFields(String name, String description, LocalDate fromDate, LocalDate toDate) { + private InterestRateChartFields(String name, String description, LocalDate fromDate, LocalDate toDate, boolean isPrimaryGroupingByAmount) { this.name = name; this.description = description; this.fromDate = fromDate.toDate(); this.endDate = (toDate == null) ? null : toDate.toDate(); + this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount; } public void update(JsonCommand command, final Map actualChanges, final DataValidatorBuilder baseDataValidator) { @@ -106,6 +112,12 @@ public class InterestRateChartFields { this.endDate = newValue.toDate(); } + if (command.isChangeInBooleanParameterNamed(isPrimaryGroupingByAmountParamName, this.isPrimaryGroupingByAmount)) { + final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isPrimaryGroupingByAmountParamName); + actualChanges.put(isPrimaryGroupingByAmountParamName, newValue); + this.isPrimaryGroupingByAmount = newValue; + } + if (isFromDateAfterToDate()) { baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date"); } @@ -144,20 +156,22 @@ public class InterestRateChartFields { final LocalDate thatFromDate = that.getFromDateAsLocalDate(); LocalDate thatEndDate = that.getEndDateAsLocalDate(); thatEndDate = thatEndDate == null ? DateUtils.getLocalDateOfTenant() : thatEndDate; - + final LocalDateInterval thisInterval = LocalDateInterval.create(thisFromDate, thisEndDate); final LocalDateInterval thatInterval = LocalDateInterval.create(thatFromDate, thatEndDate); - - if(thisInterval.containsPortionOf(thatInterval) || thatInterval.containsPortionOf(thisInterval)){ - return true; - } + + if (thisInterval.containsPortionOf(thatInterval) || thatInterval.containsPortionOf(thisInterval)) { return true; } return false;// no overlapping } - - public boolean isApplicableChartFor(final LocalDate target){ + + public boolean isApplicableChartFor(final LocalDate target) { final LocalDate endDate = this.endDate == null ? DateUtils.getLocalDateOfTenant() : this.getEndDateAsLocalDate(); final LocalDateInterval interval = LocalDateInterval.create(getFromDateAsLocalDate(), endDate); return interval.contains(target); } + public boolean isPrimaryGroupingByAmount() { + return this.isPrimaryGroupingByAmount; + } + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartSlabComparator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartSlabComparator.java new file mode 100644 index 000000000..19f414767 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartSlabComparator.java @@ -0,0 +1,92 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.interestratechart.domain; + +import java.math.BigDecimal; +import java.util.Comparator; + +import org.apache.fineract.portfolio.savings.domain.DepositAccountInterestRateChartSlabs; + +/** + * Sort InterestRateChartSlab by input range + */ +public class InterestRateChartSlabComparator implements Comparator { + + private final boolean isPrimaryGroupingByAmount; + + public InterestRateChartSlabComparator(final boolean isPrimaryGroupingByAmount) { + this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount; + } + + @Override + public int compare(final T o1, final T o2) { + int compareResult = 0; + InterestRateChartSlabFields slabs1 = null; + InterestRateChartSlabFields slabs2 = null; + if (o1 instanceof InterestRateChartSlab) { + slabs1 = ((InterestRateChartSlab) o1).slabFields(); + slabs2 = ((InterestRateChartSlab) o2).slabFields(); + } else if (o1 instanceof DepositAccountInterestRateChartSlabs) { + slabs1 = ((DepositAccountInterestRateChartSlabs) o1).slabFields(); + slabs2 = ((DepositAccountInterestRateChartSlabs) o2).slabFields(); + } else { + return compareResult; + } + + if (slabs1.isPeriodsSame(slabs2) && slabs1.isAmountSame(slabs2)) { + compareResult = 0; + } else { + if (isPrimaryGroupingByAmount) { + if (slabs1.isAmountSame(slabs2)) { + compareResult = comparePeriods(slabs1, slabs2); + } else { + compareResult = compareAmounts(slabs1, slabs2); + } + } else { + if (slabs1.isPeriodsSame(slabs2)) { + compareResult = compareAmounts(slabs1, slabs2); + } else { + compareResult = comparePeriods(slabs1, slabs2); + } + } + } + return compareResult; + } + + private int comparePeriods(final InterestRateChartSlabFields slabs1, InterestRateChartSlabFields slabs2) { + int compareResult = 0; + Integer periodFrom1 = slabs1.fromPeriod(); + Integer periodFrom2 = slabs2.fromPeriod(); + if (periodFrom1 != null && periodFrom2 != null) { + compareResult = periodFrom1.compareTo(periodFrom2); + } + return compareResult; + } + + private int compareAmounts(final InterestRateChartSlabFields slabs1, InterestRateChartSlabFields slabs2) { + int compareResult = 0; + BigDecimal amountFrom1 = slabs1.getAmountRangeFrom(); + BigDecimal amountFrom2 = slabs2.getAmountRangeFrom(); + if (amountFrom1 != null && amountFrom2 != null) { + compareResult = amountFrom1.compareTo(amountFrom2); + } + return compareResult; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartSlabFields.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartSlabFields.java index 334772c6a..b8c00de93 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartSlabFields.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/domain/InterestRateChartSlabFields.java @@ -51,15 +51,23 @@ public class InterestRateChartSlabFields { @Column(name = "period_type_enum", nullable = false) private Integer periodType; - @Column(name = "from_period", nullable = false) + @Column(name = "from_period") private Integer fromPeriod; - @Column(name = "to_period", nullable = true) + @Column(name = "to_period") private Integer toPeriod; @Column(name = "amount_range_from", scale = 6, precision = 19) private BigDecimal amountRangeFrom; + public BigDecimal getAmountRangeFrom() { + return this.amountRangeFrom; + } + + public BigDecimal getAmountRangeTo() { + return this.amountRangeTo; + } + @Column(name = "amount_range_to", scale = 6, precision = 19) private BigDecimal amountRangeTo; @@ -84,7 +92,7 @@ public class InterestRateChartSlabFields { final Integer fromPeriod, final Integer toPeriod, final BigDecimal amountRangeFrom, final BigDecimal amountRangeTo, final BigDecimal annualInterestRate, final String currencyCode) { this.description = description; - this.periodType = (periodFrequencyType == null) ? null : periodFrequencyType.getValue(); + this.periodType = (periodFrequencyType == null || periodFrequencyType.isInvalid()) ? null : periodFrequencyType.getValue(); this.fromPeriod = fromPeriod; this.toPeriod = toPeriod; this.amountRangeFrom = amountRangeFrom; @@ -183,17 +191,129 @@ public class InterestRateChartSlabFields { return this.toPeriod; } - public boolean isPeriodOverlapping(final InterestRateChartSlabFields that) { - if (that.toPeriod == null) { - if (this.toPeriod == null) { return true; } - return that.fromPeriod <= this.toPeriod; + public boolean isRateChartHasGap(final InterestRateChartSlabFields that, final boolean isPrimaryGroupingByAmount) { + boolean isPeriodSame = isPeriodsSame(that); + boolean isAmountSame = isAmountSame(that); + boolean hasPeriods = this.fromPeriod != null || that.fromPeriod != null; + boolean hasAmounts = this.amountRangeFrom != null || that.amountRangeFrom != null; + if (isPrimaryGroupingByAmount) { + if (isAmountSame) { + if (hasPeriods) { + if (this.toPeriod == null) { return true; } + return isNotProperPeriodStart(that.fromPeriod); + } + } else { + return isNotProperAmountStart(that.amountRangeFrom) || isNotProperPeriodStart(that); + } + } else { + if (isPeriodSame) { + if (hasAmounts) { + if (this.amountRangeTo == null) { return true; } + return isNotProperAmountStart(that.amountRangeFrom); + } + } else { + return isNotProperPeriodStart(that.fromPeriod) || isNotProperAmountStart(that); + } } + return false; + } + + public boolean isValidChart(boolean isPrimaryGroupingByAmount) { + return (!isPrimaryGroupingByAmount && this.fromPeriod != null) || (isPrimaryGroupingByAmount && this.amountRangeFrom != null); + } + + public boolean isNotProperChartStart() { + return isNotProperPeriodStart(this) || isNotProperAmountStart(this); + } + + public static boolean isNotProperAmountStart(final InterestRateChartSlabFields interestRateChartSlabFields) { + return interestRateChartSlabFields.amountRangeFrom != null + && (interestRateChartSlabFields.amountRangeFrom.compareTo(BigDecimal.ONE) != 0 && interestRateChartSlabFields.amountRangeFrom + .compareTo(BigDecimal.ZERO) != 0); + } + + private boolean isNotProperAmountStart(final BigDecimal amount) { + return this.amountRangeTo == null || (amount != null && amount.compareTo(this.amountRangeTo.add(BigDecimal.ONE)) != 0); + } + + private boolean isNotProperPeriodStart(final Integer period) { + return this.toPeriod == null || (period != null && period.compareTo(this.toPeriod + 1) != 0); + } + + public static boolean isNotProperPeriodStart(InterestRateChartSlabFields interestRateChartSlabFields) { + return interestRateChartSlabFields.fromPeriod != null + && !(interestRateChartSlabFields.fromPeriod.equals(1) || interestRateChartSlabFields.fromPeriod.equals(0)); + } + + public boolean isNotProperPriodEnd() { + return !(this.toPeriod == null && this.amountRangeTo == null); + + } + + public boolean isRateChartOverlapping(final InterestRateChartSlabFields that, final boolean isPrimaryGroupingByAmount) { + boolean isPeriodOverLapping = isPeriodOverlapping(that); + boolean isAmountOverLapping = isAmountOverlapping(that); + boolean isPeriodSame = isPeriodsSame(that); + boolean isAmountSame = isAmountSame(that); + boolean isOverlapping = false; + if (isPrimaryGroupingByAmount) { + isOverlapping = (isAmountOverLapping && !isAmountSame) || (isPeriodOverLapping && isAmountSame); + } else { + isOverlapping = (isPeriodOverLapping && !isPeriodSame) || (isAmountOverLapping && isPeriodSame); + } + + return isOverlapping; + } + + private boolean isPeriodOverlapping(final InterestRateChartSlabFields that) { + if (isIntegerSame(that.toPeriod, this.toPeriod)) { + return true; + } else if (isIntegerSame(that.fromPeriod, this.fromPeriod)) { + return true; + } else if (this.toPeriod == null) { + return true; + } else if (that.toPeriod == null) { return that.fromPeriod <= this.toPeriod; } return this.fromPeriod <= that.toPeriod && that.fromPeriod <= this.toPeriod; } + private boolean isAmountOverlapping(final InterestRateChartSlabFields that) { + if (isBigDecimalSame(that.amountRangeFrom, this.amountRangeFrom)) { + return true; + } else if (isBigDecimalSame(that.amountRangeTo, this.amountRangeTo)) { + return true; + } else if (this.amountRangeTo == null) { + return true; + } else if (that.amountRangeTo == null) { return that.amountRangeFrom.compareTo(this.amountRangeTo) < 1; } + return this.amountRangeFrom.compareTo(that.amountRangeTo) < 1 && that.amountRangeFrom.compareTo(this.amountRangeTo) < 1; + } + + public boolean isAmountSame(final InterestRateChartSlabFields that) { + return isBigDecimalSame(this.amountRangeFrom, that.amountRangeFrom) && isBigDecimalSame(this.amountRangeTo, that.amountRangeTo); + } + + public boolean isPeriodsSame(final InterestRateChartSlabFields that) { + return isIntegerSame(this.fromPeriod, that.fromPeriod) && isIntegerSame(this.toPeriod, that.toPeriod); + } + + public boolean isIntegerSame(final Integer obj1, final Integer obj2) { + if (obj1 == null || obj2 == null) { + if (obj1 == obj2) { return true; } + return false; + } + return obj1.equals(obj2); + } + + public boolean isBigDecimalSame(final BigDecimal obj1, final BigDecimal obj2) { + if (obj1 == null || obj2 == null) { + if (obj1 == obj2) { return true; } + return false; + } + return obj1.compareTo(obj2) == 0; + } + public boolean isBetweenPeriod(final LocalDate periodStartDate, final LocalDate periodEndDate) { final Integer compare = depositPeriod(periodStartDate, periodEndDate); - return (compare < this.fromPeriod || (this.toPeriod != null && compare > this.toPeriod)) ? false : true; + return isPeriodBetween(compare); } public boolean isAmountRangeProvided() { @@ -233,8 +353,16 @@ public class InterestRateChartSlabFields { returnValue = depositAmount.compareTo(amountRangeFrom) >= 0 && depositAmount.compareTo(amountRangeTo) <= 0; } else if (amountRangeFrom != null) { returnValue = depositAmount.compareTo(amountRangeFrom) >= 0; - } else if (amountRangeTo != null) { - returnValue = depositAmount.compareTo(amountRangeTo) <= 0; + } + return returnValue; + } + + public boolean isPeriodBetween(final Integer periods) { + boolean returnValue = true; + if (fromPeriod != null && toPeriod != null) { + returnValue = periods.compareTo(fromPeriod) >= 0 && periods.compareTo(toPeriod) <= 0; + } else if (fromPeriod != null) { + returnValue = periods.compareTo(fromPeriod) >= 0; } return returnValue; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartAssembler.java index a2a65db3a..a7f29e3b5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartAssembler.java @@ -22,6 +22,7 @@ import static org.apache.fineract.portfolio.interestratechart.InterestRateChartA import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName; +import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.isPrimaryGroupingByAmountParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName; import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.currencyCodeParamName; @@ -65,33 +66,38 @@ public class InterestRateChartAssembler { * request */ public InterestRateChart assembleFrom(final JsonCommand command) { - - final JsonElement element = command.parsedJson(); final List dataValidationErrors = new ArrayList<>(); - @SuppressWarnings("unused") final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(INTERESTRATE_CHART_RESOURCE_NAME); + final JsonElement element = command.parsedJson(); final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element); - final InterestRateChart newChart = this.assembleFrom(element, currencyCode); - + final InterestRateChart newChart = this.assembleFrom(element, currencyCode, baseDataValidator); throwExceptionIfValidationWarningsExist(dataValidationErrors); return newChart; } - public InterestRateChart assembleFrom(final JsonElement element, final String currencyCode) { + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + public InterestRateChart assembleFrom(final JsonElement element, final String currencyCode, final DataValidatorBuilder baseDataValidator) { final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element); final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); final LocalDate fromDate = this.fromApiJsonHelper.extractLocalDateNamed(fromDateParamName, element); final LocalDate toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element); - + Boolean isPrimaryGroupingByAmount = this.fromApiJsonHelper.extractBooleanNamed(isPrimaryGroupingByAmountParamName, element); + if (isPrimaryGroupingByAmount == null) { + isPrimaryGroupingByAmount = false; + } // assemble chart Slabs - final Collection newChartSlabs = this.chartSlabAssembler.assembleChartSlabsFrom(element, - currencyCode); + final Collection newChartSlabs = this.chartSlabAssembler.assembleChartSlabsFrom(element, currencyCode); - final InterestRateChartFields fields = InterestRateChartFields.createNew(name, description, fromDate, toDate); + final InterestRateChartFields fields = InterestRateChartFields.createNew(name, description, fromDate, toDate, + isPrimaryGroupingByAmount); final InterestRateChart newChart = InterestRateChart.createNew(fields, newChartSlabs); + newChart.validateChartSlabs(baseDataValidator); return newChart; } @@ -100,8 +106,4 @@ public class InterestRateChartAssembler { .findOneWithNotFoundDetection(interestRateChartId); return interestRateChart; } - - private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } - } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java index 86eefadbb..d03e502d1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java @@ -86,8 +86,21 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha @Override public Collection retrieveAllWithSlabs(Long productId) { this.context.authenticatedUser(); - String sql = "select " + this.chartExtractor.schema() + " where sp.id = ? order by irc.id, ircd.id"; - return this.jdbcTemplate.query(sql, this.chartExtractor, new Object[] { productId }); + StringBuilder sql = new StringBuilder(); + sql.append("select "); + sql.append(this.chartExtractor.schema()); + sql.append(" where sp.id = ? order by irc.id, "); + sql.append("CASE "); + sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_from "); + sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_to "); + sql.append("END,"); + sql.append("ircd.from_period, ircd.to_period,"); + sql.append("CASE "); + sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_from "); + sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_to "); + sql.append("END"); + + return this.jdbcTemplate.query(sql.toString(), this.chartExtractor, new Object[] { productId }); } @Override @@ -177,6 +190,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha sqlBuilder .append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription,") .append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ") + .append("irc.is_primary_grouping_by_amount as isPrimaryGroupingByAmount, ") .append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ") .append("ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, ") .append("ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, ") @@ -239,6 +253,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha sqlBuilder.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription, ") .append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ") + .append("irc.is_primary_grouping_by_amount as isPrimaryGroupingByAmount, ") .append("sp.id as savingsProductId, sp.name as savingsProductName ").append("from ") .append("m_interest_rate_chart irc ") .append("left join m_deposit_product_interest_rate_chart dpirc on irc.id=dpirc.interest_rate_chart_id ") @@ -253,10 +268,11 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha final String description = rs.getString("ircDescription"); final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "ircFromDate"); final LocalDate endDate = JdbcSupport.getLocalDate(rs, "ircEndDate"); + final boolean isPrimaryGroupingByAmount = rs.getBoolean("isPrimaryGroupingByAmount"); final Long savingsProductId = JdbcSupport.getLongDefaultToNullIfZero(rs, "savingsProductId"); final String savingsProductName = rs.getString("savingsProductName"); - return InterestRateChartData.instance(id, name, description, fromDate, endDate, savingsProductId, savingsProductName); + return InterestRateChartData.instance(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, savingsProductId, savingsProductName); } } @@ -299,7 +315,10 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha final Integer fromPeriod = JdbcSupport.getInteger(rs, "ircdFromPeriod"); final Integer toPeriod = JdbcSupport.getInteger(rs, "ircdToPeriod"); final Integer periodTypeId = JdbcSupport.getInteger(rs, "ircdPeriodTypeId"); - final EnumOptionData periodType = InterestRateChartEnumerations.periodType(periodTypeId); + EnumOptionData periodType = null; + if (periodTypeId != null) { + periodType = InterestRateChartEnumerations.periodType(periodTypeId); + } final BigDecimal amountRangeFrom = rs.getBigDecimal("ircdAmountRangeFrom"); final BigDecimal amountRangeTo = rs.getBigDecimal("ircdAmountRangeTo"); final BigDecimal annualInterestRate = rs.getBigDecimal("ircdAnnualInterestRate"); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountInterestRateChartData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountInterestRateChartData.java index 7b78f03b7..735f95d41 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountInterestRateChartData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountInterestRateChartData.java @@ -18,9 +18,8 @@ */ package org.apache.fineract.portfolio.savings.data; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.core.data.EnumOptionData; @@ -38,10 +37,11 @@ public class DepositAccountInterestRateChartData { private final String description; private final LocalDate fromDate; private final LocalDate endDate; + private final boolean isPrimaryGroupingByAmount; private final Long accountId; private final String accountNumber; // associations - private Set chartSlabs; + private Collection chartSlabs; // template private final Collection periodTypes; @@ -54,8 +54,8 @@ public class DepositAccountInterestRateChartData { private final Collection clientClassificationOptions; public static DepositAccountInterestRateChartData instance(Long id, String name, String description, LocalDate fromDate, - LocalDate endDate, Long accountId, String accountNumber, Set chartSlabs, - Collection periodTypes) { + LocalDate endDate, boolean isPrimaryGroupingByAmount, Long accountId, String accountNumber, + Collection chartSlabs, Collection periodTypes) { final Collection entityTypeOptions = null; final Collection attributeNameOptions = null; @@ -64,17 +64,17 @@ public class DepositAccountInterestRateChartData { final Collection genderOptions = null; final Collection clientTypeOptions = null; final Collection clientClassificationOptions = null; - return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, accountId, accountNumber, chartSlabs, - periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, - clientTypeOptions, clientClassificationOptions); + return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, accountId, + accountNumber, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, + incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions); } public static DepositAccountInterestRateChartData from(InterestRateChartData productChartData) { final Long id = null; final Long accountId = null; final String accountNumber = null; - Set fromProdChartSlabs = new HashSet<>(); - Set productChartSlabDatas = productChartData.chartSlabs(); + Collection fromProdChartSlabs = new ArrayList<>(); + Collection productChartSlabDatas = productChartData.chartSlabs(); if (productChartSlabDatas != null) { for (InterestRateChartSlabData productChartSlabData : productChartSlabDatas) { fromProdChartSlabs.add(DepositAccountInterestRateChartSlabData.from(productChartSlabData)); @@ -82,20 +82,20 @@ public class DepositAccountInterestRateChartData { } return new DepositAccountInterestRateChartData(id, productChartData.name(), productChartData.description(), - productChartData.fromDate(), productChartData.endDate(), accountId, accountNumber, fromProdChartSlabs, - productChartData.periodTypes(), productChartData.entityTypeOptions(), productChartData.attributeNameOptions(), - productChartData.conditionTypeOptions(), productChartData.incentiveTypeOptions(), productChartData.genderOptions(), - productChartData.clientTypeOptions(), productChartData.clientClassificationOptions()); + productChartData.fromDate(), productChartData.endDate(), productChartData.isPrimaryGroupingByAmount(), accountId, + accountNumber, fromProdChartSlabs, productChartData.periodTypes(), productChartData.entityTypeOptions(), + productChartData.attributeNameOptions(), productChartData.conditionTypeOptions(), productChartData.incentiveTypeOptions(), + productChartData.genderOptions(), productChartData.clientTypeOptions(), productChartData.clientClassificationOptions()); } public static DepositAccountInterestRateChartData withSlabs(DepositAccountInterestRateChartData interestRateChartData, - Set chartSlabs) { + Collection chartSlabs) { return new DepositAccountInterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description, interestRateChartData.fromDate, interestRateChartData.endDate, - interestRateChartData.accountId, interestRateChartData.accountNumber, chartSlabs, interestRateChartData.periodTypes, - interestRateChartData.entityTypeOptions, interestRateChartData.attributeNameOptions, - interestRateChartData.conditionTypeOptions, interestRateChartData.incentiveTypeOptions, - interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions, + interestRateChartData.isPrimaryGroupingByAmount, interestRateChartData.accountId, interestRateChartData.accountNumber, + chartSlabs, interestRateChartData.periodTypes, interestRateChartData.entityTypeOptions, + interestRateChartData.attributeNameOptions, interestRateChartData.conditionTypeOptions, + interestRateChartData.incentiveTypeOptions, interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions, interestRateChartData.clientClassificationOptions); } @@ -106,9 +106,9 @@ public class DepositAccountInterestRateChartData { final Collection clientTypeOptions, final Collection clientClassificationOptions) { return new DepositAccountInterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description, interestRateChartData.fromDate, interestRateChartData.endDate, - interestRateChartData.accountId, interestRateChartData.accountNumber, interestRateChartData.chartSlabs, periodTypes, - entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, clientTypeOptions, - clientClassificationOptions); + interestRateChartData.isPrimaryGroupingByAmount, interestRateChartData.accountId, interestRateChartData.accountNumber, + interestRateChartData.chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, + incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions); } public static DepositAccountInterestRateChartData template(Collection periodTypes, @@ -123,23 +123,26 @@ public class DepositAccountInterestRateChartData { final LocalDate endDate = null; final Long accountId = null; final String accountNumber = null; - final Set chartSlabs = null; - return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, accountId, accountNumber, chartSlabs, - periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, - clientTypeOptions, clientClassificationOptions); + final boolean isPrimaryGroupingByAmount = false; + final Collection chartSlabs = null; + return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, accountId, + accountNumber, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, + incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions); } private DepositAccountInterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate, - Long accountId, String accountNumber, Set chartSlabs, - Collection periodTypes, final Collection entityTypeOptions, - final Collection attributeNameOptions, final Collection conditionTypeOptions, - final Collection incentiveTypeOptions, final Collection genderOptions, - final Collection clientTypeOptions, final Collection clientClassificationOptions) { + final boolean isPrimaryGroupingByAmount, Long accountId, String accountNumber, + Collection chartSlabs, Collection periodTypes, + final Collection entityTypeOptions, final Collection attributeNameOptions, + final Collection conditionTypeOptions, final Collection incentiveTypeOptions, + final Collection genderOptions, final Collection clientTypeOptions, + final Collection clientClassificationOptions) { this.id = id; this.name = name; this.description = description; this.fromDate = fromDate; this.endDate = endDate; + this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount; this.chartSlabs = chartSlabs; this.accountId = accountId; this.accountNumber = accountNumber; @@ -155,7 +158,7 @@ public class DepositAccountInterestRateChartData { public void addChartSlab(final DepositAccountInterestRateChartSlabData chartSlab) { if (this.chartSlabs == null) { - this.chartSlabs = new HashSet<>(); + this.chartSlabs = new ArrayList<>(); } this.chartSlabs.add(chartSlab); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java index ffaf6483a..0f2a1fa3a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java @@ -35,16 +35,24 @@ import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumb import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; -import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.data.AccountTransferDTO; import org.apache.fineract.portfolio.account.domain.AccountTransferType; import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService; +import org.apache.fineract.portfolio.calendar.domain.Calendar; +import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType; +import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType; +import org.apache.fineract.portfolio.calendar.domain.CalendarInstance; +import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; +import org.apache.fineract.portfolio.calendar.domain.CalendarType; +import org.apache.fineract.portfolio.calendar.service.CalendarUtils; import org.apache.fineract.portfolio.client.domain.AccountNumberGenerator; +import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType; import org.apache.fineract.portfolio.savings.DepositAccountType; @@ -71,6 +79,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; private final ConfigurationDomainService configurationDomainService; private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository; + private final CalendarInstanceRepository calendarInstanceRepository; @Autowired public DepositAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository, @@ -79,7 +88,8 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final DepositAccountAssembler depositAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService, final AccountTransfersWritePlatformService accountTransfersWritePlatformService, final ConfigurationDomainService configurationDomainService, - final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository) { + final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, + final CalendarInstanceRepository calendarInstanceRepository) { this.savingsAccountRepository = savingsAccountRepository; this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; this.journalEntryWritePlatformService = journalEntryWritePlatformService; @@ -89,6 +99,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi this.accountTransfersWritePlatformService = accountTransfersWritePlatformService; this.configurationDomainService = configurationDomainService; this.accountNumberFormatRepository = accountNumberFormatRepository; + this.calendarInstanceRepository = calendarInstanceRepository; } @Transactional @@ -129,6 +140,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi boolean isAccountTransfer = false; final boolean isPreMatureClosure = false; final MathContext mc = MathContext.DECIMAL64; + account.updateDepositAmount(transactionAmount); final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction); @@ -241,18 +253,15 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi if (onClosureType.isReinvest()) { RecurringDepositAccount reinvestedDeposit = account.reInvest(transactionAmount); depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit); - reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, - financialYearBeginningMonth); - reinvestedDeposit.processAccountUponActivation(fmt, user); - reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, - financialYearBeginningMonth); this.savingsAccountRepository.save(reinvestedDeposit); - - Money amountForDeposit = reinvestedDeposit.activateWithBalance(); - if (amountForDeposit.isGreaterThanZero()) { - handleRDDeposit(reinvestedDeposit, fmt, reinvestedDeposit.getActivationLocalDate(), amountForDeposit.getAmount(), - paymentDetail, isRegularTransaction); - } + final CalendarInstance calendarInstance = getCalendarInstance(account, reinvestedDeposit); + this.calendarInstanceRepository.save(calendarInstance); + final Calendar calendar = calendarInstance.getCalendar(); + final PeriodFrequencyType frequencyType = CalendarFrequencyType.from(CalendarUtils.getFrequency(calendar.getRecurrence())); + Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence()); + frequency = frequency == -1 ? 1 : frequency; + reinvestedDeposit.generateSchedule(frequencyType, frequency, calendar); + reinvestedDeposit.processAccountUponActivation(fmt, user); reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); this.savingsAccountRepository.save(reinvestedDeposit); @@ -292,6 +301,31 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi return savingsTransactionId; } + private CalendarInstance getCalendarInstance(final RecurringDepositAccount account, RecurringDepositAccount reinvestedDeposit) { + CalendarInstance calendarInstance = null; + CalendarInstance parentCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( + account.getId(), CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue()); + if (account.isCalendarInherited()) { + calendarInstance = CalendarInstance.from(parentCalendarInstance.getCalendar(), account.getId(), + CalendarEntityType.SAVINGS.getValue()); + } else { + LocalDate calendarStartDate = reinvestedDeposit.depositStartDate(); + Calendar parentCalendar = parentCalendarInstance.getCalendar(); + final String recurrence = parentCalendar.getRecurrence(); + final String title = "recurring_savings_" + reinvestedDeposit.getId(); + final Calendar calendar = Calendar.createRepeatingCalendar(title, calendarStartDate, CalendarType.COLLECTION.getValue(), + recurrence); + calendarInstance = CalendarInstance.from(calendar, reinvestedDeposit.getId(), CalendarEntityType.SAVINGS.getValue()); + } + if (calendarInstance == null) { + final String defaultUserMessage = "No valid recurring details available for recurring depost account creation."; + throw new GeneralPlatformDomainRuleException( + "error.msg.recurring.deposit.account.cannot.create.no.valid.recurring.details.available", defaultUserMessage, + account.clientId()); + } + return calendarInstance; + } + private void autoGenerateAccountNumber(final SavingsAccount account) { if (account.isAccountNumberRequiresAutoGeneration()) { final AccountNumberFormat accountNumberFormat = this.accountNumberFormatRepository.findByAccountType(EntityAccountType.SAVINGS); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountInterestRateChart.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountInterestRateChart.java index 25aab57cb..7365b0fe5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountInterestRateChart.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountInterestRateChart.java @@ -92,6 +92,7 @@ public class DepositAccountInterestRateChart extends AbstractPersistable { if (this.chartSlabs == null) { this.chartSlabs = new HashSet<>(); } + return this.chartSlabs; } @@ -153,4 +154,8 @@ public class DepositAccountInterestRateChart extends AbstractPersistable { return effectiveInterestRate; } + + public boolean isPrimaryGroupingByAmount() { + return this.chartFields.isPrimaryGroupingByAmount(); + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositProductAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositProductAssembler.java index 83b0e108f..dcd446c00 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositProductAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositProductAssembler.java @@ -18,6 +18,8 @@ */ package org.apache.fineract.portfolio.savings.domain; +import static org.apache.fineract.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME; +import static org.apache.fineract.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.adjustAdvanceTowardsFuturePaymentsParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.allowWithdrawalParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.chartsParamName; @@ -52,11 +54,16 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalA import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.apache.fineract.accounting.common.AccountingRuleType; import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.ApiParameterError; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; @@ -147,7 +154,11 @@ public class DepositProductAssembler { // Savings product charges final Set charges = assembleListOfSavingsProductCharges(command, currencyCode); // Interest rate charts - final Set charts = assembleListOfCharts(command, currency.getCode()); + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME); + final Set charts = assembleListOfCharts(command, currency.getCode(), baseDataValidator); + throwExceptionIfValidationWarningsExist(dataValidationErrors); if (interestRate == null) { interestRate = BigDecimal.ZERO; } @@ -164,6 +175,10 @@ public class DepositProductAssembler { return fixedDepositProduct; } + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + public RecurringDepositProduct assembleRecurringDepositProduct(final JsonCommand command) { final String name = command.stringValueOfParameterNamed(nameParamName); @@ -225,7 +240,11 @@ public class DepositProductAssembler { // Savings product charges final Set charges = assembleListOfSavingsProductCharges(command, currencyCode); // Interest rate charts - final Set charts = assembleListOfCharts(command, currency.getCode()); + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME); + final Set charts = assembleListOfCharts(command, currency.getCode(), baseDataValidator); + throwExceptionIfValidationWarningsExist(dataValidationErrors); if (interestRate == null) { interestRate = BigDecimal.ZERO; @@ -435,15 +454,14 @@ public class DepositProductAssembler { return charges; } - private Set assembleListOfCharts(JsonCommand command, String currencyCode) { + private Set assembleListOfCharts(JsonCommand command, String currencyCode, DataValidatorBuilder baseDataValidator) { final Set charts = new HashSet<>(); - if (command.parameterExists(chartsParamName)) { final JsonArray chartsArray = command.arrayOfParameterNamed(chartsParamName); if (chartsArray != null) { for (int i = 0; i < chartsArray.size(); i++) { final JsonObject interstRateChartElement = chartsArray.get(i).getAsJsonObject(); - InterestRateChart chart = this.chartAssembler.assembleFrom(interstRateChartElement, currencyCode); + InterestRateChart chart = this.chartAssembler.assembleFrom(interstRateChartElement, currencyCode, baseDataValidator); charts.add(chart); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositProduct.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositProduct.java index 5ab9cea68..9e4e07533 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositProduct.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositProduct.java @@ -198,7 +198,7 @@ public class FixedDepositProduct extends SavingsProduct { } } else { // assemble chart - final InterestRateChart newChart = this.chartAssembler.assembleFrom(chartElement, this.currency().getCode()); + final InterestRateChart newChart = this.chartAssembler.assembleFrom(chartElement, this.currency().getCode(), baseDataValidator); this.addChart(newChart); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java index 0d6f85749..1bf4e52ca 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java @@ -174,12 +174,29 @@ public class RecurringDepositAccount extends SavingsAccount { final Map termAndPreClosureChanges = accountTermAndPreClosure.update(command, baseDataValidator); actualChanges.putAll(termAndPreClosureChanges); recurringDetail.update(command); - validateDomainRules(baseDataValidator); super.validateInterestPostingAndCompoundingPeriodTypes(baseDataValidator); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + private void updateDepositAmount() { + BigDecimal recurringAmount = getRecurringDetail().mandatoryRecommendedDepositAmount(); + Integer numberOfDepositPeriods = depositScheduleInstallments().size(); + if (this.accountTermAndPreClosure.depositPeriod() != null && recurringAmount != null && numberOfDepositPeriods != null) { + BigDecimal depositAmount = Money.of(product.currency(), recurringAmount).multipliedBy(numberOfDepositPeriods) + .plus(this.minRequiredOpeningBalance).getAmount(); + accountTermAndPreClosure.updateDepositAmount(depositAmount); + } else if (accountTermAndPreClosure.depositAmount() == null) { + accountTermAndPreClosure.updateDepositAmount(Money.zero(product.currency()).getAmount()); + } + } + + public void updateDepositAmount(final BigDecimal depositAmount) { + if (this.accountTermAndPreClosure.depositPeriod() == null) { + accountTermAndPreClosure.updateDepositAmount(accountTermAndPreClosure.depositAmount().add(depositAmount)); + } + } + @Override protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate interestPostingUpToDate) { boolean isPreMatureClosure = false; @@ -207,7 +224,7 @@ public class RecurringDepositAccount extends SavingsAccount { } if (depositCloseDate == null) { - depositCloseDate = LocalDate.now(); + depositCloseDate = DateUtils.getLocalDateOfTenant(); } final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount(); @@ -966,8 +983,8 @@ public class RecurringDepositAccount extends SavingsAccount { .failWithCode("deposit.period.must.be.greater.than.lock.in.period", "Deposit period must be greater than account lock-in period."); } - } + } if (firstDepositDateBeforeAccountSubmittedOrActivationDate()) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "expected.first.deposit.date.must.be.after.account.submitted.or.activation.date"); @@ -989,7 +1006,7 @@ public class RecurringDepositAccount extends SavingsAccount { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); LocalDate maturityDate = calculateMaturityDate(); - if (this.chart != null && maturityDate != null) { + if (this.chart != null) { final LocalDate chartFromDate = this.chart.getFromDateAsLocalDate(); LocalDate chartEndDate = this.chart.getEndDateAsLocalDate(); chartEndDate = chartEndDate == null ? DateUtils.getLocalDateOfTenant() : chartEndDate; @@ -999,7 +1016,11 @@ public class RecurringDepositAccount extends SavingsAccount { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.valid.interest.rate.slab.available.for.date.range"); } - final BigDecimal maturityAmount = this.accountTermAndPreClosure.maturityAmount(); + if (maturityDate == null) { + maturityDate = DateUtils.getLocalDateOfTenant(); + } + + final BigDecimal maturityAmount = this.accountTermAndPreClosure.depositAmount(); BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(maturityAmount, depositStartDate(), maturityDate, this.client); @@ -1012,7 +1033,7 @@ public class RecurringDepositAccount extends SavingsAccount { baseDataValidator.reset().parameter(DepositsApiConstants.nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate) .failWithCodeNoParameterAddedToErrorCode("interest.chart.or.nominal.interest.rate.required"); } - + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } /** * final boolean recurringFrequencyBeforeDepositPeriod = * recurringFrequencyBeforeDepositPeriod(); @@ -1117,6 +1138,7 @@ public class RecurringDepositAccount extends SavingsAccount { installmentDate = DepositAccountUtils.calculateNextDepositDate(installmentDate, frequency, recurringEvery); installmentNumber += 1; } + updateDepositAmount(); } private LocalDate calcualteScheduleTillDate(final PeriodFrequencyType frequency, final Integer recurringEvery) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java index 1adb84b61..451cc70e6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java @@ -90,8 +90,20 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D @Override public DepositAccountInterestRateChartData retrieveOneWithSlabs(Long chartId) { this.context.authenticatedUser(); - final String sql = "select " + this.chartExtractor.schema() + " where irc.id = ? order by ircd.id asc"; - Collection chartDatas = this.jdbcTemplate.query(sql, this.chartExtractor, + StringBuilder sql = new StringBuilder(); + sql.append("select "); + sql.append(this.chartExtractor.schema()); + sql.append(" where irc.id = ? order by irc.id asc, "); + sql.append("CASE "); + sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_from "); + sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_to "); + sql.append("END,"); + sql.append("ircd.from_period, ircd.to_period,"); + sql.append("CASE "); + sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_from "); + sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_to "); + sql.append("END"); + Collection chartDatas = this.jdbcTemplate.query(sql.toString(), this.chartExtractor, new Object[] { chartId }); if (chartDatas == null || chartDatas.isEmpty()) { throw new DepositAccountInterestRateChartNotFoundException(chartId); } @@ -122,8 +134,21 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D @Override public DepositAccountInterestRateChartData retrieveOneWithSlabsOnAccountId(Long accountId) { this.context.authenticatedUser(); - final String sql = "select " + this.chartExtractor.schema() + " where irc.savings_account_id = ? order by ircd.id asc"; - Collection chartDatas = this.jdbcTemplate.query(sql, this.chartExtractor, + StringBuilder sql = new StringBuilder(); + sql.append("select "); + sql.append(this.chartExtractor.schema()); + sql.append(" where irc.savings_account_id = ? order by irc.id asc, "); + sql.append("CASE "); + sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_from "); + sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_to "); + sql.append("END,"); + sql.append("ircd.from_period, ircd.to_period,"); + sql.append("CASE "); + sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_from "); + sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_to "); + sql.append("END"); + + Collection chartDatas = this.jdbcTemplate.query(sql.toString(), this.chartExtractor, new Object[] { accountId }); if (chartDatas == null || chartDatas.isEmpty()) { throw new DepositAccountInterestRateChartNotFoundException(accountId); } @@ -167,6 +192,7 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D sqlBuilder .append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription,") .append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ") + .append("irc.is_primary_grouping_by_amount as isPrimaryGroupingByAmount,") .append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ") .append("ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, ") .append("ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, ") @@ -231,6 +257,7 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D sqlBuilder.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription, ") .append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ") + .append("irc.is_primary_grouping_by_amount as isPrimaryGroupingByAmount,") .append("sa.id as accountId, sa.account_no as accountNumber ").append("from ") .append("m_savings_account_interest_rate_chart irc left join m_savings_account sa on irc.savings_account_id=sa.id "); this.schemaSql = sqlBuilder.toString(); @@ -243,12 +270,13 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D final String description = rs.getString("ircDescription"); final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "ircFromDate"); final LocalDate endDate = JdbcSupport.getLocalDate(rs, "ircEndDate"); + final boolean isPrimaryGroupingByAmount = rs.getBoolean("isPrimaryGroupingByAmount"); final Long accountId = rs.getLong("accountId"); final String accountNumber = rs.getString("accountNumber"); final Collection periodTypes = InterestRateChartEnumerations.periodType(PeriodFrequencyType.values()); - return DepositAccountInterestRateChartData.instance(id, name, description, fromDate, endDate, accountId, accountNumber, null, - periodTypes); + return DepositAccountInterestRateChartData.instance(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, + accountId, accountNumber, null, periodTypes); } } diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V293__interest_rate_chart_support_for_amounts.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V293__interest_rate_chart_support_for_amounts.sql new file mode 100644 index 000000000..ccd40285f --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V293__interest_rate_chart_support_for_amounts.sql @@ -0,0 +1,13 @@ +ALTER TABLE `m_interest_rate_slab` + CHANGE COLUMN `from_period` `from_period` INT(11) NULL DEFAULT NULL, + CHANGE COLUMN `period_type_enum` `period_type_enum` SMALLINT(5) NULL DEFAULT NULL ; + +ALTER TABLE `m_interest_rate_chart` + ADD COLUMN `is_primary_grouping_by_amount` TINYINT(1) NOT NULL DEFAULT '0'; + +ALTER TABLE `m_savings_account_interest_rate_chart` + ADD COLUMN `is_primary_grouping_by_amount` TINYINT NOT NULL DEFAULT '0' ; + +ALTER TABLE `m_savings_account_interest_rate_slab` + CHANGE COLUMN `period_type_enum` `period_type_enum` SMALLINT(5) NULL DEFAULT NULL, + CHANGE COLUMN `from_period` `from_period` INT(11) NULL DEFAULT NULL; \ No newline at end of file