Merge pull request #1571 from pramodn02/variableinstalmentchanges
MIFOSX-579:variable installment feature
This commit is contained in:
@@ -1245,6 +1245,30 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#loan_term_variations">Loan Term Variations</a></td>
|
||||
<td>/loans/{loanId}/schedule?command=calculateLoanSchedule</td>
|
||||
<td><a href="#loans_calculate_with_exceptions">Calculate Schedule with Loan Term Variations</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>/loans/{loanId}/schedule?command=addVariations</td>
|
||||
<td><a href="#loans_update_variations">Create Loan Term Variations</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>/loans/{loanId}/schedule?command=deleteVariations</td>
|
||||
<td><a href="#loans_delete_variations">Remove All Loan Term Variations</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -11765,6 +11789,603 @@ Request Body:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- start Loan term variations -->
|
||||
<a id="loan_term_variations" name="loan_term_variations" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h3>Loan Rescheduling</h3>
|
||||
<p>Loan Term Variations provides the ability to change due dates, amounts and number of instalments before loan approval.</p>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>loanId</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>The ID of the loan to be modified.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>exceptions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>contains exception data for variations(modifiedinstallments,newinstallments and deletedinstallments described below) </td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<p>Exception object fields</p>
|
||||
<p>Modifiedinstallments Array fields</p>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>dueDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Schedule dueDate for which this exception should be applied.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>modifiedDueDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc> this is the exception data to move the due date to specified date for a instalment</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>installmentAmount</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc> new instalment amount(principal+interest) for the specified instalment. supported only for interest method declining with equal instalments Amortization</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>principal</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc> new Principal amount for the specified instalment. supported only for interest method declining with equal instalments Amortization and for Flat loans.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>newinstallments Array fields</p>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>dueDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Schedule dueDate for inserting new instalment.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>installmentAmount</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc> new instalment amount(principal+interest) for the new instalment. supported only for interest method declining with equal instalments Amortization</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>principal</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc> new Principal amount for the new instalment. supported only for interest method declining with equal instalments Amortization and for Flat loans.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>deletedinstallments Array fields</p>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>dueDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Schedule dueDate for removing specific instalment.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<a id="loans_calculate_with_exceptions" name="loans_calculate_with_exceptions" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Calculate loan repayment schedule based on Loan term variations</h4>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>exceptions,locale,dateFormat</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
POST https://DomainName/api/v1/loans/1/schedule?command=calculateLoanSchedule
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST loans/{loanIdd}/schedule?command=calculateLoanSchedule
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"locale": "en",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"exceptions": {
|
||||
"modifiedinstallments": [{
|
||||
"dueDate": '01 April 2016',
|
||||
"modifiedDueDate": '05 April 2016'
|
||||
},
|
||||
{
|
||||
"dueDate": '05 January 2016',
|
||||
"modifiedDueDate": '01 January 2016'
|
||||
}],
|
||||
"deletedinstallments": [{
|
||||
"dueDate": "01 October 2016"
|
||||
}],
|
||||
newinstallments: [{
|
||||
dueDate: '10 October 2016',
|
||||
installmentAmount: '1000'
|
||||
}]
|
||||
}
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"loanTermInDays": 12,
|
||||
"totalPrincipalDisbursed": 10000,
|
||||
"totalPrincipalExpected": 10000,
|
||||
"totalInterestCharged": 1340.98,
|
||||
"totalFeeChargesCharged": 0,
|
||||
"totalPenaltyChargesCharged": 0,
|
||||
"totalRepaymentExpected": 11340.98,
|
||||
"periods": [{
|
||||
"period": 1,
|
||||
"fromDate": [2015,
|
||||
11,
|
||||
1],
|
||||
"dueDate": [2015,
|
||||
12,
|
||||
1],
|
||||
"daysInPeriod": 30,
|
||||
"principalOriginalDue": 745.6,
|
||||
"principalDue": 745.6,
|
||||
"principalOutstanding": 745.6,
|
||||
"principalLoanBalanceOutstanding": 9254.4,
|
||||
"interestOriginalDue": 200,
|
||||
"interestDue": 200,
|
||||
"interestOutstanding": 200,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 945.6,
|
||||
"totalDueForPeriod": 945.6,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 945.6,
|
||||
"totalOverdue": 945.6,
|
||||
"totalActualCostOfLoanForPeriod": 200
|
||||
},
|
||||
{
|
||||
"period": 2,
|
||||
"fromDate": [2015,
|
||||
12,
|
||||
1],
|
||||
"dueDate": [2016,
|
||||
1,
|
||||
1],
|
||||
"daysInPeriod": 31,
|
||||
"principalOriginalDue": 814.91,
|
||||
"principalDue": 814.91,
|
||||
"principalOutstanding": 814.91,
|
||||
"principalLoanBalanceOutstanding": 8439.49,
|
||||
"interestOriginalDue": 185.09,
|
||||
"interestDue": 185.09,
|
||||
"interestOutstanding": 185.09,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 1000,
|
||||
"totalDueForPeriod": 1000,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 1000,
|
||||
"totalActualCostOfLoanForPeriod": 185.09
|
||||
},
|
||||
{
|
||||
"period": 3,
|
||||
"fromDate": [2016,
|
||||
1,
|
||||
1],
|
||||
"dueDate": [2016,
|
||||
2,
|
||||
1],
|
||||
"daysInPeriod": 31,
|
||||
"principalOriginalDue": 770.75,
|
||||
"principalDue": 770.75,
|
||||
"principalOutstanding": 770.75,
|
||||
"principalLoanBalanceOutstanding": 7668.74,
|
||||
"interestOriginalDue": 168.79,
|
||||
"interestDue": 168.79,
|
||||
"interestOutstanding": 168.79,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.54,
|
||||
"totalDueForPeriod": 939.54,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.54,
|
||||
"totalActualCostOfLoanForPeriod": 168.79
|
||||
},
|
||||
{
|
||||
"period": 4,
|
||||
"fromDate": [2016,
|
||||
2,
|
||||
1],
|
||||
"dueDate": [2016,
|
||||
3,
|
||||
5],
|
||||
"daysInPeriod": 33,
|
||||
"principalOriginalDue": 786.17,
|
||||
"principalDue": 786.17,
|
||||
"principalOutstanding": 786.17,
|
||||
"principalLoanBalanceOutstanding": 6882.57,
|
||||
"interestOriginalDue": 153.37,
|
||||
"interestDue": 153.37,
|
||||
"interestOutstanding": 153.37,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.54,
|
||||
"totalDueForPeriod": 939.54,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.54,
|
||||
"totalActualCostOfLoanForPeriod": 153.37
|
||||
},
|
||||
{
|
||||
"period": 5,
|
||||
"fromDate": [2016,
|
||||
3,
|
||||
5],
|
||||
"dueDate": [2016,
|
||||
4,
|
||||
5],
|
||||
"daysInPeriod": 31,
|
||||
"principalOriginalDue": 801.89,
|
||||
"principalDue": 801.89,
|
||||
"principalOutstanding": 801.89,
|
||||
"principalLoanBalanceOutstanding": 6080.68,
|
||||
"interestOriginalDue": 137.65,
|
||||
"interestDue": 137.65,
|
||||
"interestOutstanding": 137.65,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.54,
|
||||
"totalDueForPeriod": 939.54,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.54,
|
||||
"totalActualCostOfLoanForPeriod": 137.65
|
||||
},
|
||||
{
|
||||
"period": 6,
|
||||
"fromDate": [2016,
|
||||
4,
|
||||
5],
|
||||
"dueDate": [2016,
|
||||
5,
|
||||
1],
|
||||
"daysInPeriod": 26,
|
||||
"principalOriginalDue": 817.93,
|
||||
"principalDue": 817.93,
|
||||
"principalOutstanding": 817.93,
|
||||
"principalLoanBalanceOutstanding": 5262.75,
|
||||
"interestOriginalDue": 121.61,
|
||||
"interestDue": 121.61,
|
||||
"interestOutstanding": 121.61,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.54,
|
||||
"totalDueForPeriod": 939.54,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.54,
|
||||
"totalActualCostOfLoanForPeriod": 121.61
|
||||
},
|
||||
{
|
||||
"period": 7,
|
||||
"fromDate": [2016,
|
||||
5,
|
||||
1],
|
||||
"dueDate": [2016,
|
||||
6,
|
||||
1],
|
||||
"daysInPeriod": 31,
|
||||
"principalOriginalDue": 834.28,
|
||||
"principalDue": 834.28,
|
||||
"principalOutstanding": 834.28,
|
||||
"principalLoanBalanceOutstanding": 4428.47,
|
||||
"interestOriginalDue": 105.26,
|
||||
"interestDue": 105.26,
|
||||
"interestOutstanding": 105.26,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.54,
|
||||
"totalDueForPeriod": 939.54,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.54,
|
||||
"totalActualCostOfLoanForPeriod": 105.26
|
||||
},
|
||||
{
|
||||
"period": 8,
|
||||
"fromDate": [2016,
|
||||
6,
|
||||
1],
|
||||
"dueDate": [2016,
|
||||
7,
|
||||
1],
|
||||
"daysInPeriod": 30,
|
||||
"principalOriginalDue": 850.97,
|
||||
"principalDue": 850.97,
|
||||
"principalOutstanding": 850.97,
|
||||
"principalLoanBalanceOutstanding": 3577.5,
|
||||
"interestOriginalDue": 88.57,
|
||||
"interestDue": 88.57,
|
||||
"interestOutstanding": 88.57,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.54,
|
||||
"totalDueForPeriod": 939.54,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.54,
|
||||
"totalActualCostOfLoanForPeriod": 88.57
|
||||
},
|
||||
{
|
||||
"period": 9,
|
||||
"fromDate": [2016,
|
||||
7,
|
||||
1],
|
||||
"dueDate": [2016,
|
||||
8,
|
||||
1],
|
||||
"daysInPeriod": 31,
|
||||
"principalOriginalDue": 867.99,
|
||||
"principalDue": 867.99,
|
||||
"principalOutstanding": 867.99,
|
||||
"principalLoanBalanceOutstanding": 2709.51,
|
||||
"interestOriginalDue": 71.55,
|
||||
"interestDue": 71.55,
|
||||
"interestOutstanding": 71.55,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.54,
|
||||
"totalDueForPeriod": 939.54,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.54,
|
||||
"totalActualCostOfLoanForPeriod": 71.55
|
||||
},
|
||||
{
|
||||
"period": 10,
|
||||
"fromDate": [2016,
|
||||
8,
|
||||
1],
|
||||
"dueDate": [2016,
|
||||
9,
|
||||
1],
|
||||
"daysInPeriod": 31,
|
||||
"principalOriginalDue": 885.35,
|
||||
"principalDue": 885.35,
|
||||
"principalOutstanding": 885.35,
|
||||
"principalLoanBalanceOutstanding": 1824.16,
|
||||
"interestOriginalDue": 54.19,
|
||||
"interestDue": 54.19,
|
||||
"interestOutstanding": 54.19,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.54,
|
||||
"totalDueForPeriod": 939.54,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.54,
|
||||
"totalActualCostOfLoanForPeriod": 54.19
|
||||
},
|
||||
{
|
||||
"period": 11,
|
||||
"fromDate": [2016,
|
||||
9,
|
||||
1],
|
||||
"dueDate": [2016,
|
||||
10,
|
||||
10],
|
||||
"daysInPeriod": 39,
|
||||
"principalOriginalDue": 903.05,
|
||||
"principalDue": 903.05,
|
||||
"principalOutstanding": 903.05,
|
||||
"principalLoanBalanceOutstanding": 921.11,
|
||||
"interestOriginalDue": 36.48,
|
||||
"interestDue": 36.48,
|
||||
"interestOutstanding": 36.48,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.53,
|
||||
"totalDueForPeriod": 939.53,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.53,
|
||||
"totalActualCostOfLoanForPeriod": 36.48
|
||||
},
|
||||
{
|
||||
"period": 12,
|
||||
"fromDate": [2016,
|
||||
10,
|
||||
10],
|
||||
"dueDate": [2016,
|
||||
11,
|
||||
1],
|
||||
"daysInPeriod": 22,
|
||||
"principalOriginalDue": 921.11,
|
||||
"principalDue": 921.11,
|
||||
"principalOutstanding": 921.11,
|
||||
"principalLoanBalanceOutstanding": 0,
|
||||
"interestOriginalDue": 18.42,
|
||||
"interestDue": 18.42,
|
||||
"interestOutstanding": 18.42,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 939.53,
|
||||
"totalDueForPeriod": 939.53,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 939.53,
|
||||
"totalActualCostOfLoanForPeriod": 18.42
|
||||
}]
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="loans_update_variations" name="loans_update_variations" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Updates loan repayment schedule based on Loan term variations</h4>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>exceptions,locale,dateFormat</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
POST https://DomainName/api/v1/loans/1/schedule?command=addVariations
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST loans/{loanIdd}/schedule?command=addVariations
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"locale": "en",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"exceptions": {
|
||||
"modifiedinstallments": [{
|
||||
"dueDate": '01 April 2016',
|
||||
"modifiedDueDate": '05 April 2016'
|
||||
},
|
||||
{
|
||||
"dueDate": '05 January 2016',
|
||||
"modifiedDueDate": '01 January 2016'
|
||||
}],
|
||||
"deletedinstallments": [{
|
||||
"dueDate": "01 October 2016"
|
||||
}],
|
||||
newinstallments: [{
|
||||
dueDate: '10 October 2016',
|
||||
installmentAmount: '1000'
|
||||
}]
|
||||
}
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"loanId": 1,
|
||||
"changes": {
|
||||
"loanTermVariations": [{
|
||||
"id": 21,
|
||||
"termType": {
|
||||
"id": 4,
|
||||
"code": "loanTermType.dueDate",
|
||||
"value": "dueDate"
|
||||
},
|
||||
"termVariationApplicableFrom": [2016,
|
||||
1,
|
||||
1],
|
||||
"dateValue": [2016,
|
||||
1,
|
||||
10],
|
||||
"isSpecificToInstallment": true
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"termType": {
|
||||
"id": 3,
|
||||
"code": "loanTermType.principalAmount",
|
||||
"value": "principalAmount"
|
||||
},
|
||||
"termVariationApplicableFrom": [2016,
|
||||
1,
|
||||
10],
|
||||
"decimalValue": 1100,
|
||||
"isSpecificToInstallment": true
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"termType": {
|
||||
"id": 3,
|
||||
"code": "loanTermType.principalAmount",
|
||||
"value": "principalAmount"
|
||||
},
|
||||
"termVariationApplicableFrom": [2016,
|
||||
8,
|
||||
1],
|
||||
"decimalValue": 1000,
|
||||
"isSpecificToInstallment": true
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"termType": {
|
||||
"id": 5,
|
||||
"code": "loanTermType.dueDate",
|
||||
"value": "insertInstallment"
|
||||
},
|
||||
"termVariationApplicableFrom": [2016,
|
||||
8,
|
||||
15],
|
||||
"decimalValue": 900,
|
||||
"isSpecificToInstallment": true
|
||||
}]
|
||||
}
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<a id="loans_delete_variations" name="loans_delete_variations" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Updates loan repayment schedule by removing Loan term variations</h4>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
POST https://DomainName/api/v1/loans/1/schedule?command=deleteVariations
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST loans/{loanIdd}/schedule?command=deleteVariations
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"loanId": 1,
|
||||
"changes": {
|
||||
"removedEntityIds": [{
|
||||
21,22}]
|
||||
}
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- End Loan Term variations-->
|
||||
<!-- start of offices content -->
|
||||
<a id="offices" name="offices" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
@@ -40862,6 +41483,30 @@ No Request Body:
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#loan_term_variations">Loan Term Variations</a></td>
|
||||
<td>/loans/{loanId}/schedule?command=calculateLoanSchedule</td>
|
||||
<td><a href="#loans_calculate_with_exceptions">Calculate Schedule with Loan Term Variations</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>/loans/{loanId}/schedule?command=addVariations</td>
|
||||
<td><a href="#loans_update_variations">Create Loan Term Variations</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>/loans/{loanId}/schedule?command=deleteVariations</td>
|
||||
<td><a href="#loans_delete_variations">Remove All Loan Term Variations</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#glaccounts">General Ledger Account</a></td>
|
||||
<td>glaccounts</td>
|
||||
|
||||
+11
-11
@@ -3605,9 +3605,9 @@ public class ClientLoanIntegrationTest {
|
||||
List<Map<String, Object>> expectedvalues = new ArrayList<>();
|
||||
todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
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, "2486.03", "42.88", "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");
|
||||
addRepaymentValues(expectedvalues, todaysDate, 1, false, "2533.71", "19.93", "0.0", "0.0");
|
||||
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||
|
||||
System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------");
|
||||
@@ -3639,9 +3639,9 @@ public class ClientLoanIntegrationTest {
|
||||
expectedvalues = new ArrayList<>();
|
||||
todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
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, "2486.03", "42.88", "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");
|
||||
addRepaymentValues(expectedvalues, todaysDate, 1, false, "2533.71", "19.93", "0.0", "0.0");
|
||||
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||
|
||||
Float earlyPayment = new Float("5100");
|
||||
@@ -3654,7 +3654,7 @@ public class ClientLoanIntegrationTest {
|
||||
expectedvalues = new ArrayList<>();
|
||||
todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
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, "5100.0", "36.16", "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);
|
||||
@@ -3672,7 +3672,7 @@ 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.81";
|
||||
String preCloseAmount = "7766.82";
|
||||
testLoanScheduleWithInterestRecalculation_WITH_REST_WEEKLY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_REDUCE_NEXT_INSTALLMENTS_PRE_CLOSE_INTEREST(
|
||||
preCloseInterestStrategy, preCloseAmount);
|
||||
|
||||
@@ -3681,7 +3681,7 @@ public class ClientLoanIntegrationTest {
|
||||
@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.79";
|
||||
String preCloseAmount = "7771.8";
|
||||
testLoanScheduleWithInterestRecalculation_WITH_REST_WEEKLY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_REDUCE_NEXT_INSTALLMENTS_PRE_CLOSE_INTEREST(
|
||||
preCloseInterestStrategy, preCloseAmount);
|
||||
|
||||
@@ -3734,9 +3734,9 @@ public class ClientLoanIntegrationTest {
|
||||
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, "2489.3", "39.61", "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");
|
||||
addRepaymentValues(expectedvalues, todaysDate, 1, false, "2527.16", "16.62", "0.0", "0.0");
|
||||
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||
|
||||
System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------");
|
||||
@@ -3768,9 +3768,9 @@ public class ClientLoanIntegrationTest {
|
||||
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, "2489.3", "39.61", "0.0", "0.0");
|
||||
addRepaymentValues(expectedvalues, todaysDate, 1, false, "2500.7", "28.21", "200", "0.0");
|
||||
addRepaymentValues(expectedvalues, todaysDate, 1, false, "2527.23", "16.61", "0.0", "0.0");
|
||||
addRepaymentValues(expectedvalues, todaysDate, 1, false, "2527.24", "16.62", "0.0", "0.0");
|
||||
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||
|
||||
HashMap prepayDetail = this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, loanID);
|
||||
|
||||
+5
-1
@@ -19,6 +19,7 @@ import java.util.Locale;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mifosplatform.integrationtests.common.ClientHelper;
|
||||
import org.mifosplatform.integrationtests.common.GlobalConfigurationHelper;
|
||||
@@ -503,6 +504,9 @@ public class SchedulerJobsTestResults {
|
||||
|
||||
}
|
||||
|
||||
// Invalid test case as it won't affect summary (Loan summary is properly
|
||||
// updated before running this job)
|
||||
@Ignore
|
||||
@Test
|
||||
public void testUpdateLoanSummaryJobOutcome() throws InterruptedException {
|
||||
this.schedulerJobHelper = new SchedulerJobHelper(this.requestSpec, this.responseSpec);
|
||||
@@ -552,7 +556,7 @@ public class SchedulerJobsTestResults {
|
||||
String JobName = "Update loan Summary";
|
||||
this.schedulerJobHelper.executeJob(JobName);
|
||||
Float expectedSummaryAfterJob = (Float) loanSummaryBefore.get("totalExpectedRepayment")
|
||||
- (Float) loanSummaryBefore.get("feeChargesPaid");
|
||||
/* - (Float) loanSummaryBefore.get("feeChargesPaid")*/;
|
||||
HashMap loanSummaryAfter = this.loanTransactionHelper.getLoanSummary(this.requestSpec, this.responseSpec, loanID);
|
||||
Assert.assertEquals("Verifying Loan Summary after Running Update Loan Summary Scheduler Job", expectedSummaryAfterJob,
|
||||
(Float) loanSummaryAfter.get("totalExpectedRepayment"));
|
||||
|
||||
+34
-17
@@ -30,8 +30,8 @@ public class CommandWrapperBuilder {
|
||||
private Long templateId;
|
||||
|
||||
public CommandWrapper build() {
|
||||
return new CommandWrapper(this.officeId, this.groupId, this.clientId, this.loanId, this.savingsId, this.actionName, this.entityName,
|
||||
this.entityId, this.subentityId, this.href, this.json, this.transactionId, this.productId, this.templateId);
|
||||
return new CommandWrapper(this.officeId, this.groupId, this.clientId, this.loanId, this.savingsId, this.actionName,
|
||||
this.entityName, this.entityId, this.subentityId, this.href, this.json, this.transactionId, this.productId, this.templateId);
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder withLoanId(final Long withLoanId) {
|
||||
@@ -1337,7 +1337,7 @@ public class CommandWrapperBuilder {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "CALENDAR";
|
||||
this.entityId = calendarId;
|
||||
this.groupId = supportedEntityId;
|
||||
this.groupId = supportedEntityId;
|
||||
this.href = "/" + supportedEntityType + "/" + supportedEntityId + "/calendars/" + calendarId;
|
||||
return this;
|
||||
}
|
||||
@@ -1815,8 +1815,7 @@ public class CommandWrapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder saveOrUpdateAttendance(final Long entityId, final String supportedEntityType,
|
||||
final Long supportedEntityId) {
|
||||
public CommandWrapperBuilder saveOrUpdateAttendance(final Long entityId, final String supportedEntityType, final Long supportedEntityId) {
|
||||
this.actionName = "SAVEORUPDATEATTENDANCE";
|
||||
this.entityName = "MEETING";
|
||||
this.entityId = entityId;
|
||||
@@ -2457,7 +2456,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/clients/" + clientId + "/transactions/" + transactionId + "?command=undo";
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder createProvisioningCategory() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "PROVISIONCATEGORY";
|
||||
@@ -2465,7 +2464,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/provisioningcategory";
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder updateProvisioningCategory(final Long cateoryId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "PROVISIONCATEGORY";
|
||||
@@ -2473,7 +2472,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/provisioningcategory/" + cateoryId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder deleteProvisioningCategory(final Long categoryId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "PROVISIONCATEGORY";
|
||||
@@ -2481,7 +2480,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/provisioningcategory/" + categoryId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder createProvisioningCriteria() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "PROVISIONCRITERIA";
|
||||
@@ -2489,7 +2488,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/provisioningcriteria";
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder updateProvisioningCriteria(final Long criteriaId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "PROVISIONCRITERIA";
|
||||
@@ -2497,7 +2496,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/provisioningcriteria/" + criteriaId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder deleteProvisioningCriteria(final Long criteriaId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "PROVISIONCRITERIA";
|
||||
@@ -2505,7 +2504,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/provisioningcriteria/" + criteriaId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder createProvisioningEntries() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "PROVISIONENTRIES";
|
||||
@@ -2513,20 +2512,20 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/provisioningentries";
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder createProvisioningJournalEntries(final Long entryId) {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "PROVISIONJOURNALENTRIES";
|
||||
this.entityId = entryId;
|
||||
this.href = "/provisioningentries/"+entryId;
|
||||
this.href = "/provisioningentries/" + entryId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder reCreateProvisioningEntries(final Long entryId) {
|
||||
this.actionName = "RECREATE";
|
||||
this.entityName = "PROVISIONENTRIES";
|
||||
this.entityId = entryId;
|
||||
this.href = "/provisioningentries/"+entryId;
|
||||
this.href = "/provisioningentries/" + entryId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -2542,7 +2541,25 @@ public class CommandWrapperBuilder {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "FLOATINGRATE";
|
||||
this.entityId = floatingRateId;
|
||||
this.href = "/floatingrates/"+floatingRateId;
|
||||
this.href = "/floatingrates/" + floatingRateId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createScheduleExceptions(final Long loanId) {
|
||||
this.actionName = "CREATESCHEDULEEXCEPTIONS";
|
||||
this.entityName = "LOAN";
|
||||
this.entityId = loanId;
|
||||
this.loanId = loanId;
|
||||
this.href = "/loans/" + loanId + "/schedule";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteScheduleExceptions(final Long loanId) {
|
||||
this.actionName = "DELETESCHEDULEEXCEPTIONS";
|
||||
this.entityName = "LOAN";
|
||||
this.entityId = loanId;
|
||||
this.loanId = loanId;
|
||||
this.href = "/loans/" + loanId + "/schedule";
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+2
@@ -14,4 +14,6 @@ public interface CurrencyReadPlatformService {
|
||||
Collection<CurrencyData> retrieveAllowedCurrencies();
|
||||
|
||||
Collection<CurrencyData> retrieveAllPlatformCurrencies();
|
||||
|
||||
CurrencyData retrieveCurrency(String code);
|
||||
}
|
||||
+8
@@ -50,6 +50,14 @@ public class CurrencyReadPlatformServiceImpl implements CurrencyReadPlatformServ
|
||||
return this.jdbcTemplate.query(sql, this.currencyRowMapper, new Object[] {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrencyData retrieveCurrency(final String code) {
|
||||
|
||||
final String sql = "select " + this.currencyRowMapper.schema() + " from m_currency c where c.code = ? order by c.name";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, this.currencyRowMapper, new Object[] { code });
|
||||
}
|
||||
|
||||
private static final class CurrencyMapper implements RowMapper<CurrencyData> {
|
||||
|
||||
@Override
|
||||
|
||||
+44
-4
@@ -45,7 +45,42 @@ public class CalendarUtils {
|
||||
public static LocalDate getNextRecurringDate(final String recurringRule, final LocalDate seedDate, final LocalDate startDate) {
|
||||
final Recur recur = CalendarUtils.getICalRecur(recurringRule);
|
||||
if (recur == null) { return null; }
|
||||
return getNextRecurringDate(recur, seedDate, startDate);
|
||||
LocalDate nextDate = getNextRecurringDate(recur, seedDate, startDate);
|
||||
nextDate = adjustDate(nextDate, seedDate, getMeetingPeriodFrequencyType(recurringRule));
|
||||
return nextDate;
|
||||
}
|
||||
|
||||
public static LocalDate adjustDate(final LocalDate date, final LocalDate seedDate, final PeriodFrequencyType frequencyType) {
|
||||
LocalDate adjustedVal = date;
|
||||
if (frequencyType.isMonthly() && seedDate.getDayOfMonth() > 28) {
|
||||
switch (date.getMonthOfYear()) {
|
||||
case 2:
|
||||
if (date.year().isLeap()) {
|
||||
adjustedVal = date.dayOfMonth().setCopy(29);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
if (seedDate.getDayOfMonth() > 30) {
|
||||
adjustedVal = date.dayOfMonth().setCopy(30);
|
||||
} else {
|
||||
adjustedVal = date.dayOfMonth().setCopy(seedDate.getDayOfMonth());
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
case 10:
|
||||
case 12:
|
||||
adjustedVal = date.dayOfMonth().setCopy(seedDate.getDayOfMonth());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return adjustedVal;
|
||||
}
|
||||
|
||||
private static LocalDate getNextRecurringDate(final Recur recur, final LocalDate seedDate, final LocalDate startDate) {
|
||||
@@ -105,17 +140,18 @@ public class CalendarUtils {
|
||||
|
||||
final Value value = new Value(Value.DATE.getValue());
|
||||
final DateList recurringDates = recur.getDates(seed, periodStart, periodEnd, value, maxCount);
|
||||
return convertToLocalDateList(recurringDates);
|
||||
return convertToLocalDateList(recurringDates, seedDate, getMeetingPeriodFrequencyType(recur));
|
||||
}
|
||||
|
||||
private static Collection<LocalDate> convertToLocalDateList(final DateList dates) {
|
||||
private static Collection<LocalDate> convertToLocalDateList(final DateList dates, final LocalDate seedDate,
|
||||
final PeriodFrequencyType frequencyType) {
|
||||
|
||||
final Collection<LocalDate> recurringDates = new ArrayList<>();
|
||||
|
||||
for (@SuppressWarnings("rawtypes")
|
||||
final Iterator iterator = dates.iterator(); iterator.hasNext();) {
|
||||
final Date date = (Date) iterator.next();
|
||||
recurringDates.add(new LocalDate(date));
|
||||
recurringDates.add(adjustDate(new LocalDate(date), seedDate, frequencyType));
|
||||
}
|
||||
|
||||
return recurringDates;
|
||||
@@ -259,6 +295,10 @@ public class CalendarUtils {
|
||||
|
||||
public static PeriodFrequencyType getMeetingPeriodFrequencyType(final String recurringRule) {
|
||||
final Recur recur = CalendarUtils.getICalRecur(recurringRule);
|
||||
return getMeetingPeriodFrequencyType(recur);
|
||||
}
|
||||
|
||||
private static PeriodFrequencyType getMeetingPeriodFrequencyType(final Recur recur) {
|
||||
PeriodFrequencyType meetingFrequencyType = PeriodFrequencyType.INVALID;
|
||||
if (recur.getFrequency().equals(Recur.DAILY)) {
|
||||
meetingFrequencyType = PeriodFrequencyType.DAYS;
|
||||
|
||||
+23
-23
@@ -40,10 +40,10 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
private final Set<String> supportedParameters = new HashSet<>(
|
||||
Arrays.asList("name", "amount", "locale", "currencyCode", "currencyOptions", "chargeAppliesTo", "chargeTimeType",
|
||||
"chargeCalculationType", "chargeCalculationTypeOptions", "penalty", "active", "chargePaymentMode", "feeOnMonthDay",
|
||||
"feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency", ChargesApiConstants.glAccountIdParamName));
|
||||
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("name", "amount", "locale", "currencyCode",
|
||||
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty",
|
||||
"active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency",
|
||||
ChargesApiConstants.glAccountIdParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -104,9 +104,9 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
|
||||
.isOneOfTheseValues(ChargeCalculationType.validValuesForLoan());
|
||||
}
|
||||
|
||||
if(chargeTimeType != null && chargeCalculationType != null){
|
||||
performChargeTimeNCalculationTypeValidation(baseDataValidator, chargeTimeType, chargeCalculationType);
|
||||
|
||||
if (chargeTimeType != null && chargeCalculationType != null) {
|
||||
performChargeTimeNCalculationTypeValidation(baseDataValidator, chargeTimeType, chargeCalculationType);
|
||||
}
|
||||
|
||||
} else if (appliesTo.isSavingsCharge()) {
|
||||
@@ -122,8 +122,8 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
|
||||
if (ctt.isWeeklyFee()) {
|
||||
final String monthDay = this.fromApiJsonHelper.extractStringNamed("feeOnMonthDay", element);
|
||||
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).mustBeBlankWhenParameterProvidedIs("chargeTimeType",
|
||||
chargeTimeType);
|
||||
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay)
|
||||
.mustBeBlankWhenParameterProvidedIs("chargeTimeType", chargeTimeType);
|
||||
}
|
||||
|
||||
if (ctt.isMonthlyFee()) {
|
||||
@@ -305,26 +305,26 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateChargeTimeNCalculationType(Integer chargeTimeType, Integer ChargeCalculationType) {
|
||||
|
||||
public void validateChargeTimeNCalculationType(Integer chargeTimeType, Integer ChargeCalculationType) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
|
||||
performChargeTimeNCalculationTypeValidation(baseDataValidator, chargeTimeType, ChargeCalculationType);
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void performChargeTimeNCalculationTypeValidation(DataValidatorBuilder baseDataValidator,
|
||||
final Integer chargeTimeType, final Integer chargeCalculationType){
|
||||
if (chargeTimeType == ChargeTimeType.TRANCHE_DISBURSEMENT.getValue()){
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
|
||||
.isOneOfTheseValues(ChargeCalculationType.validValuesForTrancheDisbursement());
|
||||
} else {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
|
||||
.isNotOneOfTheseValues(ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
private void performChargeTimeNCalculationTypeValidation(DataValidatorBuilder baseDataValidator, final Integer chargeTimeType,
|
||||
final Integer chargeCalculationType) {
|
||||
if (chargeTimeType.equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue())) {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
|
||||
.isOneOfTheseValues(ChargeCalculationType.validValuesForTrancheDisbursement());
|
||||
} else {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
|
||||
.isNotOneOfTheseValues(ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -113,7 +113,7 @@ public class ChargeEnumerations {
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
optionData = new EnumOptionData(ChargeCalculationType.PERCENT_OF_AMOUNT.getValue().longValue(),
|
||||
ChargeCalculationType.PERCENT_OF_AMOUNT.getCode(), "% Approved Amount");
|
||||
ChargeCalculationType.PERCENT_OF_AMOUNT.getCode(), "% Amount");
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
optionData = new EnumOptionData(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST.getValue().longValue(),
|
||||
|
||||
+17
-8
@@ -17,8 +17,8 @@ public interface LoanApiConstants {
|
||||
public static final String disbursementIdParameterName = "id";
|
||||
public static final String loanChargeIdParameterName = "loanChargeId";
|
||||
public static final String principalDisbursedParameterName = "transactionAmount";
|
||||
public static final String chargesParameterName = "charges" ;
|
||||
|
||||
public static final String chargesParameterName = "charges";
|
||||
|
||||
public static final String approvedLoanAmountParameterName = "approvedLoanAmount";
|
||||
public static final String approvedOnDateParameterName = "approvedOnDate";
|
||||
public static final String noteParameterName = "note";
|
||||
@@ -33,14 +33,14 @@ public interface LoanApiConstants {
|
||||
public static final String daysInMonthTypeParameterName = "daysInMonthType";
|
||||
public static final String interestRecalculationCompoundingMethodParameterName = "interestRecalculationCompoundingMethod";
|
||||
public static final String rescheduleStrategyMethodParameterName = "rescheduleStrategyMethod";
|
||||
|
||||
|
||||
// Floating interest rate related
|
||||
public static final String interestRateDifferentialParameterName = "interestRateDifferential";
|
||||
public static final String isFloatingInterestRateParameterName = "isFloatingInterestRate";
|
||||
|
||||
|
||||
// Error codes
|
||||
public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE= "loancharge.with.calculation.type.interest.not.allowed";
|
||||
public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE= "loancharge.with.calculation.type.principal.not.allowed";
|
||||
public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE = "loancharge.with.calculation.type.interest.not.allowed";
|
||||
public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE = "loancharge.with.calculation.type.principal.not.allowed";
|
||||
public static final String DISBURSEMENT_DATE_START_WITH_ERROR = "first.disbursement.date.must.start.with.expected.disbursement.date";
|
||||
public static final String PRINCIPAL_AMOUNT_SHOULD_BE_SAME = "sum.of.multi.disburse.amounts.must.equal.with.total.principal";
|
||||
public static final String DISBURSEMENT_DATE_UNIQUE_ERROR = "disbursement.date.must.be.unique.for.tranches";
|
||||
@@ -48,8 +48,17 @@ public interface LoanApiConstants {
|
||||
public static final String APPROVED_AMOUNT_IS_LESS_THAN_SUM_OF_TRANCHES = "sum.of.multi.disburse.amounts.must.be.equal.to.or.lesser.than.approved.principal";
|
||||
public static final String DISBURSEMENT_DATES_NOT_IN_ORDER = "disbursements.should.be.ordered.based.on.their.disbursement.dates";
|
||||
public static final String DISBURSEMENT_DATE_BEFORE_ERROR = "disbursement.date.of.tranche.cannot.be.before.expected.disbursement.date";
|
||||
|
||||
|
||||
public static final String isFloatingInterestRate = "isFloatingInterestRate";
|
||||
public static final String interestRateDifferential = "interestRateDifferential";
|
||||
public static final String interestRateDifferential = "interestRateDifferential";
|
||||
|
||||
public static final String exceptionParamName = "exceptions";
|
||||
public static final String modifiedinstallmentsParamName = "modifiedinstallments";
|
||||
public static final String newinstallmentsParamName = "newinstallments";
|
||||
public static final String deletedinstallmentsParamName = "deletedinstallments";
|
||||
public static final String dueDateParamName = "dueDate";
|
||||
public static final String modifiedDueDateParamName = "modifiedDueDate";
|
||||
public static final String principalParamName = "principal";
|
||||
public static final String installmentAmountParamName = "installmentAmount";
|
||||
|
||||
}
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* 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.loanaccount.api;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.commands.domain.CommandWrapper;
|
||||
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
||||
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/loans/{loanId}/schedule")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class LoanScheduleApiResource {
|
||||
|
||||
private final String resourceNameForPermissions = "LOAN";
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<LoanScheduleData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final LoanScheduleCalculationPlatformService calculationPlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleApiResource(final PlatformSecurityContext context,
|
||||
final DefaultToApiJsonSerializer<LoanScheduleData> toApiJsonSerializer,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper) {
|
||||
this.context = context;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
this.calculationPlatformService = calculationPlatformService;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String calculateLoanScheduleOrSubmitVariableSchedule(@PathParam("loanId") final Long loanId,
|
||||
@QueryParam("command") final String commandParam, @Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
|
||||
|
||||
CommandWrapper commandRequest = null;
|
||||
if (is(commandParam, "calculateLoanSchedule")) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.generateLoanScheduleForVariableInstallmentRequest(loanId,
|
||||
apiRequestBodyAsJson);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, loanSchedule, new HashSet<String>());
|
||||
} else if (is(commandParam, "addVariations")) {
|
||||
commandRequest = new CommandWrapperBuilder().createScheduleExceptions(loanId).withJson(apiRequestBodyAsJson).build();
|
||||
} else if (is(commandParam, "deleteVariations")) {
|
||||
commandRequest = new CommandWrapperBuilder().deleteScheduleExceptions(loanId).build();
|
||||
}
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
|
||||
}
|
||||
+263
-223
@@ -186,9 +186,14 @@ public class LoanAccountData {
|
||||
|
||||
// Paid In Advance
|
||||
private final PaidInAdvanceData paidInAdvance;
|
||||
|
||||
|
||||
private final Collection<InterestRatePeriodData> interestRatesPeriods;
|
||||
|
||||
// VariableInstallments
|
||||
private final Boolean isVariableInstallmentsAllowed;
|
||||
private final Integer minimumGap;
|
||||
private final Integer maximumGap;
|
||||
|
||||
/**
|
||||
* Used to produce a {@link LoanAccountData} with only collateral options
|
||||
* for now.
|
||||
@@ -293,14 +298,16 @@ public class LoanAccountData {
|
||||
final Boolean createStandingInstructionAtDisbursement = null;
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
|
||||
final Integer minimumGap = null;
|
||||
final Integer maximumGap = null;
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency,
|
||||
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType,
|
||||
repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, isFloatingInterestRate,
|
||||
interestRateDifferential, interestCalculationPeriodType,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId,
|
||||
loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal, totalOverpaid,
|
||||
inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType,
|
||||
repaymentFrequencyNthDayType, repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId,
|
||||
transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate,
|
||||
interestType, isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
|
||||
collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
@@ -311,7 +318,8 @@ public class LoanAccountData {
|
||||
disbursementData, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, maxOutstandingLoanBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap);
|
||||
|
||||
}
|
||||
|
||||
@@ -420,13 +428,17 @@ public class LoanAccountData {
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
|
||||
final Integer minimumGap = null;
|
||||
final Integer maximumGap = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency,
|
||||
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType,
|
||||
repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
|
||||
isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId,
|
||||
loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal, totalOverpaid,
|
||||
inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType,
|
||||
repaymentFrequencyNthDayType, repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId,
|
||||
transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate,
|
||||
interestType, isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
|
||||
collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
@@ -437,34 +449,36 @@ public class LoanAccountData {
|
||||
disbursementData, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, maxOutstandingLoanBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap);
|
||||
|
||||
}
|
||||
|
||||
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, clientAcc.clientId, clientAcc.clientName,
|
||||
clientAcc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate,
|
||||
acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency,
|
||||
acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
clientAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions, acc.repaymentFrequencyDaysOfWeekTypeOptions,
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
clientAcc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
|
||||
acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId,
|
||||
acc.loanOfficerName, acc.currency, acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid,
|
||||
acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
|
||||
acc.repaymentFrequencyType, acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType,
|
||||
acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType,
|
||||
acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
|
||||
acc.isFloatingInterestRate, acc.interestRateDifferential, acc.interestCalculationPeriodType,
|
||||
acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
|
||||
acc.interestChargedFromDate, clientAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule,
|
||||
acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions,
|
||||
acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions,
|
||||
acc.repaymentFrequencyDaysOfWeekTypeOptions, acc.transactionProcessingStrategyOptions,
|
||||
acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
|
||||
acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
|
||||
acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter,
|
||||
acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails,
|
||||
acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -573,13 +587,17 @@ public class LoanAccountData {
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
|
||||
final Integer minimumGap = null;
|
||||
final Integer maximumGap = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency,
|
||||
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType,
|
||||
repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
|
||||
isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId,
|
||||
loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal, totalOverpaid,
|
||||
inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType,
|
||||
repaymentFrequencyNthDayType, repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId,
|
||||
transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate,
|
||||
interestType, isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
|
||||
collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
@@ -590,34 +608,36 @@ public class LoanAccountData {
|
||||
disbursementData, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, maxOutstandingBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap);
|
||||
|
||||
}
|
||||
|
||||
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
groupAcc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
groupAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions, acc.repaymentFrequencyDaysOfWeekTypeOptions,
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
groupAcc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
|
||||
acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId,
|
||||
acc.loanOfficerName, acc.currency, acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid,
|
||||
acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
|
||||
acc.repaymentFrequencyType, acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType,
|
||||
acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType,
|
||||
acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
|
||||
acc.isFloatingInterestRate, acc.interestRateDifferential, acc.interestCalculationPeriodType,
|
||||
acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
|
||||
acc.interestChargedFromDate, groupAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule,
|
||||
acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions,
|
||||
acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions,
|
||||
acc.repaymentFrequencyDaysOfWeekTypeOptions, acc.transactionProcessingStrategyOptions,
|
||||
acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
|
||||
acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
|
||||
acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter,
|
||||
acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails,
|
||||
acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap);
|
||||
|
||||
}
|
||||
|
||||
@@ -737,14 +757,19 @@ public class LoanAccountData {
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
|
||||
final Integer minimumGap = null;
|
||||
final Integer maximumGap = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType,
|
||||
product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(), product.getFundId(), product.getFundName(), loanPurposeId,
|
||||
loanPurposeName, loanOfficerId, loanOfficerName, product.getCurrency(), proposedPrincipal, principal, principal,
|
||||
totalOverpaid, product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, numberOfRepayments,
|
||||
product.getRepaymentEvery(), product.getRepaymentFrequencyType(), null, null, product.getTransactionProcessingStrategyId(),
|
||||
product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(),
|
||||
product.getFundId(), product.getFundName(), loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
product.getCurrency(), proposedPrincipal, principal, principal, totalOverpaid, product.getInArrearsTolerance(),
|
||||
termFrequency, termPeriodFrequencyType, numberOfRepayments, product.getRepaymentEvery(),
|
||||
product.getRepaymentFrequencyType(), null, null, product.getTransactionProcessingStrategyId(),
|
||||
transactionProcessingStrategyName, product.getAmortizationType(), interestRatePerPeriod,
|
||||
product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(),
|
||||
product.isFloatingInterestRateCalculationAllowed(), product.getDefaultDifferentialLendingRate(),
|
||||
product.isFloatingInterestRateCalculationAllowed(), product.getDefaultDifferentialLendingRate(),
|
||||
product.getInterestCalculationPeriodType(), expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(),
|
||||
product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), interestChargedFromDate, timeline, summary,
|
||||
feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
|
||||
@@ -757,7 +782,9 @@ public class LoanAccountData {
|
||||
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
|
||||
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(),
|
||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
||||
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
|
||||
product.getMaximumGapBetweenInstallments());
|
||||
}
|
||||
|
||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||
@@ -794,27 +821,29 @@ public class LoanAccountData {
|
||||
}
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(), product.getFundId(),
|
||||
product.getFundName(), acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName,
|
||||
product.getCurrency(), product.getPrincipal(), product.getPrincipal(), product.getPrincipal(), acc.totalOverpaid,
|
||||
product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(),
|
||||
product.getRepaymentEvery(), product.getRepaymentFrequencyType(), null, null, product.getTransactionProcessingStrategyId(),
|
||||
product.getTransactionProcessingStrategyName(), product.getAmortizationType(), product.getInterestRatePerPeriod(),
|
||||
product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(),
|
||||
product.isFloatingInterestRateCalculationAllowed(), product.getDefaultDifferentialLendingRate(),
|
||||
product.getInterestCalculationPeriodType(), acc.expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(),
|
||||
product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), acc.interestChargedFromDate, acc.timeline,
|
||||
acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors,
|
||||
calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, null, null,
|
||||
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
|
||||
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
|
||||
loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,
|
||||
acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(),
|
||||
product.canDefineInstallmentAmount(), acc.fixedEmiAmount, product.getOutstandingLoanBalance(), acc.emiAmountVariations,
|
||||
acc.memberVariations, product, acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA,
|
||||
product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
|
||||
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement,
|
||||
paidInAdvance, acc.interestRatesPeriods);
|
||||
acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(),
|
||||
product.isLinkedToFloatingInterestRates(), product.getFundId(), product.getFundName(), acc.loanPurposeId,
|
||||
acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, product.getCurrency(), product.getPrincipal(),
|
||||
product.getPrincipal(), product.getPrincipal(), acc.totalOverpaid, product.getInArrearsTolerance(), termFrequency,
|
||||
termPeriodFrequencyType, product.getNumberOfRepayments(), product.getRepaymentEvery(), product.getRepaymentFrequencyType(),
|
||||
null, null, product.getTransactionProcessingStrategyId(), product.getTransactionProcessingStrategyName(),
|
||||
product.getAmortizationType(), product.getInterestRatePerPeriod(), product.getInterestRateFrequencyType(),
|
||||
product.getAnnualInterestRate(), product.getInterestType(), product.isFloatingInterestRateCalculationAllowed(),
|
||||
product.getDefaultDifferentialLendingRate(), product.getInterestCalculationPeriodType(), acc.expectedFirstRepaymentOnDate,
|
||||
product.getGraceOnPrincipalPayment(), product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(),
|
||||
acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule,
|
||||
transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions,
|
||||
repaymentFrequencyTypeOptions, null, null, repaymentStrategyOptions, interestRateFrequencyTypeOptions,
|
||||
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
|
||||
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), product.canDefineInstallmentAmount(),
|
||||
acc.fixedEmiAmount, product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product,
|
||||
acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, product.getDaysInMonthType(),
|
||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
|
||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, paidInAdvance, acc.interestRatesPeriods,
|
||||
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
|
||||
product.getMaximumGapBetweenInstallments());
|
||||
|
||||
}
|
||||
|
||||
@@ -825,15 +854,15 @@ public class LoanAccountData {
|
||||
public static LoanAccountData basicLoanDetails(final Long id, final String accountNo, final LoanStatusEnumData status,
|
||||
final String externalId, final Long clientId, final String clientName, final Long clientOfficeId, final GroupGeneralData group,
|
||||
final EnumOptionData loanType, final Long loanProductId, final String loanProductName, final String loanProductDescription,
|
||||
final boolean isLoanProductLinkedToFloatingRate,
|
||||
final Long fundId, final String fundName, final Long loanPurposeId, final String loanPurposeName, final Long loanOfficerId,
|
||||
final String loanOfficerName, final CurrencyData currencyData, final BigDecimal proposedPrincipal, final BigDecimal principal,
|
||||
final BigDecimal approvedPrincipal, final BigDecimal totalOverpaid, final BigDecimal inArrearsTolerance,
|
||||
final Integer termFrequency, final EnumOptionData termPeriodFrequencyType, final Integer numberOfRepayments,
|
||||
final Integer repaymentEvery, final EnumOptionData repaymentFrequencyType, EnumOptionData repaymentFrequencyNthDayType,
|
||||
final boolean isLoanProductLinkedToFloatingRate, final Long fundId, final String fundName, final Long loanPurposeId,
|
||||
final String loanPurposeName, final Long loanOfficerId, final String loanOfficerName, final CurrencyData currencyData,
|
||||
final BigDecimal proposedPrincipal, final BigDecimal principal, final BigDecimal approvedPrincipal,
|
||||
final BigDecimal totalOverpaid, final BigDecimal inArrearsTolerance, final Integer termFrequency,
|
||||
final EnumOptionData termPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
final EnumOptionData repaymentFrequencyType, EnumOptionData repaymentFrequencyNthDayType,
|
||||
EnumOptionData repaymentFrequencyDayOfWeekType, final Long transactionStrategyId, final String transactionStrategyName,
|
||||
final EnumOptionData amortizationType, final BigDecimal interestRatePerPeriod, final EnumOptionData interestRateFrequencyType,
|
||||
final BigDecimal annualInterestRate, final EnumOptionData interestType, final boolean isFloatingInterestRate,
|
||||
final BigDecimal annualInterestRate, final EnumOptionData interestType, final boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final EnumOptionData interestCalculationPeriodType,
|
||||
final LocalDate expectedFirstRepaymentOnDate, final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment,
|
||||
final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline,
|
||||
@@ -842,7 +871,8 @@ public class LoanAccountData {
|
||||
final Boolean multiDisburseLoan, Boolean canDefineInstallmentAmount, final BigDecimal fixedEmiAmont,
|
||||
final BigDecimal outstandingLoanBalance, final Boolean inArrears, final Integer graceOnArrearsAgeing, final Boolean isNPA,
|
||||
final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement) {
|
||||
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap) {
|
||||
|
||||
final LoanScheduleData repaymentSchedule = null;
|
||||
final Collection<LoanTransactionData> transactions = null;
|
||||
@@ -880,23 +910,24 @@ public class LoanAccountData {
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, proposedPrincipal, principal, approvedPrincipal, totalOverpaid, inArrearsTolerance, termFrequency,
|
||||
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType,
|
||||
repaymentFrequencyDayOfWeekType, transactionStrategyId, transactionStrategyName, amortizationType, interestRatePerPeriod,
|
||||
interestRateFrequencyType, annualInterestRate, interestType, isFloatingInterestRate, interestRateDifferential,
|
||||
interestCalculationPeriodType, expectedFirstRepaymentOnDate,
|
||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary,
|
||||
feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
|
||||
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentFrequencyNthDayTypeOptions,
|
||||
repaymentFrequencyDayOfWeekTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions,
|
||||
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
|
||||
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId,
|
||||
loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, approvedPrincipal,
|
||||
totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery,
|
||||
repaymentFrequencyType, repaymentFrequencyNthDayType, repaymentFrequencyDayOfWeekType, transactionStrategyId,
|
||||
transactionStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate,
|
||||
interestType, isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions,
|
||||
charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDayOfWeekTypeOptions, repaymentStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||
disbursementData, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmont, outstandingLoanBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -924,28 +955,28 @@ public class LoanAccountData {
|
||||
if (acc.product != null) {
|
||||
loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes();
|
||||
}
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral,
|
||||
guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDayOfWeekTypeOptions, transactionProcessingStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||
disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
|
||||
acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId,
|
||||
acc.loanOfficerName, acc.currency, acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid,
|
||||
acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
|
||||
acc.repaymentFrequencyType, acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType,
|
||||
acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType,
|
||||
acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
|
||||
acc.isFloatingInterestRate, acc.interestRateDifferential, acc.interestCalculationPeriodType,
|
||||
acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
|
||||
acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule,
|
||||
transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions,
|
||||
repaymentFrequencyTypeOptions, repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDayOfWeekTypeOptions,
|
||||
transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
|
||||
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
|
||||
loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,
|
||||
accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstallmentAmount,
|
||||
acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap);
|
||||
}
|
||||
|
||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||
@@ -957,32 +988,34 @@ public class LoanAccountData {
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance,
|
||||
acc.interestRatesPeriods);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions, acc.repaymentFrequencyDaysOfWeekTypeOptions,
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
|
||||
acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId,
|
||||
acc.loanOfficerName, acc.currency, acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid,
|
||||
acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
|
||||
acc.repaymentFrequencyType, acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType,
|
||||
acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType,
|
||||
acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
|
||||
acc.isFloatingInterestRate, acc.interestRateDifferential, acc.interestCalculationPeriodType,
|
||||
acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
|
||||
acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule,
|
||||
acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions,
|
||||
acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions,
|
||||
acc.repaymentFrequencyDaysOfWeekTypeOptions, acc.transactionProcessingStrategyOptions,
|
||||
acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
|
||||
acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
|
||||
acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter,
|
||||
acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails,
|
||||
acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||
@@ -1024,27 +1057,28 @@ public class LoanAccountData {
|
||||
}
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions, acc.repaymentFrequencyDaysOfWeekTypeOptions,
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
|
||||
acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId,
|
||||
acc.loanOfficerName, acc.currency, acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid,
|
||||
acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
|
||||
acc.repaymentFrequencyType, acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType,
|
||||
acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType,
|
||||
acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
|
||||
acc.isFloatingInterestRate, acc.interestRateDifferential, acc.interestCalculationPeriodType,
|
||||
acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
|
||||
acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule,
|
||||
acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions,
|
||||
acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions,
|
||||
acc.repaymentFrequencyDaysOfWeekTypeOptions, acc.transactionProcessingStrategyOptions,
|
||||
acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
|
||||
acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
|
||||
acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter,
|
||||
acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails,
|
||||
acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap);
|
||||
|
||||
}
|
||||
|
||||
@@ -1054,53 +1088,55 @@ public class LoanAccountData {
|
||||
final LoanInterestRecalculationData interestRecalculationData = LoanInterestRecalculationData.withCalendarData(
|
||||
acc.interestRecalculationData, calendarData, compoundingCalendarData);
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions, acc.repaymentFrequencyDaysOfWeekTypeOptions,
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
|
||||
acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId,
|
||||
acc.loanOfficerName, acc.currency, acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid,
|
||||
acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
|
||||
acc.repaymentFrequencyType, acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType,
|
||||
acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType,
|
||||
acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
|
||||
acc.isFloatingInterestRate, acc.interestRateDifferential, acc.interestCalculationPeriodType,
|
||||
acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
|
||||
acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule,
|
||||
acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions,
|
||||
acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions,
|
||||
acc.repaymentFrequencyDaysOfWeekTypeOptions, acc.transactionProcessingStrategyOptions,
|
||||
acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
|
||||
acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
|
||||
acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter,
|
||||
acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails,
|
||||
acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData,
|
||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap);
|
||||
}
|
||||
|
||||
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions, acc.repaymentFrequencyDaysOfWeekTypeOptions,
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
|
||||
acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId,
|
||||
acc.loanOfficerName, acc.currency, acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid,
|
||||
acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
|
||||
acc.repaymentFrequencyType, acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType,
|
||||
acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType,
|
||||
acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
|
||||
acc.isFloatingInterestRate, acc.interestRateDifferential, acc.interestCalculationPeriodType,
|
||||
acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
|
||||
acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule,
|
||||
acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions,
|
||||
acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions,
|
||||
acc.repaymentFrequencyDaysOfWeekTypeOptions, acc.transactionProcessingStrategyOptions,
|
||||
acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
|
||||
acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
|
||||
acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter,
|
||||
acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails,
|
||||
acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap);
|
||||
}
|
||||
|
||||
private LoanAccountData(
|
||||
@@ -1164,7 +1200,8 @@ public class LoanAccountData {
|
||||
final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
|
||||
final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance,
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods) {
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods, final Boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGap, final Integer maximumGap) {
|
||||
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
@@ -1332,6 +1369,9 @@ public class LoanAccountData {
|
||||
this.product.setloanProductConfigurableAttributes(product.getloanProductConfigurableAttributes());
|
||||
}
|
||||
this.interestRatesPeriods = interestRatesPeriods;
|
||||
this.isVariableInstallmentsAllowed = isVariableInstallmentsAllowed;
|
||||
this.minimumGap = minimumGap;
|
||||
this.maximumGap = maximumGap;
|
||||
}
|
||||
|
||||
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
|
||||
@@ -1394,7 +1434,7 @@ public class LoanAccountData {
|
||||
}
|
||||
|
||||
private static boolean isLoanCycleValuesWhenConditionEqual(Integer loanCycleNumber, LoanProductBorrowerCycleVariationData data) {
|
||||
return data.getBorrowerCycleNumber() == loanCycleNumber && data.getValueConditionType().equals(LoanProductValueConditionType.EQUAL);
|
||||
return data.getBorrowerCycleNumber().equals(loanCycleNumber) && data.getValueConditionType().equals(LoanProductValueConditionType.EQUAL);
|
||||
}
|
||||
|
||||
public LoanProductData product() {
|
||||
@@ -1431,12 +1471,12 @@ public class LoanAccountData {
|
||||
}
|
||||
|
||||
public boolean isMultiDisburseLoan() {
|
||||
return multiDisburseLoan ;
|
||||
return multiDisburseLoan;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getTotalPaidFeeCharges() {
|
||||
if(this.summary != null) return this.summary.getTotalPaidFeeCharges() ;
|
||||
return BigDecimal.ZERO ;
|
||||
if (this.summary != null) return this.summary.getTotalPaidFeeCharges();
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
}
|
||||
+48
-26
@@ -11,38 +11,42 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
|
||||
public class LoanTermVariationsData {
|
||||
public class LoanTermVariationsData implements Comparable<LoanTermVariationsData> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
private final EnumOptionData termType;
|
||||
private final LocalDate termVariationApplicableFrom;
|
||||
private final Integer termVariationApplicableFromInstallment;
|
||||
private final BigDecimal termValue;
|
||||
private final BigDecimal decimalValue;
|
||||
private final LocalDate dateValue;
|
||||
private final boolean isSpecificToInstallment;
|
||||
private Boolean isProcessed;
|
||||
|
||||
public LoanTermVariationsData(final Long id, final EnumOptionData termType, final LocalDate termVariationApplicableFrom,
|
||||
final BigDecimal termValue) {
|
||||
final BigDecimal decimalValue, final LocalDate dateValue, final boolean isSpecificToInstallment) {
|
||||
this.id = id;
|
||||
this.termType = termType;
|
||||
this.termVariationApplicableFrom = termVariationApplicableFrom;
|
||||
this.termValue = termValue;
|
||||
this.termVariationApplicableFromInstallment = null;
|
||||
this.decimalValue = decimalValue;
|
||||
this.dateValue = dateValue;
|
||||
this.isSpecificToInstallment = isSpecificToInstallment;
|
||||
}
|
||||
|
||||
|
||||
public LoanTermVariationsData(final EnumOptionData termType, final LocalDate termVariationApplicableFrom,
|
||||
final BigDecimal termValue) {
|
||||
final BigDecimal decimalValue, LocalDate dateValue, final boolean isSpecificToInstallment) {
|
||||
this.id = null;
|
||||
this.termType = termType;
|
||||
this.termVariationApplicableFrom = termVariationApplicableFrom;
|
||||
this.termValue = termValue;
|
||||
this.termVariationApplicableFromInstallment = null;
|
||||
this.decimalValue = decimalValue;
|
||||
this.dateValue = dateValue;
|
||||
this.isSpecificToInstallment = isSpecificToInstallment;
|
||||
}
|
||||
|
||||
public EnumOptionData getTermType() {
|
||||
return this.termType;
|
||||
}
|
||||
|
||||
public LoanTermVariationType getTermVariationType(){
|
||||
|
||||
public LoanTermVariationType getTermVariationType() {
|
||||
return LoanTermVariationType.fromInt(this.termType.getId().intValue());
|
||||
}
|
||||
|
||||
@@ -50,33 +54,51 @@ public class LoanTermVariationsData {
|
||||
return this.termVariationApplicableFrom;
|
||||
}
|
||||
|
||||
public BigDecimal getTermValue() {
|
||||
return this.termValue;
|
||||
public BigDecimal getDecimalValue() {
|
||||
return this.decimalValue;
|
||||
}
|
||||
|
||||
public boolean isApplicable(final LocalDate fromDate, final LocalDate dueDate, int installmentNumber) {
|
||||
return occursOnDayFromAndUpTo(fromDate, dueDate, this.termVariationApplicableFrom)
|
||||
|| isIntegerEquals(installmentNumber, this.termVariationApplicableFromInstallment);
|
||||
public boolean isApplicable(final LocalDate fromDate, final LocalDate dueDate) {
|
||||
return occursOnDayFromAndUpTo(fromDate, dueDate, this.termVariationApplicableFrom);
|
||||
}
|
||||
|
||||
private boolean occursOnDayFromAndUpTo(final LocalDate fromInclusive, final LocalDate upToNotInclusive, final LocalDate target) {
|
||||
return target != null && !target.isBefore(fromInclusive) && target.isBefore(upToNotInclusive);
|
||||
}
|
||||
|
||||
private boolean isIntegerEquals(final Integer installmentNumber, final Integer target) {
|
||||
return target != null && target.equals(installmentNumber);
|
||||
}
|
||||
|
||||
public boolean isApplicable(final LocalDate fromDate, int installmentNumber) {
|
||||
return occursBefore(fromDate, this.termVariationApplicableFrom)
|
||||
|| isIntegerGreterThan(installmentNumber, this.termVariationApplicableFromInstallment);
|
||||
public boolean isApplicable(final LocalDate fromDate) {
|
||||
return occursBefore(fromDate, this.termVariationApplicableFrom);
|
||||
}
|
||||
|
||||
private boolean occursBefore(final LocalDate date, final LocalDate target) {
|
||||
return target != null && target.isBefore(date);
|
||||
}
|
||||
|
||||
private boolean isIntegerGreterThan(final Integer installmentNumber, final Integer target) {
|
||||
return target != null && target < installmentNumber;
|
||||
public LocalDate getDateValue() {
|
||||
return this.dateValue;
|
||||
}
|
||||
|
||||
public boolean isSpecificToInstallment() {
|
||||
return this.isSpecificToInstallment;
|
||||
}
|
||||
|
||||
public Boolean isProcessed() {
|
||||
return this.isProcessed == null ? false : this.isProcessed;
|
||||
}
|
||||
|
||||
public void setProcessed(Boolean isProcessed) {
|
||||
this.isProcessed = isProcessed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LoanTermVariationsData o) {
|
||||
int comparsion = getTermApplicableFrom().compareTo(o.getTermApplicableFrom());
|
||||
if (comparsion == 0) {
|
||||
if (o.getTermVariationType().isDueDateVariation() || o.getTermVariationType().isInsertInstallment()) {
|
||||
comparsion = 1;
|
||||
}
|
||||
}
|
||||
return comparsion;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 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.loanaccount.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class LoanTermVariationsDataWrapper {
|
||||
|
||||
private final List<LoanTermVariationsData> exceptionData;
|
||||
private final ListIterator<LoanTermVariationsData> iterator;
|
||||
private final List<LoanTermVariationsData> interestRateChanges;
|
||||
private final List<LoanTermVariationsData> dueDateVariation;
|
||||
private final ListIterator<LoanTermVariationsData> dueDateIterator;
|
||||
|
||||
public LoanTermVariationsDataWrapper(final List<LoanTermVariationsData> exceptionData) {
|
||||
if (exceptionData == null) {
|
||||
this.exceptionData = new ArrayList<>(1);
|
||||
} else {
|
||||
this.exceptionData = exceptionData;
|
||||
Collections.sort(this.exceptionData);
|
||||
}
|
||||
this.interestRateChanges = new ArrayList<>();
|
||||
this.dueDateVariation = new ArrayList<>();
|
||||
for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) {
|
||||
if (loanTermVariationsData.getTermVariationType().isInterestRateVariation()) {
|
||||
this.interestRateChanges.add(loanTermVariationsData);
|
||||
} else if (loanTermVariationsData.getTermVariationType().isDueDateVariation()) {
|
||||
this.dueDateVariation.add(loanTermVariationsData);
|
||||
}
|
||||
}
|
||||
this.exceptionData.removeAll(this.interestRateChanges);
|
||||
this.exceptionData.removeAll(this.dueDateVariation);
|
||||
iterator = this.exceptionData.listIterator();
|
||||
dueDateIterator = this.dueDateVariation.listIterator();
|
||||
}
|
||||
|
||||
public boolean hasVariation(final LocalDate date) {
|
||||
ListIterator<LoanTermVariationsData> iterator = this.iterator;
|
||||
return hasNext(date, iterator);
|
||||
}
|
||||
|
||||
private boolean hasNext(final LocalDate date, ListIterator<LoanTermVariationsData> iterator) {
|
||||
boolean hasVariation = false;
|
||||
if (iterator.hasNext()) {
|
||||
LoanTermVariationsData loanTermVariationsData = iterator.next();
|
||||
if (!loanTermVariationsData.getTermApplicableFrom().isAfter(date)) {
|
||||
hasVariation = true;
|
||||
}
|
||||
iterator.previous();
|
||||
}
|
||||
return hasVariation;
|
||||
}
|
||||
|
||||
public boolean hasDueDateVariation(final LocalDate date) {
|
||||
ListIterator<LoanTermVariationsData> iterator = this.dueDateIterator;
|
||||
return hasNext(date, iterator);
|
||||
}
|
||||
|
||||
public LoanTermVariationsData nextVariation() {
|
||||
return this.iterator.next();
|
||||
}
|
||||
|
||||
public LoanTermVariationsData nextDueDateVariation() {
|
||||
return this.dueDateIterator.next();
|
||||
}
|
||||
|
||||
public List<LoanTermVariationsData> getInterestRateChanges() {
|
||||
return this.interestRateChanges;
|
||||
}
|
||||
|
||||
public List<LoanTermVariationsData> getDueDateVariation() {
|
||||
return this.dueDateVariation;
|
||||
}
|
||||
|
||||
public List<LoanTermVariationsData> getExceptionData() {
|
||||
return this.exceptionData;
|
||||
}
|
||||
|
||||
public int adjustNumberOfRepayments() {
|
||||
int repaymetsForAdjust = 0;
|
||||
for (LoanTermVariationsData loanTermVariations : this.exceptionData) {
|
||||
if (loanTermVariations.getTermVariationType().isInsertInstallment()) {
|
||||
repaymetsForAdjust++;
|
||||
} else if (loanTermVariations.getTermVariationType().isDeleteInstallment()) {
|
||||
repaymetsForAdjust--;
|
||||
}
|
||||
}
|
||||
return repaymetsForAdjust;
|
||||
}
|
||||
|
||||
public LoanTermVariationsData fetchLoanTermDueDateVariationsData(final LocalDate onDate) {
|
||||
LoanTermVariationsData data = null;
|
||||
for (LoanTermVariationsData termVariationsData : this.dueDateVariation) {
|
||||
if (onDate.isEqual(termVariationsData.getTermApplicableFrom())) {
|
||||
data = termVariationsData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
+107
-160
@@ -345,7 +345,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
@OrderBy(value = "termApplicableFrom, id")
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
|
||||
private final Set<LoanTermVariations> loanTermVariations = new HashSet<>();
|
||||
private final List<LoanTermVariations> loanTermVariations = new ArrayList<>();
|
||||
|
||||
@Column(name = "total_recovered_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalRecovered;
|
||||
@@ -577,13 +577,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
totalChargeAmt = loanCharge.amountOutstanding();
|
||||
}
|
||||
} else {
|
||||
chargeAmt = loanCharge.amount();
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}
|
||||
chargeAmt = loanCharge.amountOrPercentage();
|
||||
}
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
||||
totalChargeAmt);
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, fetchNumberOfInstallmensAfterExceptions(), totalChargeAmt);
|
||||
|
||||
// NOTE: must add new loan charge to set of loan charges before
|
||||
// reporcessing the repayment schedule.
|
||||
@@ -1087,14 +1083,10 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
|
||||
}
|
||||
} else {
|
||||
chargeAmt = loanCharge.amount();
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}
|
||||
chargeAmt = loanCharge.amountOrPercentage();
|
||||
}
|
||||
if (charge != null)
|
||||
charge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
||||
totalChargeAmt);
|
||||
charge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, fetchNumberOfInstallmensAfterExceptions(), totalChargeAmt);
|
||||
|
||||
}
|
||||
|
||||
@@ -1530,7 +1522,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
for (final LoanCharge loanCharge : charges) {
|
||||
recalculateLoanCharge(loanCharge, penaltyWaitPeriod);
|
||||
}
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
public boolean isInterestRecalculationEnabledForProduct() {
|
||||
@@ -1603,14 +1595,10 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
|
||||
}
|
||||
} else {
|
||||
chargeAmt = loanCharge.amount();
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}
|
||||
chargeAmt = loanCharge.amountOrPercentage();
|
||||
}
|
||||
if (loanCharge.isActive()) {
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
||||
totalChargeAmt);
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, fetchNumberOfInstallmensAfterExceptions(), totalChargeAmt);
|
||||
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
||||
}
|
||||
|
||||
@@ -2340,8 +2328,10 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.canDefineInstallmentAmount()) && emiAmount != null
|
||||
&& emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
||||
if (this.loanProduct.isMultiDisburseLoan()) {
|
||||
final Date dateValue = null;
|
||||
final boolean isSpecificToInstallment = false;
|
||||
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
||||
actualDisbursementDate.toDate(), emiAmount, this);
|
||||
actualDisbursementDate.toDate(), emiAmount, dateValue, isSpecificToInstallment, this);
|
||||
this.loanTermVariations.add(loanVariationTerms);
|
||||
} else {
|
||||
this.fixedEmiAmount = emiAmount;
|
||||
@@ -2580,21 +2570,6 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
for (LoanDisbursementDetails disbursementDetails : this.disbursementDetails) {
|
||||
disbursementData.add(disbursementDetails.toData());
|
||||
}
|
||||
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<>();
|
||||
boolean isDefaultEmiAmountReq = true;
|
||||
for (LoanTermVariations variationTerms : this.loanTermVariations) {
|
||||
if (variationTerms.getTermType().isEMIAmountVariation()) {
|
||||
if (variationTerms.getTermApplicableFrom().equals(this.getDisbursementDate().toDate())) {
|
||||
isDefaultEmiAmountReq = false;
|
||||
}
|
||||
loanVariationTermsData.add(variationTerms.toData());
|
||||
}
|
||||
}
|
||||
if (isDefaultEmiAmountReq) {
|
||||
LoanTermVariationsData data = new LoanTermVariationsData(null,
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), this.getDisbursementDate(), this.fixedEmiAmount);
|
||||
loanVariationTermsData.add(data);
|
||||
}
|
||||
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
@@ -2612,37 +2587,46 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(scheduleGeneratorDTO.getFloatingRateDTO());
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
FloatingRateDTO floatingRateDTO = scheduleGeneratorDTO.getFloatingRateDTO();
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
annualNominalInterestRate = constructLoanTermVariations(floatingRateDTO, annualNominalInterestRate, loanTermVariations);
|
||||
|
||||
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, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, getApprovedPrincipal(),
|
||||
annualNominalInterestRate, loanTermVariations);
|
||||
this.maxOutstandingLoanBalance, getInterestChargedFromDate(), this.loanProduct.getPrincipalThresholdForLastInstallment(),
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations);
|
||||
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
return loanSchedule;
|
||||
}
|
||||
|
||||
private BigDecimal constructFloatingInterestRates(final BigDecimal annualNominalInterestRate, final FloatingRateDTO floatingRateDTO,
|
||||
final List<LoanTermVariationsData> loanTermVariations) {
|
||||
final LocalDate dateValue = null;
|
||||
final boolean isSpecificToInstallment = false;
|
||||
BigDecimal interestRate = annualNominalInterestRate;
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate(), dateValue, isSpecificToInstallment);
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
interestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
return interestRate;
|
||||
}
|
||||
|
||||
private void handleDisbursementTransaction(final LocalDate disbursedOn, final LocalDateTime createdDate, final AppUser currentUser) {
|
||||
|
||||
// add repayment transaction to track incoming money from client to mfi
|
||||
@@ -2704,8 +2688,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
getApprovedOnDate());
|
||||
}
|
||||
|
||||
if (getExpectedFirstRepaymentOnDate() != null && disbursedOn.isAfter(getExpectedFirstRepaymentOnDate())
|
||||
&& disbursedOn.toDate().equals(this.actualDisbursementDate)) {
|
||||
if (getExpectedFirstRepaymentOnDate() != null
|
||||
&& (disbursedOn.isAfter(this.fetchRepaymentScheduleInstallment(1).getDueDate()) || disbursedOn
|
||||
.isAfter(getExpectedFirstRepaymentOnDate())) && disbursedOn.toDate().equals(this.actualDisbursementDate)) {
|
||||
final String errorMessage = "submittedOnDate cannot be after the loans expectedFirstRepaymentOnDate: "
|
||||
+ getExpectedFirstRepaymentOnDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.after.expected.first.repayment.date", errorMessage,
|
||||
@@ -2815,8 +2800,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : this.repaymentScheduleInstallments) {
|
||||
currentInstallment.resetDerivedComponents();
|
||||
}
|
||||
|
||||
this.loanTermVariations.clear();
|
||||
final List<LoanTermVariations> removeTerms = new ArrayList<>(this.loanTermVariations.size());
|
||||
for (LoanTermVariations variations : this.loanTermVariations) {
|
||||
if (variations.getOnLoanStatus().equals(LoanStatus.ACTIVE.getValue())) {
|
||||
removeTerms.add(variations);
|
||||
}
|
||||
}
|
||||
this.loanTermVariations.removeAll(removeTerms);
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
||||
|
||||
@@ -4602,7 +4592,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isFlat()) {
|
||||
amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
amount = loanCharge.amountOrPercentage();
|
||||
} else {
|
||||
amount = calculateInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage(), installment)
|
||||
.getAmount();
|
||||
@@ -5001,51 +4991,23 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
for (LoanDisbursementDetails disbursementDetails : this.disbursementDetails) {
|
||||
disbursementData.add(disbursementDetails.toData());
|
||||
}
|
||||
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<>();
|
||||
boolean isDefaultEmiAmountReq = true;
|
||||
for (LoanTermVariations variationTerms : this.loanTermVariations) {
|
||||
if (variationTerms.getTermType().isEMIAmountVariation()) {
|
||||
if (variationTerms.getTermApplicableFrom().equals(this.getDisbursementDate().toDate())) {
|
||||
isDefaultEmiAmountReq = false;
|
||||
}
|
||||
loanVariationTermsData.add(variationTerms.toData());
|
||||
}
|
||||
}
|
||||
if (isDefaultEmiAmountReq) {
|
||||
LoanTermVariationsData data = new LoanTermVariationsData(null,
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), this.getDisbursementDate(), this.fixedEmiAmount);
|
||||
loanVariationTermsData.add(data);
|
||||
}
|
||||
|
||||
RecalculationFrequencyType recalculationFrequencyType = this.loanInterestRecalculationDetails().getRestFrequencyType();
|
||||
RecalculationFrequencyType compoundingFrequencyType = this.loanInterestRecalculationDetails().getCompoundingFrequencyType();
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
annualNominalInterestRate = constructLoanTermVariations(floatingRateDTO, annualNominalInterestRate, loanTermVariations);
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
|
||||
loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
||||
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
||||
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
||||
loanVariationTermsData, getInterestChargedFromDate(), this.loanInterestRecalculationDetails,
|
||||
calendarInstanceForInterestRecalculation, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, this.loanProduct.getPrincipalThresholdForLastInstallment(),
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations);
|
||||
getInterestChargedFromDate(), this.loanInterestRecalculationDetails, calendarInstanceForInterestRecalculation,
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
this.loanProduct.preCloseInterestCalculationStrategy(), getApprovedPrincipal(), annualNominalInterestRate,
|
||||
loanTermVariations);
|
||||
return loanApplicationTerms;
|
||||
}
|
||||
|
||||
@@ -5070,22 +5032,6 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
for (LoanDisbursementDetails disbursementDetails : this.disbursementDetails) {
|
||||
disbursementData.add(disbursementDetails.toData());
|
||||
}
|
||||
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<>();
|
||||
boolean isDefaultEmiAmountReq = true;
|
||||
for (LoanTermVariations variationTerms : this.loanTermVariations) {
|
||||
if (variationTerms.getTermType().isEMIAmountVariation()) {
|
||||
if (variationTerms.getTermApplicableFrom().equals(this.getDisbursementDate().toDate())) {
|
||||
isDefaultEmiAmountReq = false;
|
||||
}
|
||||
loanVariationTermsData.add(variationTerms.toData());
|
||||
}
|
||||
}
|
||||
if (isDefaultEmiAmountReq) {
|
||||
LoanTermVariationsData data = new LoanTermVariationsData(null,
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), this.getDisbursementDate(),
|
||||
this.fixedEmiAmount);
|
||||
loanVariationTermsData.add(data);
|
||||
}
|
||||
|
||||
RecalculationFrequencyType recalculationFrequencyType = null;
|
||||
InterestRecalculationCompoundingMethod compoundingMethod = null;
|
||||
@@ -5099,26 +5045,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
annualNominalInterestRate = constructLoanTermVariations(floatingRateDTO, annualNominalInterestRate, loanTermVariations);
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(null, loanTermFrequency,
|
||||
loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), null,
|
||||
getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount,
|
||||
disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
|
||||
disbursementData, this.maxOutstandingLoanBalance, getInterestChargedFromDate(),
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod,
|
||||
@@ -5134,6 +5067,15 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return installment;
|
||||
}
|
||||
|
||||
private BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
|
||||
List<LoanTermVariationsData> loanTermVariations) {
|
||||
for (LoanTermVariations variationTerms : this.loanTermVariations) {
|
||||
loanTermVariations.add(variationTerms.toData());
|
||||
}
|
||||
annualNominalInterestRate = constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations);
|
||||
return annualNominalInterestRate;
|
||||
}
|
||||
|
||||
private LoanRepaymentScheduleInstallment getTotalOutstandingOnLoan() {
|
||||
Money feeCharges = Money.zero(loanCurrency());
|
||||
Money penaltyCharges = Money.zero(loanCurrency());
|
||||
@@ -5338,11 +5280,6 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
final List<DisbursementData> disbursementData = getDisbursmentData();
|
||||
|
||||
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<LoanTermVariationsData>();
|
||||
LoanTermVariationsData data = new LoanTermVariationsData(null,
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount);
|
||||
loanVariationTermsData.add(data);
|
||||
|
||||
RecalculationFrequencyType recalculationFrequencyType = null;
|
||||
InterestRecalculationCompoundingMethod compoundingMethod = null;
|
||||
RecalculationFrequencyType compoundingFrequencyType = null;
|
||||
@@ -5354,30 +5291,16 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
|
||||
}
|
||||
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
annualNominalInterestRate = constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations);
|
||||
|
||||
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, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(),
|
||||
annualNominalInterestRate, loanTermVariations);
|
||||
maxOutstandingBalance, interestChargedFromDate, this.loanProduct.getPrincipalThresholdForLastInstallment(),
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5624,12 +5547,36 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return this.interestRateDifferential;
|
||||
}
|
||||
|
||||
public void setIsFloatingInterestRate(Boolean isFloatingInterestRate) {
|
||||
this.isFloatingInterestRate = isFloatingInterestRate;
|
||||
}
|
||||
public void setIsFloatingInterestRate(Boolean isFloatingInterestRate) {
|
||||
this.isFloatingInterestRate = isFloatingInterestRate;
|
||||
}
|
||||
|
||||
public void setInterestRateDifferential(BigDecimal interestRateDifferential) {
|
||||
this.interestRateDifferential = interestRateDifferential;
|
||||
}
|
||||
|
||||
public List<LoanTermVariations> getLoanTermVariations() {
|
||||
return this.loanTermVariations;
|
||||
}
|
||||
|
||||
private int adjustNumberOfRepayments() {
|
||||
int repaymetsForAdjust = 0;
|
||||
for (LoanTermVariations loanTermVariations : this.loanTermVariations) {
|
||||
if (loanTermVariations.getTermType().isInsertInstallment()) {
|
||||
repaymetsForAdjust++;
|
||||
} else if (loanTermVariations.getTermType().isDeleteInstallment()) {
|
||||
repaymetsForAdjust--;
|
||||
}
|
||||
}
|
||||
return repaymetsForAdjust;
|
||||
}
|
||||
|
||||
public int fetchNumberOfInstallmensAfterExceptions() {
|
||||
return this.repaymentScheduleDetail().getNumberOfRepayments() + adjustNumberOfRepayments();
|
||||
}
|
||||
|
||||
public void setExpectedFirstRepaymentOnDate(Date expectedFirstRepaymentOnDate) {
|
||||
this.expectedFirstRepaymentOnDate = expectedFirstRepaymentOnDate;
|
||||
}
|
||||
|
||||
public void setInterestRateDifferential(BigDecimal interestRateDifferential) {
|
||||
this.interestRateDifferential = interestRateDifferential;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -56,4 +56,6 @@ public interface LoanAccountDomainService {
|
||||
|
||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan);
|
||||
|
||||
void saveLoanWithDataIntegrityViolationChecks(Loan loan);
|
||||
|
||||
}
|
||||
|
||||
+22
@@ -303,6 +303,28 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
try {
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.getId() == null) {
|
||||
this.repaymentScheduleInstallmentRepository.save(installment);
|
||||
}
|
||||
}
|
||||
this.loanRepository.save(loan);
|
||||
} catch (final DataIntegrityViolationException e) {
|
||||
final Throwable realCause = e.getCause();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
if (realCause.getMessage().toLowerCase().contains("external_id_unique")) {
|
||||
baseDataValidator.reset().parameter("externalId").failWithCode("value.must.be.unique");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
|
||||
+3
-3
@@ -255,7 +255,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
this.amountPaid = null;
|
||||
if (isInstalmentFee()) {
|
||||
if (numberOfRepayments == null) {
|
||||
numberOfRepayments = this.loan.repaymentScheduleDetail().getNumberOfRepayments();
|
||||
numberOfRepayments = this.loan.fetchNumberOfInstallmensAfterExceptions();
|
||||
}
|
||||
this.amount = chargeAmount.multiply(BigDecimal.valueOf(numberOfRepayments));
|
||||
} else {
|
||||
@@ -366,7 +366,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case FLAT:
|
||||
if (isInstalmentFee()) {
|
||||
if (numberOfRepayments == null) {
|
||||
numberOfRepayments = this.loan.repaymentScheduleDetail().getNumberOfRepayments();
|
||||
numberOfRepayments = this.loan.fetchNumberOfInstallmensAfterExceptions();
|
||||
}
|
||||
this.amount = amount.multiply(BigDecimal.valueOf(numberOfRepayments));
|
||||
} else {
|
||||
@@ -446,7 +446,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
break;
|
||||
case FLAT:
|
||||
if (isInstalmentFee()) {
|
||||
this.amount = newValue.multiply(BigDecimal.valueOf(this.loan.repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
this.amount = newValue.multiply(BigDecimal.valueOf(this.loan.fetchNumberOfInstallmensAfterExceptions()));
|
||||
} else {
|
||||
this.amount = newValue;
|
||||
}
|
||||
|
||||
+11
-11
@@ -32,18 +32,18 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
for (final LoanRepaymentScheduleInstallment period : repaymentPeriods) {
|
||||
|
||||
final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges,
|
||||
currency, period, repaymentPeriods.size(), totalPrincipal, totalInterest, true);
|
||||
currency, period, totalPrincipal, totalInterest, !period.isRecalculatedInterestComponent());
|
||||
final Money feeChargesWaivedForRepaymentPeriod = cumulativeFeeChargesWaivedWithin(startDate, period.getDueDate(), loanCharges,
|
||||
currency, true);
|
||||
currency, !period.isRecalculatedInterestComponent());
|
||||
final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(),
|
||||
loanCharges, currency, true);
|
||||
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||
|
||||
final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(),
|
||||
loanCharges, currency, period, repaymentPeriods.size(), totalPrincipal, totalInterest, true);
|
||||
loanCharges, currency, period, totalPrincipal, totalInterest, !period.isRecalculatedInterestComponent());
|
||||
final Money penaltyChargesWaivedForRepaymentPeriod = cumulativePenaltyChargesWaivedWithin(startDate, period.getDueDate(),
|
||||
loanCharges, currency, true);
|
||||
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||
final Money penaltyChargesWrittenOffForRepaymentPeriod = cumulativePenaltyChargesWrittenOffWithin(startDate,
|
||||
period.getDueDate(), loanCharges, currency, true);
|
||||
period.getDueDate(), loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||
|
||||
period.updateChargePortion(feeChargesDueForRepaymentPeriod, feeChargesWaivedForRepaymentPeriod,
|
||||
feeChargesWrittenOffForRepaymentPeriod, penaltyChargesDueForRepaymentPeriod, penaltyChargesWaivedForRepaymentPeriod,
|
||||
@@ -54,8 +54,8 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
}
|
||||
|
||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency, LoanRepaymentScheduleInstallment period, int numberOfRepayments,
|
||||
final Money totalPrincipal, final Money totalInterest, boolean isInstallmentChargeApplicable) {
|
||||
final MonetaryCurrency monetaryCurrency, LoanRepaymentScheduleInstallment period, final Money totalPrincipal,
|
||||
final Money totalInterest, boolean isInstallmentChargeApplicable) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
@@ -75,7 +75,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
cumulative = cumulative.plus(loanCharge.amountOrPercentage());
|
||||
}
|
||||
} else if (loanCharge.isOverdueInstallmentCharge()
|
||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
@@ -146,7 +146,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
|
||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, LoanRepaymentScheduleInstallment period,
|
||||
int numberOfRepayments, final Money totalPrincipal, final Money totalInterest, boolean isInstallmentChargeApplicable) {
|
||||
final Money totalPrincipal, final Money totalInterest, boolean isInstallmentChargeApplicable) {
|
||||
|
||||
Money cumulative = Money.zero(currency);
|
||||
|
||||
@@ -166,7 +166,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
cumulative = cumulative.plus(loanCharge.amountOrPercentage());
|
||||
}
|
||||
} else if (loanCharge.isOverdueInstallmentCharge()
|
||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
|
||||
+5
@@ -322,4 +322,9 @@ public final class LoanSummary {
|
||||
public BigDecimal getTotalPenaltyChargesWaived() {
|
||||
return this.totalPenaltyChargesWaived;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getTotalExpectedRepayment() {
|
||||
return this.totalExpectedRepayment;
|
||||
}
|
||||
}
|
||||
+34
-2
@@ -9,7 +9,11 @@ public enum LoanTermVariationType {
|
||||
|
||||
INVALID(0, "loanTermType.invalid"), //
|
||||
EMI_AMOUNT(1, "loanTermType.emiAmount"), //
|
||||
INTEREST_RATE(2, "loanTermType.interestRate");
|
||||
INTEREST_RATE(2, "loanTermType.interestRate"), //
|
||||
PRINCIPAL_AMOUNT(3, "loanTermType.principalAmount"), //
|
||||
DUE_DATE(4, "loanTermType.dueDate"), //
|
||||
INSERT_INSTALLMENT(5, "loanTermType.insertInstallment"), //
|
||||
DELETE_INSTALLMENT(6, "loanTermType.deleteInstallment");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -37,6 +41,18 @@ public enum LoanTermVariationType {
|
||||
case 2:
|
||||
enumeration = LoanTermVariationType.INTEREST_RATE;
|
||||
break;
|
||||
case 3:
|
||||
enumeration = LoanTermVariationType.PRINCIPAL_AMOUNT;
|
||||
break;
|
||||
case 4:
|
||||
enumeration = LoanTermVariationType.DUE_DATE;
|
||||
break;
|
||||
case 5:
|
||||
enumeration = LoanTermVariationType.INSERT_INSTALLMENT;
|
||||
break;
|
||||
case 6:
|
||||
enumeration = LoanTermVariationType.DELETE_INSTALLMENT;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
@@ -44,8 +60,24 @@ public enum LoanTermVariationType {
|
||||
public boolean isEMIAmountVariation() {
|
||||
return this.value.equals(LoanTermVariationType.EMI_AMOUNT.getValue());
|
||||
}
|
||||
|
||||
|
||||
public boolean isInterestRateVariation() {
|
||||
return this.value.equals(LoanTermVariationType.INTEREST_RATE.getValue());
|
||||
}
|
||||
|
||||
public boolean isPrincipalAmountVariation() {
|
||||
return this.value.equals(LoanTermVariationType.PRINCIPAL_AMOUNT.getValue());
|
||||
}
|
||||
|
||||
public boolean isDueDateVariation() {
|
||||
return this.value.equals(LoanTermVariationType.DUE_DATE.getValue());
|
||||
}
|
||||
|
||||
public boolean isInsertInstallment() {
|
||||
return this.value.equals(LoanTermVariationType.INSERT_INSTALLMENT.getValue());
|
||||
}
|
||||
|
||||
public boolean isDeleteInstallment() {
|
||||
return this.value.equals(LoanTermVariationType.DELETE_INSTALLMENT.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
+49
-7
@@ -34,18 +34,31 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
|
||||
private Integer termType;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "applicable_from")
|
||||
@Column(name = "applicable_date", nullable = false)
|
||||
private Date termApplicableFrom;
|
||||
|
||||
@Column(name = "term_value", scale = 6, precision = 19)
|
||||
private BigDecimal termValue;
|
||||
@Column(name = "decimal_value", scale = 6, precision = 19)
|
||||
private BigDecimal decimalValue;
|
||||
|
||||
public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal termValue, final Loan loan) {
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "date_value")
|
||||
private Date dateValue;
|
||||
|
||||
@Column(name = "is_specific_to_installment", nullable = false)
|
||||
private boolean isSpecificToInstallment;
|
||||
|
||||
@Column(name = "applied_on_loan_status", nullable = false)
|
||||
private Integer onLoanStatus;
|
||||
|
||||
public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
|
||||
final boolean isSpecificToInstallment, final Loan loan) {
|
||||
this.loan = loan;
|
||||
this.termApplicableFrom = termApplicableFrom;
|
||||
this.termType = termType;
|
||||
this.termValue = termValue;
|
||||
this.decimalValue = decimalValue;
|
||||
this.dateValue = dateValue;
|
||||
this.isSpecificToInstallment = isSpecificToInstallment;
|
||||
this.onLoanStatus = loan.status().getValue();
|
||||
}
|
||||
|
||||
protected LoanTermVariations() {
|
||||
@@ -58,16 +71,45 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
|
||||
|
||||
public LoanTermVariationsData toData() {
|
||||
LocalDate termStartDate = new LocalDate(this.termApplicableFrom);
|
||||
LocalDate dateValue = null;
|
||||
if (this.dateValue != null) {
|
||||
dateValue = new LocalDate(this.dateValue);
|
||||
}
|
||||
EnumOptionData type = LoanEnumerations.loanvariationType(this.termType);
|
||||
return new LoanTermVariationsData(getId(), type, termStartDate, this.termValue);
|
||||
return new LoanTermVariationsData(getId(), type, termStartDate, this.decimalValue, dateValue, this.isSpecificToInstallment);
|
||||
}
|
||||
|
||||
public Date getTermApplicableFrom() {
|
||||
return this.termApplicableFrom;
|
||||
}
|
||||
|
||||
public LocalDate fetchTermApplicaDate() {
|
||||
return new LocalDate(this.termApplicableFrom);
|
||||
}
|
||||
|
||||
public BigDecimal getTermValue() {
|
||||
return this.termValue;
|
||||
return this.decimalValue;
|
||||
}
|
||||
|
||||
public Date getDateValue() {
|
||||
return this.dateValue;
|
||||
}
|
||||
|
||||
public LocalDate fetchDateValue() {
|
||||
return this.dateValue == null ? null : new LocalDate(this.dateValue);
|
||||
}
|
||||
|
||||
public void setTermApplicableFrom(Date termApplicableFrom) {
|
||||
this.termApplicableFrom = termApplicableFrom;
|
||||
}
|
||||
|
||||
public void setDecimalValue(BigDecimal decimalValue) {
|
||||
this.decimalValue = decimalValue;
|
||||
}
|
||||
|
||||
|
||||
public Integer getOnLoanStatus() {
|
||||
return this.onLoanStatus;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.loanaccount.domain;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Sort loan term variations
|
||||
*/
|
||||
public class LoanTermVariationsComparator implements Comparator<LoanTermVariations> {
|
||||
|
||||
@Override
|
||||
public int compare(final LoanTermVariations o1, final LoanTermVariations o2) {
|
||||
int compareResult = 0;
|
||||
final int comparsion = o1.getTermApplicableFrom().compareTo(o2.getTermApplicableFrom());
|
||||
/**
|
||||
* For Terms bearing the same effective date, we sort based on modified
|
||||
* date (when available) and new inserted installment
|
||||
**/
|
||||
if (comparsion == 0) {
|
||||
if (o2.getTermType().isDueDateVariation() || o2.getTermType().isInsertInstallment()) {
|
||||
compareResult = 1;
|
||||
}
|
||||
} else {
|
||||
compareResult = comparsion;
|
||||
}
|
||||
|
||||
return compareResult;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -172,8 +172,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
* changedTransactionDetail accordingly
|
||||
**/
|
||||
if (LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
|
||||
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(newLoanTransaction.getLoanTransactionToRepaymentScheduleMappings());
|
||||
} else{
|
||||
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(newLoanTransaction
|
||||
.getLoanTransactionToRepaymentScheduleMappings());
|
||||
} else {
|
||||
loanTransaction.reverse();
|
||||
loanTransaction.updateExternalId(null);
|
||||
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
|
||||
|
||||
+3
-3
@@ -124,11 +124,11 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
|
||||
Guarantor guarantor = null;
|
||||
for (final Guarantor avilableGuarantor : existGuarantorList) {
|
||||
if (entityId != null && avilableGuarantor.getEntityId() != null && avilableGuarantor.getEntityId().equals(entityId)
|
||||
&& avilableGuarantor.getGurantorType() == guarantorTypeId && avilableGuarantor.isActive()) {
|
||||
&& avilableGuarantor.getGurantorType().equals(guarantorTypeId) && avilableGuarantor.isActive()) {
|
||||
if (guarantorCommand.getSavingsId() == null || avilableGuarantor.hasGuarantor(guarantorCommand.getSavingsId())) {
|
||||
/** Get the right guarantor based on guarantorType **/
|
||||
String defaultUserMessage = null;
|
||||
if (guarantorTypeId == GuarantorType.STAFF.getValue()) {
|
||||
if (guarantorTypeId.equals(GuarantorType.STAFF.getValue())) {
|
||||
defaultUserMessage = this.staffRepositoryWrapper.findOneWithNotFoundDetection(entityId).displayName();
|
||||
} else {
|
||||
defaultUserMessage = this.clientRepositoryWrapper.findOneWithNotFoundDetection(entityId).getDisplayName();
|
||||
@@ -196,7 +196,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
|
||||
if (guarantorType.isCustomer() || guarantorType.isStaff()) {
|
||||
final Long entityId = guarantorCommand.getEntityId();
|
||||
for (final Guarantor guarantor : existGuarantorList) {
|
||||
if (guarantor.getEntityId() == entityId && guarantor.getGurantorType() == guarantorTypeId
|
||||
if (guarantor.getEntityId().equals(entityId) && guarantor.getGurantorType().equals(guarantorTypeId)
|
||||
&& !guarantorForUpdate.getId().equals(guarantor.getId())) {
|
||||
String defaultUserMessage = this.clientRepositoryWrapper.findOneWithNotFoundDetection(entityId).getDisplayName();
|
||||
defaultUserMessage = defaultUserMessage + " is already exist as a guarantor for this loan";
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.annotation.CommandType;
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "LOAN", action = "CREATESCHEDULEEXCEPTIONS")
|
||||
public class LoanScheduleCreateVariationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanScheduleWritePlatformService loanScheduleWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleCreateVariationCommandHandler(final LoanScheduleWritePlatformService loanScheduleWritePlatformService) {
|
||||
this.loanScheduleWritePlatformService = loanScheduleWritePlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand command) {
|
||||
return this.loanScheduleWritePlatformService.addLoanScheduleVariations(command.getLoanId(), command);
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.annotation.CommandType;
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "LOAN", action = "DELETESCHEDULEEXCEPTIONS")
|
||||
public class LoanScheduleDeleteVariationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanScheduleWritePlatformService loanScheduleWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleDeleteVariationCommandHandler(final LoanScheduleWritePlatformService loanScheduleWritePlatformService) {
|
||||
this.loanScheduleWritePlatformService = loanScheduleWritePlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand command) {
|
||||
return this.loanScheduleWritePlatformService.deleteLoanScheduleVariations(command.getLoanId());
|
||||
}
|
||||
|
||||
}
|
||||
+436
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* 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.loanaccount.loanschedule.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.RecalculationDetail;
|
||||
|
||||
public class LoanScheduleParams {
|
||||
|
||||
// Actual period Number as per the schedule
|
||||
private int periodNumber;
|
||||
// Actual period Number plus interest only repayments
|
||||
private int instalmentNumber;
|
||||
|
||||
private LocalDate periodStartDate;
|
||||
private LocalDate actualRepaymentDate;
|
||||
|
||||
// variables for cumulative totals
|
||||
private Money totalCumulativePrincipal;
|
||||
private Money totalCumulativeInterest;
|
||||
private Money totalFeeChargesCharged;
|
||||
private Money totalPenaltyChargesCharged;
|
||||
private Money totalRepaymentExpected;
|
||||
private Money totalOutstandingInterestPaymentDueToGrace;
|
||||
|
||||
// early payments will be added here and as per the selected strategy
|
||||
// action will be performed on this value
|
||||
private Money reducePrincipal;
|
||||
|
||||
// principal changes will be added along with date(after applying rest)
|
||||
// from when these amounts will effect the outstanding balance for
|
||||
// interest calculation
|
||||
private final Map<LocalDate, Money> principalPortionMap;
|
||||
|
||||
// compounding(principal) amounts will be added along with
|
||||
// date(after applying compounding frequency)
|
||||
// from when these amounts will effect the outstanding balance for
|
||||
// interest calculation
|
||||
private final Map<LocalDate, Money> latePaymentMap;
|
||||
|
||||
// compounding(interest/Fee) amounts will be added along with
|
||||
// date(after applying compounding frequency)
|
||||
// from when these amounts will effect the outstanding balance for
|
||||
// interest calculation
|
||||
private final TreeMap<LocalDate, Money> compoundingMap;
|
||||
private final Map<LocalDate, TreeMap<LocalDate, Money>> compoundingDateVariations = new HashMap<>();
|
||||
|
||||
// disbursement map for tranche details(will added to outstanding
|
||||
// balance as per the start date)
|
||||
private final Map<LocalDate, Money> disburseDetailMap;
|
||||
private Money principalToBeScheduled;
|
||||
private Money outstandingBalance;
|
||||
|
||||
// total outstanding balance as per rest for interest calculation.
|
||||
private Money outstandingBalanceAsPerRest;
|
||||
|
||||
private final List<LoanRepaymentScheduleInstallment> installments;
|
||||
private final Collection<RecalculationDetail> recalculationDetails;
|
||||
private final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor;
|
||||
private final LocalDate scheduleTillDate;
|
||||
private final boolean partialUpdate;
|
||||
private int loanTermInDays;
|
||||
private final MonetaryCurrency currency;
|
||||
private final boolean applyInterestRecalculation;
|
||||
|
||||
private LoanScheduleParams(final int periodNumber, final int instalmentNumber, int loanTermInDays, LocalDate periodStartDate,
|
||||
final LocalDate actualRepaymentDate, final Money totalCumulativePrincipal, final Money totalCumulativeInterest,
|
||||
final Money totalFeeChargesCharged, final Money totalPenaltyChargesCharged, final Money totalRepaymentExpected,
|
||||
Money totalOutstandingInterestPaymentDueToGrace, final Money reducePrincipal, final Map<LocalDate, Money> principalPortionMap,
|
||||
final Map<LocalDate, Money> latePaymentMap, final TreeMap<LocalDate, Money> compoundingMap,
|
||||
final Map<LocalDate, Money> disburseDetailMap, Money principalToBeScheduled, final Money outstandingBalance,
|
||||
final Money outstandingBalanceAsPerRest, final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||
final boolean partialUpdate, final MonetaryCurrency currency, final boolean applyInterestRecalculation) {
|
||||
this.periodNumber = periodNumber;
|
||||
this.instalmentNumber = instalmentNumber;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
this.periodStartDate = periodStartDate;
|
||||
this.actualRepaymentDate = actualRepaymentDate;
|
||||
this.totalCumulativePrincipal = totalCumulativePrincipal;
|
||||
this.totalCumulativeInterest = totalCumulativeInterest;
|
||||
this.totalFeeChargesCharged = totalFeeChargesCharged;
|
||||
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
|
||||
this.totalRepaymentExpected = totalRepaymentExpected;
|
||||
this.totalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace;
|
||||
this.reducePrincipal = reducePrincipal;
|
||||
this.principalPortionMap = principalPortionMap;
|
||||
this.latePaymentMap = latePaymentMap;
|
||||
this.compoundingMap = compoundingMap;
|
||||
this.disburseDetailMap = disburseDetailMap;
|
||||
this.principalToBeScheduled = principalToBeScheduled;
|
||||
this.outstandingBalance = outstandingBalance;
|
||||
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
|
||||
this.installments = installments;
|
||||
this.recalculationDetails = recalculationDetails;
|
||||
this.loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessor;
|
||||
this.scheduleTillDate = scheduleTillDate;
|
||||
this.partialUpdate = partialUpdate;
|
||||
this.currency = currency;
|
||||
this.applyInterestRecalculation = applyInterestRecalculation;
|
||||
}
|
||||
|
||||
public static LoanScheduleParams createLoanScheduleParamsForPartialUpdate(final int periodNumber, final int instalmentNumber,
|
||||
int loanTermInDays, LocalDate periodStartDate, final LocalDate actualRepaymentDate, final Money totalCumulativePrincipal,
|
||||
final Money totalCumulativeInterest, final Money totalFeeChargesCharged, final Money totalPenaltyChargesCharged,
|
||||
final Money totalRepaymentExpected, Money totalOutstandingInterestPaymentDueToGrace, final Money reducePrincipal,
|
||||
final Map<LocalDate, Money> principalPortionMap, final Map<LocalDate, Money> latePaymentMap,
|
||||
final TreeMap<LocalDate, Money> compoundingMap, final Map<LocalDate, Money> disburseDetailMap, Money principalToBeScheduled,
|
||||
final Money outstandingBalance, final Money outstandingBalanceAsPerRest,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||
final MonetaryCurrency currency, final boolean applyInterestRecalculation) {
|
||||
final boolean partialUpdate = true;
|
||||
return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap,
|
||||
compoundingMap, disburseDetailMap, principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, installments,
|
||||
recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate, currency,
|
||||
applyInterestRecalculation);
|
||||
}
|
||||
|
||||
public static LoanScheduleParams createLoanScheduleParamsForCompleteUpdate(final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||
final boolean applyInterestRecalculation) {
|
||||
final int periodNumber = 1;
|
||||
final int instalmentNumber = 1;
|
||||
final LocalDate periodStartDate = null;
|
||||
final LocalDate actualRepaymentDate = null;
|
||||
final Money totalCumulativePrincipal = null;
|
||||
final Money totalCumulativeInterest = null;
|
||||
final Money totalFeeChargesCharged = null;
|
||||
final Money totalPenaltyChargesCharged = null;
|
||||
final Money totalRepaymentExpected = null;
|
||||
final Money reducePrincipal = null;
|
||||
final Map<LocalDate, Money> principalPortionMap = null;
|
||||
final Map<LocalDate, Money> latePaymentMap = null;
|
||||
final TreeMap<LocalDate, Money> compoundingMap = null;
|
||||
final Map<LocalDate, Money> disburseDetailMap = null;
|
||||
final Money principalToBeScheduled = null;
|
||||
final Money outstandingBalance = null;
|
||||
final Money outstandingBalanceAsPerRest = null;
|
||||
final List<LoanRepaymentScheduleInstallment> installments = null;
|
||||
final boolean partialUpdate = false;
|
||||
final int loanTermInDays = 0;
|
||||
final Money totalOutstandingInterestPaymentDueToGrace = null;
|
||||
final MonetaryCurrency currency = null;
|
||||
return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap,
|
||||
compoundingMap, disburseDetailMap, principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, installments,
|
||||
recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate, currency,
|
||||
applyInterestRecalculation);
|
||||
}
|
||||
|
||||
public static LoanScheduleParams createLoanScheduleParams(final MonetaryCurrency currency, final Money chargesDueAtTimeOfDisbursement,
|
||||
final LocalDate periodStartDate, final Money principalToBeScheduled) {
|
||||
final int loanTermInDays = 0;
|
||||
final int periodNumber = 1;
|
||||
final int instalmentNumber = 1;
|
||||
final Money totalCumulativePrincipal = Money.zero(currency);
|
||||
final Money totalCumulativeInterest = Money.zero(currency);
|
||||
final Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
|
||||
final LocalDate actualRepaymentDate = periodStartDate;
|
||||
final Money totalFeeChargesCharged = chargesDueAtTimeOfDisbursement;
|
||||
final Money totalPenaltyChargesCharged = Money.zero(currency);
|
||||
final Money totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
|
||||
final Money reducePrincipal = Money.zero(currency);
|
||||
final Map<LocalDate, Money> principalPortionMap = new HashMap<>();
|
||||
final Map<LocalDate, Money> latePaymentMap = new HashMap<>();
|
||||
final TreeMap<LocalDate, Money> compoundingMap = new TreeMap<>();
|
||||
final Map<LocalDate, Money> disburseDetailMap = new TreeMap<>();
|
||||
final Money outstandingBalance = principalToBeScheduled;
|
||||
final Money outstandingBalanceAsPerRest = principalToBeScheduled;
|
||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||
final boolean partialUpdate = false;
|
||||
final Collection<RecalculationDetail> recalculationDetails = null;
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = null;
|
||||
final LocalDate scheduleTillDate = null;
|
||||
final boolean applyInterestRecalculation = false;
|
||||
return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap,
|
||||
compoundingMap, disburseDetailMap, principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, installments,
|
||||
recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate, currency,
|
||||
applyInterestRecalculation);
|
||||
}
|
||||
|
||||
public static LoanScheduleParams createLoanScheduleParams(final MonetaryCurrency currency, final Money chargesDueAtTimeOfDisbursement,
|
||||
final LocalDate periodStartDate, final Money principalToBeScheduled, final LoanScheduleParams loanScheduleParams) {
|
||||
final int loanTermInDays = 0;
|
||||
final int periodNumber = 1;
|
||||
final int instalmentNumber = 1;
|
||||
final Money totalCumulativePrincipal = Money.zero(currency);
|
||||
final Money totalCumulativeInterest = Money.zero(currency);
|
||||
final Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
|
||||
final LocalDate actualRepaymentDate = periodStartDate;
|
||||
final Money totalFeeChargesCharged = chargesDueAtTimeOfDisbursement;
|
||||
final Money totalPenaltyChargesCharged = Money.zero(currency);
|
||||
final Money totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
|
||||
final Money reducePrincipal = Money.zero(currency);
|
||||
final Map<LocalDate, Money> principalPortionMap = new HashMap<>();
|
||||
final Map<LocalDate, Money> latePaymentMap = new HashMap<>();
|
||||
final TreeMap<LocalDate, Money> compoundingMap = new TreeMap<>();
|
||||
final Map<LocalDate, Money> disburseDetailMap = new TreeMap<>();
|
||||
final Money outstandingBalance = principalToBeScheduled;
|
||||
final Money outstandingBalanceAsPerRest = principalToBeScheduled;
|
||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||
final boolean partialUpdate = false;
|
||||
final Collection<RecalculationDetail> recalculationDetails = loanScheduleParams.recalculationDetails;
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanScheduleParams.loanRepaymentScheduleTransactionProcessor;
|
||||
final LocalDate scheduleTillDate = loanScheduleParams.scheduleTillDate;
|
||||
final boolean applyInterestRecalculation = loanScheduleParams.applyInterestRecalculation;
|
||||
return new LoanScheduleParams(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap,
|
||||
compoundingMap, disburseDetailMap, principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, installments,
|
||||
recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate, currency,
|
||||
applyInterestRecalculation);
|
||||
}
|
||||
|
||||
public int getPeriodNumber() {
|
||||
return this.periodNumber;
|
||||
}
|
||||
|
||||
public int getInstalmentNumber() {
|
||||
return this.instalmentNumber;
|
||||
}
|
||||
|
||||
public LocalDate getActualRepaymentDate() {
|
||||
return this.actualRepaymentDate;
|
||||
}
|
||||
|
||||
public Money getTotalCumulativePrincipal() {
|
||||
return this.totalCumulativePrincipal;
|
||||
}
|
||||
|
||||
public void addTotalCumulativePrincipal(final Money totalCumulativePrincipal) {
|
||||
this.totalCumulativePrincipal = this.totalCumulativePrincipal.plus(totalCumulativePrincipal);
|
||||
}
|
||||
|
||||
public Money getTotalCumulativeInterest() {
|
||||
return this.totalCumulativeInterest;
|
||||
}
|
||||
|
||||
public void addTotalCumulativeInterest(final Money totalCumulativeInterest) {
|
||||
this.totalCumulativeInterest = this.totalCumulativeInterest.plus(totalCumulativeInterest);
|
||||
}
|
||||
|
||||
public Money getTotalFeeChargesCharged() {
|
||||
return this.totalFeeChargesCharged;
|
||||
}
|
||||
|
||||
public void addTotalFeeChargesCharged(final Money totalFeeChargesCharged) {
|
||||
this.totalFeeChargesCharged = this.totalFeeChargesCharged.plus(totalFeeChargesCharged);
|
||||
}
|
||||
|
||||
public Money getTotalPenaltyChargesCharged() {
|
||||
return this.totalPenaltyChargesCharged;
|
||||
}
|
||||
|
||||
public void addTotalPenaltyChargesCharged(final Money totalPenaltyChargesCharged) {
|
||||
this.totalPenaltyChargesCharged = this.totalPenaltyChargesCharged.plus(totalPenaltyChargesCharged);
|
||||
}
|
||||
|
||||
public Money getTotalRepaymentExpected() {
|
||||
return this.totalRepaymentExpected;
|
||||
}
|
||||
|
||||
public void addTotalRepaymentExpected(final Money totalRepaymentExpected) {
|
||||
this.totalRepaymentExpected = this.totalRepaymentExpected.plus(totalRepaymentExpected);
|
||||
}
|
||||
|
||||
public Money getReducePrincipal() {
|
||||
return this.reducePrincipal;
|
||||
}
|
||||
|
||||
public Map<LocalDate, Money> getPrincipalPortionMap() {
|
||||
return this.principalPortionMap;
|
||||
}
|
||||
|
||||
public Map<LocalDate, Money> getLatePaymentMap() {
|
||||
return this.latePaymentMap;
|
||||
}
|
||||
|
||||
public TreeMap<LocalDate, Money> getCompoundingMap() {
|
||||
return this.compoundingMap;
|
||||
}
|
||||
|
||||
public Map<LocalDate, Money> getDisburseDetailMap() {
|
||||
return this.disburseDetailMap;
|
||||
}
|
||||
|
||||
public Money getOutstandingBalance() {
|
||||
return this.outstandingBalance;
|
||||
}
|
||||
|
||||
public Money getOutstandingBalanceAsPerRest() {
|
||||
return this.outstandingBalanceAsPerRest;
|
||||
}
|
||||
|
||||
public List<LoanRepaymentScheduleInstallment> getInstallments() {
|
||||
return this.installments;
|
||||
}
|
||||
|
||||
public Collection<RecalculationDetail> getRecalculationDetails() {
|
||||
return this.recalculationDetails;
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleTransactionProcessor getLoanRepaymentScheduleTransactionProcessor() {
|
||||
return this.loanRepaymentScheduleTransactionProcessor;
|
||||
}
|
||||
|
||||
public LocalDate getScheduleTillDate() {
|
||||
return this.scheduleTillDate;
|
||||
}
|
||||
|
||||
public boolean isPartialUpdate() {
|
||||
return this.partialUpdate;
|
||||
}
|
||||
|
||||
public LocalDate getPeriodStartDate() {
|
||||
return this.periodStartDate;
|
||||
}
|
||||
|
||||
public Money getPrincipalToBeScheduled() {
|
||||
return this.principalToBeScheduled;
|
||||
}
|
||||
|
||||
public void setPeriodNumber(int periodNumber) {
|
||||
this.periodNumber = periodNumber;
|
||||
}
|
||||
|
||||
public void incrementPeriodNumber() {
|
||||
this.periodNumber++;
|
||||
}
|
||||
|
||||
public void incrementInstalmentNumber() {
|
||||
this.instalmentNumber++;
|
||||
}
|
||||
|
||||
public void setPeriodStartDate(LocalDate periodStartDate) {
|
||||
this.periodStartDate = periodStartDate;
|
||||
}
|
||||
|
||||
public void setActualRepaymentDate(LocalDate actualRepaymentDate) {
|
||||
this.actualRepaymentDate = actualRepaymentDate;
|
||||
}
|
||||
|
||||
public void setReducePrincipal(Money reducePrincipal) {
|
||||
this.reducePrincipal = reducePrincipal;
|
||||
}
|
||||
|
||||
public void addReducePrincipal(Money reducePrincipal) {
|
||||
this.reducePrincipal = this.reducePrincipal.plus(reducePrincipal);
|
||||
}
|
||||
|
||||
public void reduceReducePrincipal(Money reducePrincipal) {
|
||||
this.reducePrincipal = this.reducePrincipal.minus(reducePrincipal);
|
||||
}
|
||||
|
||||
public void setPrincipalToBeScheduled(Money principalToBeScheduled) {
|
||||
this.principalToBeScheduled = principalToBeScheduled;
|
||||
}
|
||||
|
||||
public void addPrincipalToBeScheduled(Money principalToBeScheduled) {
|
||||
this.principalToBeScheduled = this.principalToBeScheduled.plus(principalToBeScheduled);
|
||||
}
|
||||
|
||||
public void setOutstandingBalance(Money outstandingBalance) {
|
||||
this.outstandingBalance = outstandingBalance;
|
||||
}
|
||||
|
||||
public void addOutstandingBalance(Money outstandingBalance) {
|
||||
this.outstandingBalance = this.outstandingBalance.plus(outstandingBalance);
|
||||
}
|
||||
|
||||
public void reduceOutstandingBalance(Money outstandingBalance) {
|
||||
this.outstandingBalance = this.outstandingBalance.minus(outstandingBalance);
|
||||
}
|
||||
|
||||
public void setOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
|
||||
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
|
||||
}
|
||||
|
||||
public void addOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
|
||||
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.plus(outstandingBalanceAsPerRest);
|
||||
}
|
||||
|
||||
public void reduceOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
|
||||
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.minus(outstandingBalanceAsPerRest);
|
||||
}
|
||||
|
||||
public int getLoanTermInDays() {
|
||||
return this.loanTermInDays;
|
||||
}
|
||||
|
||||
public void addLoanTermInDays(int loanTermInDays) {
|
||||
this.loanTermInDays += loanTermInDays;
|
||||
}
|
||||
|
||||
public Money getTotalOutstandingInterestPaymentDueToGrace() {
|
||||
return this.totalOutstandingInterestPaymentDueToGrace;
|
||||
}
|
||||
|
||||
public void setTotalOutstandingInterestPaymentDueToGrace(Money totalOutstandingInterestPaymentDueToGrace) {
|
||||
this.totalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace;
|
||||
}
|
||||
|
||||
public Map<LocalDate, TreeMap<LocalDate, Money>> getCompoundingDateVariations() {
|
||||
return this.compoundingDateVariations;
|
||||
}
|
||||
|
||||
public MonetaryCurrency getCurrency() {
|
||||
return this.currency;
|
||||
}
|
||||
|
||||
public boolean applyInterestRecalculation() {
|
||||
return this.applyInterestRecalculation;
|
||||
}
|
||||
|
||||
}
|
||||
-212
@@ -1,212 +0,0 @@
|
||||
/**
|
||||
* 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.loanaccount.loanschedule.data;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.RecalculationDetail;
|
||||
|
||||
public class LoanScheduleRecalculationDTO {
|
||||
|
||||
private final int periodNumber;
|
||||
private final int instalmentNumber;
|
||||
private final LocalDate periodStartDate;
|
||||
private final LocalDate actualRepaymentDate;
|
||||
private final Money totalCumulativePrincipal;
|
||||
private final Money totalCumulativeInterest;
|
||||
private final Money totalFeeChargesCharged;
|
||||
private final Money totalPenaltyChargesCharged;
|
||||
private final Money totalRepaymentExpected;
|
||||
private final Money reducePrincipal;
|
||||
private final Map<LocalDate, Money> principalPortionMap;
|
||||
private final Map<LocalDate, Money> latePaymentMap;
|
||||
private final TreeMap<LocalDate, Money> compoundingMap;
|
||||
private final Map<LocalDate, Money> disburseDetailMap;
|
||||
private final Money principalToBeScheduled;
|
||||
private final Money outstandingBalance;
|
||||
private final Money outstandingBalanceAsPerRest;
|
||||
private final List<LoanRepaymentScheduleInstallment> installments;
|
||||
private final Collection<RecalculationDetail> recalculationDetails;
|
||||
private final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor;
|
||||
private final LocalDate scheduleTillDate;
|
||||
private final boolean partialUpdate;
|
||||
|
||||
private LoanScheduleRecalculationDTO(final int periodNumber, final int instalmentNumber, LocalDate periodStartDate,
|
||||
final LocalDate actualRepaymentDate, final Money totalCumulativePrincipal, final Money totalCumulativeInterest,
|
||||
final Money totalFeeChargesCharged, final Money totalPenaltyChargesCharged, final Money totalRepaymentExpected,
|
||||
final Money reducePrincipal, final Map<LocalDate, Money> principalPortionMap, final Map<LocalDate, Money> latePaymentMap,
|
||||
final TreeMap<LocalDate, Money> compoundingMap, final Map<LocalDate, Money> disburseDetailMap, Money principalToBeScheduled,
|
||||
final Money outstandingBalance, final Money outstandingBalanceAsPerRest,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||
final boolean partialUpdate) {
|
||||
this.periodNumber = periodNumber;
|
||||
this.instalmentNumber = instalmentNumber;
|
||||
this.periodStartDate = periodStartDate;
|
||||
this.actualRepaymentDate = actualRepaymentDate;
|
||||
this.totalCumulativePrincipal = totalCumulativePrincipal;
|
||||
this.totalCumulativeInterest = totalCumulativeInterest;
|
||||
this.totalFeeChargesCharged = totalFeeChargesCharged;
|
||||
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
|
||||
this.totalRepaymentExpected = totalRepaymentExpected;
|
||||
this.reducePrincipal = reducePrincipal;
|
||||
this.principalPortionMap = principalPortionMap;
|
||||
this.latePaymentMap = latePaymentMap;
|
||||
this.compoundingMap = compoundingMap;
|
||||
this.disburseDetailMap = disburseDetailMap;
|
||||
this.principalToBeScheduled = principalToBeScheduled;
|
||||
this.outstandingBalance = outstandingBalance;
|
||||
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
|
||||
this.installments = installments;
|
||||
this.recalculationDetails = recalculationDetails;
|
||||
this.loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessor;
|
||||
this.scheduleTillDate = scheduleTillDate;
|
||||
this.partialUpdate = partialUpdate;
|
||||
}
|
||||
|
||||
public static LoanScheduleRecalculationDTO createLoanScheduleDTOForPartialUpdate(final int periodNumber, final int instalmentNumber,
|
||||
LocalDate periodStartDate, final LocalDate actualRepaymentDate, final Money totalCumulativePrincipal,
|
||||
final Money totalCumulativeInterest, final Money totalFeeChargesCharged, final Money totalPenaltyChargesCharged,
|
||||
final Money totalRepaymentExpected, final Money reducePrincipal, final Map<LocalDate, Money> principalPortionMap,
|
||||
final Map<LocalDate, Money> latePaymentMap, final TreeMap<LocalDate, Money> compoundingMap,
|
||||
final Map<LocalDate, Money> disburseDetailMap, Money principalToBeScheduled, final Money outstandingBalance,
|
||||
final Money outstandingBalanceAsPerRest, final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate) {
|
||||
final boolean partialUpdate = true;
|
||||
return new LoanScheduleRecalculationDTO(periodNumber, instalmentNumber, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, disburseDetailMap,
|
||||
principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, installments, recalculationDetails,
|
||||
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate);
|
||||
}
|
||||
|
||||
public static LoanScheduleRecalculationDTO createLoanScheduleDTOForCompleteUpdate(
|
||||
final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate) {
|
||||
final int periodNumber = 1;
|
||||
final int instalmentNumber = 1;
|
||||
final LocalDate periodStartDate = null;
|
||||
final LocalDate actualRepaymentDate = null;
|
||||
final Money totalCumulativePrincipal = null;
|
||||
final Money totalCumulativeInterest = null;
|
||||
final Money totalFeeChargesCharged = null;
|
||||
final Money totalPenaltyChargesCharged = null;
|
||||
final Money totalRepaymentExpected = null;
|
||||
final Money reducePrincipal = null;
|
||||
final Map<LocalDate, Money> principalPortionMap = null;
|
||||
final Map<LocalDate, Money> latePaymentMap = null;
|
||||
final TreeMap<LocalDate, Money> compoundingMap = null;
|
||||
final Map<LocalDate, Money> disburseDetailMap = null;
|
||||
final Money principalToBeScheduled = null;
|
||||
final Money outstandingBalance = null;
|
||||
final Money outstandingBalanceAsPerRest = null;
|
||||
final List<LoanRepaymentScheduleInstallment> installments = null;
|
||||
final boolean partialUpdate = false;
|
||||
return new LoanScheduleRecalculationDTO(periodNumber, instalmentNumber, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, disburseDetailMap,
|
||||
principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, installments, recalculationDetails,
|
||||
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate);
|
||||
}
|
||||
|
||||
public int getPeriodNumber() {
|
||||
return this.periodNumber;
|
||||
}
|
||||
|
||||
public int getInstalmentNumber() {
|
||||
return this.instalmentNumber;
|
||||
}
|
||||
|
||||
public LocalDate getActualRepaymentDate() {
|
||||
return this.actualRepaymentDate;
|
||||
}
|
||||
|
||||
public Money getTotalCumulativePrincipal() {
|
||||
return this.totalCumulativePrincipal;
|
||||
}
|
||||
|
||||
public Money getTotalCumulativeInterest() {
|
||||
return this.totalCumulativeInterest;
|
||||
}
|
||||
|
||||
public Money getTotalFeeChargesCharged() {
|
||||
return this.totalFeeChargesCharged;
|
||||
}
|
||||
|
||||
public Money getTotalPenaltyChargesCharged() {
|
||||
return this.totalPenaltyChargesCharged;
|
||||
}
|
||||
|
||||
public Money getTotalRepaymentExpected() {
|
||||
return this.totalRepaymentExpected;
|
||||
}
|
||||
|
||||
public Money getReducePrincipal() {
|
||||
return this.reducePrincipal;
|
||||
}
|
||||
|
||||
public Map<LocalDate, Money> getPrincipalPortionMap() {
|
||||
return this.principalPortionMap;
|
||||
}
|
||||
|
||||
public Map<LocalDate, Money> getLatePaymentMap() {
|
||||
return this.latePaymentMap;
|
||||
}
|
||||
|
||||
public TreeMap<LocalDate, Money> getCompoundingMap() {
|
||||
return this.compoundingMap;
|
||||
}
|
||||
|
||||
public Map<LocalDate, Money> getDisburseDetailMap() {
|
||||
return this.disburseDetailMap;
|
||||
}
|
||||
|
||||
public Money getOutstandingBalance() {
|
||||
return this.outstandingBalance;
|
||||
}
|
||||
|
||||
public Money getOutstandingBalanceAsPerRest() {
|
||||
return this.outstandingBalanceAsPerRest;
|
||||
}
|
||||
|
||||
public List<LoanRepaymentScheduleInstallment> getInstallments() {
|
||||
return this.installments;
|
||||
}
|
||||
|
||||
public Collection<RecalculationDetail> getRecalculationDetails() {
|
||||
return this.recalculationDetails;
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleTransactionProcessor getLoanRepaymentScheduleTransactionProcessor() {
|
||||
return this.loanRepaymentScheduleTransactionProcessor;
|
||||
}
|
||||
|
||||
public LocalDate getScheduleTillDate() {
|
||||
return this.scheduleTillDate;
|
||||
}
|
||||
|
||||
public boolean isPartialUpdate() {
|
||||
return this.partialUpdate;
|
||||
}
|
||||
|
||||
public LocalDate getPeriodStartDate() {
|
||||
return this.periodStartDate;
|
||||
}
|
||||
|
||||
|
||||
public Money getPrincipalToBeScheduled() {
|
||||
return this.principalToBeScheduled;
|
||||
}
|
||||
|
||||
}
|
||||
+1078
-756
File diff suppressed because it is too large
Load Diff
+12
-31
@@ -53,8 +53,7 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
@SuppressWarnings("unused") final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
|
||||
final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc,
|
||||
final TreeMap<LocalDate, Money> principalVariation, final Map<LocalDate, Money> compoundingMap,
|
||||
final LocalDate periodStartDate, final LocalDate periodEndDate, final int daysForInterestInFullPeriod,
|
||||
final Collection<LoanTermVariationsData> termVariations) {
|
||||
final LocalDate periodStartDate, final LocalDate periodEndDate, final Collection<LoanTermVariationsData> termVariations) {
|
||||
|
||||
LocalDate interestStartDate = periodStartDate;
|
||||
Money interestForThisInstallment = totalCumulativePrincipal.zero();
|
||||
@@ -62,16 +61,15 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
Money compoundedInterest = totalCumulativePrincipal.zero();
|
||||
Money balanceForInterestCalculation = outstandingBalance;
|
||||
Money cumulatingInterestDueToGrace = cumulatingInterestPaymentDueToGrace;
|
||||
final int daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDate, periodEndDate).getDays();
|
||||
Map<LocalDate, BigDecimal> interestRates = new HashMap<>(termVariations.size());
|
||||
for (LoanTermVariationsData loanTermVariation : termVariations) {
|
||||
if (loanTermVariation.getTermVariationType().isInterestRateVariation()
|
||||
&& loanTermVariation.isApplicable(periodStartDate, periodEndDate, periodNumber)) {
|
||||
&& loanTermVariation.isApplicable(periodStartDate, periodEndDate)) {
|
||||
LocalDate fromDate = loanTermVariation.getTermApplicableFrom();
|
||||
if (fromDate == null) {
|
||||
fromDate = periodStartDate;
|
||||
}
|
||||
interestRates.put(fromDate, loanTermVariation.getTermValue());
|
||||
interestRates.put(fromDate, loanTermVariation.getDecimalValue());
|
||||
if (!principalVariation.containsKey(fromDate)) {
|
||||
principalVariation.put(fromDate, balanceForInterestCalculation.zero());
|
||||
}
|
||||
@@ -93,18 +91,9 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
if (interestForDays > 0) {
|
||||
final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace,
|
||||
interestForDays, balanceForInterestCalculation);
|
||||
if (loanApplicationTerms.getInterestCalculationPeriodMethod().isDaily()) {
|
||||
interestForThisInstallment = interestForThisInstallment.plus(result.interest());
|
||||
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
||||
} else {
|
||||
interestForThisInstallment = interestForThisInstallment.plus(calculateInterestForDays(
|
||||
daysForInterestInFullPeriod, result.interest().getAmount(), interestForDays));
|
||||
cumulatingInterestDueToGrace = cumulatingInterestDueToGrace.plus(calculateInterestForDays(
|
||||
daysForInterestInFullPeriod, result.interestPaymentDueToGrace().minus(cumulatingInterestDueToGrace)
|
||||
.getAmount(), interestForDays));
|
||||
}
|
||||
|
||||
balanceForInterestCalculation, interestStartDate, principal.getKey());
|
||||
interestForThisInstallment = interestForThisInstallment.plus(result.interest());
|
||||
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
||||
interestStartDate = principal.getKey();
|
||||
}
|
||||
Money compoundFee = totalCumulativePrincipal.zero();
|
||||
@@ -136,20 +125,12 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
clearMapDetails(periodEndDate, compoundingMap);
|
||||
}
|
||||
}
|
||||
int interestForDays = Days.daysBetween(interestStartDate, periodEndDate).getDays();
|
||||
|
||||
final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace, interestForDays,
|
||||
balanceForInterestCalculation);
|
||||
if (loanApplicationTerms.getInterestCalculationPeriodMethod().isDaily()) {
|
||||
interestForThisInstallment = interestForThisInstallment.plus(result.interest());
|
||||
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
||||
} else {
|
||||
interestForThisInstallment = interestForThisInstallment.plus(calculateInterestForDays(daysForInterestInFullPeriod, result
|
||||
.interest().getAmount(), interestForDays));
|
||||
cumulatingInterestDueToGrace = cumulatingInterestDueToGrace.plus(calculateInterestForDays(daysForInterestInFullPeriod, result
|
||||
.interestPaymentDueToGrace().minus(cumulatingInterestDueToGrace).getAmount(), interestForDays));
|
||||
}
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace,
|
||||
balanceForInterestCalculation, interestStartDate, periodEndDate);
|
||||
interestForThisInstallment = interestForThisInstallment.plus(result.interest());
|
||||
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
||||
|
||||
Money interestForPeriod = interestForThisInstallment;
|
||||
if (interestForPeriod.isGreaterThanZero()) {
|
||||
@@ -157,8 +138,8 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
} else {
|
||||
interestForPeriod = cumulatingInterestDueToGrace.minus(cumulatingInterestPaymentDueToGrace);
|
||||
}
|
||||
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator,
|
||||
daysInPeriodApplicableForInterest, outstandingBalance, periodNumber, mc, interestForPeriod);
|
||||
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, outstandingBalance,
|
||||
periodNumber, mc, interestForPeriod);
|
||||
|
||||
// update cumulative fields for principal & interest
|
||||
final Money interestBroughtFowardDueToGrace = cumulatingInterestDueToGrace;
|
||||
|
||||
+1
@@ -29,4 +29,5 @@ public class DefaultLoanScheduleGeneratorFactory implements LoanScheduleGenerato
|
||||
|
||||
return loanScheduleGenerator;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-6
@@ -14,15 +14,11 @@ import org.mifosplatform.organisation.holiday.service.HolidayUtil;
|
||||
import org.mifosplatform.organisation.workingdays.domain.RepaymentRescheduleType;
|
||||
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarHistory;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.common.domain.DayOfWeekType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
|
||||
@Override
|
||||
@@ -52,6 +48,8 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery(), lastRepaymentDate, loanApplicationTerms.getNthDay(),
|
||||
loanApplicationTerms.getWeekDayType());
|
||||
dueRepaymentPeriodDate = CalendarUtils.adjustDate(dueRepaymentPeriodDate, loanApplicationTerms.getSeedDate(),
|
||||
loanApplicationTerms.getRepaymentPeriodFrequencyType());
|
||||
if (currentCalendar != null) {
|
||||
// If we have currentCalendar object, this means there is a
|
||||
// calendar associated with
|
||||
@@ -78,8 +76,8 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
loanApplicationTerms.getRepaymentEvery(), adjustedDate, loanApplicationTerms.getNthDay(),
|
||||
loanApplicationTerms.getWeekDayType());
|
||||
|
||||
final RepaymentRescheduleType rescheduleType = RepaymentRescheduleType
|
||||
.fromInt(holidayDetailDTO.getWorkingDays().getRepaymentReschedulingType());
|
||||
final RepaymentRescheduleType rescheduleType = RepaymentRescheduleType.fromInt(holidayDetailDTO.getWorkingDays()
|
||||
.getRepaymentReschedulingType());
|
||||
|
||||
/**
|
||||
* Fix for https://mifosforge.jira.com/browse/MIFOSX-1357
|
||||
@@ -91,6 +89,8 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery(), nextDueRepaymentPeriodDate, loanApplicationTerms.getNthDay(),
|
||||
loanApplicationTerms.getWeekDayType());
|
||||
nextDueRepaymentPeriodDate = CalendarUtils.adjustDate(nextDueRepaymentPeriodDate, loanApplicationTerms.getSeedDate(),
|
||||
loanApplicationTerms.getRepaymentPeriodFrequencyType());
|
||||
|
||||
}
|
||||
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate,
|
||||
|
||||
+4
-6
@@ -10,7 +10,6 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
@@ -24,14 +23,13 @@ public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGener
|
||||
final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc,
|
||||
@SuppressWarnings("unused") TreeMap<LocalDate, Money> principalVariation,
|
||||
@SuppressWarnings("unused") Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate,
|
||||
@SuppressWarnings("unused") int daysForInterestInFullPeriod, @SuppressWarnings("unused") Collection<LoanTermVariationsData> termVariations) {
|
||||
final int daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDate, periodEndDate).getDays();
|
||||
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator,
|
||||
daysInPeriodApplicableForInterest, outstandingBalance, periodNumber, mc, null);
|
||||
@SuppressWarnings("unused") Collection<LoanTermVariationsData> termVariations) {
|
||||
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, outstandingBalance,
|
||||
periodNumber, mc, null);
|
||||
|
||||
final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace,
|
||||
daysInPeriodApplicableForInterest, outstandingBalance);
|
||||
outstandingBalance, periodStartDate, periodEndDate);
|
||||
Money interestForThisInstallment = result.interest();
|
||||
|
||||
// update cumulative fields for principal & interest
|
||||
|
||||
+233
-114
@@ -7,14 +7,16 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.Months;
|
||||
import org.joda.time.Period;
|
||||
import org.joda.time.PeriodType;
|
||||
import org.joda.time.Weeks;
|
||||
import org.joda.time.Years;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
@@ -28,6 +30,7 @@ import org.mifosplatform.portfolio.common.domain.NthDayType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
@@ -46,6 +49,7 @@ public final class LoanApplicationTerms {
|
||||
private Integer loanTermFrequency;
|
||||
private final PeriodFrequencyType loanTermPeriodFrequencyType;
|
||||
private Integer numberOfRepayments;
|
||||
private Integer actualNumberOfRepayments;
|
||||
private final Integer repaymentEvery;
|
||||
private final PeriodFrequencyType repaymentPeriodFrequencyType;
|
||||
private final Integer nthDay;
|
||||
@@ -119,12 +123,14 @@ public final class LoanApplicationTerms {
|
||||
|
||||
private BigDecimal fixedPrincipalAmount;
|
||||
|
||||
private BigDecimal currentPeriodFixedEmiAmount;
|
||||
|
||||
private BigDecimal currentPeriodFixedPrincipalAmount;
|
||||
|
||||
private final BigDecimal actualFixedEmiAmount;
|
||||
|
||||
private final BigDecimal maxOutstandingBalance;
|
||||
|
||||
private final List<LoanTermVariationsData> emiAmountVariations;
|
||||
|
||||
private Money totalInterestDue;
|
||||
|
||||
private final DaysInMonthType daysInMonthType;
|
||||
@@ -152,7 +158,11 @@ public final class LoanApplicationTerms {
|
||||
|
||||
private Money approvedPrincipal = null;
|
||||
|
||||
private final Collection<LoanTermVariationsData> loanTermVariations;
|
||||
private final LoanTermVariationsDataWrapper variationsDataWrapper;
|
||||
|
||||
private Money adjustPrincipalForFlatLoans;
|
||||
|
||||
private final LocalDate seedDate;
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
@@ -164,14 +174,13 @@ public final class LoanApplicationTerms {
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final Integer graceOnPrincipalPayment,
|
||||
final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate,
|
||||
final Money inArrearsTolerance, final boolean multiDisburseLoan, final BigDecimal emiAmount,
|
||||
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
||||
final List<LoanTermVariationsData> emiAmountVariations, final Integer graceOnArrearsAgeing,
|
||||
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance, final Integer graceOnArrearsAgeing,
|
||||
final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance,
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar,
|
||||
BigDecimal approvedAmount, Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations) {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
||||
@@ -180,7 +189,7 @@ public final class LoanApplicationTerms {
|
||||
interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment,
|
||||
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
|
||||
disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
||||
disbursementDatas, maxOutstandingBalance, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
||||
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
@@ -192,23 +201,21 @@ public final class LoanApplicationTerms {
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance,
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount,
|
||||
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
||||
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance,
|
||||
final InterestRecalculationCompoundingMethod compoundingMethod, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LocalDate interestChargedFromDate, final BigDecimal principalThresholdForLastInstalment,
|
||||
final Integer installmentAmountInMultiplesOf, final RecalculationFrequencyType recalculationFrequencyType,
|
||||
final CalendarInstance restCalendarInstance, final InterestRecalculationCompoundingMethod compoundingMethod,
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, BigDecimal approvedAmount, BigDecimal annualNominalInterestRate,
|
||||
Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
List<LoanTermVariationsData> loanTermVariations) {
|
||||
final Calendar loanCalendar = null;
|
||||
|
||||
return assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDay, dayOfWeek,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsTolerance,
|
||||
loanProductRelatedDetail, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations,
|
||||
interestChargedFromDate, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType,
|
||||
restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
loanPreClosureInterestCalculationStrategy, rescheduleStrategyMethod, loanCalendar, approvedAmount,
|
||||
annualNominalInterestRate, loanTermVariations);
|
||||
loanProductRelatedDetail, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, interestChargedFromDate,
|
||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType, restCalendarInstance,
|
||||
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy,
|
||||
rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -217,14 +224,13 @@ public final class LoanApplicationTerms {
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance,
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount,
|
||||
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
||||
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance,
|
||||
final InterestRecalculationCompoundingMethod compoundingMethod, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LocalDate interestChargedFromDate, final BigDecimal principalThresholdForLastInstalment,
|
||||
final Integer installmentAmountInMultiplesOf, final RecalculationFrequencyType recalculationFrequencyType,
|
||||
final CalendarInstance restCalendarInstance, final InterestRecalculationCompoundingMethod compoundingMethod,
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar, BigDecimal approvedAmount,
|
||||
BigDecimal annualNominalInterestRate, Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -251,11 +257,11 @@ public final class LoanApplicationTerms {
|
||||
interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
||||
principalMoney, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance,
|
||||
multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations,
|
||||
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, loanProductRelatedDetail.getGraceOnDueDate(),
|
||||
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod, compoundingMethod,
|
||||
restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy,
|
||||
loanCalendar, approvedAmount, loanTermVariations);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -263,13 +269,12 @@ public final class LoanApplicationTerms {
|
||||
final LocalDate repaymentsStartingFromDate, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final Money inArrearsTolerance, final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan,
|
||||
final BigDecimal emiAmount, final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
||||
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
|
||||
final LoanInterestRecalculationDetails interestRecalculationDetails, final CalendarInstance restCalendarInstance,
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType, final BigDecimal principalThresholdForLastInstalment,
|
||||
final Integer installmentAmountInMultiplesOf,
|
||||
final LocalDate interestChargedFromDate, final LoanInterestRecalculationDetails interestRecalculationDetails,
|
||||
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType,
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, BigDecimal approvedAmount,
|
||||
final BigDecimal annualNominalInterestRate, final Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
final BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -303,11 +308,31 @@ public final class LoanApplicationTerms {
|
||||
interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment,
|
||||
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
|
||||
disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(),
|
||||
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
|
||||
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
disbursementDatas, maxOutstandingBalance, loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType,
|
||||
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final LoanApplicationTerms applicationTerms,
|
||||
final List<LoanTermVariationsData> loanTermVariations) {
|
||||
return new LoanApplicationTerms(applicationTerms.currency, applicationTerms.loanTermFrequency,
|
||||
applicationTerms.loanTermPeriodFrequencyType, applicationTerms.numberOfRepayments, applicationTerms.repaymentEvery,
|
||||
applicationTerms.repaymentPeriodFrequencyType, applicationTerms.nthDay, applicationTerms.weekDayType,
|
||||
applicationTerms.amortizationMethod, applicationTerms.interestMethod, applicationTerms.interestRatePerPeriod,
|
||||
applicationTerms.interestRatePeriodFrequencyType, applicationTerms.annualNominalInterestRate,
|
||||
applicationTerms.interestCalculationPeriodMethod, applicationTerms.principal, applicationTerms.expectedDisbursementDate,
|
||||
applicationTerms.repaymentsStartingFromDate, applicationTerms.calculatedRepaymentsStartingFromDate,
|
||||
applicationTerms.principalGrace, applicationTerms.interestPaymentGrace, applicationTerms.interestChargingGrace,
|
||||
applicationTerms.interestChargedFromDate, applicationTerms.inArrearsTolerance, applicationTerms.multiDisburseLoan,
|
||||
applicationTerms.actualFixedEmiAmount, applicationTerms.disbursementDatas, applicationTerms.maxOutstandingBalance,
|
||||
applicationTerms.graceOnArrearsAgeing, applicationTerms.daysInMonthType, applicationTerms.daysInYearType,
|
||||
applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
|
||||
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance,
|
||||
applicationTerms.recalculationFrequencyType, applicationTerms.compoundingCalendarInstance,
|
||||
applicationTerms.compoundingFrequencyType, applicationTerms.principalThresholdForLastInstalment,
|
||||
applicationTerms.installmentAmountInMultiplesOf, applicationTerms.preClosureInterestCalculationStrategy,
|
||||
applicationTerms.loanCalendar, applicationTerms.approvedPrincipal.getAmount(), loanTermVariations);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
@@ -320,15 +345,15 @@ public final class LoanApplicationTerms {
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final Integer principalGrace, final Integer interestPaymentGrace,
|
||||
final Integer interestChargingGrace, final LocalDate interestChargedFromDate, final Money inArrearsTolerance,
|
||||
final boolean multiDisburseLoan, final BigDecimal emiAmount, final List<DisbursementData> disbursementDatas,
|
||||
final BigDecimal maxOutstandingBalance, final List<LoanTermVariationsData> emiAmountVariations,
|
||||
final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
|
||||
final boolean isInterestRecalculationEnabled, final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
|
||||
final BigDecimal maxOutstandingBalance, final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType,
|
||||
final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
|
||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod,
|
||||
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType,
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar,
|
||||
BigDecimal approvedAmount, Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations) {
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
||||
@@ -361,7 +386,6 @@ public final class LoanApplicationTerms {
|
||||
this.actualFixedEmiAmount = emiAmount;
|
||||
this.disbursementDatas = disbursementDatas;
|
||||
this.maxOutstandingBalance = maxOutstandingBalance;
|
||||
this.emiAmountVariations = emiAmountVariations;
|
||||
this.graceOnArrearsAgeing = graceOnArrearsAgeing;
|
||||
this.daysInMonthType = daysInMonthType;
|
||||
this.daysInYearType = daysInYearType;
|
||||
@@ -378,7 +402,14 @@ public final class LoanApplicationTerms {
|
||||
|
||||
this.loanCalendar = loanCalendar;
|
||||
this.approvedPrincipal = Money.of(principal.getCurrency(), approvedAmount);
|
||||
this.loanTermVariations = loanTermVariations;
|
||||
this.variationsDataWrapper = new LoanTermVariationsDataWrapper(loanTermVariations);
|
||||
this.actualNumberOfRepayments = numberOfRepayments + getLoanTermVariations().adjustNumberOfRepayments();
|
||||
this.adjustPrincipalForFlatLoans = principal.zero();
|
||||
if (this.calculatedRepaymentsStartingFromDate == null) {
|
||||
this.seedDate = this.expectedDisbursementDate;
|
||||
} else {
|
||||
this.seedDate = this.calculatedRepaymentsStartingFromDate;
|
||||
}
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -398,7 +429,7 @@ public final class LoanApplicationTerms {
|
||||
if (difference.isLessThan(principalThreshold)) {
|
||||
adjusted = principalForPeriod.plus(difference.abs());
|
||||
}
|
||||
} else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
|
||||
} else if (isLastRepaymentPeriod(this.actualNumberOfRepayments, periodNumber)) {
|
||||
|
||||
final Money difference = totalCumulativePrincipalToDate.minus(this.principal);
|
||||
if (difference.isLessThanZero()) {
|
||||
@@ -421,7 +452,7 @@ public final class LoanApplicationTerms {
|
||||
// paid too much interest, subtract amount that overpays from
|
||||
// interest paid for period.
|
||||
adjusted = interestForThisPeriod.minus(totalInterestRemaining.abs());
|
||||
} else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
|
||||
} else if (isLastRepaymentPeriod(this.actualNumberOfRepayments, periodNumber)) {
|
||||
final Money interestDifference = totalCumulativeInterestToDate.minus(totalInterestDueForLoan);
|
||||
if (interestDifference.isLessThanZero()) {
|
||||
adjusted = interestForThisPeriod.plus(interestDifference.abs());
|
||||
@@ -463,22 +494,19 @@ public final class LoanApplicationTerms {
|
||||
return totalInterestCharged;
|
||||
}
|
||||
|
||||
public Money calculateTotalPrincipalForPeriod(final PaymentPeriodsInOneYearCalculator calculator, final int daysInPeriod,
|
||||
final Money outstandingBalance, final int periodNumber, final MathContext mc, Money interestForThisInstallment) {
|
||||
public Money calculateTotalPrincipalForPeriod(final PaymentPeriodsInOneYearCalculator calculator, final Money outstandingBalance,
|
||||
final int periodNumber, final MathContext mc, Money interestForThisInstallment) {
|
||||
|
||||
Money principalForInstallment = this.principal.zero();
|
||||
|
||||
switch (this.interestMethod) {
|
||||
case FLAT:
|
||||
principalForInstallment = calculateTotalPrincipalPerPeriodWithoutGrace(mc);
|
||||
if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
|
||||
principalForInstallment = Money.zero(principalForInstallment.getCurrency());
|
||||
}
|
||||
principalForInstallment = calculateTotalPrincipalPerPeriodWithoutGrace(mc, periodNumber);
|
||||
break;
|
||||
case DECLINING_BALANCE:
|
||||
switch (this.amortizationMethod) {
|
||||
case EQUAL_INSTALLMENTS:
|
||||
Money totalPmtForThisInstallment = pmtForInstallment(calculator, daysInPeriod, outstandingBalance, periodNumber, mc);
|
||||
Money totalPmtForThisInstallment = pmtForInstallment(calculator, outstandingBalance, periodNumber, mc);
|
||||
principalForInstallment = calculatePrincipalDueForInstallment(periodNumber, totalPmtForThisInstallment,
|
||||
interestForThisInstallment);
|
||||
break;
|
||||
@@ -496,22 +524,47 @@ public final class LoanApplicationTerms {
|
||||
return principalForInstallment;
|
||||
}
|
||||
|
||||
public Money pmtForInstallment(final PaymentPeriodsInOneYearCalculator calculator, final int daysInPeriod,
|
||||
final Money outstandingBalance, final int periodNumber, final MathContext mc) {
|
||||
public Money pmtForInstallment(final PaymentPeriodsInOneYearCalculator calculator, final Money outstandingBalance,
|
||||
final int periodNumber, final MathContext mc) {
|
||||
// Calculate exact period from disbursement date
|
||||
final LocalDate periodStartDate = getExpectedDisbursementDate().withDayOfMonth(1);
|
||||
final LocalDate periodEndDate = getPeriodEndDate(periodStartDate);
|
||||
// equal installments
|
||||
final int periodsElapsed = periodNumber - 1;
|
||||
// with periodic interest for default month and year for
|
||||
// equal installment
|
||||
final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(calculator, mc, daysInPeriod,
|
||||
DaysInMonthType.DAYS_30, DaysInYearType.DAYS_365);
|
||||
final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(calculator, mc, DaysInMonthType.DAYS_30,
|
||||
DaysInYearType.DAYS_365, periodStartDate, periodEndDate);
|
||||
Money totalPmtForThisInstallment = calculateTotalDueForEqualInstallmentRepaymentPeriod(periodicInterestRateForRepaymentPeriod,
|
||||
outstandingBalance, periodsElapsed);
|
||||
return totalPmtForThisInstallment;
|
||||
}
|
||||
|
||||
private LocalDate getPeriodEndDate(final LocalDate startDate) {
|
||||
LocalDate dueRepaymentPeriodDate = startDate;
|
||||
switch (this.repaymentPeriodFrequencyType) {
|
||||
case DAYS:
|
||||
dueRepaymentPeriodDate = startDate.plusDays(this.repaymentEvery);
|
||||
break;
|
||||
case WEEKS:
|
||||
dueRepaymentPeriodDate = startDate.plusWeeks(this.repaymentEvery);
|
||||
break;
|
||||
case MONTHS:
|
||||
dueRepaymentPeriodDate = startDate.plusMonths(this.repaymentEvery);
|
||||
break;
|
||||
case YEARS:
|
||||
dueRepaymentPeriodDate = startDate.plusYears(this.repaymentEvery);
|
||||
break;
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
return dueRepaymentPeriodDate;
|
||||
}
|
||||
|
||||
public PrincipalInterest calculateTotalInterestForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
|
||||
final double interestCalculationGraceOnRepaymentPeriodFraction, final int periodNumber, final MathContext mc,
|
||||
final Money cumulatingInterestPaymentDueToGrace, final int daysInPeriodApplicableForInterest, final Money outstandingBalance) {
|
||||
final Money cumulatingInterestPaymentDueToGrace, final Money outstandingBalance, final LocalDate periodStartDate,
|
||||
final LocalDate periodEndDate) {
|
||||
|
||||
Money interestForInstallment = this.principal.zero();
|
||||
Money interestBroughtForwardDueToGrace = cumulatingInterestPaymentDueToGrace.copy();
|
||||
@@ -541,11 +594,11 @@ public final class LoanApplicationTerms {
|
||||
case DECLINING_BALANCE:
|
||||
|
||||
final Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(
|
||||
calculator, mc, daysInPeriodApplicableForInterest, outstandingBalance);
|
||||
calculator, mc, outstandingBalance, periodStartDate, periodEndDate);
|
||||
|
||||
final Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(
|
||||
calculator, interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriodApplicableForInterest,
|
||||
outstandingBalance, periodNumber);
|
||||
calculator, interestCalculationGraceOnRepaymentPeriodFraction, mc, outstandingBalance, periodNumber,
|
||||
periodStartDate, periodEndDate);
|
||||
|
||||
interestForInstallment = interestForThisInstallmentAfterGrace;
|
||||
if (interestForThisInstallmentAfterGrace.isGreaterThanZero()) {
|
||||
@@ -628,12 +681,45 @@ public final class LoanApplicationTerms {
|
||||
case INVALID:
|
||||
break;
|
||||
case SAME_AS_REPAYMENT_PERIOD:
|
||||
LocalDate startDate = getExpectedDisbursementDate();
|
||||
periodsInLoanTerm = calculatePeriodsBetweenDates(startDate, this.loanEndDate);
|
||||
break;
|
||||
}
|
||||
|
||||
return periodsInLoanTerm;
|
||||
}
|
||||
|
||||
public BigDecimal calculatePeriodsBetweenDates(final LocalDate startDate, final LocalDate endDate) {
|
||||
BigDecimal numberOfPeriods = BigDecimal.ZERO;
|
||||
switch (this.repaymentPeriodFrequencyType) {
|
||||
case DAYS:
|
||||
int numberOfDays = Days.daysBetween(startDate, endDate).getDays();
|
||||
numberOfPeriods = BigDecimal.valueOf((double) numberOfDays);
|
||||
break;
|
||||
case WEEKS:
|
||||
int numberOfWeeks = Weeks.weeksBetween(startDate, endDate).getWeeks();
|
||||
int daysLeftAfterWeeks = Days.daysBetween(startDate.plusWeeks(numberOfWeeks), endDate).getDays();
|
||||
numberOfPeriods = numberOfPeriods.add(BigDecimal.valueOf(numberOfWeeks)).add(
|
||||
BigDecimal.valueOf((double) daysLeftAfterWeeks / 7));
|
||||
break;
|
||||
case MONTHS:
|
||||
int numberOfMonths = Months.monthsBetween(startDate, endDate).getMonths();
|
||||
int daysLeftAfterMonths = Days.daysBetween(startDate.plusMonths(numberOfMonths), endDate).getDays();
|
||||
numberOfPeriods = numberOfPeriods.add(BigDecimal.valueOf(numberOfMonths)).add(
|
||||
BigDecimal.valueOf((double) daysLeftAfterMonths / 30));
|
||||
break;
|
||||
case YEARS:
|
||||
int numberOfYears = Years.yearsBetween(startDate, endDate).getYears();
|
||||
int daysLeftAfterYears = Days.daysBetween(startDate.plusYears(numberOfYears), endDate).getDays();
|
||||
numberOfPeriods = numberOfPeriods.add(BigDecimal.valueOf(numberOfYears)).add(
|
||||
BigDecimal.valueOf((double) daysLeftAfterYears / 365));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return numberOfPeriods;
|
||||
}
|
||||
|
||||
public void updateLoanEndDate(final LocalDate loanEndDate) {
|
||||
this.loanEndDate = loanEndDate;
|
||||
}
|
||||
@@ -642,12 +728,23 @@ public final class LoanApplicationTerms {
|
||||
|
||||
final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc);
|
||||
|
||||
return totalInterestForLoanTerm.dividedBy(Long.valueOf(this.numberOfRepayments), mc.getRoundingMode());
|
||||
return totalInterestForLoanTerm.dividedBy(Long.valueOf(this.actualNumberOfRepayments), mc.getRoundingMode());
|
||||
}
|
||||
|
||||
private Money calculateTotalPrincipalPerPeriodWithoutGrace(final MathContext mc) {
|
||||
private Money calculateTotalPrincipalPerPeriodWithoutGrace(final MathContext mc, final int periodNumber) {
|
||||
final int totalRepaymentsWithCapitalPayment = calculateNumberOfRepaymentsWithPrincipalPayment();
|
||||
return this.principal.dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode());
|
||||
Money principalPerPeriod = this.principal.dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode()).plus(
|
||||
this.adjustPrincipalForFlatLoans);
|
||||
if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
|
||||
principalPerPeriod = principalPerPeriod.zero();
|
||||
}
|
||||
if (!isPrincipalGraceApplicableForThisPeriod(periodNumber) && currentPeriodFixedPrincipalAmount != null) {
|
||||
this.adjustPrincipalForFlatLoans = this.adjustPrincipalForFlatLoans.plus(principalPerPeriod.minus(
|
||||
currentPeriodFixedPrincipalAmount).dividedBy(this.actualNumberOfRepayments - periodNumber, mc.getRoundingMode()));
|
||||
principalPerPeriod = this.principal.zero().plus(currentPeriodFixedPrincipalAmount);
|
||||
|
||||
}
|
||||
return principalPerPeriod;
|
||||
}
|
||||
|
||||
private PrincipalInterest calculateTotalFlatInterestForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
|
||||
@@ -693,7 +790,7 @@ public final class LoanApplicationTerms {
|
||||
final Money totalInterestFree = interestPerGracePeriod.multipliedBy(getInterestChargingGrace());
|
||||
final Money realTotalInterestForLoan = totalInterestForLoanTerm.minus(totalInterestFree);
|
||||
|
||||
final Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.numberOfRepayments);
|
||||
final Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.actualNumberOfRepayments);
|
||||
|
||||
interestForInstallment = realTotalInterestForLoan
|
||||
.dividedBy(BigDecimal.valueOf(interestPaymentDuePeriods), mc.getRoundingMode());
|
||||
@@ -702,8 +799,8 @@ public final class LoanApplicationTerms {
|
||||
return interestForInstallment;
|
||||
}
|
||||
|
||||
private BigDecimal periodicInterestRate(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc, final int actualDays,
|
||||
final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType) {
|
||||
private BigDecimal periodicInterestRate(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc,
|
||||
final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, LocalDate periodStartDate, LocalDate periodEndDate) {
|
||||
|
||||
final long loanTermPeriodsInOneYear = calculatePeriodsInOneYear(calculator);
|
||||
|
||||
@@ -711,13 +808,13 @@ public final class LoanApplicationTerms {
|
||||
final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
|
||||
|
||||
BigDecimal periodicInterestRate = BigDecimal.ZERO;
|
||||
|
||||
BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsBetweenDates(periodStartDate, periodEndDate);
|
||||
switch (this.interestCalculationPeriodMethod) {
|
||||
case INVALID:
|
||||
break;
|
||||
case DAILY:
|
||||
// For daily work out number of days in the period
|
||||
int numberOfDaysInPeriod = actualDays;
|
||||
BigDecimal numberOfDaysInPeriod = BigDecimal.valueOf(Days.daysBetween(periodStartDate, periodEndDate).getDays());
|
||||
|
||||
final BigDecimal oneDayOfYearInterestRate = this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc)
|
||||
.divide(divisor, mc);
|
||||
@@ -726,38 +823,36 @@ public final class LoanApplicationTerms {
|
||||
case INVALID:
|
||||
break;
|
||||
case DAYS:
|
||||
final BigDecimal loanTermFrequencyBigDecimal = BigDecimal.valueOf(numberOfDaysInPeriod);
|
||||
periodicInterestRate = oneDayOfYearInterestRate.multiply(loanTermFrequencyBigDecimal, mc);
|
||||
periodicInterestRate = oneDayOfYearInterestRate.multiply(numberOfDaysInPeriod, mc);
|
||||
break;
|
||||
case WEEKS:
|
||||
periodicInterestRate = oneDayOfYearInterestRate.multiply(BigDecimal.valueOf(numberOfDaysInPeriod), mc);
|
||||
periodicInterestRate = oneDayOfYearInterestRate.multiply(numberOfDaysInPeriod, mc);
|
||||
break;
|
||||
case MONTHS:
|
||||
if (daysInMonthType.isDaysInMonth_30()) {
|
||||
numberOfDaysInPeriod = this.repaymentEvery * 30;
|
||||
numberOfDaysInPeriod = loanTermFrequencyBigDecimal.multiply(BigDecimal.valueOf(30), mc);
|
||||
}
|
||||
periodicInterestRate = oneDayOfYearInterestRate.multiply(BigDecimal.valueOf(numberOfDaysInPeriod), mc);
|
||||
periodicInterestRate = oneDayOfYearInterestRate.multiply(numberOfDaysInPeriod, mc);
|
||||
break;
|
||||
case YEARS:
|
||||
switch (daysInYearType) {
|
||||
case DAYS_360:
|
||||
numberOfDaysInPeriod = this.repaymentEvery * 360;
|
||||
numberOfDaysInPeriod = loanTermFrequencyBigDecimal.multiply(BigDecimal.valueOf(360), mc);
|
||||
break;
|
||||
case DAYS_364:
|
||||
numberOfDaysInPeriod = this.repaymentEvery * 364;
|
||||
numberOfDaysInPeriod = loanTermFrequencyBigDecimal.multiply(BigDecimal.valueOf(364), mc);
|
||||
break;
|
||||
case DAYS_365:
|
||||
numberOfDaysInPeriod = this.repaymentEvery * 365;
|
||||
numberOfDaysInPeriod = loanTermFrequencyBigDecimal.multiply(BigDecimal.valueOf(365), mc);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
periodicInterestRate = oneDayOfYearInterestRate.multiply(BigDecimal.valueOf(numberOfDaysInPeriod), mc);
|
||||
periodicInterestRate = oneDayOfYearInterestRate.multiply(numberOfDaysInPeriod, mc);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SAME_AS_REPAYMENT_PERIOD:
|
||||
final BigDecimal loanTermFrequencyBigDecimal = BigDecimal.valueOf(this.repaymentEvery);
|
||||
periodicInterestRate = this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
||||
.multiply(loanTermFrequencyBigDecimal);
|
||||
break;
|
||||
@@ -808,7 +903,7 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
|
||||
private int calculateNumberOfRepaymentsWithPrincipalPayment() {
|
||||
return this.numberOfRepayments - getPrincipalGrace();
|
||||
return this.actualNumberOfRepayments - getPrincipalGrace();
|
||||
}
|
||||
|
||||
private Integer calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(final Integer totalNumberOfRepaymentPeriods) {
|
||||
@@ -861,11 +956,11 @@ public final class LoanApplicationTerms {
|
||||
|
||||
private double paymentPerPeriod(final BigDecimal periodicInterestRate, final Money balance, final int periodsElapsed) {
|
||||
|
||||
if (this.fixedEmiAmount == null) {
|
||||
if (getFixedEmiAmount() == null) {
|
||||
final double futureValue = 0;
|
||||
final double principalDouble = balance.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
|
||||
|
||||
final Integer periodsRemaining = this.numberOfRepayments - periodsElapsed;
|
||||
final Integer periodsRemaining = this.actualNumberOfRepayments - periodsElapsed;
|
||||
|
||||
double installmentAmount = FinanicalFunctions.pmt(periodicInterestRate.doubleValue(), periodsRemaining.doubleValue(),
|
||||
principalDouble, futureValue, false);
|
||||
@@ -875,26 +970,27 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
setFixedEmiAmount(BigDecimal.valueOf(installmentAmount));
|
||||
}
|
||||
return this.fixedEmiAmount.doubleValue();
|
||||
return getFixedEmiAmount().doubleValue();
|
||||
}
|
||||
|
||||
private Money calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(final PaymentPeriodsInOneYearCalculator calculator,
|
||||
final MathContext mc, final int daysInPeriod, final Money outstandingBalance) {
|
||||
final MathContext mc, final Money outstandingBalance, LocalDate periodStartDate, LocalDate periodEndDate) {
|
||||
|
||||
Money interestDue = Money.zero(outstandingBalance.getCurrency());
|
||||
|
||||
final BigDecimal periodicInterestRate = periodicInterestRate(calculator, mc, daysInPeriod, this.daysInMonthType,
|
||||
this.daysInYearType);
|
||||
final BigDecimal periodicInterestRate = periodicInterestRate(calculator, mc, this.daysInMonthType, this.daysInYearType,
|
||||
periodStartDate, periodEndDate);
|
||||
interestDue = outstandingBalance.multiplyRetainScale(periodicInterestRate, mc.getRoundingMode());
|
||||
|
||||
return interestDue;
|
||||
}
|
||||
|
||||
private Money calculateDecliningInterestDueForInstallmentAfterApplyingGrace(final PaymentPeriodsInOneYearCalculator calculator,
|
||||
final double interestCalculationGraceOnRepaymentPeriodFraction, final MathContext mc, final int daysInPeriod,
|
||||
final Money outstandingBalance, final int periodNumber) {
|
||||
final double interestCalculationGraceOnRepaymentPeriodFraction, final MathContext mc, final Money outstandingBalance,
|
||||
final int periodNumber, LocalDate periodStartDate, LocalDate periodEndDate) {
|
||||
|
||||
Money interest = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator, mc, daysInPeriod, outstandingBalance);
|
||||
Money interest = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator, mc, outstandingBalance,
|
||||
periodStartDate, periodEndDate);
|
||||
|
||||
if (isInterestPaymentGraceApplicableForThisPeriod(periodNumber)) {
|
||||
interest = interest.zero();
|
||||
@@ -927,15 +1023,13 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
|
||||
private Money calculateEqualPrincipalDueForInstallment(final MathContext mc, final int periodNumber) {
|
||||
|
||||
final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfPrincipalPaymentPeriods(this.numberOfRepayments);
|
||||
Money principal = this.principal;
|
||||
if (this.fixedPrincipalAmount == null) {
|
||||
final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfPrincipalPaymentPeriods(this.actualNumberOfRepayments);
|
||||
principal = this.principal.dividedBy(numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
|
||||
this.fixedPrincipalAmount = principal.getAmount();
|
||||
} else {
|
||||
principal = Money.of(getCurrency(), this.fixedPrincipalAmount);
|
||||
}
|
||||
principal = Money.of(getCurrency(), getFixedPrincipalAmount());
|
||||
|
||||
if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
|
||||
principal = principal.zero();
|
||||
@@ -944,11 +1038,15 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
|
||||
public void updateFixedPrincipalAmount(final MathContext mc, final int periodNumber, final Money outstandingAmount) {
|
||||
final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfPrincipalPaymentPeriods(this.numberOfRepayments);
|
||||
final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfPrincipalPaymentPeriods(this.actualNumberOfRepayments);
|
||||
Money principal = outstandingAmount.dividedBy(numberOfPrincipalPaymentPeriods - periodNumber + 1, mc.getRoundingMode());
|
||||
this.fixedPrincipalAmount = principal.getAmount();
|
||||
}
|
||||
|
||||
public void setFixedPrincipalAmount(BigDecimal fixedPrincipalAmount) {
|
||||
this.fixedPrincipalAmount = fixedPrincipalAmount;
|
||||
}
|
||||
|
||||
private Money calculatePrincipalDueForInstallment(final int periodNumber, final Money totalDuePerInstallment, final Money periodInterest) {
|
||||
|
||||
Money principal = totalDuePerInstallment.minus(periodInterest);
|
||||
@@ -1067,7 +1165,11 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
|
||||
public BigDecimal getFixedEmiAmount() {
|
||||
return this.fixedEmiAmount;
|
||||
BigDecimal fixedEmiAmount = this.fixedEmiAmount;
|
||||
if (getCurrentPeriodFixedEmiAmount() != null) {
|
||||
fixedEmiAmount = getCurrentPeriodFixedEmiAmount();
|
||||
}
|
||||
return fixedEmiAmount;
|
||||
}
|
||||
|
||||
public Integer getNthDay() {
|
||||
@@ -1078,19 +1180,6 @@ public final class LoanApplicationTerms {
|
||||
return this.weekDayType;
|
||||
}
|
||||
|
||||
public void setFixedEmiAmountForPeriod(LocalDate periodDate) {
|
||||
LocalDate startDate = expectedDisbursementDate;
|
||||
for (LoanTermVariationsData loanVariationTermsData : this.emiAmountVariations) {
|
||||
if (!periodDate.isBefore(loanVariationTermsData.getTermApplicableFrom())
|
||||
&& !startDate.isAfter(loanVariationTermsData.getTermApplicableFrom())) {
|
||||
if (loanVariationTermsData.getTermValue() != null) {
|
||||
this.fixedEmiAmount = loanVariationTermsData.getTermValue();
|
||||
startDate = loanVariationTermsData.getTermApplicableFrom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setFixedEmiAmount(BigDecimal fixedEmiAmount) {
|
||||
this.fixedEmiAmount = fixedEmiAmount;
|
||||
}
|
||||
@@ -1185,6 +1274,8 @@ public final class LoanApplicationTerms {
|
||||
|
||||
public void updateNumberOfRepayments(final Integer numberOfRepayments) {
|
||||
this.numberOfRepayments = numberOfRepayments;
|
||||
this.actualNumberOfRepayments = numberOfRepayments + getLoanTermVariations().adjustNumberOfRepayments();
|
||||
|
||||
}
|
||||
|
||||
public void updatePrincipalGrace(final Integer principalGrace) {
|
||||
@@ -1259,11 +1350,39 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
|
||||
public BigDecimal getFixedPrincipalAmount() {
|
||||
return this.fixedPrincipalAmount;
|
||||
BigDecimal fixedPrincipalAmount = this.fixedPrincipalAmount;
|
||||
if (getCurrentPeriodFixedPrincipalAmount() != null) {
|
||||
fixedPrincipalAmount = getCurrentPeriodFixedPrincipalAmount();
|
||||
}
|
||||
return fixedPrincipalAmount;
|
||||
}
|
||||
|
||||
public Collection<LoanTermVariationsData> getLoanTermVariations() {
|
||||
return this.loanTermVariations;
|
||||
public LoanTermVariationsDataWrapper getLoanTermVariations() {
|
||||
return this.variationsDataWrapper;
|
||||
}
|
||||
|
||||
public BigDecimal getCurrentPeriodFixedEmiAmount() {
|
||||
return this.currentPeriodFixedEmiAmount;
|
||||
}
|
||||
|
||||
public void setCurrentPeriodFixedEmiAmount(BigDecimal currentPeriodFixedEmiAmount) {
|
||||
this.currentPeriodFixedEmiAmount = currentPeriodFixedEmiAmount;
|
||||
}
|
||||
|
||||
public BigDecimal getCurrentPeriodFixedPrincipalAmount() {
|
||||
return this.currentPeriodFixedPrincipalAmount;
|
||||
}
|
||||
|
||||
public void setCurrentPeriodFixedPrincipalAmount(BigDecimal currentPeriodFixedPrincipalAmount) {
|
||||
this.currentPeriodFixedPrincipalAmount = currentPeriodFixedPrincipalAmount;
|
||||
}
|
||||
|
||||
public Integer fetchNumberOfRepaymentsAfterExceptions() {
|
||||
return this.actualNumberOfRepayments;
|
||||
}
|
||||
|
||||
public LocalDate getSeedDate() {
|
||||
return this.seedDate;
|
||||
}
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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.loanaccount.loanschedule.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ScheduleDateException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ScheduleDateException(final String defaultUserMessage, final Object... defaultUserMessageArgs) {
|
||||
super("error.msg.loanschedule.schedule.due.date.not.valid", defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
|
||||
}
|
||||
+382
-18
@@ -10,15 +10,25 @@ import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.holiday.domain.HolidayRepository;
|
||||
import org.mifosplatform.organisation.holiday.domain.HolidayStatusType;
|
||||
@@ -36,6 +46,7 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarFrequencyType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarRepository;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarType;
|
||||
import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException;
|
||||
@@ -57,10 +68,14 @@ import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationsComparator;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateException;
|
||||
@@ -70,7 +85,9 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicati
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.serialization.VariableLoanScheduleFromApiJsonValidator;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeAssembler;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
@@ -80,9 +97,11 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductInterestRecalculationDetails;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductVariableInstallmentConfig;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -106,6 +125,10 @@ public class LoanScheduleAssembler {
|
||||
private final GroupRepositoryWrapper groupRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
private final VariableLoanScheduleFromApiJsonValidator variableLoanScheduleFromApiJsonValidator;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final PlatformSecurityContext context;
|
||||
private final LoanUtilService loanUtilService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
|
||||
@@ -115,7 +138,10 @@ public class LoanScheduleAssembler {
|
||||
final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService,
|
||||
final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService,
|
||||
final VariableLoanScheduleFromApiJsonValidator variableLoanScheduleFromApiJsonValidator,
|
||||
final CalendarInstanceRepository calendarInstanceRepository, final PlatformSecurityContext context,
|
||||
final LoanUtilService loanUtilService) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
@@ -129,6 +155,10 @@ public class LoanScheduleAssembler {
|
||||
this.groupRepository = groupRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
this.variableLoanScheduleFromApiJsonValidator = variableLoanScheduleFromApiJsonValidator;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.context = context;
|
||||
this.loanUtilService = loanUtilService;
|
||||
}
|
||||
|
||||
public LoanApplicationTerms assembleLoanTerms(final JsonElement element) {
|
||||
@@ -256,11 +286,6 @@ public class LoanScheduleAssembler {
|
||||
|
||||
final List<DisbursementData> disbursementDatas = fetchDisbursementData(element.getAsJsonObject());
|
||||
|
||||
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<>();
|
||||
LoanTermVariationsData data = new LoanTermVariationsData(null,
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount);
|
||||
loanVariationTermsData.add(data);
|
||||
|
||||
/**
|
||||
* Interest recalculation settings copy from product definition
|
||||
*/
|
||||
@@ -307,28 +332,29 @@ public class LoanScheduleAssembler {
|
||||
|
||||
final Integer installmentAmountInMultiplesOf = loanProduct.getInstallmentAmountInMultiplesOf();
|
||||
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
final BigDecimal interestRateDiff = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
LoanApiConstants.interestRateDifferentialParameterName, element);
|
||||
final Boolean isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(
|
||||
LoanApiConstants.isFloatingInterestRateParameterName, element);
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = null;
|
||||
try{
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
}catch(final FloatingRateNotFoundException ex){
|
||||
// Do not do anything
|
||||
try {
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate().getRatePeriods();
|
||||
} catch (final FloatingRateNotFoundException ex) {
|
||||
// Do not do anything
|
||||
}
|
||||
FloatingRateDTO floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, expectedDisbursementDate, interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
final LocalDate dateValue = null;
|
||||
final boolean isSpecificToInstallment = false;
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
periodData.getInterestRate(), dateValue, isSpecificToInstallment);
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
@@ -342,11 +368,10 @@ public class LoanScheduleAssembler {
|
||||
interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
||||
principalMoney, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsToleranceMoney,
|
||||
loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, loanVariationTermsData,
|
||||
graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, recalculationFrequencyType,
|
||||
restCalendarInstance, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO,
|
||||
loanTermVariations);
|
||||
loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, graceOnArrearsAgeing,
|
||||
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, recalculationFrequencyType, restCalendarInstance,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO, loanTermVariations);
|
||||
}
|
||||
|
||||
private CalendarInstance createInterestRecalculationCalendarInstance(final LocalDate calendarStartDate,
|
||||
@@ -534,6 +559,345 @@ public class LoanScheduleAssembler {
|
||||
|
||||
}
|
||||
|
||||
public void assempleVariableScheduleFrom(final Loan loan, final String json) {
|
||||
this.variableLoanScheduleFromApiJsonValidator.validateSchedule(json, loan);
|
||||
|
||||
List<LoanTermVariations> variations = loan.getLoanTermVariations();
|
||||
List<LoanTermVariations> newVariations = new ArrayList<>();
|
||||
extractLoanTermVariations(loan, json, newVariations);
|
||||
|
||||
final Map<LocalDate, LocalDate> adjustDueDateVariations = new HashMap<>();
|
||||
|
||||
if (!variations.isEmpty()) {
|
||||
List<LoanTermVariations> retainVariations = adjustExistingVariations(variations, newVariations, adjustDueDateVariations);
|
||||
newVariations = retainVariations;
|
||||
}
|
||||
variations.addAll(newVariations);
|
||||
Collections.sort(variations, new LoanTermVariationsComparator());
|
||||
|
||||
/*
|
||||
* List<LoanTermVariationsData> loanTermVariationsDatas = new
|
||||
* ArrayList<>();
|
||||
* loanTermVariationsDatas.addAll(loanApplicationTerms.getLoanTermVariations
|
||||
* ().getExceptionData()); loanApplicationTerms =
|
||||
* LoanApplicationTerms.assembleFrom(loanApplicationTerms,
|
||||
* loanTermVariationsDatas);
|
||||
*/
|
||||
|
||||
// date validations
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
|
||||
Set<LocalDate> dueDates = new TreeSet<>();
|
||||
LocalDate graceApplicable = loan.getExpectedDisbursedOnLocalDate();
|
||||
Integer graceOnPrincipal = loan.getLoanProductRelatedDetail().graceOnPrincipalPayment();
|
||||
if (graceOnPrincipal == null) {
|
||||
graceOnPrincipal = 0;
|
||||
}
|
||||
LocalDate lastDate = loan.getExpectedDisbursedOnLocalDate();
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
dueDates.add(installment.getDueDate());
|
||||
if (lastDate.isBefore(installment.getDueDate())) {
|
||||
lastDate = installment.getDueDate();
|
||||
}
|
||||
if (graceOnPrincipal.equals(installment.getInstallmentNumber())) {
|
||||
graceApplicable = installment.getDueDate();
|
||||
}
|
||||
}
|
||||
Collection<LocalDate> keySet = adjustDueDateVariations.keySet();
|
||||
dueDates.addAll(keySet);
|
||||
for (final LocalDate date : keySet) {
|
||||
LocalDate removeDate = adjustDueDateVariations.get(date);
|
||||
if (removeDate != null) {
|
||||
dueDates.remove(removeDate);
|
||||
}
|
||||
}
|
||||
|
||||
Set<LocalDate> actualDueDates = new TreeSet<>(dueDates);
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
List<LocalDate> overlappings = new ArrayList<>();
|
||||
for (LoanTermVariations termVariations : variations) {
|
||||
switch (termVariations.getTermType()) {
|
||||
case INSERT_INSTALLMENT:
|
||||
if (dueDates.contains(termVariations.fetchTermApplicaDate())) {
|
||||
overlappings.add(termVariations.fetchTermApplicaDate());
|
||||
} else {
|
||||
dueDates.add(termVariations.fetchTermApplicaDate());
|
||||
}
|
||||
if (!graceApplicable.isBefore(termVariations.fetchTermApplicaDate())) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||
"variable.schedule.insert.not.allowed.before.grace.period", "Loan schedule insert request invalid");
|
||||
}
|
||||
if (termVariations.fetchTermApplicaDate().isAfter(lastDate)) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||
"variable.schedule.insert.not.allowed.after.last.period.date", "Loan schedule insert request invalid");
|
||||
} else if(termVariations.fetchTermApplicaDate().isBefore(loan.getExpectedDisbursedOnLocalDate())){
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||
"variable.schedule.insert.not.allowed.before.disbursement.date", "Loan schedule insert request invalid");
|
||||
}
|
||||
break;
|
||||
case DELETE_INSTALLMENT:
|
||||
if (dueDates.contains(termVariations.fetchTermApplicaDate())) {
|
||||
dueDates.remove(termVariations.fetchTermApplicaDate());
|
||||
} else {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("variable.schedule.remove.date.invalid",
|
||||
"Loan schedule remove request invalid");
|
||||
}
|
||||
if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||
"variable.schedule.delete.not.allowed.for.last.period.date", "Loan schedule remove request invalid");
|
||||
}
|
||||
break;
|
||||
case DUE_DATE:
|
||||
if (dueDates.contains(termVariations.fetchTermApplicaDate())) {
|
||||
|
||||
if (overlappings.contains(termVariations.fetchTermApplicaDate())) {
|
||||
overlappings.remove(termVariations.fetchTermApplicaDate());
|
||||
} else {
|
||||
dueDates.remove(termVariations.fetchTermApplicaDate());
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("variable.schedule.modify.date.invalid",
|
||||
"Loan schedule modify due date request invalid");
|
||||
}
|
||||
if (dueDates.contains(termVariations.fetchDateValue())) {
|
||||
overlappings.add(termVariations.fetchDateValue());
|
||||
} else {
|
||||
dueDates.add(termVariations.fetchDateValue());
|
||||
}
|
||||
if(termVariations.fetchDateValue().isBefore(loan.getExpectedDisbursedOnLocalDate())){
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||
"variable.schedule.insert.not.allowed.before.disbursement.date", "Loan schedule insert request invalid");
|
||||
}
|
||||
if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) {
|
||||
lastDate = termVariations.fetchDateValue();
|
||||
}
|
||||
break;
|
||||
case PRINCIPAL_AMOUNT:
|
||||
case EMI_AMOUNT:
|
||||
if (!graceApplicable.isBefore(termVariations.fetchTermApplicaDate())) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||
"variable.schedule.amount.update.not.allowed.before.grace.period", "Loan schedule modify request invalid");
|
||||
}
|
||||
if (!dueDates.contains(termVariations.fetchTermApplicaDate())) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||
"variable.schedule.amount.update.from.date.invalid", "Loan schedule modify request invalid");
|
||||
}
|
||||
if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||
"variable.schedule.amount.update.not.allowed.for.last.period", "Loan schedule modify request invalid");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (!overlappings.isEmpty()) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("variable.schedule.modify.date.can.not.be.due.date",
|
||||
overlappings);
|
||||
}
|
||||
LoanProductVariableInstallmentConfig installmentConfig = loan.loanProduct().loanProductVariableInstallmentConfig();
|
||||
final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
Calendar loanCalendar = null;
|
||||
if (loanCalendarInstance != null) {
|
||||
loanCalendar = loanCalendarInstance.getCalendar();
|
||||
}
|
||||
final Integer minGap = installmentConfig.getMinimumGap();
|
||||
final Integer maxGap = installmentConfig.getMaximumGap();
|
||||
|
||||
LocalDate previousDate = loan.getDisbursementDate();
|
||||
for (LocalDate duedate : dueDates) {
|
||||
int gap = Days.daysBetween(previousDate, duedate).getDays();
|
||||
previousDate = duedate;
|
||||
if (gap < minGap || (maxGap != null && gap > maxGap)) {
|
||||
baseDataValidator.reset().value(duedate)
|
||||
.failWithCodeNoParameterAddedToErrorCode("variable.schedule.date.must.be.in.min.max.range", "Loan schedule date invalid");
|
||||
} else if (loanCalendar != null && !actualDueDates.contains(duedate) && !loanCalendar.isValidRecurringDate(duedate)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.value(duedate)
|
||||
.failWithCodeNoParameterAddedToErrorCode("variable.schedule.date.not.meeting.date",
|
||||
"Loan schedule date not in sync with meeting date");
|
||||
}
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
if (loan.getExpectedFirstRepaymentOnDate() == null) {
|
||||
loan.setExpectedFirstRepaymentOnDate(loan.fetchRepaymentScheduleInstallment(1).getDueDate().toDate());
|
||||
}
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan);
|
||||
AppUser currentUser = this.context.getAuthenticatedUserIfPresent();
|
||||
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
||||
}
|
||||
|
||||
private List<LoanTermVariations> adjustExistingVariations(List<LoanTermVariations> variations, List<LoanTermVariations> newVariations,
|
||||
final Map<LocalDate, LocalDate> adjustDueDateVariations) {
|
||||
Map<LocalDate, LoanTermVariations> amountVariations = new HashMap<>();
|
||||
Map<LocalDate, LoanTermVariations> dueDateVariations = new HashMap<>();
|
||||
Map<LocalDate, LoanTermVariations> insertVariations = new HashMap<>();
|
||||
for (LoanTermVariations loanTermVariations : variations) {
|
||||
switch (loanTermVariations.getTermType()) {
|
||||
case EMI_AMOUNT:
|
||||
case PRINCIPAL_AMOUNT:
|
||||
amountVariations.put(loanTermVariations.fetchTermApplicaDate(), loanTermVariations);
|
||||
break;
|
||||
case DUE_DATE:
|
||||
dueDateVariations.put(loanTermVariations.fetchDateValue(), loanTermVariations);
|
||||
adjustDueDateVariations.put(loanTermVariations.fetchTermApplicaDate(), loanTermVariations.fetchDateValue());
|
||||
break;
|
||||
case INSERT_INSTALLMENT:
|
||||
insertVariations.put(loanTermVariations.fetchTermApplicaDate(), loanTermVariations);
|
||||
adjustDueDateVariations.put(loanTermVariations.fetchTermApplicaDate(), loanTermVariations.fetchTermApplicaDate());
|
||||
break;
|
||||
case DELETE_INSTALLMENT:
|
||||
adjustDueDateVariations.put(loanTermVariations.fetchTermApplicaDate(), null);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<LoanTermVariations> retainVariations = new ArrayList<>();
|
||||
for (LoanTermVariations loanTermVariations : newVariations) {
|
||||
boolean retain = true;
|
||||
switch (loanTermVariations.getTermType()) {
|
||||
case DUE_DATE:
|
||||
if (amountVariations.containsKey(loanTermVariations.fetchTermApplicaDate())) {
|
||||
amountVariations.get(loanTermVariations.fetchTermApplicaDate()).setTermApplicableFrom(
|
||||
loanTermVariations.getDateValue());
|
||||
} else if (insertVariations.containsKey(loanTermVariations.fetchTermApplicaDate())) {
|
||||
insertVariations.get(loanTermVariations.fetchTermApplicaDate()).setTermApplicableFrom(
|
||||
loanTermVariations.getDateValue());
|
||||
retain = false;
|
||||
}
|
||||
if (dueDateVariations.containsKey(loanTermVariations.fetchTermApplicaDate())) {
|
||||
LoanTermVariations existingVariation = dueDateVariations.get(loanTermVariations.fetchTermApplicaDate());
|
||||
if (existingVariation.fetchTermApplicaDate().isEqual(loanTermVariations.fetchDateValue())) {
|
||||
variations.remove(existingVariation);
|
||||
} else {
|
||||
existingVariation.setTermApplicableFrom(loanTermVariations.getDateValue());
|
||||
}
|
||||
retain = false;
|
||||
}
|
||||
break;
|
||||
case EMI_AMOUNT:
|
||||
case PRINCIPAL_AMOUNT:
|
||||
if (amountVariations.containsKey(loanTermVariations.fetchTermApplicaDate())) {
|
||||
amountVariations.get(loanTermVariations.fetchTermApplicaDate()).setDecimalValue(loanTermVariations.getTermValue());
|
||||
retain = false;
|
||||
} else if (insertVariations.containsKey(loanTermVariations.fetchTermApplicaDate())) {
|
||||
insertVariations.get(loanTermVariations.fetchTermApplicaDate()).setDecimalValue(loanTermVariations.getTermValue());
|
||||
retain = false;
|
||||
}
|
||||
break;
|
||||
case DELETE_INSTALLMENT:
|
||||
if (amountVariations.containsKey(loanTermVariations.fetchTermApplicaDate())) {
|
||||
variations.remove(amountVariations.get(loanTermVariations.fetchTermApplicaDate()));
|
||||
|
||||
} else if (insertVariations.containsKey(loanTermVariations.fetchTermApplicaDate())) {
|
||||
variations.remove(insertVariations.get(loanTermVariations.fetchTermApplicaDate()));
|
||||
retain = false;
|
||||
}
|
||||
if (dueDateVariations.containsKey(loanTermVariations.fetchTermApplicaDate())) {
|
||||
variations.remove(amountVariations.get(loanTermVariations.fetchTermApplicaDate()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (retain) {
|
||||
retainVariations.add(loanTermVariations);
|
||||
}
|
||||
}
|
||||
return retainVariations;
|
||||
}
|
||||
|
||||
private void extractLoanTermVariations(final Loan loan, final String json, final List<LoanTermVariations> loanTermVariations) {
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
if (loan.loanProduct().allowVariabeInstallments()) {
|
||||
if (element.isJsonObject() && this.fromApiJsonHelper.parameterExists(LoanApiConstants.exceptionParamName, element)) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
final JsonObject exceptionObject = topLevelJsonElement.getAsJsonObject(LoanApiConstants.exceptionParamName);
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.modifiedinstallmentsParamName, exceptionObject)
|
||||
&& exceptionObject.get(LoanApiConstants.modifiedinstallmentsParamName).isJsonArray()) {
|
||||
final JsonArray modificationsArray = exceptionObject.get(LoanApiConstants.modifiedinstallmentsParamName)
|
||||
.getAsJsonArray();
|
||||
extractLoanTermVariations(loan, dateFormat, locale, modificationsArray, false, false, loanTermVariations);
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.newinstallmentsParamName, exceptionObject)
|
||||
&& exceptionObject.get(LoanApiConstants.newinstallmentsParamName).isJsonArray()) {
|
||||
final JsonArray array = exceptionObject.get(LoanApiConstants.newinstallmentsParamName).getAsJsonArray();
|
||||
extractLoanTermVariations(loan, dateFormat, locale, array, true, false, loanTermVariations);
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.deletedinstallmentsParamName, exceptionObject)
|
||||
&& exceptionObject.get(LoanApiConstants.deletedinstallmentsParamName).isJsonArray()) {
|
||||
final JsonArray array = exceptionObject.get(LoanApiConstants.deletedinstallmentsParamName).getAsJsonArray();
|
||||
extractLoanTermVariations(loan, dateFormat, locale, array, false, true, loanTermVariations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void extractLoanTermVariations(final Loan loan, final String dateFormat, final Locale locale,
|
||||
final JsonArray modificationsArray, final boolean isInsertInstallment, final boolean isDeleteInstallment,
|
||||
final List<LoanTermVariations> loanTermVariations) {
|
||||
for (int i = 1; i <= modificationsArray.size(); i++) {
|
||||
final JsonObject arrayElement = modificationsArray.get(i - 1).getAsJsonObject();
|
||||
BigDecimal decimalValue = null;
|
||||
LoanTermVariationType decimalValueVariationType = LoanTermVariationType.INVALID;
|
||||
if (loan.getLoanProductRelatedDetail().getAmortizationMethod().isEqualInstallment()
|
||||
&& loan.getLoanProductRelatedDetail().getInterestMethod().isDecliningBalnce()) {
|
||||
decimalValue = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.installmentAmountParamName, arrayElement,
|
||||
locale);
|
||||
decimalValueVariationType = LoanTermVariationType.EMI_AMOUNT;
|
||||
} else {
|
||||
decimalValue = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.principalParamName, arrayElement, locale);
|
||||
decimalValueVariationType = LoanTermVariationType.PRINCIPAL_AMOUNT;
|
||||
}
|
||||
|
||||
LocalDate duedateLocalDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.dueDateParamName, arrayElement,
|
||||
dateFormat, locale);
|
||||
Date dueDate = duedateLocalDate.toDate();
|
||||
|
||||
LocalDate modifiedDuedateLocalDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.modifiedDueDateParamName,
|
||||
arrayElement, dateFormat, locale);
|
||||
Date modifiedDuedate = null;
|
||||
if (modifiedDuedateLocalDate != null) {
|
||||
modifiedDuedate = modifiedDuedateLocalDate.toDate();
|
||||
}
|
||||
boolean isSpecificToInstallment = true;
|
||||
if (isInsertInstallment) {
|
||||
LoanTermVariations data = new LoanTermVariations(LoanTermVariationType.INSERT_INSTALLMENT.getValue(), dueDate,
|
||||
decimalValue, modifiedDuedate, isSpecificToInstallment, loan);
|
||||
loanTermVariations.add(data);
|
||||
} else if (isDeleteInstallment) {
|
||||
LoanTermVariations data = new LoanTermVariations(LoanTermVariationType.DELETE_INSTALLMENT.getValue(), dueDate,
|
||||
decimalValue, modifiedDuedate, isSpecificToInstallment, loan);
|
||||
loanTermVariations.add(data);
|
||||
} else {
|
||||
if (modifiedDuedate != null) {
|
||||
BigDecimal amountData = null;
|
||||
LoanTermVariations data = new LoanTermVariations(LoanTermVariationType.DUE_DATE.getValue(), dueDate, amountData,
|
||||
modifiedDuedate, isSpecificToInstallment, loan);
|
||||
loanTermVariations.add(data);
|
||||
}
|
||||
if (decimalValue != null) {
|
||||
if (modifiedDuedate == null) {
|
||||
modifiedDuedate = dueDate;
|
||||
}
|
||||
Date date = null;
|
||||
LoanTermVariations data = new LoanTermVariations(decimalValueVariationType.getValue(), modifiedDuedate, decimalValue,
|
||||
date, isSpecificToInstallment, loan);
|
||||
loanTermVariations.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDisbursementDateIsOnNonWorkingDay(final LocalDate disbursementDate, final WorkingDays workingDays) {
|
||||
if (!WorkingDaysUtil.isWorkingDay(workingDays, disbursementDate)) {
|
||||
final String errorMessage = "The expected disbursement date cannot be on a non working day";
|
||||
|
||||
+2
@@ -14,4 +14,6 @@ public interface LoanScheduleCalculationPlatformService {
|
||||
LoanScheduleModel calculateLoanSchedule(JsonQuery query, Boolean validateParams);
|
||||
|
||||
void updateFutureSchedule(LoanScheduleData loanScheduleData, Long loanId);
|
||||
|
||||
LoanScheduleData generateLoanScheduleForVariableInstallmentRequest(Long loanId, String json);
|
||||
}
|
||||
|
||||
+94
-18
@@ -8,7 +8,10 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
@@ -18,10 +21,12 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
@@ -32,6 +37,7 @@ import org.mifosplatform.portfolio.floatingrates.exception.FloatingRateNotFoundE
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
@@ -68,6 +74,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
private final CurrencyReadPlatformService currencyReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleCalculationPlatformServiceImpl(final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer,
|
||||
@@ -78,7 +85,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
final LoanAccountDomainService accountDomainService, final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final ConfigurationDomainService configurationDomainService,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService,
|
||||
final CurrencyReadPlatformService currencyReadPlatformService) {
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
this.loanScheduleAssembler = loanScheduleAssembler;
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
@@ -93,6 +101,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
this.currencyReadPlatformService = currencyReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,18 +171,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
}
|
||||
}
|
||||
}
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
final CalendarInstance restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
|
||||
final CalendarInstance compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
LocalDate calculatedRepaymentsStartingFromDate = accountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||
loan.getDisbursementDate(), loan, calendarInstance);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, restCalendarInstance, compoundingCalendarInstance, floatingRateDTO);
|
||||
LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(loan);
|
||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(currency,
|
||||
today, loanApplicationTerms, loan.charges(), loan.getOfficeId(),
|
||||
loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor);
|
||||
@@ -218,21 +216,99 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
loanScheduleData.updateFuturePeriods(futureInstallments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanScheduleData generateLoanScheduleForVariableInstallmentRequest(Long loanId, final String json) {
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
this.loanScheduleAssembler.assempleVariableScheduleFrom(loan, json);
|
||||
return constructLoanScheduleData(loan);
|
||||
}
|
||||
|
||||
private LoanScheduleData constructLoanScheduleData(Loan loan) {
|
||||
Collection<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
final List<LoanSchedulePeriodData> installmentData = new ArrayList<>();
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
Money outstanding = loan.getPrincpal();
|
||||
|
||||
Set<LoanDisbursementDetails> disbursementDetails = new HashSet<>();
|
||||
if (loan.isMultiDisburmentLoan()) {
|
||||
disbursementDetails = loan.getDisbursementDetails();
|
||||
outstanding = outstanding.zero();
|
||||
}
|
||||
Money principal = outstanding;
|
||||
Iterator<LoanDisbursementDetails> disbursementItr = disbursementDetails.iterator();
|
||||
LoanDisbursementDetails loanDisbursementDetails = null;
|
||||
if (disbursementItr.hasNext()) {
|
||||
loanDisbursementDetails = disbursementItr.next();
|
||||
}
|
||||
|
||||
Money totalInterest = principal.zero();
|
||||
Money totalCharge = principal.zero();
|
||||
Money totalPenalty = principal.zero();
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (loanDisbursementDetails != null
|
||||
&& !loanDisbursementDetails.expectedDisbursementDateAsLocalDate().isAfter(installment.getDueDate())) {
|
||||
outstanding = outstanding.plus(loanDisbursementDetails.principal());
|
||||
principal = principal.plus(loanDisbursementDetails.principal());
|
||||
if (disbursementItr.hasNext()) {
|
||||
loanDisbursementDetails = disbursementItr.next();
|
||||
} else {
|
||||
loanDisbursementDetails = null;
|
||||
}
|
||||
}
|
||||
outstanding = outstanding.minus(installment.getPrincipal(currency));
|
||||
LoanSchedulePeriodData loanSchedulePeriodData = LoanSchedulePeriodData.repaymentOnlyPeriod(installment.getInstallmentNumber(),
|
||||
installment.getFromDate(), installment.getDueDate(), installment.getPrincipal(currency).getAmount(),
|
||||
outstanding.getAmount(), installment.getInterestCharged(currency).getAmount(),
|
||||
installment.getFeeChargesCharged(currency).getAmount(), installment.getPenaltyChargesCharged(currency).getAmount(),
|
||||
installment.getDue(currency).getAmount());
|
||||
installmentData.add(loanSchedulePeriodData);
|
||||
totalInterest = totalInterest.plus(installment.getInterestCharged(currency));
|
||||
totalCharge = totalCharge.plus(installment.getFeeChargesCharged(currency));
|
||||
totalPenalty = totalPenalty.plus(installment.getPenaltyChargesCharged(currency));
|
||||
}
|
||||
|
||||
CurrencyData currencyData = this.currencyReadPlatformService.retrieveCurrency(currency.getCode());
|
||||
|
||||
LoanScheduleData scheduleData = new LoanScheduleData(currencyData, installmentData, loan.getLoanRepaymentScheduleDetail()
|
||||
.getNumberOfRepayments(), principal.getAmount(), principal.getAmount(), totalInterest.getAmount(), totalCharge.getAmount(),
|
||||
totalPenalty.getAmount(), principal.plus(totalCharge).plus(totalInterest).plus(totalPenalty).getAmount());
|
||||
|
||||
return scheduleData;
|
||||
}
|
||||
|
||||
private LoanApplicationTerms constructLoanApplicationTerms(final Loan loan) {
|
||||
MonetaryCurrency currency = loan.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
final CalendarInstance restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
|
||||
final CalendarInstance compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
LocalDate calculatedRepaymentsStartingFromDate = accountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||
loan.getDisbursementDate(), loan, calendarInstance);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, restCalendarInstance, compoundingCalendarInstance, floatingRateDTO);
|
||||
return loanApplicationTerms;
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = null;
|
||||
try{
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
}catch(final FloatingRateNotFoundException ex){
|
||||
// Do not do anything
|
||||
try {
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate().getRatePeriods();
|
||||
} catch (final FloatingRateNotFoundException ex) {
|
||||
// Do not do anything
|
||||
}
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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.loanaccount.loanschedule.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface LoanScheduleWritePlatformService {
|
||||
|
||||
public CommandProcessingResult addLoanScheduleVariations(final Long loanId, final JsonCommand command);
|
||||
|
||||
CommandProcessingResult deleteLoanScheduleVariations(final Long loanId);
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 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.loanaccount.loanschedule.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class LoanScheduleWritePlatformServiceImpl implements LoanScheduleWritePlatformService {
|
||||
|
||||
private final LoanAccountDomainService loanAccountDomainService;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanScheduleAssembler loanScheduleAssembler;
|
||||
private final PlatformSecurityContext context;
|
||||
private final LoanUtilService loanUtilService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleWritePlatformServiceImpl(final LoanAccountDomainService loanAccountDomainService,
|
||||
final LoanScheduleAssembler loanScheduleAssembler, final LoanAssembler loanAssembler, final PlatformSecurityContext context,
|
||||
final LoanUtilService loanUtilService) {
|
||||
this.loanAccountDomainService = loanAccountDomainService;
|
||||
this.loanScheduleAssembler = loanScheduleAssembler;
|
||||
this.loanAssembler = loanAssembler;
|
||||
this.context = context;
|
||||
this.loanUtilService = loanUtilService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult addLoanScheduleVariations(final Long loanId, final JsonCommand command) {
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
Map<Long, LoanTermVariations> loanTermVariations = new HashMap<>();
|
||||
for (LoanTermVariations termVariations : loan.getLoanTermVariations()) {
|
||||
loanTermVariations.put(termVariations.getId(), termVariations);
|
||||
}
|
||||
this.loanScheduleAssembler.assempleVariableScheduleFrom(loan, command.json());
|
||||
|
||||
this.loanAccountDomainService.saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
final Map<String, Object> changes = new HashMap<>();
|
||||
List<LoanTermVariationsData> newVariationsData = new ArrayList<>();
|
||||
List<LoanTermVariations> modifiedVariations = loan.getLoanTermVariations();
|
||||
for (LoanTermVariations termVariations : modifiedVariations) {
|
||||
if (loanTermVariations.containsKey(termVariations.getId())) {
|
||||
loanTermVariations.remove(termVariations.getId());
|
||||
} else {
|
||||
newVariationsData.add(termVariations.toData());
|
||||
}
|
||||
}
|
||||
if (!loanTermVariations.isEmpty()) {
|
||||
changes.put("removedVariations", loanTermVariations.keySet());
|
||||
}
|
||||
changes.put("loanTermVariations", newVariationsData);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withLoanId(loanId) //
|
||||
.with(changes) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult deleteLoanScheduleVariations(final Long loanId) {
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
List<LoanTermVariations> variations = loan.getLoanTermVariations();
|
||||
List<Long> deletedVariations = new ArrayList<>(variations.size());
|
||||
for (LoanTermVariations loanTermVariations : variations) {
|
||||
deletedVariations.add(loanTermVariations.getId());
|
||||
}
|
||||
final Map<String, Object> changes = new HashMap<>();
|
||||
changes.put("removedEntityIds", deletedVariations);
|
||||
loan.getLoanTermVariations().clear();
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan);
|
||||
AppUser currentUser = this.context.getAuthenticatedUserIfPresent();
|
||||
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
||||
this.loanAccountDomainService.saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withLoanId(loanId) //
|
||||
.with(changes) //
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
+25
-15
@@ -222,18 +222,22 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
"interestRatePerPeriod param is not supported, selected Loan Product is linked with floating interest rate.");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)){
|
||||
final Boolean isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(
|
||||
LoanApiConstants.isFloatingInterestRate, element);
|
||||
if(isFloatingInterestRate != null && isFloatingInterestRate && !loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed()){
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).failWithCode("true.not.supported.for.selected.loanproduct",
|
||||
"isFloatingInterestRate value of true not supported for selected Loan Product.");
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)) {
|
||||
final Boolean isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isFloatingInterestRate,
|
||||
element);
|
||||
if (isFloatingInterestRate != null && isFloatingInterestRate
|
||||
&& !loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed()) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanApiConstants.isFloatingInterestRate)
|
||||
.failWithCode("true.not.supported.for.selected.loanproduct",
|
||||
"isFloatingInterestRate value of true not supported for selected Loan Product.");
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).trueOrFalseRequired(false);
|
||||
}
|
||||
|
||||
if (interestType != null && interestType == InterestMethod.FLAT.getValue()) {
|
||||
if (interestType != null && interestType.equals(InterestMethod.FLAT.getValue())) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(interestTypeParameterName)
|
||||
@@ -595,15 +599,17 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
}
|
||||
|
||||
Boolean isFloatingInterestRate = existingLoanApplication.getIsFloatingInterestRate();
|
||||
if(this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)){
|
||||
isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isFloatingInterestRate, element);
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)) {
|
||||
isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isFloatingInterestRate, element);
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if(isFloatingInterestRate != null){
|
||||
if(isFloatingInterestRate
|
||||
&& !loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed()){
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).failWithCode("true.not.supported.for.selected.loanproduct",
|
||||
"isFloatingInterestRate value of true not supported for selected Loan Product.");
|
||||
if (isFloatingInterestRate != null) {
|
||||
if (isFloatingInterestRate && !loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed()) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanApiConstants.isFloatingInterestRate)
|
||||
.failWithCode("true.not.supported.for.selected.loanproduct",
|
||||
"isFloatingInterestRate value of true not supported for selected Loan Product.");
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).trueOrFalseRequired(false);
|
||||
@@ -612,7 +618,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
if (interestType == null) {
|
||||
interestType = existingLoanApplication.getLoanProductRelatedDetail().getInterestMethod().getValue();
|
||||
}
|
||||
if (interestType != null && interestType == InterestMethod.FLAT.getValue()) {
|
||||
if (interestType != null && interestType.equals(InterestMethod.FLAT.getValue())) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(interestTypeParameterName)
|
||||
@@ -731,6 +737,10 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||
repaymentsStartingFromDateParameterName, element);
|
||||
baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull();
|
||||
if (!existingLoanApplication.getLoanTermVariations().isEmpty()) {
|
||||
baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate)
|
||||
.failWithCode("invalid.due.to.variable.installments");
|
||||
}
|
||||
}
|
||||
|
||||
final String submittedOnDateParameterName = "submittedOnDate";
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 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.loanaccount.serialization;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@Component
|
||||
public class VariableLoanScheduleFromApiJsonValidator {
|
||||
|
||||
final Set<String> variableSchedulesupportedParameters = new HashSet<>(Arrays.asList(LoanApiConstants.exceptionParamName,
|
||||
LoanApiConstants.localeParameterName, LoanApiConstants.dateFormatParameterName));
|
||||
final Set<String> variableSchedulesupportedArrayParameters = new HashSet<>(Arrays.asList(
|
||||
LoanApiConstants.modifiedinstallmentsParamName, LoanApiConstants.newinstallmentsParamName,
|
||||
LoanApiConstants.deletedinstallmentsParamName));
|
||||
final Set<String> variableScheduleModifiedParameters = new HashSet<>(Arrays.asList(LoanApiConstants.dueDateParamName,
|
||||
LoanApiConstants.modifiedDueDateParamName, LoanApiConstants.principalParamName, LoanApiConstants.installmentAmountParamName));
|
||||
final Set<String> variableScheduleNewInstallmentParameters = new HashSet<>(Arrays.asList(LoanApiConstants.dueDateParamName,
|
||||
LoanApiConstants.principalParamName, LoanApiConstants.installmentAmountParamName));
|
||||
final Set<String> variableScheduleDeleteInstallmentParameters = new HashSet<>(Arrays.asList(LoanApiConstants.dueDateParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public VariableLoanScheduleFromApiJsonValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
public void validateSchedule(final String json, final Loan loan) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.variableSchedulesupportedParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
|
||||
if (!loan.isSubmittedAndPendingApproval()) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("account.is.not.submitted.and.pending.state",
|
||||
"Loan is not in submited state");
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
if (loan.loanProduct().allowVariabeInstallments()) {
|
||||
if (element.isJsonObject() && this.fromApiJsonHelper.parameterExists(LoanApiConstants.exceptionParamName, element)) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
final JsonObject exceptionObject = topLevelJsonElement.getAsJsonObject(LoanApiConstants.exceptionParamName);
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(exceptionObject, this.variableSchedulesupportedArrayParameters);
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.modifiedinstallmentsParamName, exceptionObject)
|
||||
&& exceptionObject.get(LoanApiConstants.modifiedinstallmentsParamName).isJsonArray()) {
|
||||
final JsonArray modificationsArray = exceptionObject.get(LoanApiConstants.modifiedinstallmentsParamName)
|
||||
.getAsJsonArray();
|
||||
validateLoanTermVariations(loan, baseDataValidator, dateFormat, locale, modificationsArray,
|
||||
this.variableScheduleModifiedParameters, LoanApiConstants.modifiedinstallmentsParamName);
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.newinstallmentsParamName, exceptionObject)
|
||||
&& exceptionObject.get(LoanApiConstants.newinstallmentsParamName).isJsonArray()) {
|
||||
final JsonArray array = exceptionObject.get(LoanApiConstants.newinstallmentsParamName).getAsJsonArray();
|
||||
validateLoanTermVariations(loan, baseDataValidator, dateFormat, locale, array,
|
||||
this.variableScheduleNewInstallmentParameters, LoanApiConstants.newinstallmentsParamName);
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.deletedinstallmentsParamName, exceptionObject)
|
||||
&& exceptionObject.get(LoanApiConstants.deletedinstallmentsParamName).isJsonArray()) {
|
||||
final JsonArray array = exceptionObject.get(LoanApiConstants.deletedinstallmentsParamName).getAsJsonArray();
|
||||
validateLoanTermVariations(loan, baseDataValidator, dateFormat, locale, array,
|
||||
this.variableScheduleDeleteInstallmentParameters, LoanApiConstants.deletedinstallmentsParamName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("variable.schedule.not.supported",
|
||||
"Loan schedule modification not allowed");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
private void validateLoanTermVariations(final Loan loan, final DataValidatorBuilder baseDataValidator, final String dateFormat,
|
||||
final Locale locale, final JsonArray modificationsArray, final Set<String> supportParams, final String arrayName) {
|
||||
for (int i = 1; i <= modificationsArray.size(); i++) {
|
||||
|
||||
final JsonObject arrayElement = modificationsArray.get(i - 1).getAsJsonObject();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(arrayElement, supportParams);
|
||||
final BigDecimal installmentAmount = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.installmentAmountParamName,
|
||||
arrayElement, locale);
|
||||
baseDataValidator.reset().parameter(arrayName).parameterAtIndexArray(LoanApiConstants.installmentAmountParamName, i)
|
||||
.value(installmentAmount).positiveAmount();
|
||||
final BigDecimal principalAmount = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.principalParamName,
|
||||
arrayElement, locale);
|
||||
baseDataValidator.reset().parameter(arrayName).parameterAtIndexArray(LoanApiConstants.principalParamName, i)
|
||||
.value(principalAmount).zeroOrPositiveAmount();
|
||||
|
||||
if (loan.getLoanProductRelatedDetail().getInterestMethod().isDecliningBalnce()
|
||||
&& loan.getLoanProductRelatedDetail().getAmortizationMethod().isEqualInstallment() && principalAmount != null) {
|
||||
List<String> unsupportedParams = new ArrayList<>(1);
|
||||
unsupportedParams.add(LoanApiConstants.principalParamName);
|
||||
throw new UnsupportedParameterException(unsupportedParams);
|
||||
} else if ((!loan.getLoanProductRelatedDetail().getInterestMethod().isDecliningBalnce() || loan.getLoanProductRelatedDetail()
|
||||
.getAmortizationMethod().isEqualPrincipal())
|
||||
&& installmentAmount != null) {
|
||||
List<String> unsupportedParams = new ArrayList<>(1);
|
||||
unsupportedParams.add(LoanApiConstants.installmentAmountParamName);
|
||||
throw new UnsupportedParameterException(unsupportedParams);
|
||||
}
|
||||
|
||||
LocalDate duedate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.dueDateParamName, arrayElement, dateFormat,
|
||||
locale);
|
||||
baseDataValidator.reset().parameter(arrayName).parameterAtIndexArray(LoanApiConstants.dueDateParamName, i).value(duedate)
|
||||
.notNull().validateDateAfter(loan.getExpectedDisbursedOnLocalDate());
|
||||
|
||||
LocalDate modifiedDuedate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.modifiedDueDateParamName,
|
||||
arrayElement, dateFormat, locale);
|
||||
baseDataValidator.reset().parameter(arrayName).parameterAtIndexArray(LoanApiConstants.modifiedDueDateParamName, i)
|
||||
.value(modifiedDuedate).validateDateAfter(loan.getExpectedDisbursedOnLocalDate());
|
||||
if (arrayName.equals(LoanApiConstants.modifiedinstallmentsParamName) && modifiedDuedate == null && installmentAmount == null
|
||||
&& principalAmount == null) {
|
||||
baseDataValidator.reset().parameter(arrayName).failWithCode("variation.required", "At least one vario");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
-22
@@ -476,15 +476,15 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
loanId); }
|
||||
|
||||
final String productIdParamName = "productId";
|
||||
LoanProduct newLoanProduct = null;
|
||||
LoanProduct newLoanProduct = null;
|
||||
if (command.isChangeInLongParameterNamed(productIdParamName, existingLoanApplication.loanProduct().getId())) {
|
||||
final Long productId = command.longValueOfParameterNamed(productIdParamName);
|
||||
newLoanProduct = this.loanProductRepository.findOne(productId);
|
||||
if (newLoanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||
}
|
||||
|
||||
LoanProduct loanProductForValidations = newLoanProduct == null? existingLoanApplication.loanProduct(): newLoanProduct;
|
||||
|
||||
|
||||
LoanProduct loanProductForValidations = newLoanProduct == null ? existingLoanApplication.loanProduct() : newLoanProduct;
|
||||
|
||||
this.fromApiJsonDeserializer.validateForModify(command.json(), loanProductForValidations, existingLoanApplication);
|
||||
|
||||
checkClientOrGroupActive(existingLoanApplication);
|
||||
@@ -495,21 +495,23 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
LoanChargeData chargeData = new LoanChargeData(charge.getId(), charge.getDueLocalDate(), charge.amountOrPercentage());
|
||||
chargesMap.put(charge.getId(), chargeData);
|
||||
}
|
||||
Set<LoanDisbursementDetails> disbursementDetails = this.loanAssembler.fetchDisbursementData(command.parsedJson().getAsJsonObject());
|
||||
Set<LoanDisbursementDetails> disbursementDetails = this.loanAssembler.fetchDisbursementData(command.parsedJson()
|
||||
.getAsJsonObject());
|
||||
|
||||
/**
|
||||
* Stores all charges which are passed in during modify loan
|
||||
* application
|
||||
**/
|
||||
final Set<LoanCharge> possiblyModifedLoanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson(),disbursementDetails);
|
||||
final Set<LoanCharge> possiblyModifedLoanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson(),
|
||||
disbursementDetails);
|
||||
/** Boolean determines if any charge has been modified **/
|
||||
boolean isChargeModified = false;
|
||||
|
||||
Set<Charge> newTrancheChages = this.loanChargeAssembler.getNewLoanTrancheCharges(command.parsedJson()) ;
|
||||
Set<Charge> newTrancheChages = this.loanChargeAssembler.getNewLoanTrancheCharges(command.parsedJson());
|
||||
for (Charge charge : newTrancheChages) {
|
||||
existingLoanApplication.addTrancheLoanCharge(charge) ;
|
||||
existingLoanApplication.addTrancheLoanCharge(charge);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If there are any charges already present, which are now not
|
||||
* passed in as a part of the request, deem the charges as modified
|
||||
@@ -579,19 +581,19 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId, newLoanProduct.getId());
|
||||
} else if (groupId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue(),
|
||||
newLoanProduct.getId());
|
||||
newLoanProduct.getId());
|
||||
}
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
|
||||
newLoanProduct, cycleNumber);
|
||||
newLoanProduct, cycleNumber);
|
||||
} else {
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
|
||||
newLoanProduct);
|
||||
newLoanProduct);
|
||||
}
|
||||
if(newLoanProduct.isLinkedToFloatingInterestRate()){
|
||||
existingLoanApplication.getLoanProductRelatedDetail().updateForFloatingInterestRates();
|
||||
}else{
|
||||
existingLoanApplication.setInterestRateDifferential(null);
|
||||
existingLoanApplication.setIsFloatingInterestRate(null);
|
||||
if (newLoanProduct.isLinkedToFloatingInterestRate()) {
|
||||
existingLoanApplication.getLoanProductRelatedDetail().updateForFloatingInterestRates();
|
||||
} else {
|
||||
existingLoanApplication.setInterestRateDifferential(null);
|
||||
existingLoanApplication.setIsFloatingInterestRate(null);
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
@@ -644,7 +646,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
if (changes.containsKey(chargesParamName)) {
|
||||
existingLoanApplication.updateLoanCharges(possiblyModifedLoanCharges);
|
||||
}
|
||||
|
||||
|
||||
if (changes.containsKey("recalculateLoanSchedule")) {
|
||||
changes.remove("recalculateLoanSchedule");
|
||||
|
||||
@@ -656,7 +658,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
existingLoanApplication.recalculateAllCharges();
|
||||
}
|
||||
|
||||
|
||||
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(),
|
||||
existingLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
|
||||
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
|
||||
@@ -723,7 +724,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
isModified = true;
|
||||
} else {
|
||||
final SavingsAccount savingsAccount = accountAssociations.linkedSavingsAccount();
|
||||
if (savingsAccount == null || savingsAccount.getId() != savingsAccountId) {
|
||||
if (savingsAccount == null || !savingsAccount.getId().equals(savingsAccountId)) {
|
||||
isModified = true;
|
||||
}
|
||||
}
|
||||
@@ -891,7 +892,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
|
||||
// If loan approved amount less than loan demanded amount, then need
|
||||
// to recompute the schedule
|
||||
if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName) || changes.containsKey("recalculateLoanSchedule")) {
|
||||
if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName) || changes.containsKey("recalculateLoanSchedule")
|
||||
|| changes.containsKey("expectedDisbursementDate")) {
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanAccountDomainService.buildScheduleGeneratorDTO(loan);
|
||||
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
||||
}
|
||||
@@ -1082,7 +1084,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.getId() == null) {
|
||||
this.repaymentScheduleInstallmentRepository.save(installment);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.loanRepository.saveAndFlush(loan);
|
||||
} catch (final DataIntegrityViolationException e) {
|
||||
|
||||
+3
-3
@@ -141,7 +141,7 @@ public class LoanChargeAssembler {
|
||||
}
|
||||
}
|
||||
|
||||
if (chargeDefinition.getChargeTimeType() == ChargeTimeType.DISBURSEMENT.getValue()) {
|
||||
if ( ChargeTimeType.DISBURSEMENT.getValue().equals(chargeDefinition.getChargeTimeType())) {
|
||||
for (LoanDisbursementDetails disbursementDetail : disbursementDetails) {
|
||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
|
||||
if (chargeDefinition.isPercentageOfApprovedAmount()
|
||||
@@ -164,10 +164,10 @@ public class LoanChargeAssembler {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (chargeDefinition.getChargeTimeType() == ChargeTimeType.TRANCHE_DISBURSEMENT.getValue()) {
|
||||
} else if (ChargeTimeType.TRANCHE_DISBURSEMENT.getValue().equals(chargeDefinition.getChargeTimeType())) {
|
||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
|
||||
for (LoanDisbursementDetails disbursementDetail : disbursementDetails) {
|
||||
if (chargeDefinition.getChargeTimeType() == ChargeTimeType.TRANCHE_DISBURSEMENT.getValue()) {
|
||||
if (ChargeTimeType.TRANCHE_DISBURSEMENT.getValue().equals(chargeDefinition.getChargeTimeType())) {
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition,
|
||||
disbursementDetail.principal(), amount, chargeTime, chargeCalculation,
|
||||
disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum,
|
||||
|
||||
+42
-43
@@ -17,11 +17,8 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
@@ -66,9 +63,9 @@ import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
|
||||
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.service.CommonEnumerations;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.exception.FloatingRateNotFoundException;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
@@ -559,6 +556,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " l.loan_type_enum as loanType, l.loanpurpose_cv_id as loanPurposeId, cv.code_value as loanPurposeName,"
|
||||
+ " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription,"
|
||||
+ " lp.is_linked_to_floating_interest_rates as isLoanProductLinkedToFloatingRate, "
|
||||
+ " lp.allow_variabe_installments as isvariableInstallmentsAllowed, "
|
||||
+ " lp.allow_multiple_disbursals as multiDisburseLoan,"
|
||||
+ " lp.can_define_fixed_emi_amount as canDefineInstallmentAmount,"
|
||||
+ " c.id as clientId, c.display_name as clientName, c.office_id as clientOfficeId,"
|
||||
@@ -630,7 +628,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " lir.compounding_freqency_date as compoundingFrequencyDate, "
|
||||
+ " l.is_floating_interest_rate as isFloatingInterestRate, "
|
||||
+ " l.interest_rate_differential as interestRateDifferential, "
|
||||
+ " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement "
|
||||
+ " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement, "
|
||||
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap "
|
||||
+ " from m_loan l" //
|
||||
+ " join m_product_loan lp on lp.id = l.product_id" //
|
||||
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id "
|
||||
@@ -647,7 +646,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " left join m_appuser dbu on dbu.id = l.disbursedon_userid"
|
||||
+ " left join m_appuser cbu on cbu.id = l.closedon_userid"
|
||||
+ " left join m_code_value cv on cv.id = l.loanpurpose_cv_id"
|
||||
+ " left join ref_loan_transaction_processing_strategy lps on lps.id = l.loan_transaction_strategy_id";
|
||||
+ " left join ref_loan_transaction_processing_strategy lps on lps.id = l.loan_transaction_strategy_id"
|
||||
+ " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id";
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -735,6 +736,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
final LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, "expectedMaturityDate");
|
||||
|
||||
final Boolean isvariableInstallmentsAllowed = rs.getBoolean("isvariableInstallmentsAllowed");
|
||||
final Integer minimumGap = rs.getInt("minimuminstallmentgap");
|
||||
final Integer maximumGap = rs.getInt("maximuminstallmentgap");
|
||||
|
||||
final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername,
|
||||
submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname,
|
||||
withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername,
|
||||
@@ -907,17 +912,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
|
||||
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupData,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName,
|
||||
loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, approvedPrincipal, totalOverpaid,
|
||||
inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType,
|
||||
repaymentFrequencyNthDayType, repaymentFrequencyDayOfWeekType, transactionStrategyId, transactionStrategyName,
|
||||
amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
|
||||
isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType, expectedFirstRepaymentOnDate,
|
||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline,
|
||||
loanSummary, feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting, loanCounter, loanProductCounter,
|
||||
multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears, graceOnArrearsAgeing,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData,
|
||||
createStandingInstructionAtDisbursement);
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal,
|
||||
approvedPrincipal, totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments,
|
||||
repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType, repaymentFrequencyDayOfWeekType,
|
||||
transactionStrategyId, transactionStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType,
|
||||
annualInterestRate, interestType, isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting,
|
||||
loanCounter, loanProductCounter, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance,
|
||||
inArrears, graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
interestRecalculationData, createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1468,30 +1474,21 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallments(final Long penaltyWaitPeriod, final Boolean backdatePenalties) {
|
||||
public Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallments(final Long penaltyWaitPeriod,
|
||||
final Boolean backdatePenalties) {
|
||||
final MusoniOverdueLoanScheduleMapper rm = new MusoniOverdueLoanScheduleMapper();
|
||||
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder
|
||||
.append("select ")
|
||||
.append(rm.schema())
|
||||
.append(" where DATE_SUB(CURDATE(),INTERVAL ? DAY) > ls.duedate ")
|
||||
.append( " and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 ")
|
||||
sqlBuilder.append("select ").append(rm.schema()).append(" where DATE_SUB(CURDATE(),INTERVAL ? DAY) > ls.duedate ")
|
||||
.append(" and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 ")
|
||||
.append(" and mc.charge_time_enum = 9 and ml.loan_status_id = 300 ");
|
||||
|
||||
if (backdatePenalties) { return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { penaltyWaitPeriod }); }
|
||||
// Only apply for duedate = yesterday (so that we don't apply
|
||||
// penalties on the duedate itself)
|
||||
sqlBuilder.append(" and ls.duedate >= DATE_SUB(CURDATE(),INTERVAL (? + 1) DAY)");
|
||||
|
||||
if(backdatePenalties)
|
||||
{
|
||||
return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { penaltyWaitPeriod });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only apply for duedate = yesterday (so that we don't apply penalties on the duedate itself)
|
||||
sqlBuilder.append(" and ls.duedate >= DATE_SUB(CURDATE(),INTERVAL (? + 1) DAY)");
|
||||
|
||||
return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { penaltyWaitPeriod, penaltyWaitPeriod });
|
||||
}
|
||||
|
||||
return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { penaltyWaitPeriod, penaltyWaitPeriod });
|
||||
|
||||
}
|
||||
|
||||
@@ -1558,7 +1555,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
private static final class LoanTermVariationsMapper implements RowMapper<LoanTermVariationsData> {
|
||||
|
||||
public String schema() {
|
||||
return "tv.id as id,tv.applicable_from as variationApplicableFrom,tv.term_value as termValue "
|
||||
return "tv.id as id,tv.applicable_date as variationApplicableFrom,tv.decimal_value as decimalValue, tv.date_value as dateValue, tv.is_specific_to_installment as isSpecificToInstallment "
|
||||
+ "from m_loan_term_variations tv";
|
||||
}
|
||||
|
||||
@@ -1566,10 +1563,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
public LoanTermVariationsData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final LocalDate variationApplicableFrom = JdbcSupport.getLocalDate(rs, "variationApplicableFrom");
|
||||
final BigDecimal termValue = rs.getBigDecimal("termValue");
|
||||
final BigDecimal decimalValue = rs.getBigDecimal("decimalValue");
|
||||
final LocalDate dateValue = JdbcSupport.getLocalDate(rs, "dateValue");
|
||||
final boolean isSpecificToInstallment = rs.getBoolean("isSpecificToInstallment");
|
||||
|
||||
final LoanTermVariationsData loanTermVariationsData = new LoanTermVariationsData(id,
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), variationApplicableFrom, termValue);
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), variationApplicableFrom, decimalValue, dateValue,
|
||||
isSpecificToInstallment);
|
||||
return loanTermVariationsData;
|
||||
}
|
||||
|
||||
@@ -1993,11 +1993,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = null;
|
||||
try{
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
}catch(final FloatingRateNotFoundException ex){
|
||||
// Do not do anything
|
||||
try {
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate().getRatePeriods();
|
||||
} catch (final FloatingRateNotFoundException ex) {
|
||||
// Do not do anything
|
||||
}
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
|
||||
+1
-5
@@ -18,7 +18,6 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation
|
||||
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
|
||||
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.mifosplatform.infrastructure.jobs.service.JobName;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -32,16 +31,13 @@ public class LoanSchedularServiceImpl implements LoanSchedularService {
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final LoanWritePlatformService loanWritePlatformService;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanSchedularServiceImpl(final ConfigurationDomainService configurationDomainService,
|
||||
final LoanReadPlatformService loanReadPlatformService, final LoanWritePlatformService loanWritePlatformService,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
final LoanReadPlatformService loanReadPlatformService, final LoanWritePlatformService loanWritePlatformService) {
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.loanWritePlatformService = loanWritePlatformService;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 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.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.holiday.domain.HolidayRepository;
|
||||
import org.mifosplatform.organisation.holiday.domain.HolidayStatusType;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.exception.FloatingRateNotFoundException;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class LoanUtilService {
|
||||
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanUtilService(final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService,
|
||||
final HolidayRepository holidayRepository, final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
public LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan,
|
||||
final CalendarInstance calendarInstance) {
|
||||
final Calendar calendar = calendarInstance == null ? null : calendarInstance.getCalendar();
|
||||
LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
|
||||
if (calendar != null) {// sync repayments
|
||||
|
||||
// TODO: AA - user provided first repayment date takes precedence
|
||||
// over recalculated meeting date
|
||||
if (calculatedRepaymentsStartingFromDate == null) {
|
||||
// FIXME: AA - Possibility of having next meeting date
|
||||
// immediately after disbursement date,
|
||||
// need to have minimum number of days gap between disbursement
|
||||
// and first repayment date.
|
||||
final LoanProductRelatedDetail repaymentScheduleDetails = loan.repaymentScheduleDetail();
|
||||
if (repaymentScheduleDetails != null) {// Not expecting to be
|
||||
// null
|
||||
final Integer repayEvery = repaymentScheduleDetails.getRepayEvery();
|
||||
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentScheduleDetails
|
||||
.getRepaymentPeriodFrequencyType());
|
||||
calculatedRepaymentsStartingFromDate = CalendarUtils.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate,
|
||||
repayEvery, frequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
return calculatedRepaymentsStartingFromDate;
|
||||
}
|
||||
|
||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan) {
|
||||
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
LocalDate calculatedRepaymentsStartingFromDate = this.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan,
|
||||
calendarInstance);
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
|
||||
.getExpectedDisbursedOnLocalDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||
allowTransactionsOnNonWorkingDay);
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
}
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, floatingRateDTO);
|
||||
|
||||
return scheduleGeneratorDTO;
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = null;
|
||||
try {
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate().getRatePeriods();
|
||||
} catch (final FloatingRateNotFoundException ex) {
|
||||
// Do not do anything
|
||||
}
|
||||
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -2441,6 +2441,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void applyOverdueChargesForLoan(final Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas) {
|
||||
|
||||
|
||||
+7
@@ -102,5 +102,12 @@ public interface LoanProductConstants {
|
||||
public static final String inArrearsToleranceParamName = "inArrearsTolerance";
|
||||
public static final String repaymentEveryParamName = "repaymentEvery";
|
||||
public static final String graceOnPrincipalAndInterestPaymentParamName = "graceOnPrincipalAndInterestPayment";
|
||||
|
||||
//Variable Installments Settings
|
||||
public static final String allowVariableInstallmentsParamName = "allowVariableInstallments" ;
|
||||
public static final String minimumGapBetweenInstallments = "minimumGap" ;
|
||||
public static final String maximumGapBetweenInstallments = "maximumGap" ;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+59
-28
@@ -68,7 +68,7 @@ public class LoanProductData {
|
||||
private final BigDecimal maxInterestRatePerPeriod;
|
||||
private final EnumOptionData interestRateFrequencyType;
|
||||
private final BigDecimal annualInterestRate;
|
||||
|
||||
|
||||
private final boolean isLinkedToFloatingInterestRates;
|
||||
private final Integer floatingRateId;
|
||||
private final String floatingRateName;
|
||||
@@ -78,6 +78,11 @@ public class LoanProductData {
|
||||
private final BigDecimal maxDifferentialLendingRate;
|
||||
private final boolean isFloatingInterestRateCalculationAllowed;
|
||||
|
||||
// Variable Installments Settings
|
||||
private final boolean allowVariableInstallments;
|
||||
private final Integer minimumGap;
|
||||
private final Integer maximumGap;
|
||||
|
||||
// settings
|
||||
private final EnumOptionData amortizationType;
|
||||
private final EnumOptionData interestType;
|
||||
@@ -185,6 +190,9 @@ public class LoanProductData {
|
||||
final BigDecimal defaultDifferentialLendingRate = null;
|
||||
final BigDecimal maxDifferentialLendingRate = null;
|
||||
final boolean isFloatingInterestRateCalculationAllowed = false;
|
||||
final boolean isVariableInstallmentsAllowed = false;
|
||||
final Integer minimumGap = null;
|
||||
final Integer maximumGap = null;
|
||||
final EnumOptionData repaymentFrequencyType = null;
|
||||
final EnumOptionData interestRateFrequencyType = null;
|
||||
final EnumOptionData amortizationType = null;
|
||||
@@ -238,8 +246,8 @@ public class LoanProductData {
|
||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
|
||||
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap);
|
||||
|
||||
}
|
||||
|
||||
@@ -266,6 +274,9 @@ public class LoanProductData {
|
||||
final BigDecimal defaultDifferentialLendingRate = null;
|
||||
final BigDecimal maxDifferentialLendingRate = null;
|
||||
final boolean isFloatingInterestRateCalculationAllowed = false;
|
||||
final boolean isVariableInstallmentsAllowed = false;
|
||||
final Integer minimumGap = null;
|
||||
final Integer maximumGap = null;
|
||||
final EnumOptionData repaymentFrequencyType = null;
|
||||
final EnumOptionData interestRateFrequencyType = null;
|
||||
final EnumOptionData amortizationType = null;
|
||||
@@ -321,8 +332,8 @@ public class LoanProductData {
|
||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
|
||||
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap);
|
||||
|
||||
}
|
||||
|
||||
@@ -353,6 +364,9 @@ public class LoanProductData {
|
||||
final BigDecimal defaultDifferentialLendingRate = null;
|
||||
final BigDecimal maxDifferentialLendingRate = null;
|
||||
final boolean isFloatingInterestRateCalculationAllowed = false;
|
||||
final boolean isVariableInstallmentsAllowed = false;
|
||||
final Integer minimumGap = null;
|
||||
final Integer maximumGap = null;
|
||||
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(PeriodFrequencyType.MONTHS);
|
||||
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(PeriodFrequencyType.MONTHS);
|
||||
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(AmortizationMethod.EQUAL_INSTALLMENTS);
|
||||
@@ -411,9 +425,9 @@ public class LoanProductData {
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment,
|
||||
holdGuaranteeFunds, productGuaranteeData, principalThresholdForLastInstallment,
|
||||
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf,
|
||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
||||
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap);
|
||||
|
||||
}
|
||||
|
||||
@@ -449,9 +463,10 @@ public class LoanProductData {
|
||||
final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstallment,
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean canDefineInstallmentAmount,
|
||||
Integer installmentAmountInMultiplesOf, LoanProductConfigurableAttributes allowAttributeOverrides,
|
||||
boolean isLinkedToFloatingInterestRates, Integer floatingRateId, String floatingRateName,
|
||||
BigDecimal interestRateDifferential, BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
||||
BigDecimal maxDifferentialLendingRate, boolean isFloatingInterestRateCalculationAllowed) {
|
||||
boolean isLinkedToFloatingInterestRates, Integer floatingRateId, String floatingRateName, BigDecimal interestRateDifferential,
|
||||
BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate,
|
||||
boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -480,6 +495,9 @@ public class LoanProductData {
|
||||
this.defaultDifferentialLendingRate = defaultDifferentialLendingRate;
|
||||
this.maxDifferentialLendingRate = maxDifferentialLendingRate;
|
||||
this.isFloatingInterestRateCalculationAllowed = isFloatingInterestRateCalculationAllowed;
|
||||
this.allowVariableInstallments = isVariableInstallmentsAllowed;
|
||||
this.minimumGap = minimumGapBetweenInstallments;
|
||||
this.maximumGap = maximumGapBetweenInstallments;
|
||||
this.repaymentFrequencyType = repaymentFrequencyType;
|
||||
this.interestRateFrequencyType = interestRateFrequencyType;
|
||||
this.amortizationType = amortizationType;
|
||||
@@ -587,6 +605,9 @@ public class LoanProductData {
|
||||
this.defaultDifferentialLendingRate = productData.defaultDifferentialLendingRate;
|
||||
this.maxDifferentialLendingRate = productData.maxDifferentialLendingRate;
|
||||
this.isFloatingInterestRateCalculationAllowed = productData.isFloatingInterestRateCalculationAllowed;
|
||||
this.allowVariableInstallments = productData.allowVariableInstallments;
|
||||
this.minimumGap = productData.minimumGap;
|
||||
this.maximumGap = productData.maximumGap;
|
||||
this.repaymentFrequencyType = productData.repaymentFrequencyType;
|
||||
this.interestRateFrequencyType = productData.interestRateFrequencyType;
|
||||
this.amortizationType = productData.amortizationType;
|
||||
@@ -950,25 +971,35 @@ public class LoanProductData {
|
||||
this.allowAttributeOverrides = loanProductConfigurableAttributes;
|
||||
}
|
||||
|
||||
public boolean isLinkedToFloatingInterestRates() {
|
||||
return this.isLinkedToFloatingInterestRates;
|
||||
}
|
||||
public boolean isLinkedToFloatingInterestRates() {
|
||||
return this.isLinkedToFloatingInterestRates;
|
||||
}
|
||||
|
||||
public BigDecimal getMinDifferentialLendingRate() {
|
||||
return this.minDifferentialLendingRate;
|
||||
}
|
||||
public BigDecimal getMinDifferentialLendingRate() {
|
||||
return this.minDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public BigDecimal getDefaultDifferentialLendingRate() {
|
||||
return this.defaultDifferentialLendingRate;
|
||||
}
|
||||
public BigDecimal getDefaultDifferentialLendingRate() {
|
||||
return this.defaultDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public BigDecimal getMaxDifferentialLendingRate() {
|
||||
return this.maxDifferentialLendingRate;
|
||||
}
|
||||
public BigDecimal getMaxDifferentialLendingRate() {
|
||||
return this.maxDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public boolean isFloatingInterestRateCalculationAllowed() {
|
||||
return this.isFloatingInterestRateCalculationAllowed;
|
||||
}
|
||||
|
||||
|
||||
public boolean isFloatingInterestRateCalculationAllowed() {
|
||||
return this.isFloatingInterestRateCalculationAllowed;
|
||||
}
|
||||
|
||||
public boolean isVariableInstallmentsAllowed() {
|
||||
return this.allowVariableInstallments;
|
||||
}
|
||||
|
||||
public Integer getMinimumGapBetweenInstallments() {
|
||||
return this.minimumGap;
|
||||
}
|
||||
|
||||
public Integer getMaximumGapBetweenInstallments() {
|
||||
return this.maximumGap;
|
||||
}
|
||||
}
|
||||
+56
-9
@@ -51,7 +51,6 @@ import org.mifosplatform.portfolio.fund.domain.Fund;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
@@ -164,6 +163,13 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
|
||||
private LoanProductFloatingRates floatingRates;
|
||||
|
||||
@Column(name = "allow_variabe_installments", nullable = false)
|
||||
private boolean allowVariabeInstallments;
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
|
||||
private LoanProductVariableInstallmentConfig variableInstallmentConfig;
|
||||
|
||||
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate) {
|
||||
|
||||
@@ -195,6 +201,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
BigDecimal maxDifferentialLendingRate = null;
|
||||
BigDecimal defaultDifferentialLendingRate = null;
|
||||
Boolean isFloatingInterestRateCalculationAllowed = null;
|
||||
|
||||
Integer minimumGapBetweenInstallments = null;
|
||||
Integer maximumGapBetweenInstallments = null;
|
||||
|
||||
final Boolean isLinkedToFloatingInterestRates = command.booleanObjectValueOfParameterNamed("isLinkedToFloatingInterestRates");
|
||||
if (isLinkedToFloatingInterestRates != null && isLinkedToFloatingInterestRates) {
|
||||
interestRateDifferential = command.bigDecimalValueOfParameterNamed("interestRateDifferential");
|
||||
@@ -211,6 +221,13 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
annualInterestRate = aprCalculator.calculateFrom(interestFrequencyType, interestRatePerPeriod);
|
||||
}
|
||||
|
||||
final Boolean isVariableInstallmentsAllowed = command
|
||||
.booleanObjectValueOfParameterNamed(LoanProductConstants.allowVariableInstallmentsParamName);
|
||||
if (isVariableInstallmentsAllowed != null && isVariableInstallmentsAllowed) {
|
||||
minimumGapBetweenInstallments = command.integerValueOfParameterNamed(LoanProductConstants.minimumGapBetweenInstallments);
|
||||
maximumGapBetweenInstallments = command.integerValueOfParameterNamed(LoanProductConstants.maximumGapBetweenInstallments);
|
||||
}
|
||||
|
||||
final Integer repaymentEvery = command.integerValueOfParameterNamed("repaymentEvery");
|
||||
final Integer numberOfRepayments = command.integerValueOfParameterNamed("numberOfRepayments");
|
||||
final Integer minNumberOfRepayments = command.integerValueOfParameterNamed("minNumberOfRepayments");
|
||||
@@ -304,7 +321,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineEmiAmount, installmentAmountInMultiplesOf,
|
||||
loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate, interestRateDifferential,
|
||||
minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed);
|
||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGapBetweenInstallments,
|
||||
maximumGapBetweenInstallments);
|
||||
|
||||
}
|
||||
|
||||
@@ -534,7 +552,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
final Integer installmentAmountInMultiplesOf, final LoanProductConfigurableAttributes loanProductConfigurableAttributes,
|
||||
Boolean isLinkedToFloatingInterestRates, FloatingRate floatingRate, BigDecimal interestRateDifferential,
|
||||
BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
||||
Boolean isFloatingInterestRateCalculationAllowed) {
|
||||
Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||
this.name = name.trim();
|
||||
@@ -555,6 +574,13 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
maxDifferentialLendingRate, defaultDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
}
|
||||
|
||||
this.allowVariabeInstallments = isVariableInstallmentsAllowed == null ? false : isVariableInstallmentsAllowed;
|
||||
|
||||
if (allowVariabeInstallments) {
|
||||
this.variableInstallmentConfig = new LoanProductVariableInstallmentConfig(this, minimumGapBetweenInstallments,
|
||||
maximumGapBetweenInstallments);
|
||||
}
|
||||
|
||||
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod,
|
||||
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
|
||||
repaymentFrequencyType, defaultNumberOfInstallments, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||
@@ -674,8 +700,20 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
actualChanges.putAll(loanProductFloatingRates().update(command, floatingRate));
|
||||
this.loanProductRelatedDetail.updateForFloatingInterestRates();
|
||||
this.loanProductMinMaxConstraints.updateForFloatingInterestRates();
|
||||
}else{
|
||||
this.floatingRates = null;
|
||||
} else {
|
||||
this.floatingRates = null;
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.allowVariableInstallmentsParamName, this.allowVariabeInstallments)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.allowVariableInstallmentsParamName);
|
||||
actualChanges.put(LoanProductConstants.allowVariableInstallmentsParamName, newValue);
|
||||
this.allowVariabeInstallments = newValue;
|
||||
}
|
||||
|
||||
if (this.allowVariabeInstallments) {
|
||||
actualChanges.putAll(loanProductVariableInstallmentConfig().update(command));
|
||||
} else {
|
||||
this.variableInstallmentConfig = null;
|
||||
}
|
||||
|
||||
final String accountingTypeParamName = "accountingRule";
|
||||
@@ -964,13 +1002,18 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
private LoanProductFloatingRates loanProductFloatingRates() {
|
||||
this.floatingRates = this.floatingRates == null
|
||||
? new LoanProductFloatingRates(null, this, null, null, null, null, false)
|
||||
: this.floatingRates;
|
||||
private LoanProductFloatingRates loanProductFloatingRates() {
|
||||
this.floatingRates = this.floatingRates == null ? new LoanProductFloatingRates(null, this, null, null, null, null, false)
|
||||
: this.floatingRates;
|
||||
return this.floatingRates;
|
||||
}
|
||||
|
||||
public LoanProductVariableInstallmentConfig loanProductVariableInstallmentConfig() {
|
||||
this.variableInstallmentConfig = this.variableInstallmentConfig == null ? new LoanProductVariableInstallmentConfig(this, null, null)
|
||||
: this.variableInstallmentConfig;
|
||||
return this.variableInstallmentConfig;
|
||||
}
|
||||
|
||||
public boolean isAccountingDisabled() {
|
||||
return AccountingRuleType.NONE.getValue().equals(this.accountingRule);
|
||||
}
|
||||
@@ -1268,4 +1311,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
return applicableRates;
|
||||
}
|
||||
|
||||
public boolean allowVariabeInstallments() {
|
||||
return this.allowVariabeInstallments;
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_product_loan_variable_installment_config")
|
||||
public class LoanProductVariableInstallmentConfig extends AbstractPersistable<Long> {
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "loan_product_id", nullable = false)
|
||||
private LoanProduct loanProduct;
|
||||
|
||||
@Column(name = "minimum_gap")
|
||||
private Integer minimumGap;
|
||||
|
||||
@Column(name = "maximum_gap")
|
||||
private Integer maximumGap;
|
||||
|
||||
protected LoanProductVariableInstallmentConfig() {
|
||||
|
||||
}
|
||||
|
||||
public LoanProductVariableInstallmentConfig(final LoanProduct loanProduct, final Integer minimumGap, final Integer maximumGap) {
|
||||
this.loanProduct = loanProduct;
|
||||
this.minimumGap = minimumGap;
|
||||
this.maximumGap = maximumGap;
|
||||
}
|
||||
|
||||
public void setLoanProduct(final LoanProduct loanProduct) {
|
||||
this.loanProduct = loanProduct;
|
||||
}
|
||||
|
||||
public Map<? extends String, ? extends Object> update(JsonCommand command) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(3);
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.minimumGapBetweenInstallments, this.minimumGap)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.minimumGapBetweenInstallments);
|
||||
actualChanges.put(LoanProductConstants.minimumGapBetweenInstallments, newValue);
|
||||
this.minimumGap = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.maximumGapBetweenInstallments, this.maximumGap)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.maximumGapBetweenInstallments);
|
||||
actualChanges.put(LoanProductConstants.maximumGapBetweenInstallments, newValue);
|
||||
this.maximumGap = newValue;
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public Integer getMinimumGap() {
|
||||
return this.minimumGap;
|
||||
}
|
||||
|
||||
public Integer getMaximumGap() {
|
||||
return this.maximumGap;
|
||||
}
|
||||
|
||||
}
|
||||
+285
-168
@@ -87,7 +87,9 @@ public final class LoanProductDataValidator {
|
||||
LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstallmentParamName,
|
||||
LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName, LoanProductConstants.canDefineEmiAmountParamName,
|
||||
LoanProductConstants.installmentAmountInMultiplesOfParamName,
|
||||
LoanProductConstants.preClosureInterestCalculationStrategyParamName, LoanProductConstants.allowAttributeOverridesParamName));
|
||||
LoanProductConstants.preClosureInterestCalculationStrategyParamName, LoanProductConstants.allowAttributeOverridesParamName,
|
||||
LoanProductConstants.allowVariableInstallmentsParamName, LoanProductConstants.minimumGapBetweenInstallments,
|
||||
LoanProductConstants.maximumGapBetweenInstallments));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -290,58 +292,74 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
}
|
||||
|
||||
//interest rates
|
||||
if(this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)
|
||||
&& this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true){
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
// interest rates
|
||||
if (this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)
|
||||
&& this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true) {
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("interestRatePerPeriod")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("minInterestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("minInterestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"minInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("minInterestRatePerPeriod", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("minInterestRatePerPeriod")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"minInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("maxInterestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("maxInterestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"maxInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("maxInterestRatePerPeriod", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("maxInterestRatePerPeriod")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"maxInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)){
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRateFrequencyType param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("interestRateFrequencyType")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRateFrequencyType param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
if((interestType == null || interestType != InterestMethod.DECLINING_BALANCE.getValue())
|
||||
|| (isInterestRecalculationEnabled == null || isInterestRecalculationEnabled == false)){
|
||||
baseDataValidator.reset().parameter("isLinkedToFloatingInterestRates").failWithCode("supported.only.for.declining.balance.interest.recalculation.enabled",
|
||||
"Floating interest rates are supported only for declining balance and interest recalculation enabled loan products");
|
||||
}
|
||||
|
||||
final Integer floatingRatesId = this.fromApiJsonHelper.extractIntegerNamed("floatingRatesId", element,
|
||||
Locale.getDefault());
|
||||
if ((interestType == null || interestType != InterestMethod.DECLINING_BALANCE.getValue())
|
||||
|| (isInterestRecalculationEnabled == null || isInterestRecalculationEnabled == false)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("isLinkedToFloatingInterestRates")
|
||||
.failWithCode("supported.only.for.declining.balance.interest.recalculation.enabled",
|
||||
"Floating interest rates are supported only for declining balance and interest recalculation enabled loan products");
|
||||
}
|
||||
|
||||
final Integer floatingRatesId = this.fromApiJsonHelper.extractIntegerNamed("floatingRatesId", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("floatingRatesId").value(floatingRatesId).notNull();
|
||||
|
||||
final BigDecimal interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRateDifferential", element);
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").value(interestRateDifferential).notNull().zeroOrPositiveAmount();
|
||||
|
||||
final BigDecimal interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRateDifferential",
|
||||
element);
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").value(interestRateDifferential).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String minDifferentialLendingRateParameterName = "minDifferentialLendingRate";
|
||||
BigDecimal minDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minDifferentialLendingRateParameterName,
|
||||
element);
|
||||
BigDecimal minDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
minDifferentialLendingRateParameterName, element);
|
||||
baseDataValidator.reset().parameter(minDifferentialLendingRateParameterName).value(minDifferentialLendingRate).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String defaultDifferentialLendingRateParameterName = "defaultDifferentialLendingRate";
|
||||
BigDecimal defaultDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(defaultDifferentialLendingRateParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(defaultDifferentialLendingRateParameterName).value(defaultDifferentialLendingRate).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
BigDecimal defaultDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
defaultDifferentialLendingRateParameterName, element);
|
||||
baseDataValidator.reset().parameter(defaultDifferentialLendingRateParameterName).value(defaultDifferentialLendingRate)
|
||||
.notNull().zeroOrPositiveAmount();
|
||||
|
||||
final String maxDifferentialLendingRateParameterName = "maxDifferentialLendingRate";
|
||||
BigDecimal maxDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxDifferentialLendingRateParameterName,
|
||||
element);
|
||||
BigDecimal maxDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
maxDifferentialLendingRateParameterName, element);
|
||||
baseDataValidator.reset().parameter(maxDifferentialLendingRateParameterName).value(maxDifferentialLendingRate).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
@@ -349,57 +367,76 @@ public final class LoanProductDataValidator {
|
||||
.notLessThanMin(minDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate)
|
||||
.notLessThanMin(minDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate)
|
||||
.notLessThanMin(defaultDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final Boolean isFloatingInterestRateCalculationAllowed = this.fromApiJsonHelper.extractBooleanNamed(
|
||||
"isFloatingInterestRateCalculationAllowed", element);
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed")
|
||||
.value(isFloatingInterestRateCalculationAllowed).notNull().isOneOfTheseValues(true, false);
|
||||
"isFloatingInterestRateCalculationAllowed", element);
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").value(isFloatingInterestRateCalculationAllowed)
|
||||
.notNull().isOneOfTheseValues(true, false);
|
||||
} else {
|
||||
if(this.fromApiJsonHelper.parameterExists("floatingRatesId", element)){
|
||||
baseDataValidator.reset().parameter("floatingRatesId").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"floatingRatesId param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
if (this.fromApiJsonHelper.parameterExists("floatingRatesId", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("floatingRatesId")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"floatingRatesId param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)){
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"interestRateDifferential param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("interestRateDifferential")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"interestRateDifferential param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("minDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("minDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"minDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("minDifferentialLendingRate", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("minDifferentialLendingRate")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"minDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("defaultDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("defaultDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"defaultDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("defaultDifferentialLendingRate", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("defaultDifferentialLendingRate")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"defaultDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("maxDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"maxDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("maxDifferentialLendingRate", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("maxDifferentialLendingRate")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"maxDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)){
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("isFloatingInterestRateCalculationAllowed")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod",
|
||||
element);
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
|
||||
|
||||
final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
|
||||
@@ -442,7 +479,6 @@ public final class LoanProductDataValidator {
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
|
||||
}
|
||||
|
||||
|
||||
// Guarantee Funds
|
||||
Boolean holdGuaranteeFunds = false;
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.holdGuaranteeFundsParamName, element)) {
|
||||
@@ -563,9 +599,58 @@ public final class LoanProductDataValidator {
|
||||
|
||||
validateLoanConfigurableAttributes(baseDataValidator, element);
|
||||
|
||||
validateVariableInstallmentSettings(baseDataValidator, element);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void validateVariableInstallmentSettings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.allowVariableInstallmentsParamName, element)
|
||||
&& this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.allowVariableInstallmentsParamName, element)) {
|
||||
|
||||
Long minimumGapBetweenInstallments = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumGapBetweenInstallments, element)) {
|
||||
minimumGapBetweenInstallments = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.minimumGapBetweenInstallments,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.minimumGapBetweenInstallments)
|
||||
.value(minimumGapBetweenInstallments).notNull();
|
||||
} else {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanProductConstants.minimumGapBetweenInstallments)
|
||||
.failWithCode("is.mandatory.when.allowVariableInstallments.is.true",
|
||||
"minimumGap param is mandatory when allowVariableInstallments is true");
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.maximumGapBetweenInstallments, element)) {
|
||||
final Long maximumGapBetweenInstallments = this.fromApiJsonHelper.extractLongNamed(
|
||||
LoanProductConstants.maximumGapBetweenInstallments, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.minimumGapBetweenInstallments)
|
||||
.value(maximumGapBetweenInstallments).notNull();
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.maximumGapBetweenInstallments)
|
||||
.value(maximumGapBetweenInstallments).notNull().longGreaterThanNumber(minimumGapBetweenInstallments);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumGapBetweenInstallments, element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanProductConstants.minimumGapBetweenInstallments)
|
||||
.failWithCode("not.supported.when.allowVariableInstallments.is.false",
|
||||
"minimumGap param is not supported when allowVariableInstallments is not supplied or false");
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.maximumGapBetweenInstallments, element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanProductConstants.maximumGapBetweenInstallments)
|
||||
.failWithCode("not.supported.when.allowVariableInstallments.is.false",
|
||||
"maximumGap param is not supported when allowVariableInstallments is not supplied or false");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void validateLoanConfigurableAttributes(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.allowAttributeOverridesParamName, element)) {
|
||||
@@ -752,13 +837,13 @@ public final class LoanProductDataValidator {
|
||||
LoanProductConstants.recalculationCompoundingFrequencyIntervalParameterName, element)) {
|
||||
final Integer recurrenceInterval = this.fromApiJsonHelper.extractIntegerNamed(
|
||||
LoanProductConstants.recalculationCompoundingFrequencyIntervalParameterName, element, Locale.getDefault());
|
||||
Integer repaymentEvery = null;
|
||||
Integer repaymentEvery = null;
|
||||
if (loanProduct == null) {
|
||||
repaymentEvery = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
|
||||
} else {
|
||||
repaymentEvery = loanProduct.getLoanProductRelatedDetail().getRepayEvery();
|
||||
}
|
||||
|
||||
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.recalculationCompoundingFrequencyIntervalParameterName)
|
||||
.value(recurrenceInterval).notNull().integerInMultiplesOfNumber(repaymentEvery);
|
||||
}
|
||||
@@ -989,82 +1074,99 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
}
|
||||
|
||||
//interest rates
|
||||
// interest rates
|
||||
boolean isLinkedToFloatingInterestRates = loanProduct.isLinkedToFloatingInterestRate();
|
||||
if(this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)){
|
||||
isLinkedToFloatingInterestRates = this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element);
|
||||
if (this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)) {
|
||||
isLinkedToFloatingInterestRates = this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element);
|
||||
}
|
||||
if(isLinkedToFloatingInterestRates){
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
if (isLinkedToFloatingInterestRates) {
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("interestRatePerPeriod")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("minInterestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("minInterestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"minInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("minInterestRatePerPeriod", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("minInterestRatePerPeriod")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"minInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("maxInterestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("maxInterestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"maxInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("maxInterestRatePerPeriod", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("maxInterestRatePerPeriod")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"maxInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)){
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRateFrequencyType param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("interestRateFrequencyType")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRateFrequencyType param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
Integer interestType = this.fromApiJsonHelper.parameterExists("interestType", element)
|
||||
? this.fromApiJsonHelper.extractIntegerNamed("interestType", element, Locale.getDefault())
|
||||
: loanProduct.getLoanProductRelatedDetail().getInterestMethod().getValue();
|
||||
if((interestType == null || interestType != InterestMethod.DECLINING_BALANCE.getValue())
|
||||
|| (isInterestRecalculationEnabled == null || isInterestRecalculationEnabled == false)){
|
||||
baseDataValidator.reset().parameter("isLinkedToFloatingInterestRates").failWithCode("supported.only.for.declining.balance.interest.recalculation.enabled",
|
||||
"Floating interest rates are supported only for declining balance and interest recalculation enabled loan products");
|
||||
}
|
||||
|
||||
Long floatingRatesId = loanProduct.getFloatingRates() == null? null: loanProduct.getFloatingRates().getFloatingRate().getId();
|
||||
if(this.fromApiJsonHelper.parameterExists("floatingRatesId", element)){
|
||||
|
||||
Integer interestType = this.fromApiJsonHelper.parameterExists("interestType", element) ? this.fromApiJsonHelper
|
||||
.extractIntegerNamed("interestType", element, Locale.getDefault()) : loanProduct.getLoanProductRelatedDetail()
|
||||
.getInterestMethod().getValue();
|
||||
if ((interestType == null || interestType != InterestMethod.DECLINING_BALANCE.getValue())
|
||||
|| (isInterestRecalculationEnabled == null || isInterestRecalculationEnabled == false)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("isLinkedToFloatingInterestRates")
|
||||
.failWithCode("supported.only.for.declining.balance.interest.recalculation.enabled",
|
||||
"Floating interest rates are supported only for declining balance and interest recalculation enabled loan products");
|
||||
}
|
||||
|
||||
Long floatingRatesId = loanProduct.getFloatingRates() == null ? null : loanProduct.getFloatingRates().getFloatingRate().getId();
|
||||
if (this.fromApiJsonHelper.parameterExists("floatingRatesId", element)) {
|
||||
floatingRatesId = this.fromApiJsonHelper.extractLongNamed("floatingRatesId", element);
|
||||
}
|
||||
}
|
||||
baseDataValidator.reset().parameter("floatingRatesId").value(floatingRatesId).notNull();
|
||||
|
||||
BigDecimal interestRateDifferential = loanProduct.getFloatingRates() == null? null: loanProduct.getFloatingRates().getInterestRateDifferential();
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)){
|
||||
BigDecimal interestRateDifferential = loanProduct.getFloatingRates() == null ? null : loanProduct.getFloatingRates()
|
||||
.getInterestRateDifferential();
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)) {
|
||||
interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRateDifferential", element);
|
||||
}
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").value(interestRateDifferential).notNull().zeroOrPositiveAmount();
|
||||
}
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").value(interestRateDifferential).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String minDifferentialLendingRateParameterName = "minDifferentialLendingRate";
|
||||
BigDecimal minDifferentialLendingRate = loanProduct.getFloatingRates() == null
|
||||
? null:loanProduct.getFloatingRates().getMinDifferentialLendingRate();
|
||||
BigDecimal minDifferentialLendingRate = loanProduct.getFloatingRates() == null ? null : loanProduct.getFloatingRates()
|
||||
.getMinDifferentialLendingRate();
|
||||
if (this.fromApiJsonHelper.parameterExists(minDifferentialLendingRateParameterName, element)) {
|
||||
minDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minDifferentialLendingRateParameterName,
|
||||
element);
|
||||
minDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
minDifferentialLendingRateParameterName, element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(minDifferentialLendingRateParameterName).value(minDifferentialLendingRate).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String defaultDifferentialLendingRateParameterName = "defaultDifferentialLendingRate";
|
||||
BigDecimal defaultDifferentialLendingRate = loanProduct.getFloatingRates() == null
|
||||
? null:loanProduct.getFloatingRates().getDefaultDifferentialLendingRate();
|
||||
BigDecimal defaultDifferentialLendingRate = loanProduct.getFloatingRates() == null ? null : loanProduct.getFloatingRates()
|
||||
.getDefaultDifferentialLendingRate();
|
||||
if (this.fromApiJsonHelper.parameterExists(defaultDifferentialLendingRateParameterName, element)) {
|
||||
defaultDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(defaultDifferentialLendingRateParameterName,
|
||||
element);
|
||||
defaultDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
defaultDifferentialLendingRateParameterName, element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(defaultDifferentialLendingRateParameterName).value(defaultDifferentialLendingRate).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
baseDataValidator.reset().parameter(defaultDifferentialLendingRateParameterName).value(defaultDifferentialLendingRate)
|
||||
.notNull().zeroOrPositiveAmount();
|
||||
|
||||
final String maxDifferentialLendingRateParameterName = "maxDifferentialLendingRate";
|
||||
BigDecimal maxDifferentialLendingRate = loanProduct.getFloatingRates() == null
|
||||
? null:loanProduct.getFloatingRates().getMaxDifferentialLendingRate();
|
||||
BigDecimal maxDifferentialLendingRate = loanProduct.getFloatingRates() == null ? null : loanProduct.getFloatingRates()
|
||||
.getMaxDifferentialLendingRate();
|
||||
if (this.fromApiJsonHelper.parameterExists(maxDifferentialLendingRateParameterName, element)) {
|
||||
maxDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxDifferentialLendingRateParameterName,
|
||||
element);
|
||||
maxDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
maxDifferentialLendingRateParameterName, element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(maxDifferentialLendingRateParameterName).value(maxDifferentialLendingRate).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
@@ -1072,60 +1174,78 @@ public final class LoanProductDataValidator {
|
||||
.notLessThanMin(minDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate)
|
||||
.notLessThanMin(minDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate)
|
||||
.notLessThanMin(defaultDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
Boolean isFloatingInterestRateCalculationAllowed = loanProduct.getFloatingRates() == null ? null
|
||||
: loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed();
|
||||
if(this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)){
|
||||
|
||||
Boolean isFloatingInterestRateCalculationAllowed = loanProduct.getFloatingRates() == null ? null : loanProduct
|
||||
.getFloatingRates().isFloatingInterestRateCalculationAllowed();
|
||||
if (this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)) {
|
||||
isFloatingInterestRateCalculationAllowed = this.fromApiJsonHelper.extractBooleanNamed(
|
||||
"isFloatingInterestRateCalculationAllowed", element);
|
||||
"isFloatingInterestRateCalculationAllowed", element);
|
||||
}
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed")
|
||||
.value(isFloatingInterestRateCalculationAllowed).notNull().isOneOfTheseValues(true, false);
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").value(isFloatingInterestRateCalculationAllowed)
|
||||
.notNull().isOneOfTheseValues(true, false);
|
||||
} else {
|
||||
if(this.fromApiJsonHelper.parameterExists("floatingRatesId", element)){
|
||||
baseDataValidator.reset().parameter("floatingRatesId").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"floatingRatesId param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
if (this.fromApiJsonHelper.parameterExists("floatingRatesId", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("floatingRatesId")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"floatingRatesId param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)){
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"interestRateDifferential param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("interestRateDifferential")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"interestRateDifferential param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("minDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("minDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"minDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("minDifferentialLendingRate", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("minDifferentialLendingRate")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"minDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("defaultDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("defaultDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"defaultDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("defaultDifferentialLendingRate", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("defaultDifferentialLendingRate")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"defaultDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("maxDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"maxDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("maxDifferentialLendingRate", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("maxDifferentialLendingRate")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"maxDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)){
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("isFloatingInterestRateCalculationAllowed")
|
||||
.failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
|
||||
final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
|
||||
BigDecimal minInterestRatePerPeriod = loanProduct.getMinNominalInterestRatePerPeriod();
|
||||
if (this.fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) {
|
||||
@@ -1133,7 +1253,7 @@ public final class LoanProductDataValidator {
|
||||
element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod";
|
||||
BigDecimal maxInterestRatePerPeriod = loanProduct.getMaxNominalInterestRatePerPeriod();
|
||||
@@ -1142,12 +1262,11 @@ public final class LoanProductDataValidator {
|
||||
element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
BigDecimal interestRatePerPeriod = loanProduct.getLoanProductRelatedDetail().getNominalInterestRatePerPeriod();
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||
interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod",
|
||||
element);
|
||||
interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
|
||||
}
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
|
||||
|
||||
@@ -1275,6 +1394,7 @@ public final class LoanProductDataValidator {
|
||||
|
||||
// validateLoanConfigurableAttributes(baseDataValidator,element);
|
||||
|
||||
validateVariableInstallmentSettings(baseDataValidator, element);
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -1556,11 +1676,8 @@ public final class LoanProductDataValidator {
|
||||
private void validateNominalInterestRatePerPeriodMinMaxConstraint(final JsonElement element, final LoanProduct loanProduct,
|
||||
final DataValidatorBuilder baseDataValidator) {
|
||||
|
||||
if((this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)
|
||||
&& this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true)
|
||||
|| loanProduct.isLinkedToFloatingInterestRate()){
|
||||
return;
|
||||
}
|
||||
if ((this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element) && this.fromApiJsonHelper
|
||||
.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true) || loanProduct.isLinkedToFloatingInterestRate()) { return; }
|
||||
boolean iRPUpdated = false;
|
||||
boolean minIRPUpdated = false;
|
||||
boolean maxIRPUpdated = false;
|
||||
|
||||
+17
-1
@@ -514,7 +514,23 @@ public class LoanEnumerations {
|
||||
case INTEREST_RATE:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.INTEREST_RATE.getValue().longValue(),
|
||||
LoanTermVariationType.INTEREST_RATE.getCode(), "interestRate");
|
||||
break;
|
||||
break;
|
||||
case DELETE_INSTALLMENT:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.DELETE_INSTALLMENT.getValue().longValue(),
|
||||
LoanTermVariationType.DELETE_INSTALLMENT.getCode(), "deleteInstallment");
|
||||
break;
|
||||
case DUE_DATE:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.DUE_DATE.getValue().longValue(),
|
||||
LoanTermVariationType.DUE_DATE.getCode(), "dueDate");
|
||||
break;
|
||||
case INSERT_INSTALLMENT:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.INSERT_INSTALLMENT.getValue().longValue(),
|
||||
LoanTermVariationType.DUE_DATE.getCode(), "insertInstallment");
|
||||
break;
|
||||
case PRINCIPAL_AMOUNT:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.PRINCIPAL_AMOUNT.getValue().longValue(),
|
||||
LoanTermVariationType.PRINCIPAL_AMOUNT.getCode(), "principalAmount");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.INVALID.getValue().longValue(),
|
||||
LoanTermVariationType.INVALID.getCode(), "Invalid");
|
||||
|
||||
+15
-6
@@ -200,7 +200,10 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "lfr.min_differential_lending_rate as minDifferentialLendingRate, "
|
||||
+ "lfr.default_differential_lending_rate as defaultDifferentialLendingRate, "
|
||||
+ "lfr.max_differential_lending_rate as maxDifferentialLendingRate, "
|
||||
+ "lfr.is_floating_interest_rate_calculation_allowed as isFloatingInterestRateCalculationAllowed "
|
||||
+ "lfr.is_floating_interest_rate_calculation_allowed as isFloatingInterestRateCalculationAllowed, "
|
||||
+ "lp.allow_variabe_installments as isVariableIntallmentsAllowed, "
|
||||
+ "lvi.minimum_gap as minimumGap, "
|
||||
+ "lvi.maximum_gap as maximumGap "
|
||||
+ " from m_product_loan lp "
|
||||
+ " left join m_fund f on f.id = lp.fund_id "
|
||||
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
|
||||
@@ -209,7 +212,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ " left join m_product_loan_configurable_attributes lca on lca.loan_product_id = lp.id "
|
||||
+ " left join m_product_loan_floating_rates as lfr on lfr.loan_product_id = lp.id "
|
||||
+ " left join m_floating_rates as fr on lfr.floating_rates_id = fr.id "
|
||||
+ " left join m_product_loan_variable_installment_config as lvi on lvi.loan_product_id = lp.id "
|
||||
+ " join m_currency curr on curr.code = lp.currency_code";
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -268,7 +273,11 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final BigDecimal defaultDifferentialLendingRate = rs.getBigDecimal("defaultDifferentialLendingRate");
|
||||
final BigDecimal maxDifferentialLendingRate = rs.getBigDecimal("maxDifferentialLendingRate");
|
||||
final boolean isFloatingInterestRateCalculationAllowed = rs.getBoolean("isFloatingInterestRateCalculationAllowed");
|
||||
|
||||
|
||||
final boolean isVariableIntallmentsAllowed = rs.getBoolean("isVariableIntallmentsAllowed");
|
||||
final Integer minimumGap = rs.getInt("minimumGap");
|
||||
final Integer maximumGap = rs.getInt("maximumGap");
|
||||
|
||||
final int repaymentFrequencyTypeId = JdbcSupport.getInteger(rs, "repaymentPeriodFrequency");
|
||||
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeId);
|
||||
|
||||
@@ -341,8 +350,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final Integer compoundingFrequencyEnumValue = JdbcSupport.getInteger(rs, "compoundingFrequencyTypeEnum");
|
||||
EnumOptionData compoundingFrequencyType = null;
|
||||
if (compoundingFrequencyEnumValue != null) {
|
||||
compoundingFrequencyType = LoanEnumerations
|
||||
.interestRecalculationFrequencyType(compoundingFrequencyEnumValue);
|
||||
compoundingFrequencyType = LoanEnumerations.interestRecalculationFrequencyType(compoundingFrequencyEnumValue);
|
||||
}
|
||||
final Integer compoundingInterval = JdbcSupport.getInteger(rs, "compoundingInterval");
|
||||
final LocalDate compoundingFrequencyDate = JdbcSupport.getLocalDate(rs, "compoundingFrequencyDate");
|
||||
@@ -397,8 +405,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData,
|
||||
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
|
||||
installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
|
||||
maximumGap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
ALTER TABLE `m_product_loan`
|
||||
ADD COLUMN `allow_variabe_installments` BIT(1) NOT NULL DEFAULT 0 AFTER `is_linked_to_floating_interest_rates` ;
|
||||
|
||||
CREATE TABLE `m_product_loan_variable_installment_config` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`loan_product_id` BIGINT(20) NOT NULL,
|
||||
`minimum_gap` INT(4) NOT NULL,
|
||||
`maximum_gap` INT(4) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `FK_mappings_m_variable_product_loan_id` FOREIGN KEY (`loan_product_id`) REFERENCES `m_product_loan` (`id`)
|
||||
);
|
||||
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'CREATESCHEDULEEXCEPTIONS_LOAN', 'LOAN', 'CREATESCHEDULEEXCEPTIONS', 0), ('portfolio', 'CREATESCHEDULEEXCEPTIONS_LOAN_CHECKER', 'LOAN', 'CREATESCHEDULEEXCEPTIONS_CHECKER', 0), ('portfolio', 'DELETESCHEDULEEXCEPTIONS_LOAN', 'LOAN', 'DELETESCHEDULEEXCEPTIONS', 0),('portfolio', 'DELETESCHEDULEEXCEPTIONS_LOAN_CHECKER', 'LOAN', 'DELETESCHEDULEEXCEPTIONS_CHECKER', 0);
|
||||
|
||||
|
||||
ALTER TABLE `m_loan_term_variations`
|
||||
ALTER `applicable_from` DROP DEFAULT,
|
||||
ALTER `term_value` DROP DEFAULT;
|
||||
ALTER TABLE `m_loan_term_variations`
|
||||
CHANGE COLUMN `applicable_from` `applicable_date` DATE NOT NULL AFTER `term_type`,
|
||||
CHANGE COLUMN `term_value` `decimal_value` DECIMAL(19,6) NULL AFTER `applicable_date`,
|
||||
ADD COLUMN `date_value` DATE NULL AFTER `decimal_value`,
|
||||
ADD COLUMN `is_specific_to_installment` TINYINT NOT NULL DEFAULT '0' AFTER `date_value`,
|
||||
ADD COLUMN `applied_on_loan_status` SMALLINT(5) NOT NULL AFTER `is_specific_to_installment`;
|
||||
|
||||
UPDATE `m_loan_term_variations` SET `applied_on_loan_status`=300;
|
||||
Reference in New Issue
Block a user