MIFOSX-1918 : added Pre-closure interest calculation strategy

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