Merge pull request #1334 from pramodn02/develop

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