commit for MIFOSX-1523, Loan Rescheduling module
This commit is contained in:
@@ -1041,6 +1041,46 @@
|
||||
<td><a href="#collaterals_update">Update a Collateral</a></td>
|
||||
<td><a href="#collaterals_delete">Delete a Collateral</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#loan_rescheduling">Loan Rescheduling</a></td>
|
||||
<td>rescheduleloans</td>
|
||||
<td><a href="#loan_reschedule_request_create">Create new loan reschedule request</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>rescheduleloans/{requestId}</td>
|
||||
<td></td>
|
||||
<td><a href="#loan_reschedule_request_retrieve">Retrieve a Loan Reschedule Request</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>rescheduleloans/{requestId}?command=previewLoanReschedule</td>
|
||||
<td></td>
|
||||
<td><a href="#loan_reschedule_preview_retrieve">Retrieve a Preview of The New Loan Repayment Schedule</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>rescheduleloans/{requestId}?command=reject</td>
|
||||
<td><a href="#loan_reschedule_request_reject">Reject Loan Reschedule Request</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>rescheduleloans/{requestId}?command=approve</td>
|
||||
<td><a href="#loan_reschedule_request_approve">Approve Loan Reschedule Request</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -9860,6 +9900,382 @@ No Request Body:
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loan Rescheduling content -->
|
||||
<a id="loan_rescheduling" name="loan_rescheduling" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h3>Loan Rescheduling</h3>
|
||||
<p>Loan rescheduling provides the ability to give a client extra grace periods, extend loan term by adding extra installments and adjust the interest rate of a loan.</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 rescheduled.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>rescheduleFromDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Due date of the start point (installment) of the rescheduling.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>graceOnPrincipal</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Number of installments to be added with zero principal.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>graceOnInterest</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Number of installments to be added with zero interest.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>extraTerms</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Number of installments to be added after the last loan repayment schedule installment.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>recalculateInterest</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>If recalculateInterest="true", the total interest amount of the loan is recalculated. If recalculateInterest="false", the total interest amount remains the same.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>newInterestRate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>New interest rate to be used in calculating the interest amount for each rescheduled period/installment.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>adjustedDueDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>New due date for the start point (installment) of the rescheduling.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>rescheduleReasonId</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>The ID of the code value that indicates the reason for rescheduling the loan.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>rescheduleReasonComment</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Text provided as extra reason for the rescheduling of the loan.</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>submittedOnDate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>The date on which the loan reschedule request was made.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="loan_reschedule_request_create" name="loan_reschedule_request_create" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Create a Loan Reschedule Request</h4>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>loanId, rescheduleFromDate, rescheduleReasonId, submittedOnDate,<br />
|
||||
graceOnPrincipal OR graceOnInterest OR extraTerms OR newInterestRate OR adjustedDueDate
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Optional Fields</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>recalculateInterest, rescheduleReasonComment</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
POST https://DomainName/api/v1/rescheduleloans
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST rescheduleloans
|
||||
Content-Type: application/json Request Body:
|
||||
{
|
||||
"loanId": 1,
|
||||
"graceOnPrincipal": 2,
|
||||
"graceOnInterest": 3,
|
||||
"extraTerms": 2,
|
||||
"rescheduleFromDate": "04 December 2014",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"locale": "en",
|
||||
"recalculateInterest": true,
|
||||
"submittedOnDate": "04 September 2014",
|
||||
"newInterestRate" : 28,
|
||||
"rescheduleReasonId": 1
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"loanId": 1,
|
||||
"resourceId": 1
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-request">
|
||||
POST rescheduleloans
|
||||
Content-Type: application/json Request Body:
|
||||
{
|
||||
"loanId": 1,
|
||||
"graceOnPrincipal": 2,
|
||||
"rescheduleFromDate": "04 December 2014",
|
||||
"adjustedDueDate": "20 December 2014",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"locale": "en",
|
||||
"submittedOnDate": "04 September 2014",
|
||||
"rescheduleReasonComment" : "Client has gone AWOL",
|
||||
"rescheduleReasonId": 1
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"loanId": 1,
|
||||
"resourceId": 1
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<a id="loan_reschedule_request_retrieve" name="loan_reschedule_request_retrieve" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Retrieve a Loan Reschedule Request</h4>
|
||||
<p>Example Requests:</p>
|
||||
<div class=apiClick>rescheduleloans/1</div>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
GET https://DomainName/api/v1/rescheduleloans/{requestId}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"id": 2,
|
||||
"loanId": 17,
|
||||
"clientId": 4,
|
||||
"clientName": "Fernando Michael Torres",
|
||||
"loanAccountNumber": "000000017",
|
||||
"statusEnum": {
|
||||
"id": 500,
|
||||
"code": "loanStatusType.rejected",
|
||||
"value": "Rejected",
|
||||
"pendingApproval": false,
|
||||
"approved": false,
|
||||
"rejected": true
|
||||
},
|
||||
"rescheduleFromInstallment": 5,
|
||||
"graceOnPrincipal": 3,
|
||||
"rescheduleFromDate": [
|
||||
2013,
|
||||
12,
|
||||
6
|
||||
],
|
||||
"recalculateInterest": false,
|
||||
"rescheduleReasonCodeValue": {
|
||||
"id": 239,
|
||||
"name": "client cannot pay on time"
|
||||
},
|
||||
"timeline": {
|
||||
"submittedOnDate": [
|
||||
2014,
|
||||
8,
|
||||
4
|
||||
],
|
||||
"submittedByUsername": "mifos",
|
||||
"submittedByFirstname": "App",
|
||||
"submittedByLastname": "Administrator",
|
||||
"rejectedOnDate": [
|
||||
2014,
|
||||
8,
|
||||
5
|
||||
],
|
||||
"rejectedByUsername": "mifos",
|
||||
"rejectedByFirstname": "App",
|
||||
"rejectedByLastname": "Administrator"
|
||||
},
|
||||
"rescheduleReasonComment": ""
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<a id="loan_reschedule_preview_retrieve" name="loan_reschedule_preview_retrieve" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Retrieve a Preview of The New Loan Repayment Schedule</h4>
|
||||
<p>Example Requests:</p>
|
||||
<div class=apiClick>rescheduleloans/1?command=previewLoanReschedule</div>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
GET https://DomainName/api/v1/rescheduleloans/{requestId}?command=previewLoanReschedule
|
||||
</code>
|
||||
<code class="method-response">
|
||||
|
||||
"currency": {
|
||||
"code": "KES",
|
||||
"name": "Kenyan Shilling",
|
||||
"decimalPlaces": 0,
|
||||
"inMultiplesOf": 10,
|
||||
"displaySymbol": "KSh",
|
||||
"nameCode": "currency.KES",
|
||||
"displayLabel": "Kenyan Shilling (KSh)"
|
||||
},
|
||||
"loanTermInDays": 182,
|
||||
"totalPrincipalDisbursed": 10000,
|
||||
"totalPrincipalExpected": 10000,
|
||||
"totalPrincipalPaid": 900.000000,
|
||||
"totalInterestCharged": 960,
|
||||
"totalFeeChargesCharged": 0.000000,
|
||||
"totalPenaltyChargesCharged": 2500.000000,
|
||||
"totalRepaymentExpected": 13460,
|
||||
"totalOutstanding": 12060.000000,
|
||||
"periods": [
|
||||
{
|
||||
"period": 1,
|
||||
"fromDate": [
|
||||
2013,
|
||||
11,
|
||||
8
|
||||
],
|
||||
"dueDate": [
|
||||
2013,
|
||||
11,
|
||||
8
|
||||
],
|
||||
"daysInPeriod": 0,
|
||||
"principalOriginalDue": 0,
|
||||
"principalDue": 0,
|
||||
"principalOutstanding": 0,
|
||||
"principalLoanBalanceOutstanding": 10000,
|
||||
"interestOriginalDue": 0,
|
||||
"interestDue": 0,
|
||||
"interestOutstanding": 0,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 0,
|
||||
"totalDueForPeriod": 0,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 0,
|
||||
"totalOverdue": 0,
|
||||
"totalActualCostOfLoanForPeriod": 0
|
||||
},
|
||||
...
|
||||
...
|
||||
{
|
||||
"period": 30,
|
||||
"fromDate": [
|
||||
2014,
|
||||
5,
|
||||
23
|
||||
],
|
||||
"dueDate": [
|
||||
2014,
|
||||
5,
|
||||
30
|
||||
],
|
||||
"daysInPeriod": 7,
|
||||
"principalOriginalDue": 300,
|
||||
"principalDue": 300,
|
||||
"principalOutstanding": 300,
|
||||
"principalLoanBalanceOutstanding": 0,
|
||||
"interestOriginalDue": 130,
|
||||
"interestDue": 130,
|
||||
"interestOutstanding": 130,
|
||||
"feeChargesDue": 0,
|
||||
"penaltyChargesDue": 0,
|
||||
"totalOriginalDueForPeriod": 430,
|
||||
"totalDueForPeriod": 430,
|
||||
"totalPaidForPeriod": 0,
|
||||
"totalOutstandingForPeriod": 430,
|
||||
"totalOverdue": 430,
|
||||
"totalActualCostOfLoanForPeriod": 130
|
||||
}
|
||||
]
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<a id="loan_reschedule_request_reject" name="loan_reschedule_request_reject" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Reject a Loan Reschedule Request</h4>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">POST https://DomainName/api/v1/rescheduleloans/{requestId}?command=reject</code>
|
||||
<code class="method-request">POST rescheduleloans/1?command=reject
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"locale": "en",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"rejectedOnDate": "11 September 2014"
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"loanId": 1,
|
||||
"resourceId": 1,
|
||||
"changes": {
|
||||
"locale": "en",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"rejectedOnDate": "11 September 2014",
|
||||
"rejectedByUserId": 1
|
||||
}
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<a id="loan_reschedule_request_approve" name="loan_reschedule_request_approve" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Approve a Loan Reschedule Request</h4>
|
||||
<p>Rescheduling of a loan happens once a loan reschedule request is approved.</p>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">POST https://DomainName/api/v1/rescheduleloans/{requestId}?command=approve</code>
|
||||
<code class="method-request">POST rescheduleloans/1?command=approve
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"locale": "en",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"approvedOnDate": "11 September 2014"
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"loanId": 1,
|
||||
"resourceId": 1,
|
||||
"changes": {
|
||||
"locale": "en",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"approvedOnDate": "11 September 2014",
|
||||
"approvedByUserId": 1
|
||||
}
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- start of offices content -->
|
||||
<a id="offices" name="offices" class="old-syle-anchor"> </a>
|
||||
@@ -32215,6 +32631,46 @@ No Request Body:
|
||||
<td><a href="#collaterals_update">Update a Collateral</a></td>
|
||||
<td><a href="#collaterals_delete">Delete a Collateral</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#loan_rescheduling">Loan Rescheduling</a></td>
|
||||
<td>rescheduleloans</td>
|
||||
<td><a href="#loan_reschedule_request_create">Create a Loan Reschedule Request</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>rescheduleloans/{requestId}</td>
|
||||
<td></td>
|
||||
<td><a href="#loan_reschedule_request_retrieve">Retrieve a Loan Reschedule Request</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>rescheduleloans/{requestId}?command=previewLoanReschedule</td>
|
||||
<td></td>
|
||||
<td><a href="#loan_reschedule_preview_retrieve">Retrieve a Preview of The New Loan Repayment Schedule</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>rescheduleloans/{requestId}?command=reject</td>
|
||||
<td><a href="#loan_reschedule_request_reject">Reject Loan Reschedule Request</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>rescheduleloans/{requestId}?command=approve</td>
|
||||
<td><a href="#loan_reschedule_request_approve">Approve Loan Reschedule Request</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#glaccounts">General Ledger Account</a></td>
|
||||
<td>glaccounts</td>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
create table m_loan_reschedule_request (
|
||||
id bigint(20) primary key auto_increment,
|
||||
loan_id bigint(20) not null,
|
||||
status_enum smallint(5) not null,
|
||||
reschedule_from_installment smallint(5) not null comment 'Rescheduling will start from this installment',
|
||||
grace_on_principal smallint(5) null comment 'Number of installments that should be added with 0 principal amount',
|
||||
grace_on_interest smallint(5) null comment 'Number of installments that should be added with 0 interest rate',
|
||||
reschedule_from_date date not null comment 'Rescheduling will start from the installment with due date similar to this date.',
|
||||
extra_terms smallint(5) comment 'Number of extra terms to be added to the schedule',
|
||||
interest_rate decimal(19,6) null comment 'If provided, the interest rate for the unpaid installments will be recalculated',
|
||||
recalculate_interest tinyint(1) null comment 'If set to 1, interest will be recalculated starting from the reschedule period.',
|
||||
adjusted_due_date date null comment 'New due date for the first rescheduled installment',
|
||||
reschedule_reason_cv_id int(11) null comment 'ID of code value of reason for rescheduling',
|
||||
reschedule_reason_comment varchar(500) null comment 'Text provided in addition to the reason code value',
|
||||
submitted_on_date date not null,
|
||||
submitted_by_user_id bigint(20) not null,
|
||||
approved_on_date date null,
|
||||
approved_by_user_id bigint(20) null,
|
||||
rejected_on_date date null,
|
||||
rejected_by_user_id bigint(20) null,
|
||||
foreign key (loan_id) references m_loan(id),
|
||||
foreign key (reschedule_reason_cv_id) references m_code_value(id),
|
||||
foreign key (submitted_by_user_id) references m_appuser(id),
|
||||
foreign key (approved_by_user_id) references m_appuser(id),
|
||||
foreign key (rejected_by_user_id) references m_appuser(id)
|
||||
);
|
||||
|
||||
create table m_loan_repayment_schedule_history (
|
||||
id bigint(20) primary key auto_increment,
|
||||
loan_id bigint(20) not null,
|
||||
loan_reschedule_request_id bigint(20) null,
|
||||
fromdate date null,
|
||||
duedate date not null,
|
||||
installment smallint(5) not null,
|
||||
principal_amount decimal(19,6) null,
|
||||
principal_completed_derived decimal(19,6) null,
|
||||
principal_writtenoff_derived decimal(19,6) null,
|
||||
interest_amount decimal(19,6) null,
|
||||
interest_completed_derived decimal(19,6) null,
|
||||
interest_writtenoff_derived decimal(19,6) null,
|
||||
interest_waived_derived decimal(19,6) null,
|
||||
accrual_interest_derived decimal(19,6) null,
|
||||
fee_charges_amount decimal(19,6) null,
|
||||
fee_charges_completed_derived decimal(19,6) null,
|
||||
fee_charges_writtenoff_derived decimal(19,6) null,
|
||||
fee_charges_waived_derived decimal(19,6) null,
|
||||
accrual_fee_charges_derived decimal(19,6) null,
|
||||
penalty_charges_amount decimal(19,6) null,
|
||||
penalty_charges_completed_derived decimal(19,6) null,
|
||||
penalty_charges_writtenoff_derived decimal(19,6) null,
|
||||
penalty_charges_waived_derived decimal(19,6) null,
|
||||
accrual_penalty_charges_derived decimal(19,6) null,
|
||||
total_paid_in_advance_derived decimal(19,6) null,
|
||||
total_paid_late_derived decimal(19,6) null,
|
||||
completed_derived bit(1) not null,
|
||||
obligations_met_on_date date null,
|
||||
createdby_id bigint(20) null,
|
||||
created_date datetime null,
|
||||
lastmodified_date datetime null,
|
||||
lastmodifiedby_id bigint(20) null,
|
||||
foreign key (loan_id) references m_loan(id),
|
||||
foreign key (loan_reschedule_request_id) references m_loan_reschedule_request(id)
|
||||
);
|
||||
|
||||
alter table m_loan add rescheduledon_userid bigint(20) null after rescheduledon_date;
|
||||
|
||||
insert into `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) values ('loan_reschedule', 'READ_RESCHEDULELOAN', 'RESCHEDULELOAN', 'READ', '0');
|
||||
|
||||
insert into `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) values ('loan_reschedule', 'CREATE_RESCHEDULELOAN', 'RESCHEDULELOAN', 'CREATE', '0');
|
||||
|
||||
insert into `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) values ('loan_reschedule', 'REJECT_RESCHEDULELOAN', 'RESCHEDULELOAN', 'REJECT', '0');
|
||||
|
||||
insert into `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) values ('loan_reschedule', 'APPROVE_RESCHEDULELOAN', 'RESCHEDULELOAN', 'APPROVE', '0');
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* 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.integrationtests;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mifosplatform.integrationtests.common.ClientHelper;
|
||||
import org.mifosplatform.integrationtests.common.LoanRescheduleRequestHelper;
|
||||
import org.mifosplatform.integrationtests.common.Utils;
|
||||
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
|
||||
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.mifosplatform.integrationtests.common.loans.LoanRescheduleRequestTestBuilder;
|
||||
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
|
||||
|
||||
import com.jayway.restassured.builder.RequestSpecBuilder;
|
||||
import com.jayway.restassured.builder.ResponseSpecBuilder;
|
||||
import com.jayway.restassured.http.ContentType;
|
||||
import com.jayway.restassured.specification.RequestSpecification;
|
||||
import com.jayway.restassured.specification.ResponseSpecification;
|
||||
|
||||
/**
|
||||
* Test the creation, approval and rejection of a loan reschedule request
|
||||
**/
|
||||
@SuppressWarnings({ "rawtypes" })
|
||||
public class LoanRescheduleRequestTest {
|
||||
private ResponseSpecification responseSpec;
|
||||
private ResponseSpecification generalResponseSpec;
|
||||
private RequestSpecification requestSpec;
|
||||
private LoanTransactionHelper loanTransactionHelper;
|
||||
private LoanRescheduleRequestHelper loanRescheduleRequestHelper;
|
||||
private Integer clientId;
|
||||
private Integer loanProductId;
|
||||
private Integer loanId;
|
||||
private Integer loanRescheduleRequestId;
|
||||
private String loanPrincipalAmount = "100000.00";
|
||||
private String numberOfRepayments = "12";
|
||||
private String interestRatePerPeriod = "18";
|
||||
private String dateString = "4 September 2014";
|
||||
|
||||
@Before
|
||||
public void initialize() {
|
||||
Utils.initializeRESTAssured();
|
||||
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
this.loanRescheduleRequestHelper = new LoanRescheduleRequestHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
this.generalResponseSpec = new ResponseSpecBuilder().build();
|
||||
|
||||
// create all required entities
|
||||
this.createRequiredEntities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the client, loan product, and loan entities
|
||||
**/
|
||||
private void createRequiredEntities() {
|
||||
this.createClientEntity();
|
||||
this.createLoanProductEntity();
|
||||
this.createLoanEntity();
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new client
|
||||
**/
|
||||
private void createClientEntity() {
|
||||
this.clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
|
||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, this.clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new loan product
|
||||
**/
|
||||
private void createLoanProductEntity() {
|
||||
System.out.println("---------------------------------CREATING LOAN PRODUCT------------------------------------------");
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder()
|
||||
.withPrincipal(loanPrincipalAmount)
|
||||
.withNumberOfRepayments(numberOfRepayments)
|
||||
.withinterestRatePerPeriod(interestRatePerPeriod)
|
||||
.withInterestRateFrequencyTypeAsYear()
|
||||
.build(null);
|
||||
|
||||
this.loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
System.out.println("Successfully created loan product (ID: " + this.loanProductId + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* submit a new loan application, approve and disburse the loan
|
||||
**/
|
||||
private void createLoanEntity() {
|
||||
System.out.println("---------------------------------NEW LOAN APPLICATION------------------------------------------");
|
||||
|
||||
final String loanApplicationJSON = new LoanApplicationTestBuilder()
|
||||
.withPrincipal(loanPrincipalAmount)
|
||||
.withLoanTermFrequency(numberOfRepayments)
|
||||
.withLoanTermFrequencyAsMonths()
|
||||
.withNumberOfRepayments(numberOfRepayments)
|
||||
.withRepaymentEveryAfter("1")
|
||||
.withRepaymentFrequencyTypeAsMonths()
|
||||
.withAmortizationTypeAsEqualInstallments()
|
||||
.withInterestCalculationPeriodTypeAsDays()
|
||||
.withInterestRatePerPeriod(interestRatePerPeriod)
|
||||
.withLoanTermFrequencyAsMonths()
|
||||
.withSubmittedOnDate(dateString)
|
||||
.withExpectedDisbursementDate(dateString)
|
||||
.withPrincipalGrace("2")
|
||||
.withInterestGrace("2")
|
||||
.build(this.clientId.toString(), this.loanProductId.toString(), null);
|
||||
|
||||
this.loanId = this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
|
||||
System.out.println("Sucessfully created loan (ID: " + this.loanId + ")");
|
||||
|
||||
this.approveLoanApplication();
|
||||
this.disburseLoan();
|
||||
}
|
||||
|
||||
/**
|
||||
* approve the loan application
|
||||
**/
|
||||
private void approveLoanApplication() {
|
||||
|
||||
if(this.loanId != null) {
|
||||
this.loanTransactionHelper.approveLoan(this.dateString, this.loanId);
|
||||
System.out.println("Successfully approved loan (ID: " + this.loanId + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disburse the newly created loan
|
||||
**/
|
||||
private void disburseLoan() {
|
||||
|
||||
if(this.loanId != null) {
|
||||
this.loanTransactionHelper.disburseLoan(this.dateString, this.loanId);
|
||||
System.out.println("Successfully disbursed loan (ID: " + this.loanId + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create new loan reschedule request
|
||||
**/
|
||||
private void createLoanRescheduleRequest() {
|
||||
System.out.println("---------------------------------CREATING LOAN RESCHEDULE REQUEST------------------------------------------");
|
||||
|
||||
final String requestJSON = new LoanRescheduleRequestTestBuilder().build(this.loanId.toString());
|
||||
|
||||
this.loanRescheduleRequestId = this.loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
|
||||
this.loanRescheduleRequestHelper.verifyCreationOfLoanRescheduleRequest(this.loanRescheduleRequestId);
|
||||
|
||||
System.out.println("Successfully created loan reschedule request (ID: " + this.loanRescheduleRequestId + ")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateLoanRescheduleRequest() {
|
||||
this.createLoanRescheduleRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectLoanRescheduleRequest() {
|
||||
this.createLoanRescheduleRequest();
|
||||
|
||||
System.out.println("-----------------------------REJECTING LOAN RESCHEDULE REQUEST--------------------------");
|
||||
|
||||
final String requestJSON = new LoanRescheduleRequestTestBuilder().getRejectLoanRescheduleRequestJSON();
|
||||
this.loanRescheduleRequestHelper.rejectLoanRescheduleRequest(this.loanRescheduleRequestId, requestJSON);
|
||||
|
||||
final HashMap response = (HashMap) this.loanRescheduleRequestHelper.getLoanRescheduleRequest(loanRescheduleRequestId, "statusEnum");
|
||||
assertTrue((Boolean)response.get("rejected"));
|
||||
|
||||
System.out.println("Successfully rejected loan reschedule request (ID: " + this.loanRescheduleRequestId + ")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApproveLoanRescheduleRequest() {
|
||||
this.createLoanRescheduleRequest();
|
||||
|
||||
System.out.println("-----------------------------APPROVING LOAN RESCHEDULE REQUEST--------------------------");
|
||||
|
||||
final String requestJSON = new LoanRescheduleRequestTestBuilder().getApproveLoanRescheduleRequestJSON();
|
||||
this.loanRescheduleRequestHelper.approveLoanRescheduleRequest(this.loanRescheduleRequestId, requestJSON);
|
||||
|
||||
final HashMap response = (HashMap) this.loanRescheduleRequestHelper.getLoanRescheduleRequest(loanRescheduleRequestId, "statusEnum");
|
||||
assertTrue((Boolean)response.get("approved"));
|
||||
|
||||
final Integer numberOfRepayments = (Integer) this.loanTransactionHelper.getLoanDetail(requestSpec, generalResponseSpec, loanId, "numberOfRepayments");
|
||||
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
||||
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
||||
|
||||
assertEquals("NUMBER OF REPAYMENTS SHOULD BE 16, NOT 12", "16", numberOfRepayments.toString());
|
||||
assertEquals("TOTAL EXPECTED REPAYMENT MUST BE EQUAL TO 118000.0", "118000.0", totalExpectedRepayment.toString());
|
||||
|
||||
System.out.println("Successfully approved loan reschedule request (ID: " + this.loanRescheduleRequestId + ")");
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -28,7 +28,7 @@ import com.jayway.restassured.specification.ResponseSpecification;
|
||||
*/
|
||||
public class BatchHelper {
|
||||
|
||||
private static final String BATCH_API_URL = "/mifosng-provider/api/v1/batches?tenantIdentifier=default";
|
||||
private static final String BATCH_API_URL = "/mifosng-provider/api/v1/batches?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String BATCH_API_URL_EXT = BATCH_API_URL + "&enclosingTransaction=true";
|
||||
|
||||
private BatchHelper() {
|
||||
@@ -346,7 +346,7 @@ public class BatchHelper {
|
||||
public static void verifyClientCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String externalId) {
|
||||
System.out.println("------------------------------CHECK CLIENT DETAILS------------------------------------\n");
|
||||
final String CLIENT_URL = "/mifosng-provider/api/v1/clients?externalId=" + externalId + "&tenantIdentifier=default";
|
||||
final String CLIENT_URL = "/mifosng-provider/api/v1/clients?externalId=" + externalId + "&" + Utils.TENANT_IDENTIFIER;
|
||||
final Integer responseRecords = Utils.performServerGet(requestSpec, responseSpec, CLIENT_URL, "totalFilteredRecords");
|
||||
Assert.assertEquals("No records found with given externalId", (long) responseRecords, (long) 0);
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ import com.jayway.restassured.specification.ResponseSpecification;
|
||||
|
||||
public class ClientHelper {
|
||||
|
||||
private static final String CREATE_CLIENT_URL = "/mifosng-provider/api/v1/clients?tenantIdentifier=default";
|
||||
private static final String CREATE_CLIENT_URL = "/mifosng-provider/api/v1/clients?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
public static Integer createClient(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
return createClient(requestSpec, responseSpec, "04 March 2011");
|
||||
@@ -50,7 +50,7 @@ public class ClientHelper {
|
||||
public static void verifyClientCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Integer generatedClientID) {
|
||||
System.out.println("------------------------------CHECK CLIENT DETAILS------------------------------------\n");
|
||||
final String CLIENT_URL = "/mifosng-provider/api/v1/clients/" + generatedClientID + "?tenantIdentifier=default";
|
||||
final String CLIENT_URL = "/mifosng-provider/api/v1/clients/" + generatedClientID + "?" + Utils.TENANT_IDENTIFIER;
|
||||
final Integer responseClientID = Utils.performServerGet(requestSpec, responseSpec, CLIENT_URL, "id");
|
||||
assertEquals("ERROR IN CREATING THE CLIENT", generatedClientID, responseClientID);
|
||||
}
|
||||
|
||||
+6
-6
@@ -25,7 +25,7 @@ public class GlobalConfigurationHelper {
|
||||
}
|
||||
|
||||
public static ArrayList getAllGlobalConfigurations(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
final String GET_ALL_GLOBAL_CONFIG_URL = "/mifosng-provider/api/v1/configurations?tenantIdentifier=default";
|
||||
final String GET_ALL_GLOBAL_CONFIG_URL = "/mifosng-provider/api/v1/configurations?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RETRIEVING ALL GLOBAL CONFIGURATIONS -------------------------");
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, GET_ALL_GLOBAL_CONFIG_URL, "");
|
||||
return (ArrayList) response.get("globalConfiguration");
|
||||
@@ -33,14 +33,14 @@ public class GlobalConfigurationHelper {
|
||||
|
||||
public static HashMap getGlobalConfigurationById(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String configId) {
|
||||
final String GET_GLOBAL_CONFIG_BY_ID_URL = "/mifosng-provider/api/v1/configurations/" + configId + "?tenantIdentifier=default";
|
||||
final String GET_GLOBAL_CONFIG_BY_ID_URL = "/mifosng-provider/api/v1/configurations/" + configId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RETRIEVING GLOBAL CONFIGURATION BY ID -------------------------");
|
||||
return Utils.performServerGet(requestSpec, responseSpec, GET_GLOBAL_CONFIG_BY_ID_URL, "");
|
||||
}
|
||||
|
||||
public static Integer updateValueForGlobalConfiguration(final RequestSpecification requestSpec,
|
||||
final ResponseSpecification responseSpec, final String configId, final String value) {
|
||||
final String GLOBAL_CONFIG_UPDATE_URL = "/mifosng-provider/api/v1/configurations/" + configId + "?tenantIdentifier=default";
|
||||
final String GLOBAL_CONFIG_UPDATE_URL = "/mifosng-provider/api/v1/configurations/" + configId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("---------------------------------UPDATE VALUE FOR GLOBAL CONFIG---------------------------------------------");
|
||||
return Utils.performServerPut(requestSpec, responseSpec, GLOBAL_CONFIG_UPDATE_URL, updateGlobalConfigUpdateValueAsJSON(value),
|
||||
"resourceId");
|
||||
@@ -48,7 +48,7 @@ public class GlobalConfigurationHelper {
|
||||
|
||||
public static Integer updateEnabledFlagForGlobalConfiguration(final RequestSpecification requestSpec,
|
||||
final ResponseSpecification responseSpec, final String configId, final Boolean enabled) {
|
||||
final String GLOBAL_CONFIG_UPDATE_URL = "/mifosng-provider/api/v1/configurations/" + configId + "?tenantIdentifier=default";
|
||||
final String GLOBAL_CONFIG_UPDATE_URL = "/mifosng-provider/api/v1/configurations/" + configId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out
|
||||
.println("---------------------------------UPDATE GLOBAL CONFIG FOR ENABLED FLAG---------------------------------------------");
|
||||
return Utils.performServerPut(requestSpec, responseSpec, GLOBAL_CONFIG_UPDATE_URL,
|
||||
@@ -57,7 +57,7 @@ public class GlobalConfigurationHelper {
|
||||
|
||||
public static ArrayList getGlobalConfigurationIsCacheEnabled(final RequestSpecification requestSpec,
|
||||
final ResponseSpecification responseSpec) {
|
||||
final String GET_IS_CACHE_GLOBAL_CONFIG_URL = "/mifosng-provider/api/v1/caches?tenantIdentifier=default";
|
||||
final String GET_IS_CACHE_GLOBAL_CONFIG_URL = "/mifosng-provider/api/v1/caches?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RETRIEVING IS CACHE ENABLED GLOBAL CONFIGURATION -------------------------");
|
||||
final ArrayList<HashMap> response = Utils.performServerGet(requestSpec, responseSpec, GET_IS_CACHE_GLOBAL_CONFIG_URL, "");
|
||||
return response;
|
||||
@@ -65,7 +65,7 @@ public class GlobalConfigurationHelper {
|
||||
|
||||
public static HashMap updateIsCacheEnabledForGlobalConfiguration(final RequestSpecification requestSpec,
|
||||
final ResponseSpecification responseSpec, final String cacheType) {
|
||||
final String IS_CACHE_GLOBAL_CONFIG_UPDATE_URL = "/mifosng-provider/api/v1/caches?tenantIdentifier=default";
|
||||
final String IS_CACHE_GLOBAL_CONFIG_UPDATE_URL = "/mifosng-provider/api/v1/caches?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------UPDATE GLOBAL CONFIG FOR IS CACHE ENABLED----------------------");
|
||||
return Utils.performServerPut(requestSpec, responseSpec, IS_CACHE_GLOBAL_CONFIG_UPDATE_URL,
|
||||
updateIsCacheEnabledGlobalConfigUpdateAsJSON(cacheType), "changes");
|
||||
|
||||
+12
-12
@@ -21,7 +21,7 @@ import com.jayway.restassured.specification.ResponseSpecification;
|
||||
|
||||
public class GroupHelper {
|
||||
|
||||
private static final String CREATE_GROUP_URL = "/mifosng-provider/api/v1/groups?tenantIdentifier=default";
|
||||
private static final String CREATE_GROUP_URL = "/mifosng-provider/api/v1/groups?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
public static Integer createGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
@SuppressWarnings("unused") final boolean active) {
|
||||
@@ -43,7 +43,7 @@ public class GroupHelper {
|
||||
public static Integer associateClient(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String groupId, final String clientMember) {
|
||||
final String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId
|
||||
+ "?command=associateClients&tenantIdentifier=default";
|
||||
+ "?command=associateClients&" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("---------------------------------Associate Client To A GROUP---------------------------------------------");
|
||||
return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, associateClientAsJSON(clientMember), "groupId");
|
||||
}
|
||||
@@ -51,27 +51,27 @@ public class GroupHelper {
|
||||
public static Integer disAssociateClient(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String groupId, final String clientMember) {
|
||||
final String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId
|
||||
+ "?command=disassociateClients&tenantIdentifier=default";
|
||||
+ "?command=disassociateClients&" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("---------------------------------Disassociate Client To A GROUP---------------------------------------------");
|
||||
return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, associateClientAsJSON(clientMember), "groupId");
|
||||
}
|
||||
|
||||
public static Integer activateGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String groupId) {
|
||||
final String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?command=activate&tenantIdentifier=default";
|
||||
final String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?command=activate&" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("---------------------------------Activate A GROUP---------------------------------------------");
|
||||
return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, activateGroupAsJSON(""), "groupId");
|
||||
}
|
||||
|
||||
public static Integer updateGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final String name,
|
||||
final String groupId) {
|
||||
final String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?tenantIdentifier=default";
|
||||
final String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("---------------------------------UPDATE GROUP---------------------------------------------");
|
||||
return Utils.performServerPut(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, updateGroupAsJSON(name), "groupId");
|
||||
}
|
||||
|
||||
public static Integer deleteGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final String groupId) {
|
||||
final String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?tenantIdentifier=default";
|
||||
final String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("---------------------------------DELETE GROUP---------------------------------------------");
|
||||
return Utils.performServerDelete(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, "groupId");
|
||||
}
|
||||
@@ -129,7 +129,7 @@ public class GroupHelper {
|
||||
public static void verifyGroupCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Integer generatedGroupID) {
|
||||
System.out.println("------------------------------CHECK GROUP DETAILS------------------------------------\n");
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default";
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?" + Utils.TENANT_IDENTIFIER;
|
||||
final Integer responseGroupID = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "id");
|
||||
assertEquals("ERROR IN CREATING THE GROUP", generatedGroupID, responseGroupID);
|
||||
}
|
||||
@@ -137,7 +137,7 @@ public class GroupHelper {
|
||||
public static void verifyGroupDetails(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Integer generatedGroupID, final String field, final String expectedValue) {
|
||||
System.out.println("------------------------------CHECK GROUP DETAILS------------------------------------\n");
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default";
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?" + Utils.TENANT_IDENTIFIER;
|
||||
final String responseValue = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, field);
|
||||
assertEquals("ERROR IN CREATING THE GROUP", expectedValue, responseValue);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ public class GroupHelper {
|
||||
public static void verifyGroupActivatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Integer generatedGroupID, final boolean generatedGroupStatus) {
|
||||
System.out.println("------------------------------CHECK GROUP STATUS------------------------------------\n");
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default";
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?" + Utils.TENANT_IDENTIFIER;
|
||||
final Boolean responseGroupStatus = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "active");
|
||||
assertEquals("ERROR IN ACTIVATING THE GROUP", generatedGroupStatus, responseGroupStatus);
|
||||
}
|
||||
@@ -155,7 +155,7 @@ public class GroupHelper {
|
||||
List<String> list = new ArrayList<>();
|
||||
System.out.println("------------------------------CHECK GROUP MEMBERS------------------------------------\n");
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID
|
||||
+ "?associations=clientMembers&tenantIdentifier=default";
|
||||
+ "?associations=clientMembers&" + Utils.TENANT_IDENTIFIER;
|
||||
list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "clientMembers");
|
||||
assertTrue("ERROR IN GROUP MEMBER", list.toString().contains("id=" + groupMember.toString()));
|
||||
}
|
||||
@@ -165,7 +165,7 @@ public class GroupHelper {
|
||||
List<String> list = new ArrayList<>();
|
||||
System.out.println("------------------------------CHECK EMPTY GROUP MEMBER LIST------------------------------------\n");
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID
|
||||
+ "?associations=clientMembers&tenantIdentifier=default";
|
||||
+ "?associations=clientMembers&" + Utils.TENANT_IDENTIFIER;
|
||||
list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "clientMembers");
|
||||
assertEquals("GROUP MEMBER LIST NOT EMPTY", list, null);
|
||||
}
|
||||
@@ -174,7 +174,7 @@ public class GroupHelper {
|
||||
final Integer generatedGroupID) {
|
||||
List<String> list = new ArrayList<>();
|
||||
System.out.println("------------------------------CHECK GROUP DELETED------------------------------------\n");
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/?tenantIdentifier=default";
|
||||
final String GROUP_URL = "/mifosng-provider/api/v1/groups/?" + Utils.TENANT_IDENTIFIER;
|
||||
list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "pageItems");
|
||||
|
||||
assertFalse("GROUP NOT DELETED", list.toString().contains("id=" + generatedGroupID.toString()));
|
||||
|
||||
+2
-2
@@ -64,13 +64,13 @@ public class HolidayHelper {
|
||||
}
|
||||
|
||||
public static Integer activateHolidays(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final String holidayID) {
|
||||
final String ACTIVATE_HOLIDAY_URL = HOLIDAYS_URL + "/" + holidayID + "?command=activate";
|
||||
final String ACTIVATE_HOLIDAY_URL = HOLIDAYS_URL + "/" + holidayID + "?command=activate&" + Utils.TENANT_IDENTIFIER;
|
||||
return Utils.performServerPost(requestSpec, responseSpec, ACTIVATE_HOLIDAY_URL, getActivateHolidayDataAsJSON(), "resourceId");
|
||||
}
|
||||
|
||||
public static HashMap getHolidayById(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String holidayID) {
|
||||
final String GET_HOLIDAY_BY_ID_URL = HOLIDAYS_URL + "/" + holidayID + "?tenantIdentifier=default";
|
||||
final String GET_HOLIDAY_BY_ID_URL = HOLIDAYS_URL + "/" + holidayID + "?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RETRIEVING HOLIDAY BY ID -------------------------");
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, GET_HOLIDAY_BY_ID_URL, "");
|
||||
return response;
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.integrationtests.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import com.jayway.restassured.specification.RequestSpecification;
|
||||
import com.jayway.restassured.specification.ResponseSpecification;
|
||||
|
||||
public class LoanRescheduleRequestHelper {
|
||||
private final RequestSpecification requestSpec;
|
||||
private final ResponseSpecification responseSpec;
|
||||
|
||||
private static final String LOAN_RESCHEDULE_REQUEST_URL = "/mifosng-provider/api/v1/rescheduleloans";
|
||||
|
||||
public LoanRescheduleRequestHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
this.requestSpec = requestSpec;
|
||||
this.responseSpec = responseSpec;
|
||||
}
|
||||
|
||||
public Integer createLoanRescheduleRequest(final String requestJSON) {
|
||||
final String URL = LOAN_RESCHEDULE_REQUEST_URL + "?" + Utils.TENANT_IDENTIFIER;
|
||||
return Utils.performServerPost(this.requestSpec, this.responseSpec, URL, requestJSON, "resourceId");
|
||||
}
|
||||
|
||||
public Integer rejectLoanRescheduleRequest(final Integer requestId, final String requestJSON) {
|
||||
final String URL = LOAN_RESCHEDULE_REQUEST_URL + "/" + requestId + "?" + Utils.TENANT_IDENTIFIER + "&command=reject";
|
||||
|
||||
return Utils.performServerPost(this.requestSpec, this.responseSpec, URL, requestJSON, "resourceId");
|
||||
}
|
||||
|
||||
public Integer approveLoanRescheduleRequest(final Integer requestId, final String requestJSON) {
|
||||
final String URL = LOAN_RESCHEDULE_REQUEST_URL + "/" + requestId + "?" + Utils.TENANT_IDENTIFIER + "&command=approve";
|
||||
|
||||
return Utils.performServerPost(this.requestSpec, this.responseSpec, URL, requestJSON, "resourceId");
|
||||
}
|
||||
|
||||
public Object getLoanRescheduleRequest(final Integer requestId, final String param) {
|
||||
final String URL = LOAN_RESCHEDULE_REQUEST_URL + "/" + requestId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
return Utils.performServerGet(requestSpec, responseSpec, URL, param);
|
||||
}
|
||||
|
||||
public void verifyCreationOfLoanRescheduleRequest(final Integer requestId) {
|
||||
final String URL = LOAN_RESCHEDULE_REQUEST_URL + "/" + requestId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
final Integer id = Utils.performServerGet(requestSpec, responseSpec, URL, "id");
|
||||
assertEquals("ERROR IN CREATING LOAN RESCHEDULE REQUEST", requestId, id);
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -28,7 +28,7 @@ public class SchedulerJobHelper {
|
||||
}
|
||||
|
||||
public static ArrayList getAllSchedulerJobs(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
final String GET_ALL_SCHEDULER_JOBS_URL = "/mifosng-provider/api/v1/jobs?tenantIdentifier=default";
|
||||
final String GET_ALL_SCHEDULER_JOBS_URL = "/mifosng-provider/api/v1/jobs?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RETRIEVING ALL SCHEDULER JOBS -------------------------");
|
||||
final ArrayList response = Utils.performServerGet(requestSpec, responseSpec, GET_ALL_SCHEDULER_JOBS_URL, "");
|
||||
return response;
|
||||
@@ -36,28 +36,28 @@ public class SchedulerJobHelper {
|
||||
|
||||
public static HashMap getSchedulerJobById(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String jobId) {
|
||||
final String GET_SCHEDULER_JOB_BY_ID_URL = "/mifosng-provider/api/v1/jobs/" + jobId + "?tenantIdentifier=default";
|
||||
final String GET_SCHEDULER_JOB_BY_ID_URL = "/mifosng-provider/api/v1/jobs/" + jobId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RETRIEVING SCHEDULER JOB BY ID -------------------------");
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, GET_SCHEDULER_JOB_BY_ID_URL, "");
|
||||
return response;
|
||||
}
|
||||
|
||||
public static HashMap getSchedulerStatus(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
final String GET_SCHEDULER_STATUS_URL = "/mifosng-provider/api/v1/scheduler?tenantIdentifier=default";
|
||||
final String GET_SCHEDULER_STATUS_URL = "/mifosng-provider/api/v1/scheduler?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RETRIEVING SCHEDULER STATUS -------------------------");
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, GET_SCHEDULER_STATUS_URL, "");
|
||||
return response;
|
||||
}
|
||||
|
||||
public static void updateSchedulerStatus(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final String command) {
|
||||
final String UPDATE_SCHEDULER_STATUS_URL = "/mifosng-provider/api/v1/scheduler?command=" + command +"&tenantIdentifier=default";
|
||||
final String UPDATE_SCHEDULER_STATUS_URL = "/mifosng-provider/api/v1/scheduler?command=" + command +"&" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ UPDATING SCHEDULER STATUS -------------------------");
|
||||
Utils.performServerPost(requestSpec, responseSpec, UPDATE_SCHEDULER_STATUS_URL, runSchedulerJobAsJSON(), null);
|
||||
}
|
||||
|
||||
public static HashMap updateSchedulerJob(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String jobId, final String active) {
|
||||
final String UPDATE_SCHEDULER_JOB_URL = "/mifosng-provider/api/v1/jobs/" + jobId + "?tenantIdentifier=default";
|
||||
final String UPDATE_SCHEDULER_JOB_URL = "/mifosng-provider/api/v1/jobs/" + jobId + "?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ UPDATING SCHEDULER JOB -------------------------");
|
||||
final HashMap response = Utils.performServerPut(requestSpec, responseSpec, UPDATE_SCHEDULER_JOB_URL,
|
||||
updateSchedulerJobAsJSON(active), "changes");
|
||||
@@ -73,7 +73,7 @@ public class SchedulerJobHelper {
|
||||
|
||||
public static ArrayList getSchedulerJobHistory(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String jobId) {
|
||||
final String GET_SCHEDULER_STATUS_URL = "/mifosng-provider/api/v1/jobs/" + jobId + "/runhistory?tenantIdentifier=default";
|
||||
final String GET_SCHEDULER_STATUS_URL = "/mifosng-provider/api/v1/jobs/" + jobId + "/runhistory?" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RETRIEVING SCHEDULER JOB HISTORY -------------------------");
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, GET_SCHEDULER_STATUS_URL, "");
|
||||
return (ArrayList) response.get("pageItems");
|
||||
@@ -81,7 +81,7 @@ public class SchedulerJobHelper {
|
||||
|
||||
public static void runSchedulerJob(final RequestSpecification requestSpec, final String jobId) {
|
||||
final ResponseSpecification responseSpec = new ResponseSpecBuilder().expectStatusCode(202).build();
|
||||
final String RUN_SCHEDULER_JOB_URL = "/mifosng-provider/api/v1/jobs/" + jobId + "?command=executeJob&tenantIdentifier=default";
|
||||
final String RUN_SCHEDULER_JOB_URL = "/mifosng-provider/api/v1/jobs/" + jobId + "?command=executeJob&" + Utils.TENANT_IDENTIFIER;
|
||||
System.out.println("------------------------ RUN SCHEDULER JOB -------------------------");
|
||||
Utils.performServerPost(requestSpec, responseSpec, RUN_SCHEDULER_JOB_URL, runSchedulerJobAsJSON(), null);
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class SurveyHelper {
|
||||
|
||||
private static final String FULFIL_SURVEY_URL = "/mifosng-provider/api/v1/survey/ppi_kenya_2009/clientId?tenantIdentifier=default";
|
||||
private static final String FULFIL_SURVEY_URL = "/mifosng-provider/api/v1/survey/ppi_kenya_2009/clientId?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
public static Integer fulfilSurvey(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
return fulfilSurvey(requestSpec, responseSpec, "04 March 2011");
|
||||
@@ -52,7 +52,7 @@ public class SurveyHelper {
|
||||
public static void verifySurveyCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Integer generatedClientID) {
|
||||
System.out.println("------------------------------CHECK CLIENT DETAILS------------------------------------\n");
|
||||
final String SURVEY_URL = "/mifosng-provider/api/v1/Survey/ppi_kenya_2009/clientid/entryId" + generatedClientID + "?tenantIdentifier=default";
|
||||
final String SURVEY_URL = "/mifosng-provider/api/v1/Survey/ppi_kenya_2009/clientid/entryId" + generatedClientID + "?" + Utils.TENANT_IDENTIFIER;
|
||||
final Integer responseClientID = Utils.performServerGet(requestSpec, responseSpec, SURVEY_URL, "id");
|
||||
assertEquals("ERROR IN CREATING THE CLIENT", generatedClientID, responseClientID);
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import com.jayway.restassured.specification.ResponseSpecification;
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class AccountHelper {
|
||||
|
||||
private final String CREATE_GL_ACCOUNT_URL = "/mifosng-provider/api/v1/glaccounts?tenantIdentifier=default";
|
||||
private final String CREATE_GL_ACCOUNT_URL = "/mifosng-provider/api/v1/glaccounts?" + Utils.TENANT_IDENTIFIER;
|
||||
private final String GL_ACCOUNT_ID_RESPONSE = "resourceId";
|
||||
|
||||
private final RequestSpecification requestSpec;
|
||||
|
||||
+15
@@ -43,6 +43,7 @@ public class LoanApplicationTestBuilder {
|
||||
private String fixedEmiAmount = "10000";
|
||||
private String maxOutstandingLoanBalance = "36000";
|
||||
private String graceOnPrincipalPayment = null;
|
||||
private String graceOnInterestPayment = null;
|
||||
private List<HashMap> disbursementData = null;
|
||||
private List<HashMap> charges = new ArrayList<HashMap>();
|
||||
|
||||
@@ -81,6 +82,10 @@ public class LoanApplicationTestBuilder {
|
||||
if (graceOnPrincipalPayment != null) {
|
||||
map.put("graceOnPrincipalPayment", graceOnPrincipalPayment);
|
||||
}
|
||||
|
||||
if(graceOnInterestPayment != null) {
|
||||
map.put("graceOnInterestPayment", graceOnInterestPayment);
|
||||
}
|
||||
|
||||
if (disbursementData != null) {
|
||||
map.put("disbursementData", disbursementData);
|
||||
@@ -146,6 +151,11 @@ public class LoanApplicationTestBuilder {
|
||||
this.repaymentFrequencyType = WEEKS;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanApplicationTestBuilder withRepaymentFrequencyTypeAsYear() {
|
||||
this.repaymentFrequencyType = YEARS;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanApplicationTestBuilder withInterestRatePerPeriod(final String interestRate) {
|
||||
this.interestRate = interestRate;
|
||||
@@ -206,6 +216,11 @@ public class LoanApplicationTestBuilder {
|
||||
this.graceOnPrincipalPayment = graceOnPrincipalPayment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanApplicationTestBuilder withInterestGrace(final String graceOnInterestPayment) {
|
||||
this.graceOnInterestPayment = graceOnInterestPayment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanApplicationTestBuilder withTranches(final List<HashMap> disbursementData) {
|
||||
this.disbursementData = disbursementData;
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 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.integrationtests.common.loans;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class LoanRescheduleRequestTestBuilder {
|
||||
private String rescheduleFromDate = "04 December 2014";
|
||||
private String graceOnPrincipal = "2";
|
||||
private String graceOnInterest = "2";
|
||||
private String extraTerms = "2";
|
||||
private Boolean recalculateInterest = false;
|
||||
private String newInterestRate = null;
|
||||
private String adjustedDueDate = null;
|
||||
private String rescheduleReasonId = "1";
|
||||
private String rescheduleReasonComment = null;
|
||||
private String submittedOnDate = "04 September 2014";
|
||||
|
||||
public String build(final String loanId) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
map.put("dateFormat", "dd MMMM yyyy");
|
||||
map.put("locale", "en_GB");
|
||||
map.put("loanId", loanId);
|
||||
map.put("submittedOnDate", submittedOnDate);
|
||||
map.put("rescheduleFromDate", rescheduleFromDate);
|
||||
|
||||
if(graceOnPrincipal != null) {
|
||||
map.put("graceOnPrincipal", graceOnPrincipal);
|
||||
}
|
||||
|
||||
if(graceOnInterest != null) {
|
||||
map.put("graceOnInterest", graceOnInterest);
|
||||
}
|
||||
|
||||
if(extraTerms != null) {
|
||||
map.put("extraTerms", extraTerms);
|
||||
}
|
||||
|
||||
map.put("recalculateInterest", recalculateInterest);
|
||||
|
||||
if(newInterestRate != null) {
|
||||
map.put("newInterestRate", newInterestRate);
|
||||
}
|
||||
|
||||
if(adjustedDueDate != null) {
|
||||
map.put("adjustedDueDate", adjustedDueDate);
|
||||
}
|
||||
|
||||
map.put("rescheduleReasonId", rescheduleReasonId);
|
||||
|
||||
if(rescheduleReasonComment != null) {
|
||||
map.put("rescheduleReasonComment", rescheduleReasonComment);
|
||||
}
|
||||
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateRescheduleFromDate(final String rescheduleFromDate) {
|
||||
if(rescheduleFromDate != null) {
|
||||
this.rescheduleFromDate = rescheduleFromDate;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateGraceOnPrincipal(final String graceOnPrincipal) {
|
||||
this.graceOnPrincipal = graceOnPrincipal;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateGraceOnInterest(final String graceOnInterest) {
|
||||
this.graceOnInterest = graceOnInterest;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateExtraTerms(final String extraTerms) {
|
||||
this.extraTerms = extraTerms;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateRecalculateInterest(final Boolean recalculateInterest) {
|
||||
this.recalculateInterest = recalculateInterest;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateNewInterestRate(final String newInterestRate) {
|
||||
this.newInterestRate = newInterestRate;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateAdjustedDueDate(final String adjustedDueDate) {
|
||||
this.adjustedDueDate = adjustedDueDate;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateRescheduleReasonId(final String rescheduleReasonId) {
|
||||
this.rescheduleReasonId = rescheduleReasonId;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanRescheduleRequestTestBuilder updateRescheduleReasonComment(final String rescheduleReasonComment) {
|
||||
this.rescheduleReasonComment = rescheduleReasonComment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRejectLoanRescheduleRequestJSON() {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("locale", "en");
|
||||
map.put("dateFormat", "dd MMMM yyyy");
|
||||
map.put("rejectedOnDate", submittedOnDate);
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
|
||||
public String getApproveLoanRescheduleRequestJSON() {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("locale", "en");
|
||||
map.put("dateFormat", "dd MMMM yyyy");
|
||||
map.put("approvedOnDate", submittedOnDate);
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class LoanStatusChecker {
|
||||
|
||||
public static HashMap getStatusOfLoan(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Integer loanID) {
|
||||
final String url = "/mifosng-provider/api/v1/loans/" + loanID + "?tenantIdentifier=default";
|
||||
final String url = "/mifosng-provider/api/v1/loans/" + loanID + "?" + Utils.TENANT_IDENTIFIER;
|
||||
return Utils.performServerGet(requestSpec, responseSpec, url, "status");
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -24,8 +24,8 @@ public class LoanTransactionHelper {
|
||||
private final RequestSpecification requestSpec;
|
||||
private final ResponseSpecification responseSpec;
|
||||
|
||||
private static final String CREATE_LOAN_PRODUCT_URL = "/mifosng-provider/api/v1/loanproducts?tenantIdentifier=default";
|
||||
private static final String APPLY_LOAN_URL = "/mifosng-provider/api/v1/loans?tenantIdentifier=default";
|
||||
private static final String CREATE_LOAN_PRODUCT_URL = "/mifosng-provider/api/v1/loanproducts?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String APPLY_LOAN_URL = "/mifosng-provider/api/v1/loans?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String APPROVE_LOAN_COMMAND = "approve";
|
||||
private static final String UNDO_APPROVAL_LOAN_COMMAND = "undoApproval";
|
||||
private static final String DISBURSE_LOAN_COMMAND = "disburse";
|
||||
@@ -51,25 +51,25 @@ public class LoanTransactionHelper {
|
||||
|
||||
public Integer updateLoan(final Integer id, final String loanApplicationJSON) {
|
||||
return Utils.performServerPut(this.requestSpec, this.responseSpec, "/mifosng-provider/api/v1/loans/" + id
|
||||
+ "?tenantIdentifier=default", loanApplicationJSON, "loanId");
|
||||
+ "?" + Utils.TENANT_IDENTIFIER, loanApplicationJSON, "loanId");
|
||||
}
|
||||
|
||||
public ArrayList getLoanRepaymentSchedule(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Integer loanID) {
|
||||
final String URL = "/mifosng-provider/api/v1/loans/" + loanID + "?associations=repaymentSchedule&tenantIdentifier=default";
|
||||
final String URL = "/mifosng-provider/api/v1/loans/" + loanID + "?associations=repaymentSchedule&" + Utils.TENANT_IDENTIFIER;
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, "repaymentSchedule");
|
||||
return (ArrayList) response.get("periods");
|
||||
}
|
||||
|
||||
public HashMap getLoanSummary(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final Integer loanID) {
|
||||
final String URL = "/mifosng-provider/api/v1/loans/" + loanID + "?associations=all&tenantIdentifier=default";
|
||||
final String URL = "/mifosng-provider/api/v1/loans/" + loanID + "?associations=all&" + Utils.TENANT_IDENTIFIER;
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, "summary");
|
||||
return response;
|
||||
}
|
||||
|
||||
public Object getLoanDetail(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final Integer loanID,
|
||||
final String param) {
|
||||
final String URL = "/mifosng-provider/api/v1/loans/" + loanID + "?associations=all&tenantIdentifier=default";
|
||||
final String URL = "/mifosng-provider/api/v1/loans/" + loanID + "?associations=all&" + Utils.TENANT_IDENTIFIER;
|
||||
return Utils.performServerGet(requestSpec, responseSpec, URL, param);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ public class LoanTransactionHelper {
|
||||
|
||||
public Integer addChargesForLoan(final Integer loanId, final String request) {
|
||||
System.out.println("--------------------------------- ADD CHARGES FOR LOAN --------------------------------");
|
||||
final String ADD_CHARGES_URL = "/mifosng-provider/api/v1/loans/" + loanId + "/charges?tenantIdentifier=default";
|
||||
final String ADD_CHARGES_URL = "/mifosng-provider/api/v1/loans/" + loanId + "/charges?" + Utils.TENANT_IDENTIFIER;
|
||||
final HashMap response = Utils.performServerPost(requestSpec, responseSpec, ADD_CHARGES_URL, request, "");
|
||||
return (Integer) response.get("resourceId");
|
||||
}
|
||||
@@ -141,7 +141,7 @@ public class LoanTransactionHelper {
|
||||
public Integer updateChargesForLoan(final Integer loanId, final Integer loanchargeId, final String request) {
|
||||
System.out.println("--------------------------------- ADD CHARGES FOR LOAN --------------------------------");
|
||||
final String UPDATE_CHARGES_URL = "/mifosng-provider/api/v1/loans/" + loanId + "/charges/" + loanchargeId
|
||||
+ "?tenantIdentifier=default";
|
||||
+ "?" + Utils.TENANT_IDENTIFIER;
|
||||
final HashMap response = Utils.performServerPut(requestSpec, responseSpec, UPDATE_CHARGES_URL, request, "");
|
||||
return (Integer) response.get("resourceId");
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class LoanTransactionHelper {
|
||||
public Integer deleteChargesForLoan(final Integer loanId, final Integer loanchargeId) {
|
||||
System.out.println("--------------------------------- DELETE CHARGES FOR LOAN --------------------------------");
|
||||
final String DELETE_CHARGES_URL = "/mifosng-provider/api/v1/loans/" + loanId + "/charges/" + loanchargeId
|
||||
+ "?tenantIdentifier=default";
|
||||
+ "?" + Utils.TENANT_IDENTIFIER;
|
||||
final HashMap response = Utils.performServerDelete(requestSpec, responseSpec, DELETE_CHARGES_URL, "");
|
||||
return (Integer) response.get("resourceId");
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public class LoanTransactionHelper {
|
||||
public Integer waiveChargesForLoan(final Integer loanId, final Integer loanchargeId, final String json) {
|
||||
System.out.println("--------------------------------- WAIVE CHARGES FOR LOAN --------------------------------");
|
||||
final String CHARGES_URL = "/mifosng-provider/api/v1/loans/" + loanId + "/charges/" + loanchargeId
|
||||
+ "?command=waive&tenantIdentifier=default";
|
||||
+ "?command=waive&" + Utils.TENANT_IDENTIFIER;
|
||||
final HashMap response = Utils.performServerPost(requestSpec, responseSpec, CHARGES_URL, json, "");
|
||||
return (Integer) response.get("resourceId");
|
||||
}
|
||||
@@ -165,7 +165,7 @@ public class LoanTransactionHelper {
|
||||
public Integer payChargesForLoan(final Integer loanId, final Integer loanchargeId, final String json) {
|
||||
System.out.println("--------------------------------- WAIVE CHARGES FOR LOAN --------------------------------");
|
||||
final String CHARGES_URL = "/mifosng-provider/api/v1/loans/" + loanId + "/charges/" + loanchargeId
|
||||
+ "?command=pay&tenantIdentifier=default";
|
||||
+ "?command=pay&" + Utils.TENANT_IDENTIFIER;
|
||||
final HashMap response = Utils.performServerPost(requestSpec, responseSpec, CHARGES_URL, json, "");
|
||||
return (Integer) response.get("resourceId");
|
||||
}
|
||||
@@ -326,11 +326,11 @@ public class LoanTransactionHelper {
|
||||
}
|
||||
|
||||
private String createLoanOperationURL(final String command, final Integer loanID) {
|
||||
return "/mifosng-provider/api/v1/loans/" + loanID + "?command=" + command + "&tenantIdentifier=default";
|
||||
return "/mifosng-provider/api/v1/loans/" + loanID + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
|
||||
}
|
||||
|
||||
private String createLoanTransactionURL(final String command, final Integer loanID) {
|
||||
return "/mifosng-provider/api/v1/loans/" + loanID + "/transactions?command=" + command + "&tenantIdentifier=default";
|
||||
return "/mifosng-provider/api/v1/loans/" + loanID + "/transactions?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
|
||||
}
|
||||
|
||||
private HashMap performLoanTransaction(final String postURLForLoanTransaction, final String jsonToBeSent) {
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ public class XBRLIntegrationTestHelper {
|
||||
private final RequestSpecification requestSpec;
|
||||
private final ResponseSpecification responseSpec;
|
||||
|
||||
private static final String GET_TAXONOMY_LIST_URL = "/mifosng-provider/api/v1/mixtaxonomy?tenantIdentifier=default";
|
||||
private static final String TAXONOMY_MAPPING_URL = "/mifosng-provider/api/v1/mixmapping?tenantIdentifier=default";
|
||||
private static final String GET_TAXONOMY_LIST_URL = "/mifosng-provider/api/v1/mixtaxonomy?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String TAXONOMY_MAPPING_URL = "/mifosng-provider/api/v1/mixmapping?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
public XBRLIntegrationTestHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
this.requestSpec = requestSpec;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
package org.mifosplatform.commands.domain;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
|
||||
public class CommandWrapper {
|
||||
|
||||
private final Long commandId;
|
||||
@@ -239,6 +241,29 @@ public class CommandWrapper {
|
||||
return this.entityName.equalsIgnoreCase("SMS");
|
||||
}
|
||||
|
||||
public boolean isSmsCampaignResource(){
|
||||
return this.entityName.equals("SMS_CAMPAIGN");
|
||||
}
|
||||
|
||||
public boolean isLoanRescheduleResource() {
|
||||
return this.entityName.equals(RescheduleLoansApiConstants.ENTITY_NAME);
|
||||
}
|
||||
|
||||
public boolean isApprove() {
|
||||
return this.actionName.equalsIgnoreCase("APPROVE");
|
||||
}
|
||||
|
||||
public boolean isReject() {
|
||||
return this.actionName.equalsIgnoreCase("REJECT");
|
||||
}
|
||||
|
||||
public boolean isSmsCampaignActivation() {
|
||||
return this.actionName.equalsIgnoreCase("ACTIVATE") && this.entityName.equalsIgnoreCase("SMS_CAMPAIGN");
|
||||
}
|
||||
public boolean isSmsCampaignClosure() {
|
||||
return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("SMS_CAMPAIGN");
|
||||
}
|
||||
|
||||
public boolean isCodeResource() {
|
||||
return this.entityName.equalsIgnoreCase("CODE");
|
||||
}
|
||||
|
||||
+23
@@ -2050,4 +2050,27 @@ public class CommandWrapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createLoanRescheduleRequest(final String entityName) {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = entityName;
|
||||
this.entityId = null;
|
||||
this.href = "/rescheduleloans";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder approveLoanRescheduleRequest(final String entityName, final Long requestId) {
|
||||
this.actionName = "APPROVE";
|
||||
this.entityName = entityName;
|
||||
this.entityId = requestId;
|
||||
this.href = "/rescheduleloans/" + requestId + "?command=approve";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder rejectLoanRescheduleRequest(final String entityName, final Long requestId) {
|
||||
this.actionName = "REJECT";
|
||||
this.entityName = entityName;
|
||||
this.entityId = requestId;
|
||||
this.href = "/rescheduleloans/" + requestId + "?command=reject";
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+10
@@ -822,6 +822,16 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
} else {
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
}
|
||||
} else if (wrapper.isLoanRescheduleResource()) {
|
||||
if (wrapper.isCreate()) {
|
||||
handler = this.applicationContext.getBean("createLoanRescheduleRequestCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if(wrapper.isApprove()) {
|
||||
handler = this.applicationContext.getBean("approveLoanRescheduleRequestCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if(wrapper.isReject()) {
|
||||
handler = this.applicationContext.getBean("rejectLoanRescheduleRequestCommandHandler", NewCommandSourceHandler.class);
|
||||
} else {
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
}
|
||||
} else {
|
||||
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
|
||||
+178
@@ -13,6 +13,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -61,6 +62,7 @@ import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
@@ -91,8 +93,11 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleG
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
@@ -221,6 +226,10 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "rescheduledon_date")
|
||||
private Date rescheduledOnDate;
|
||||
|
||||
@ManyToOne(optional = true)
|
||||
@JoinColumn(name = "rescheduledon_userid", nullable = true)
|
||||
private AppUser rescheduledByUser;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "expected_maturedon_date")
|
||||
@@ -4289,4 +4298,173 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return this.isNpa;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of loan repayments schedule objects
|
||||
**/
|
||||
public List<LoanRepaymentScheduleInstallment> getRepaymentScheduleInstallments() {
|
||||
return this.repaymentScheduleInstallments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Loan product minimum repayments schedule related detail
|
||||
**/
|
||||
public LoanProductRelatedDetail getLoanRepaymentScheduleDetail() {
|
||||
return this.loanRepaymentScheduleDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Loan Fixed Emi amount
|
||||
**/
|
||||
public BigDecimal getFixedEmiAmount() {
|
||||
return this.fixedEmiAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return maximum outstanding loan balance
|
||||
**/
|
||||
public BigDecimal getMaxOutstandingLoanBalance() {
|
||||
return this.maxOutstandingLoanBalance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dueDate the due date of the installment
|
||||
* @return a schedule installment with similar due date to the one provided
|
||||
**/
|
||||
public LoanRepaymentScheduleInstallment getRepaymentScheduleInstallment(LocalDate dueDate) {
|
||||
LoanRepaymentScheduleInstallment installment = null;
|
||||
|
||||
if(dueDate != null) {
|
||||
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : this.repaymentScheduleInstallments) {
|
||||
|
||||
if(repaymentScheduleInstallment.getDueDate().isEqual(dueDate)) {
|
||||
installment = repaymentScheduleInstallment;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return installment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loan the Loan object
|
||||
* @return loan disbursement data
|
||||
**/
|
||||
public List<DisbursementData> getDisbursmentData() {
|
||||
Iterator<LoanDisbursementDetails> iterator = this.getDisbursementDetails().iterator();
|
||||
List<DisbursementData> disbursementData = new ArrayList<DisbursementData>();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
LoanDisbursementDetails loanDisbursementDetails = iterator.next();
|
||||
|
||||
LocalDate expectedDisbursementDate = null;
|
||||
LocalDate actualDisbursementDate = null;
|
||||
|
||||
if(loanDisbursementDetails.expectedDisbursementDate() != null) {
|
||||
expectedDisbursementDate = new LocalDate(loanDisbursementDetails.expectedDisbursementDate());
|
||||
}
|
||||
|
||||
if(loanDisbursementDetails.actualDisbursementDate() != null) {
|
||||
actualDisbursementDate = new LocalDate(loanDisbursementDetails.actualDisbursementDate());
|
||||
}
|
||||
|
||||
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate,
|
||||
actualDisbursementDate, loanDisbursementDetails.principal(), loanDisbursementDetails.approvedPrincipal()));
|
||||
}
|
||||
|
||||
return disbursementData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loan the Loan object
|
||||
* @return application terms of the Loan object
|
||||
**/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency) {
|
||||
LoanProduct loanProduct = loanProduct();
|
||||
//LoanProductRelatedDetail loanProductRelatedDetail = getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getCurrency();
|
||||
|
||||
final Integer loanTermFrequency = getTermFrequency();
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
|
||||
final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
|
||||
|
||||
final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod();
|
||||
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = this.loanRepaymentScheduleDetail.getInterestCalculationPeriodMethod();
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
|
||||
final BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
final Money principalMoney = this.loanRepaymentScheduleDetail.getPrincipal();
|
||||
|
||||
final LocalDate expectedDisbursementDate = getExpectedDisbursedOnLocalDate();
|
||||
final LocalDate repaymentsStartingFromDate = getExpectedFirstRepaymentOnDate();
|
||||
LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
|
||||
|
||||
// TODO get calender linked to loan if any exist. As of 17-07-2014, could not find a link in the database.
|
||||
// skip for now and set the Calender object to null
|
||||
Calendar calendar = null;
|
||||
|
||||
final Integer graceOnPrincipalPayment = this.loanRepaymentScheduleDetail.graceOnPrincipalPayment();
|
||||
final Integer graceOnInterestPayment = this.loanRepaymentScheduleDetail.graceOnInterestPayment();
|
||||
final Integer graceOnInterestCharged = this.loanRepaymentScheduleDetail.graceOnInterestCharged();
|
||||
final LocalDate interestChargedFromDate = getInterestChargedFromDate();
|
||||
final Integer graceOnArrearsAgeing = this.loanRepaymentScheduleDetail.getGraceOnDueDate();
|
||||
|
||||
final Money inArrearsToleranceMoney = this.loanRepaymentScheduleDetail.getInArrearsTolerance();
|
||||
final BigDecimal emiAmount = getFixedEmiAmount();
|
||||
final BigDecimal maxOutstandingBalance = getMaxOutstandingLoanBalance();
|
||||
|
||||
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);
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsToleranceMoney,
|
||||
this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData, maxOutstandingBalance,
|
||||
loanVariationTermsData, interestChargedFromDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Loan summary embedded object
|
||||
**/
|
||||
public LoanSummary getLoanSummary() {
|
||||
return this.summary;
|
||||
}
|
||||
|
||||
public void updateRescheduledByUser(AppUser rescheduledByUser) {
|
||||
this.rescheduledByUser = rescheduledByUser;
|
||||
}
|
||||
|
||||
public LoanProductRelatedDetail getLoanProductRelatedDetail() {
|
||||
return this.loanRepaymentScheduleDetail;
|
||||
}
|
||||
|
||||
public void updateNumberOfRepayments(Integer numberOfRepayments) {
|
||||
this.loanRepaymentScheduleDetail.updateNumberOfRepayments(numberOfRepayments);
|
||||
}
|
||||
|
||||
public void updateRescheduledOnDate(LocalDate rescheduledOnDate) {
|
||||
|
||||
if(rescheduledOnDate != null) {
|
||||
this.rescheduledOnDate = rescheduledOnDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTermFrequency(Integer termFrequency) {
|
||||
|
||||
if(termFrequency != null) {
|
||||
this.termFrequency = termFrequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -84,6 +84,10 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
|
||||
public BigDecimal principal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
public BigDecimal approvedPrincipal() {
|
||||
return this.approvedPrincipal;
|
||||
}
|
||||
|
||||
public void updatePrincipal(BigDecimal principal) {
|
||||
this.principal = principal;
|
||||
|
||||
+70
-1
@@ -6,6 +6,7 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
@@ -31,7 +32,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
private Loan loan;
|
||||
|
||||
@Column(name = "installment", nullable = false)
|
||||
private final Integer installmentNumber;
|
||||
private Integer installmentNumber;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "fromdate", nullable = true)
|
||||
@@ -123,6 +124,14 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
this.obligationsMet = false;
|
||||
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment(final Loan loan) {
|
||||
this.loan = loan;
|
||||
this.installmentNumber = null;
|
||||
this.fromDate = null;
|
||||
this.dueDate = null;
|
||||
this.obligationsMet = false;
|
||||
}
|
||||
|
||||
private BigDecimal defaultToNullIfZero(final BigDecimal value) {
|
||||
BigDecimal result = value;
|
||||
@@ -252,6 +261,11 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
public void updateLoan(final Loan loan) {
|
||||
this.loan = loan;
|
||||
}
|
||||
|
||||
public boolean isPartlyPaid() {
|
||||
return !this.obligationsMet && (this.interestPaid != null || this.feeChargesPaid != null
|
||||
|| this.principalCompleted != null);
|
||||
}
|
||||
|
||||
public boolean isObligationsMet() {
|
||||
return this.obligationsMet;
|
||||
@@ -552,4 +566,59 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
public void setRecalculatedInterestComponent(boolean recalculatedInterestComponent) {
|
||||
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||
}
|
||||
|
||||
public void updateInstallmentNumber(final Integer installmentNumber) {
|
||||
if(installmentNumber != null) {
|
||||
this.installmentNumber = installmentNumber;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateInterestCharged(final BigDecimal interestCharged) {
|
||||
this.interestCharged = interestCharged;
|
||||
}
|
||||
|
||||
public void updateObligationMet(final Boolean obligationMet) {
|
||||
this.obligationsMet = obligationMet;
|
||||
}
|
||||
|
||||
public void updateObligationMetOnDate(final LocalDate obligationsMetOnDate) {
|
||||
this.obligationsMetOnDate = (obligationsMetOnDate != null) ? obligationsMetOnDate.toDate() : null;
|
||||
}
|
||||
|
||||
public void updateInterestWrittenOff(final BigDecimal interestWrittenOff) {
|
||||
this.interestWrittenOff = interestWrittenOff;
|
||||
}
|
||||
|
||||
public void updatePrincipal(final BigDecimal principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public static Comparator<LoanRepaymentScheduleInstallment> installmentNumberComparator
|
||||
= new Comparator<LoanRepaymentScheduleInstallment>() {
|
||||
|
||||
@Override
|
||||
public int compare(LoanRepaymentScheduleInstallment arg0,
|
||||
LoanRepaymentScheduleInstallment arg1) {
|
||||
|
||||
return arg0.getInstallmentNumber().compareTo(arg1.getInstallmentNumber());
|
||||
}
|
||||
};
|
||||
|
||||
public BigDecimal getTotalPaidInAdvance() {
|
||||
return this.totalPaidInAdvance;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalPaidLate() {
|
||||
return this.totalPaidLate;
|
||||
}
|
||||
|
||||
public LocalDate getObligationsMetOnDate() {
|
||||
LocalDate obligationsMetOnDate = null;
|
||||
|
||||
if(this.obligationsMetOnDate != null) {
|
||||
obligationsMetOnDate = new LocalDate(this.obligationsMetOnDate);
|
||||
}
|
||||
|
||||
return obligationsMetOnDate;
|
||||
}
|
||||
}
|
||||
+15
@@ -286,4 +286,19 @@ public final class LoanSummary {
|
||||
public BigDecimal getTotalWrittenOff() {
|
||||
return this.totalWrittenOff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return total interest repaid
|
||||
**/
|
||||
public BigDecimal getTotalInterestRepaid() {
|
||||
return this.totalInterestRepaid;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalFeeChargesCharged() {
|
||||
return this.totalFeeChargesCharged;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalPenaltyChargesCharged() {
|
||||
return this.totalPenaltyChargesCharged;
|
||||
}
|
||||
}
|
||||
+369
-1
@@ -10,6 +10,8 @@ import java.math.MathContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -25,13 +27,21 @@ import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementDisbursementDateException;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -472,6 +482,355 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected,
|
||||
totalOutstanding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
|
||||
// create an archive of the current loan schedule installments
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = createLoanScheduleArchive(loanRescheduleRequest);
|
||||
|
||||
// get the initial list of repayment installments
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
|
||||
// sort list by installment number in ASC order
|
||||
Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = new ArrayList<LoanRescheduleModelRepaymentPeriod>();
|
||||
|
||||
Money outstandingLoanBalance = loan.getPrincpal();
|
||||
|
||||
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
|
||||
Integer oldPeriodNumber = repaymentScheduleInstallment.getInstallmentNumber();
|
||||
LocalDate fromDate = repaymentScheduleInstallment.getFromDate();
|
||||
LocalDate dueDate = repaymentScheduleInstallment.getDueDate();
|
||||
Money principalDue = repaymentScheduleInstallment.getPrincipal(currency);
|
||||
Money interestDue = repaymentScheduleInstallment.getInterestCharged(currency);
|
||||
Money feeChargesDue = repaymentScheduleInstallment.getFeeChargesCharged(currency);
|
||||
Money penaltyChargesDue = repaymentScheduleInstallment.getPenaltyChargesCharged(currency);
|
||||
Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue);
|
||||
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(principalDue);
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(oldPeriodNumber, oldPeriodNumber,
|
||||
fromDate, dueDate, principalDue, outstandingLoanBalance, interestDue, feeChargesDue, penaltyChargesDue,
|
||||
totalDue, false);
|
||||
|
||||
periods.add(period);
|
||||
}
|
||||
|
||||
Money outstandingBalance = loan.getPrincpal();
|
||||
Money totalCumulativePrincipal = Money.zero(currency);
|
||||
Money totalCumulativeInterest = Money.zero(currency);
|
||||
Money actualTotalCumulativeInterest = Money.zero(currency);
|
||||
Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
|
||||
Money totalPrincipalBeforeReschedulePeriod = Money.zero(currency);
|
||||
|
||||
LocalDate installmentDueDate = null;
|
||||
LocalDate adjustedInstallmentDueDate = null;
|
||||
LocalDate installmentFromDate = null;
|
||||
Integer rescheduleFromInstallmentNo = defaultToZeroIfNull(loanRescheduleRequest.getRescheduleFromInstallment());
|
||||
Integer installmentNumber = rescheduleFromInstallmentNo;
|
||||
Integer graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
|
||||
Integer graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
|
||||
Integer extraTerms = defaultToZeroIfNull(loanRescheduleRequest.getExtraTerms());
|
||||
final boolean recalculateInterest = loanRescheduleRequest.getRecalculateInterest();
|
||||
Integer numberOfRepayments = repaymentScheduleInstallments.size();
|
||||
Integer rescheduleNumberOfRepayments = numberOfRepayments;
|
||||
final Money principal = loan.getPrincpal();
|
||||
final Money totalPrincipalOutstanding = Money.of(currency, loanSummary.getTotalPrincipalOutstanding());
|
||||
LocalDate adjustedDueDate = loanRescheduleRequest.getAdjustedDueDate();
|
||||
BigDecimal newInterestRate = loanRescheduleRequest.getInterestRate();
|
||||
int loanTermInDays = Integer.valueOf(0);
|
||||
|
||||
if(rescheduleFromInstallmentNo > 0) {
|
||||
// this will hold the loan repayment installment that is before the reschedule start installment
|
||||
// (rescheduleFrominstallment)
|
||||
LoanRepaymentScheduleInstallment previousInstallment = null;
|
||||
|
||||
// get the install number of the previous installment
|
||||
int previousInstallmentNo = rescheduleFromInstallmentNo - 1;
|
||||
|
||||
// only fetch the installment if the number is greater than 0
|
||||
if(previousInstallmentNo > 0) {
|
||||
previousInstallment = loan.fetchRepaymentScheduleInstallment(previousInstallmentNo);
|
||||
}
|
||||
|
||||
LoanRepaymentScheduleInstallment firstInstallment = loan.fetchRepaymentScheduleInstallment(1);
|
||||
|
||||
// the "installment from date" is equal to the due date of the previous installment, if it exists
|
||||
if(previousInstallment != null) {
|
||||
installmentFromDate = previousInstallment.getDueDate();
|
||||
}
|
||||
|
||||
else {
|
||||
installmentFromDate = firstInstallment.getFromDate();
|
||||
}
|
||||
|
||||
installmentDueDate = installmentFromDate;
|
||||
LocalDate periodStartDateApplicableForInterest = installmentFromDate;
|
||||
Integer periodNumber = 1;
|
||||
outstandingLoanBalance = loan.getPrincpal();
|
||||
|
||||
for(LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if(period.periodDueDate().isBefore(loanRescheduleRequest.getRescheduleFromDate())) {
|
||||
|
||||
totalPrincipalBeforeReschedulePeriod = totalPrincipalBeforeReschedulePeriod.plus(period.principalDue());
|
||||
actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
|
||||
rescheduleNumberOfRepayments--;
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
|
||||
outstandingBalance = outstandingBalance.minus(period.principalDue());
|
||||
}
|
||||
}
|
||||
|
||||
while(graceOnPrincipal > 0 || graceOnInterest > 0) {
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
|
||||
new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
|
||||
Money.zero(currency), Money.zero(currency), true);
|
||||
|
||||
periods.add(period);
|
||||
|
||||
if(graceOnPrincipal > 0) {
|
||||
graceOnPrincipal--;
|
||||
}
|
||||
|
||||
if(graceOnInterest > 0) {
|
||||
graceOnInterest--;
|
||||
}
|
||||
|
||||
rescheduleNumberOfRepayments++;
|
||||
numberOfRepayments++;
|
||||
}
|
||||
|
||||
while(extraTerms > 0) {
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
|
||||
new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
|
||||
Money.zero(currency), Money.zero(currency), true);
|
||||
|
||||
periods.add(period);
|
||||
|
||||
extraTerms--;
|
||||
rescheduleNumberOfRepayments++;
|
||||
numberOfRepayments++;
|
||||
}
|
||||
|
||||
// get the loan application terms from the Loan object
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency);
|
||||
|
||||
// update the number of repayments
|
||||
loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
|
||||
|
||||
LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled,
|
||||
holidays, workingDays);
|
||||
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
||||
|
||||
if(newInterestRate != null) {
|
||||
loanApplicationTerms.updateAnnualNominalInterestRate(newInterestRate);
|
||||
loanApplicationTerms.updateInterestRatePerPeriod(newInterestRate);
|
||||
}
|
||||
|
||||
graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
|
||||
graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
|
||||
|
||||
loanApplicationTerms.updateInterestPaymentGrace(graceOnInterest);
|
||||
loanApplicationTerms.updatePrincipalGrace(graceOnPrincipal);
|
||||
|
||||
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
|
||||
loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments);
|
||||
loanApplicationTerms.updateLoanTermFrequency(rescheduleNumberOfRepayments);
|
||||
loanApplicationTerms.updateInterestChargedFromDate(periodStartDateApplicableForInterest);
|
||||
|
||||
Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
|
||||
this.paymentPeriodsInOneYearCalculator, mathContext);
|
||||
|
||||
if(!recalculateInterest && newInterestRate == null) {
|
||||
totalInterestChargedForFullLoanTerm = Money.of(currency, loanSummary.getTotalInterestCharged());
|
||||
totalInterestChargedForFullLoanTerm = totalInterestChargedForFullLoanTerm.minus(actualTotalCumulativeInterest);
|
||||
|
||||
loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
|
||||
}
|
||||
|
||||
for(LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if(period.periodDueDate().isEqual(loanRescheduleRequest.getRescheduleFromDate()) ||
|
||||
period.periodDueDate().isAfter(loanRescheduleRequest.getRescheduleFromDate()) ||
|
||||
period.isNew()) {
|
||||
|
||||
installmentDueDate = this.scheduledDateGenerator.generateNextRepaymentDate(installmentDueDate, loanApplicationTerms,
|
||||
false);
|
||||
|
||||
if(adjustedDueDate != null && periodNumber == 1) {
|
||||
installmentDueDate = adjustedDueDate;
|
||||
}
|
||||
|
||||
adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate).getDays();
|
||||
|
||||
period.updatePeriodNumber(installmentNumber);
|
||||
period.updatePeriodFromDate(installmentFromDate);
|
||||
period.updatePeriodDueDate(adjustedInstallmentDueDate);
|
||||
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
|
||||
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, adjustedInstallmentDueDate,
|
||||
periodStartDateApplicableForInterest, loanApplicationTerms.getLoanTermPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery());
|
||||
|
||||
// ========================= Calculate the interest due ========================================
|
||||
|
||||
// change the principal to => Principal Disbursed - Total Principal Paid
|
||||
// interest calculation is always based on the total principal outstanding
|
||||
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
|
||||
|
||||
// determine the interest & principal for the period
|
||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal,
|
||||
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
||||
daysInInstallment, outstandingBalance, loanApplicationTerms, periodNumber, mathContext);
|
||||
|
||||
// update the interest due for the period
|
||||
period.updateInterestDue(principalInterestForThisPeriod.interest());
|
||||
|
||||
// =============================================================================================
|
||||
|
||||
// ========================== Calculate the principal due ======================================
|
||||
|
||||
// change the principal to => Principal Disbursed - Total cumulative Principal Amount before the reschedule installment
|
||||
loanApplicationTerms.setPrincipal(principal.minus(totalPrincipalBeforeReschedulePeriod));
|
||||
|
||||
principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal,
|
||||
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
||||
daysInInstallment, outstandingBalance, loanApplicationTerms, periodNumber, mathContext);
|
||||
|
||||
period.updatePrincipalDue(principalInterestForThisPeriod.principal());
|
||||
|
||||
// ==============================================================================================
|
||||
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
|
||||
period.updateOutstandingLoanBalance(outstandingLoanBalance);
|
||||
|
||||
Money principalDue = Money.of(currency, period.principalDue());
|
||||
Money interestDue = Money.of(currency, period.interestDue());
|
||||
Money feeChargesDue = Money.of(currency, period.feeChargesDue());
|
||||
Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
|
||||
|
||||
Money totalDue = principalDue
|
||||
.plus(interestDue)
|
||||
.plus(feeChargesDue)
|
||||
.plus(penaltyChargesDue);
|
||||
|
||||
period.updateTotalDue(totalDue);
|
||||
|
||||
// update cumulative fields for principal & interest
|
||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(period.principalDue());
|
||||
totalCumulativeInterest = totalCumulativeInterest.plus(period.interestDue());
|
||||
actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
|
||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||
|
||||
installmentFromDate = adjustedInstallmentDueDate;
|
||||
installmentNumber++;
|
||||
periodNumber++;
|
||||
loanTermInDays += daysInInstallment;
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(period.principalDue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Money totalRepaymentExpected = principal // get the loan Principal amount
|
||||
.plus(actualTotalCumulativeInterest) // add the actual total cumulative interest
|
||||
.plus(loanSummary.getTotalFeeChargesCharged()) // add the total fees charged
|
||||
.plus(loanSummary.getTotalPenaltyChargesCharged()); // finally add the total penalty charged
|
||||
|
||||
return LoanRescheduleModel.instance(periods, loanRepaymentScheduleHistoryList, applicationCurrency, loanTermInDays,
|
||||
loan.getPrincpal(), loan.getPrincpal().getAmount(), loanSummary.getTotalPrincipalRepaid(), actualTotalCumulativeInterest.getAmount(),
|
||||
loanSummary.getTotalFeeChargesCharged(), loanSummary.getTotalPenaltyChargesCharged(), totalRepaymentExpected.getAmount(),
|
||||
loanSummary.getTotalOutstanding());
|
||||
}
|
||||
|
||||
private Collection<LoanRepaymentScheduleHistory> createLoanScheduleArchive(final LoanRescheduleRequest loanRescheduleRequest) {
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
List<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = new ArrayList<LoanRepaymentScheduleHistory>();
|
||||
|
||||
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
final Integer installmentNumber = repaymentScheduleInstallment.getInstallmentNumber();
|
||||
Date fromDate = null;
|
||||
Date dueDate = null;
|
||||
|
||||
if(repaymentScheduleInstallment.getFromDate() != null) {
|
||||
fromDate = repaymentScheduleInstallment.getFromDate().toDate();
|
||||
}
|
||||
|
||||
if(repaymentScheduleInstallment.getDueDate() != null) {
|
||||
dueDate = repaymentScheduleInstallment.getDueDate().toDate();
|
||||
}
|
||||
|
||||
final BigDecimal principal = repaymentScheduleInstallment.getPrincipal(currency).getAmount();
|
||||
final BigDecimal principalCompleted = repaymentScheduleInstallment.getPrincipalCompleted(currency).getAmount();
|
||||
final BigDecimal principalWrittenOff = repaymentScheduleInstallment.getPrincipalWrittenOff(currency).getAmount();
|
||||
final BigDecimal interestCharged = repaymentScheduleInstallment.getInterestCharged(currency).getAmount();
|
||||
final BigDecimal interestPaid = repaymentScheduleInstallment.getInterestPaid(currency).getAmount();
|
||||
final BigDecimal interestWaived = repaymentScheduleInstallment.getInterestWaived(currency).getAmount();
|
||||
final BigDecimal interestWrittenOff = repaymentScheduleInstallment.getInterestWrittenOff(currency).getAmount();
|
||||
final BigDecimal feeChargesCharged = repaymentScheduleInstallment.getFeeChargesCharged(currency).getAmount();
|
||||
final BigDecimal feeChargesPaid = repaymentScheduleInstallment.getFeeChargesPaid(currency).getAmount();
|
||||
final BigDecimal feeChargesWrittenOff = repaymentScheduleInstallment.getFeeChargesWrittenOff(currency).getAmount();
|
||||
final BigDecimal feeChargesWaived = repaymentScheduleInstallment.getFeeChargesWaived(currency).getAmount();
|
||||
final BigDecimal penaltyCharges = repaymentScheduleInstallment.getPenaltyChargesCharged(currency).getAmount();
|
||||
final BigDecimal penaltyChargesPaid = repaymentScheduleInstallment.getPenaltyChargesPaid(currency).getAmount();
|
||||
final BigDecimal penaltyChargesWrittenOff = repaymentScheduleInstallment.getPenaltyChargesWrittenOff(currency).getAmount();
|
||||
final BigDecimal penaltyChargesWaived = repaymentScheduleInstallment.getPenaltyChargesWaived(currency).getAmount();
|
||||
final BigDecimal totalPaidInAdvance = repaymentScheduleInstallment.getTotalPaidInAdvance();
|
||||
final BigDecimal totalPaidLate = repaymentScheduleInstallment.getTotalPaidLate();
|
||||
final boolean obligationsMet = repaymentScheduleInstallment.isObligationsMet();
|
||||
Date obligationsMetOnDate = null;
|
||||
|
||||
if(repaymentScheduleInstallment.getObligationsMetOnDate() != null) {
|
||||
obligationsMetOnDate = repaymentScheduleInstallment.getObligationsMetOnDate().toDate();
|
||||
}
|
||||
|
||||
Date createdOnDate = null;
|
||||
|
||||
if(repaymentScheduleInstallment.getCreatedDate() != null) {
|
||||
createdOnDate = repaymentScheduleInstallment.getCreatedDate().toDate();
|
||||
}
|
||||
|
||||
final AppUser createdByUser = repaymentScheduleInstallment.getCreatedBy();
|
||||
final AppUser lastModifiedByUser = repaymentScheduleInstallment.getLastModifiedBy();
|
||||
|
||||
Date lastModifiedOnDate = null;
|
||||
|
||||
if(repaymentScheduleInstallment.getLastModifiedDate() != null) {
|
||||
lastModifiedOnDate = repaymentScheduleInstallment.getLastModifiedDate().toDate();
|
||||
}
|
||||
|
||||
LoanRepaymentScheduleHistory loanRepaymentScheduleHistory = LoanRepaymentScheduleHistory.instance(loan,
|
||||
loanRescheduleRequest, installmentNumber, fromDate, dueDate, principal, principalCompleted,
|
||||
principalWrittenOff, interestCharged, interestPaid, interestWaived, interestWrittenOff,
|
||||
feeChargesCharged, feeChargesPaid, feeChargesWrittenOff, feeChargesWaived, penaltyCharges,
|
||||
penaltyChargesPaid, penaltyChargesWrittenOff, penaltyChargesWaived, totalPaidInAdvance, totalPaidLate,
|
||||
obligationsMet, obligationsMetOnDate, createdOnDate, createdByUser, lastModifiedByUser, lastModifiedOnDate);
|
||||
|
||||
loanRepaymentScheduleHistoryList.add(loanRepaymentScheduleHistory);
|
||||
}
|
||||
|
||||
return loanRepaymentScheduleHistoryList;
|
||||
}
|
||||
|
||||
private BigDecimal calculateInterestForSpecificDays(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||
int periodNumber, Money totalOutstandingInterestPaymentDueToGrace, int daysInPeriodApplicableForInterest,
|
||||
@@ -1205,5 +1564,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return precloseTransaction;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set the value to zero if the provided value is null
|
||||
*
|
||||
* @return integer value equal/greater than 0
|
||||
**/
|
||||
private Integer defaultToZeroIfNull(Integer value) {
|
||||
|
||||
return (value != null) ? value : 0;
|
||||
}
|
||||
}
|
||||
+78
-12
@@ -36,17 +36,17 @@ import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType
|
||||
public final class LoanApplicationTerms {
|
||||
|
||||
private final ApplicationCurrency currency;
|
||||
private final Integer loanTermFrequency;
|
||||
private Integer loanTermFrequency;
|
||||
private final PeriodFrequencyType loanTermPeriodFrequencyType;
|
||||
private final Integer numberOfRepayments;
|
||||
private Integer numberOfRepayments;
|
||||
private final Integer repaymentEvery;
|
||||
private final PeriodFrequencyType repaymentPeriodFrequencyType;
|
||||
private final AmortizationMethod amortizationMethod;
|
||||
|
||||
private final InterestMethod interestMethod;
|
||||
private final BigDecimal interestRatePerPeriod;
|
||||
private BigDecimal interestRatePerPeriod;
|
||||
private final PeriodFrequencyType interestRatePeriodFrequencyType;
|
||||
private final BigDecimal annualNominalInterestRate;
|
||||
private BigDecimal annualNominalInterestRate;
|
||||
private final InterestCalculationPeriodMethod interestCalculationPeriodMethod;
|
||||
|
||||
private Money principal;
|
||||
@@ -58,7 +58,7 @@ public final class LoanApplicationTerms {
|
||||
* installments where 'grace' should apply to the principal component of a
|
||||
* loans repayment period (installment).
|
||||
*/
|
||||
private final Integer principalGrace;
|
||||
private Integer principalGrace;
|
||||
|
||||
/**
|
||||
* Integer representing the number of 'repayment frequencies' or
|
||||
@@ -68,7 +68,7 @@ public final class LoanApplicationTerms {
|
||||
* <b>Note:</b> Interest is still calculated taking into account the full
|
||||
* loan term, the interest is simply offset to a later period.
|
||||
*/
|
||||
private final Integer interestPaymentGrace;
|
||||
private Integer interestPaymentGrace;
|
||||
|
||||
/**
|
||||
* Integer representing the number of 'repayment frequencies' or
|
||||
@@ -93,7 +93,7 @@ public final class LoanApplicationTerms {
|
||||
* to indicate from whence interest should be charged.
|
||||
* </p>
|
||||
*/
|
||||
private final LocalDate interestChargedFromDate;
|
||||
private LocalDate interestChargedFromDate;
|
||||
private final Money inArrearsTolerance;
|
||||
|
||||
private final Integer graceOnArrearsAgeing;
|
||||
@@ -110,6 +110,8 @@ public final class LoanApplicationTerms {
|
||||
private BigDecimal maxOutstandingBalance;
|
||||
|
||||
private List<LoanTermVariationsData> emiAmountVariations;
|
||||
|
||||
private Money totalInterestDue;
|
||||
|
||||
private final DaysInMonthType daysInMonthType;
|
||||
|
||||
@@ -498,6 +500,15 @@ public final class LoanApplicationTerms {
|
||||
return periodNumber == numberOfRepayments;
|
||||
}
|
||||
|
||||
private Money calculateTotalDueForEqualInstallmentLoan(final BigDecimal periodicInterestRate) {
|
||||
|
||||
final int periodsElapsed = 0;
|
||||
final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
|
||||
double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
|
||||
|
||||
return Money.of(this.principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
|
||||
}
|
||||
|
||||
/**
|
||||
* general method to calculate totalInterestDue discounting any grace
|
||||
* settings
|
||||
@@ -509,14 +520,18 @@ public final class LoanApplicationTerms {
|
||||
switch (this.interestMethod) {
|
||||
case FLAT:
|
||||
final BigDecimal interestRateForLoanTerm = calculateFlatInterestRateForLoanTerm(calculator, mc);
|
||||
|
||||
totalInterestDue = this.principal.multiplyRetainScale(interestRateForLoanTerm, mc.getRoundingMode());
|
||||
|
||||
break;
|
||||
case DECLINING_BALANCE:
|
||||
break;
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
if(this.totalInterestDue != null) {
|
||||
totalInterestDue = this.totalInterestDue;
|
||||
}
|
||||
|
||||
return totalInterestDue;
|
||||
}
|
||||
@@ -529,7 +544,7 @@ public final class LoanApplicationTerms {
|
||||
final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
|
||||
|
||||
final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
|
||||
|
||||
|
||||
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
||||
.multiply(loanTermFrequencyBigDecimal);
|
||||
}
|
||||
@@ -752,7 +767,7 @@ public final class LoanApplicationTerms {
|
||||
return periodNumber > 0 && periodNumber <= getInterestChargingGrace();
|
||||
}
|
||||
|
||||
private Integer getPrincipalGrace() {
|
||||
public Integer getPrincipalGrace() {
|
||||
Integer graceOnPrincipalPayments = Integer.valueOf(0);
|
||||
if (this.principalGrace != null) {
|
||||
graceOnPrincipalPayments = this.principalGrace;
|
||||
@@ -760,7 +775,7 @@ public final class LoanApplicationTerms {
|
||||
return graceOnPrincipalPayments;
|
||||
}
|
||||
|
||||
private Integer getInterestPaymentGrace() {
|
||||
public Integer getInterestPaymentGrace() {
|
||||
Integer graceOnInterestPayments = Integer.valueOf(0);
|
||||
if (this.interestPaymentGrace != null) {
|
||||
graceOnInterestPayments = this.interestPaymentGrace;
|
||||
@@ -768,7 +783,7 @@ public final class LoanApplicationTerms {
|
||||
return graceOnInterestPayments;
|
||||
}
|
||||
|
||||
private Integer getInterestChargingGrace() {
|
||||
public Integer getInterestChargingGrace() {
|
||||
Integer graceOnInterestCharged = Integer.valueOf(0);
|
||||
if (this.interestChargingGrace != null) {
|
||||
graceOnInterestCharged = this.interestChargingGrace;
|
||||
@@ -1067,4 +1082,55 @@ public final class LoanApplicationTerms {
|
||||
public RecalculationFrequencyType getRecalculationFrequencyType() {
|
||||
return this.recalculationFrequencyType;
|
||||
}
|
||||
|
||||
public void updateNumberOfRepayments(final Integer numberOfRepayments) {
|
||||
this.numberOfRepayments = numberOfRepayments;
|
||||
}
|
||||
|
||||
public void updatePrincipalGrace(final Integer principalGrace) {
|
||||
this.principalGrace = principalGrace;
|
||||
}
|
||||
|
||||
public void updateInterestPaymentGrace(final Integer interestPaymentGrace) {
|
||||
this.interestPaymentGrace = interestPaymentGrace;
|
||||
}
|
||||
|
||||
public void updateInterestRatePerPeriod(BigDecimal interestRatePerPeriod) {
|
||||
if(interestRatePerPeriod != null) {
|
||||
this.interestRatePerPeriod = interestRatePerPeriod;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAnnualNominalInterestRate(BigDecimal annualNominalInterestRate) {
|
||||
if(annualNominalInterestRate != null) {
|
||||
this.annualNominalInterestRate = annualNominalInterestRate;
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRatePerPeriod() {
|
||||
return this.interestRatePerPeriod;
|
||||
}
|
||||
|
||||
public BigDecimal getAnnualNominalInterestRate() {
|
||||
return this.annualNominalInterestRate;
|
||||
}
|
||||
|
||||
public void updateInterestChargedFromDate(LocalDate interestChargedFromDate) {
|
||||
if(interestChargedFromDate != null) {
|
||||
this.interestChargedFromDate = interestChargedFromDate;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLoanTermFrequency(Integer loanTermFrequency) {
|
||||
if(loanTermFrequency != null) {
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTotalInterestDue(Money totalInterestDue) {
|
||||
|
||||
if(totalInterestDue != null) {
|
||||
this.totalInterestDue = totalInterestDue;
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -18,6 +18,8 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
|
||||
public interface LoanScheduleGenerator {
|
||||
|
||||
@@ -32,4 +34,8 @@ public interface LoanScheduleGenerator {
|
||||
|
||||
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
||||
LocalDate onDate, LocalDate interestChargedFromLocalDate, LoanApplicationTerms loanApplicationTerms, MathContext mc);
|
||||
|
||||
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays);
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.rescheduleloan;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class RescheduleLoansApiConstants {
|
||||
public final static String ENTITY_NAME = "RESCHEDULELOAN";
|
||||
|
||||
// general
|
||||
public static final String localeParamName = "locale";
|
||||
public static final String dateFormatParamName = "dateFormat";
|
||||
|
||||
// create action request parameters
|
||||
public static final String loanIdParamName = "loanId";
|
||||
public static final String graceOnPrincipalParamName = "graceOnPrincipal";
|
||||
public static final String graceOnInterestParamName = "graceOnInterest";
|
||||
public static final String extraTermsParamName = "extraTerms";
|
||||
public static final String rescheduleFromDateParamName = "rescheduleFromDate";
|
||||
public static final String recalculateInterestParamName = "recalculateInterest";
|
||||
public static final String newInterestRateParamName = "newInterestRate";
|
||||
public static final String rescheduleReasonIdParamName = "rescheduleReasonId";
|
||||
public static final String rescheduleReasonCommentParamName = "rescheduleReasonComment";
|
||||
public static final String submittedOnDateParamName = "submittedOnDate";
|
||||
public static final String adjustedDueDateParamName = "adjustedDueDate";
|
||||
|
||||
public static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, graceOnPrincipalParamName, graceOnInterestParamName, extraTermsParamName,
|
||||
rescheduleFromDateParamName, newInterestRateParamName, rescheduleReasonIdParamName, rescheduleReasonCommentParamName,
|
||||
submittedOnDateParamName, loanIdParamName, adjustedDueDateParamName, recalculateInterestParamName));
|
||||
|
||||
// reject action request parameters
|
||||
public static final String rejectedOnDateParam = "rejectedOnDate";
|
||||
|
||||
public static final Set<String> REJECT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, rejectedOnDateParam));
|
||||
|
||||
// approve action request parameters
|
||||
public static final String approvedOnDateParam = "approvedOnDate";
|
||||
|
||||
public static final Set<String> APPROVE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, approvedOnDateParam));
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* 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.rescheduleloan.api;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
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.exception.UnrecognizedQueryParamException;
|
||||
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.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestData;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.service.LoanReschedulePreviewPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.service.LoanRescheduleRequestReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/rescheduleloans")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class RescheduleLoansApiResource {
|
||||
|
||||
private final DefaultToApiJsonSerializer<LoanRescheduleRequestData> loanRescheduleRequestToApiJsonSerializer;
|
||||
private final DefaultToApiJsonSerializer<LoanScheduleData> loanRescheduleToApiJsonSerializer;
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService;
|
||||
private final LoanReschedulePreviewPlatformService loanReschedulePreviewPlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
|
||||
@Autowired
|
||||
public RescheduleLoansApiResource(
|
||||
final DefaultToApiJsonSerializer<LoanRescheduleRequestData> loanRescheduleRequestToApiJsonSerializer,
|
||||
final PlatformSecurityContext platformSecurityContext,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final DefaultToApiJsonSerializer<LoanScheduleData> loanRescheduleToApiJsonSerializer,
|
||||
final LoanReschedulePreviewPlatformService loanReschedulePreviewPlatformService) {
|
||||
this.loanRescheduleRequestToApiJsonSerializer = loanRescheduleRequestToApiJsonSerializer;
|
||||
this.platformSecurityContext = platformSecurityContext;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
this.loanRescheduleRequestReadPlatformService = loanRescheduleRequestReadPlatformService;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.loanRescheduleToApiJsonSerializer = loanRescheduleToApiJsonSerializer;
|
||||
this.loanReschedulePreviewPlatformService = loanReschedulePreviewPlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{requestId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String readLoanRescheduleRequest(@Context final UriInfo uriInfo, @PathParam("requestId") final Long requestId,
|
||||
@QueryParam("command") final String command) {
|
||||
this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
|
||||
.process(uriInfo.getQueryParameters());
|
||||
|
||||
if(compareIgnoreCase(command, "previewLoanReschedule")) {
|
||||
final LoanRescheduleModel loanRescheduleModel = this.loanReschedulePreviewPlatformService.previewLoanReschedule(requestId);
|
||||
|
||||
return this.loanRescheduleToApiJsonSerializer.serialize(settings, loanRescheduleModel.toData(), new HashSet<String>());
|
||||
}
|
||||
|
||||
final LoanRescheduleRequestData loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
|
||||
.readLoanRescheduleRequest(requestId);
|
||||
|
||||
return this.loanRescheduleRequestToApiJsonSerializer.serialize(settings, loanRescheduleRequestData);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String createLoanRescheduleRequest(final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandWrapper = new CommandWrapperBuilder()
|
||||
.createLoanRescheduleRequest(RescheduleLoansApiConstants.ENTITY_NAME)
|
||||
.withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
|
||||
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
|
||||
|
||||
return this.loanRescheduleRequestToApiJsonSerializer.serialize(commandProcessingResult);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{requestId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String updateLoanRescheduleRequest(@PathParam("requestId") final Long requestId,
|
||||
@QueryParam("command") final String command, final String apiRequestBodyAsJson) {
|
||||
CommandWrapper commandWrapper = null;
|
||||
|
||||
if(compareIgnoreCase(command, "approve")) {
|
||||
commandWrapper = new CommandWrapperBuilder()
|
||||
.approveLoanRescheduleRequest(RescheduleLoansApiConstants.ENTITY_NAME, requestId)
|
||||
.withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
}
|
||||
|
||||
else if(compareIgnoreCase(command, "reject")) {
|
||||
commandWrapper = new CommandWrapperBuilder()
|
||||
.rejectLoanRescheduleRequest(RescheduleLoansApiConstants.ENTITY_NAME, requestId)
|
||||
.withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
}
|
||||
|
||||
else {
|
||||
throw new UnrecognizedQueryParamException("command", command, new Object[] { "approve", "reject" });
|
||||
}
|
||||
|
||||
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
|
||||
|
||||
return this.loanRescheduleRequestToApiJsonSerializer.serialize(commandProcessingResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two strings, ignoring differences in case
|
||||
*
|
||||
* @param firstString the first string
|
||||
* @param secondString the second string
|
||||
* @return true if the two strings are equal, else false
|
||||
**/
|
||||
private boolean compareIgnoreCase(String firstString, String secondString) {
|
||||
return StringUtils.isNotBlank(firstString) && firstString.trim().equalsIgnoreCase(secondString);
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* 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.rescheduleloan.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||
|
||||
/**
|
||||
* Immutable data object representing loan reschedule request data.
|
||||
**/
|
||||
public class LoanRescheduleRequestData {
|
||||
|
||||
private final Long id;
|
||||
private final Long loanId;
|
||||
private final Long clientId;
|
||||
private final String clientName;
|
||||
private final String loanAccountNumber;
|
||||
private final LoanRescheduleRequestStatusEnumData statusEnum;
|
||||
private final Integer rescheduleFromInstallment;
|
||||
private final Integer graceOnPrincipal;
|
||||
private final Integer graceOnInterest;
|
||||
private final LocalDate rescheduleFromDate;
|
||||
private final Integer extraTerms;
|
||||
private final BigDecimal interestRate;
|
||||
private final Boolean recalculateInterest;
|
||||
private final CodeValueData rescheduleReasonCodeValue;
|
||||
private final LoanRescheduleRequestTimelineData timeline;
|
||||
private final String rescheduleReasonComment;
|
||||
private final LocalDate adjustedDueDate;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestData constructor
|
||||
**/
|
||||
private LoanRescheduleRequestData(Long id, Long loanId, LoanRescheduleRequestStatusEnumData statusEnum,
|
||||
Integer rescheduleFromInstallment, Integer graceOnPrincipal, Integer graceOnInterest,
|
||||
LocalDate rescheduleFromDate, LocalDate adjustedDueDate, Integer extraTerms, BigDecimal interestRate,
|
||||
CodeValueData rescheduleReasonCodeValue, String rescheduleReasonComment, LoanRescheduleRequestTimelineData timeline,
|
||||
final String clientName, final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest) {
|
||||
|
||||
this.id = id;
|
||||
this.loanId = loanId;
|
||||
this.statusEnum = statusEnum;
|
||||
this.rescheduleFromInstallment = rescheduleFromInstallment;
|
||||
this.graceOnPrincipal = graceOnPrincipal;
|
||||
this.graceOnInterest = graceOnInterest;
|
||||
this.rescheduleFromDate = rescheduleFromDate;
|
||||
this.extraTerms = extraTerms;
|
||||
this.interestRate = interestRate;
|
||||
this.rescheduleReasonCodeValue = rescheduleReasonCodeValue;
|
||||
this.rescheduleReasonComment = rescheduleReasonComment;
|
||||
this.adjustedDueDate = adjustedDueDate;
|
||||
this.timeline = timeline;
|
||||
this.clientName = clientName;
|
||||
this.loanAccountNumber = loanAccountNumber;
|
||||
this.clientId = clientId;
|
||||
this.recalculateInterest = recalculateInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an instance of the LoanRescheduleRequestData class
|
||||
**/
|
||||
public static LoanRescheduleRequestData instance(Long id, Long loanId, LoanRescheduleRequestStatusEnumData statusEnum,
|
||||
Integer rescheduleFromInstallment, Integer graceOnPrincipal, Integer graceOnInterest,
|
||||
LocalDate rescheduleFromDate, LocalDate adjustedDueDate, Integer extraTerms, BigDecimal interestRate,
|
||||
CodeValueData rescheduleReasonCodeValue, String rescheduleReasonComment, LoanRescheduleRequestTimelineData timeline,
|
||||
final String clientName, final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest) {
|
||||
|
||||
return new LoanRescheduleRequestData(id, loanId, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
|
||||
rescheduleFromDate, adjustedDueDate, extraTerms, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment,
|
||||
timeline, clientName, loanAccountNumber, clientId, recalculateInterest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the id
|
||||
*/
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the loanId
|
||||
*/
|
||||
public Long getLoanId() {
|
||||
return loanId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the statusEnum
|
||||
*/
|
||||
public LoanRescheduleRequestStatusEnumData getStatusEnum() {
|
||||
return statusEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reschedule from installment number
|
||||
*/
|
||||
public Integer getRescheduleFromInstallment() {
|
||||
return rescheduleFromInstallment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the graceOnPrincipal
|
||||
*/
|
||||
public Integer getGraceOnPrincipal() {
|
||||
return graceOnPrincipal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the graceOnInterest
|
||||
*/
|
||||
public Integer getGraceOnInterest() {
|
||||
return graceOnInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reschedule from date
|
||||
*/
|
||||
public LocalDate getRescheduleFromDate() {
|
||||
return rescheduleFromDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the extraTerms
|
||||
*/
|
||||
public Integer getExtraTerms() {
|
||||
return extraTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the interestRate
|
||||
*/
|
||||
public BigDecimal getInterestRate() {
|
||||
return interestRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rescheduleReasonCodeValueId
|
||||
*/
|
||||
public CodeValueData getRescheduleReasonCodeValueId() {
|
||||
return rescheduleReasonCodeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rescheduleReasonText
|
||||
*/
|
||||
public String getRescheduleReasonComment() {
|
||||
return rescheduleReasonComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the timeline
|
||||
**/
|
||||
public LoanRescheduleRequestTimelineData getTimeline() {
|
||||
return this.timeline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the adjustedDueDate
|
||||
*/
|
||||
public LocalDate getAdjustedDueDate() {
|
||||
return adjustedDueDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the clientName
|
||||
*/
|
||||
public String getClientName() {
|
||||
return clientName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the loanAccountNumber
|
||||
*/
|
||||
public String getLoanAccountNumber() {
|
||||
return loanAccountNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the clientId
|
||||
*/
|
||||
public Long getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the recalculateInterest
|
||||
*/
|
||||
public Boolean getRecalculateInterest() {
|
||||
boolean value = false;
|
||||
|
||||
if(recalculateInterest != null) {
|
||||
value = recalculateInterest;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
/**
|
||||
* 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.rescheduleloan.data;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
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.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.service.LoanRescheduleRequestReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@Component
|
||||
public class LoanRescheduleRequestDataValidator {
|
||||
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
private final LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanRescheduleRequestDataValidator(FromJsonHelper fromJsonHelper,
|
||||
LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService) {
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
this.loanRescheduleRequestReadPlatformService = loanRescheduleRequestReadPlatformService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the request to create a new loan reschedule entry
|
||||
*
|
||||
* @param jsonCommand the JSON command object (instance of the JsonCommand class)
|
||||
* @return void
|
||||
**/
|
||||
public void validateForCreateAction(final JsonCommand jsonCommand, final Loan loan) {
|
||||
|
||||
final String jsonString = jsonCommand.json();
|
||||
|
||||
if (StringUtils.isBlank(jsonString)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Type typeToken = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromJsonHelper.checkForUnsupportedParameters(typeToken, jsonString,
|
||||
RescheduleLoansApiConstants.CREATE_REQUEST_DATA_PARAMETERS);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(StringUtils.lowerCase(RescheduleLoansApiConstants.ENTITY_NAME));
|
||||
|
||||
final JsonElement jsonElement = jsonCommand.parsedJson();
|
||||
|
||||
if(!loan.status().isActive()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("loan.is.not.active",
|
||||
"Loan is not active");
|
||||
}
|
||||
|
||||
final Long loanId = this.fromJsonHelper.extractLongNamed(
|
||||
RescheduleLoansApiConstants.loanIdParamName, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.loanIdParamName)
|
||||
.value(loanId).notNull().integerGreaterThanZero();
|
||||
|
||||
final LocalDate submittedOnDate = this.fromJsonHelper.extractLocalDateNamed(
|
||||
RescheduleLoansApiConstants.submittedOnDateParamName, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.submittedOnDateParamName)
|
||||
.value(submittedOnDate).notNull();
|
||||
|
||||
if(submittedOnDate != null && loan.getDisbursementDate().isAfter(submittedOnDate)) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.parameter(RescheduleLoansApiConstants.submittedOnDateParamName)
|
||||
.failWithCode("before.loan.disbursement.date",
|
||||
"Submission date cannot be before the loan disbursement date");
|
||||
}
|
||||
|
||||
final LocalDate rescheduleFromDate = this.fromJsonHelper.extractLocalDateNamed(
|
||||
RescheduleLoansApiConstants.rescheduleFromDateParamName, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)
|
||||
.value(rescheduleFromDate).notNull();
|
||||
|
||||
final Integer graceOnPrincipal = this.fromJsonHelper.extractIntegerWithLocaleNamed(
|
||||
RescheduleLoansApiConstants.graceOnPrincipalParamName, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.graceOnPrincipalParamName)
|
||||
.value(graceOnPrincipal).ignoreIfNull().integerGreaterThanZero();
|
||||
|
||||
final Integer graceOnInterest = this.fromJsonHelper.extractIntegerWithLocaleNamed(
|
||||
RescheduleLoansApiConstants.graceOnInterestParamName, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.graceOnInterestParamName)
|
||||
.value(graceOnInterest).ignoreIfNull().integerGreaterThanZero();
|
||||
|
||||
final Integer extraTerms = this.fromJsonHelper.extractIntegerWithLocaleNamed(
|
||||
RescheduleLoansApiConstants.extraTermsParamName, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.extraTermsParamName)
|
||||
.value(extraTerms).ignoreIfNull().integerGreaterThanZero();
|
||||
|
||||
final Long rescheduleReasonId = this.fromJsonHelper.extractLongNamed(
|
||||
RescheduleLoansApiConstants.rescheduleReasonIdParamName, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleReasonIdParamName)
|
||||
.value(rescheduleReasonId).notNull().integerGreaterThanZero();
|
||||
|
||||
final String rescheduleReasonComment = this.fromJsonHelper.extractStringNamed(
|
||||
RescheduleLoansApiConstants.rescheduleReasonCommentParamName, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleReasonCommentParamName)
|
||||
.value(rescheduleReasonComment).ignoreIfNull().notExceedingLengthOf(500);
|
||||
|
||||
final LocalDate adjustedDueDate = this.fromJsonHelper.extractLocalDateNamed(
|
||||
RescheduleLoansApiConstants.adjustedDueDateParamName, jsonElement);
|
||||
|
||||
if(adjustedDueDate != null && rescheduleFromDate != null && adjustedDueDate.isBefore(rescheduleFromDate)) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)
|
||||
.failWithCode("adjustedDueDate.before.rescheduleFromDate",
|
||||
"Adjusted due date cannot be before the reschedule from date");
|
||||
}
|
||||
|
||||
// at least one of the following must be provided => graceOnPrincipal, graceOnInterest, extraTerms, newInterestRate
|
||||
if(!this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.graceOnPrincipalParamName, jsonElement)
|
||||
&& !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.graceOnInterestParamName, jsonElement)
|
||||
&& !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.extraTermsParamName, jsonElement)
|
||||
&& !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.newInterestRateParamName, jsonElement)
|
||||
&& !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.adjustedDueDateParamName, jsonElement)) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.parameter(RescheduleLoansApiConstants.graceOnPrincipalParamName)
|
||||
.notNull();
|
||||
}
|
||||
|
||||
if(rescheduleFromDate != null) {
|
||||
LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(rescheduleFromDate);
|
||||
|
||||
if(installment == null) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)
|
||||
.failWithCode("repayment.schedule.installment.does.not.exist",
|
||||
"Repayment schedule installment does not exist");
|
||||
}
|
||||
|
||||
if(installment != null && installment.isObligationsMet()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)
|
||||
.failWithCode("repayment.schedule.installment.obligation.met",
|
||||
"Repayment schedule installment obligation met");
|
||||
}
|
||||
|
||||
if(installment != null && installment.isPartlyPaid()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)
|
||||
.failWithCode("repayment.schedule.installment.partly.paid",
|
||||
"Repayment schedule installment is partly paid");
|
||||
}
|
||||
|
||||
if(installment != null) {
|
||||
|
||||
if(installment.getPenaltyChargesOutstanding(loan.getCurrency()).isGreaterThanZero()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("repayment.schedule.installment.has.an.outstanding.penalty.charge",
|
||||
"Repayment schedule installment has an outsatnding penalty charge.");
|
||||
}
|
||||
|
||||
if(installment.getFeeChargesOutstanding(loan.getCurrency()).isGreaterThanZero()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("repayment.schedule.installment.has.an.outstanding.fee.charge",
|
||||
"Repayment schedule installment has an outsatnding fee charge.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(loanId != null) {
|
||||
List<LoanRescheduleRequestData> loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
|
||||
.readLoanRescheduleRequests(loanId, LoanStatus.APPROVED.getValue());
|
||||
|
||||
if(loanRescheduleRequestData.size() > 0) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("loan.already.rescheduled",
|
||||
"The loan can only be rescheduled once.");
|
||||
}
|
||||
}
|
||||
|
||||
if(!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a user request to approve a loan reschedule request
|
||||
*
|
||||
* @param jsonCommand the JSON command object (instance of the JsonCommand class)
|
||||
* @return void
|
||||
**/
|
||||
public void validateForApproveAction(final JsonCommand jsonCommand, LoanRescheduleRequest loanRescheduleRequest) {
|
||||
final String jsonString = jsonCommand.json();
|
||||
|
||||
if (StringUtils.isBlank(jsonString)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Type typeToken = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromJsonHelper.checkForUnsupportedParameters(typeToken, jsonString,
|
||||
RescheduleLoansApiConstants.APPROVE_REQUEST_DATA_PARAMETERS);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(StringUtils.lowerCase(RescheduleLoansApiConstants.ENTITY_NAME));
|
||||
|
||||
final JsonElement jsonElement = jsonCommand.parsedJson();
|
||||
|
||||
final LocalDate approvedOnDate = this.fromJsonHelper.extractLocalDateNamed(
|
||||
RescheduleLoansApiConstants.approvedOnDateParam, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.approvedOnDateParam)
|
||||
.value(approvedOnDate).notNull();
|
||||
|
||||
if(approvedOnDate != null && loanRescheduleRequest.getSubmittedOnDate().isAfter(approvedOnDate)) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.parameter(RescheduleLoansApiConstants.approvedOnDateParam)
|
||||
.failWithCode("before.submission.date",
|
||||
"Approval date cannot be before the request submission date.");
|
||||
}
|
||||
|
||||
LoanRescheduleRequestStatusEnumData loanRescheduleRequestStatusEnumData = LoanRescheduleRequestEnumerations
|
||||
.status(loanRescheduleRequest.getStatusEnum());
|
||||
|
||||
if(!loanRescheduleRequestStatusEnumData.isPendingApproval()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode(
|
||||
"request.is.not.in.submitted.and.pending.state",
|
||||
"Loan reschedule request approval is not allowed. "
|
||||
+ "Loan reschedule request is not in submitted and pending approval state.");
|
||||
}
|
||||
|
||||
LocalDate rescheduleFromDate = loanRescheduleRequest.getRescheduleFromDate();
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
|
||||
if(loan != null) {
|
||||
Long loanId = loan.getId();
|
||||
|
||||
if(!loan.status().isActive()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("loan.is.not.active",
|
||||
"Loan is not active");
|
||||
}
|
||||
|
||||
if(rescheduleFromDate != null) {
|
||||
LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(rescheduleFromDate);
|
||||
|
||||
if(installment == null) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("loan.repayment.schedule.installment.does.not.exist",
|
||||
"Repayment schedule installment does not exist");
|
||||
}
|
||||
|
||||
if(installment != null && installment.isObligationsMet()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("loan.repayment.schedule.installment."
|
||||
+ "obligation.met", "Repayment schedule installment obligation met");
|
||||
}
|
||||
|
||||
if(installment != null) {
|
||||
|
||||
if(installment.getPenaltyChargesOutstanding(loan.getCurrency()).isGreaterThanZero()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("repayment.schedule.installment.has.an.outstanding.penalty.charge",
|
||||
"Repayment schedule installment has an outsatnding penalty charge.");
|
||||
}
|
||||
|
||||
if(installment.getFeeChargesOutstanding(loan.getCurrency()).isGreaterThanZero()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("repayment.schedule.installment.has.an.outstanding.fee.charge",
|
||||
"Repayment schedule installment has an outsatnding fee charge.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(loanId != null) {
|
||||
List<LoanRescheduleRequestData> loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
|
||||
.readLoanRescheduleRequests(loanId, LoanStatus.APPROVED.getValue());
|
||||
|
||||
if(loanRescheduleRequestData.size() > 0) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode("loan.already.rescheduled",
|
||||
"The loan can only be rescheduled once.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a user request to reject a loan reschedule request
|
||||
*
|
||||
* @param jsonCommand the JSON command object (instance of the JsonCommand class)
|
||||
* @return void
|
||||
**/
|
||||
public void validateForRejectAction(final JsonCommand jsonCommand, LoanRescheduleRequest loanRescheduleRequest) {
|
||||
final String jsonString = jsonCommand.json();
|
||||
|
||||
if (StringUtils.isBlank(jsonString)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Type typeToken = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromJsonHelper.checkForUnsupportedParameters(typeToken, jsonString,
|
||||
RescheduleLoansApiConstants.REJECT_REQUEST_DATA_PARAMETERS);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(StringUtils.lowerCase(RescheduleLoansApiConstants.ENTITY_NAME));
|
||||
|
||||
final JsonElement jsonElement = jsonCommand.parsedJson();
|
||||
|
||||
final LocalDate rejectedOnDate = this.fromJsonHelper.extractLocalDateNamed(
|
||||
RescheduleLoansApiConstants.rejectedOnDateParam, jsonElement);
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rejectedOnDateParam)
|
||||
.value(rejectedOnDate).notNull();
|
||||
|
||||
if(rejectedOnDate != null && loanRescheduleRequest.getSubmittedOnDate().isAfter(rejectedOnDate)) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.parameter(RescheduleLoansApiConstants.rejectedOnDateParam)
|
||||
.failWithCode("before.submission.date",
|
||||
"Rejection date cannot be before the request submission date.");
|
||||
}
|
||||
|
||||
LoanRescheduleRequestStatusEnumData loanRescheduleRequestStatusEnumData = LoanRescheduleRequestEnumerations
|
||||
.status(loanRescheduleRequest.getStatusEnum());
|
||||
|
||||
if(!loanRescheduleRequestStatusEnumData.isPendingApproval()) {
|
||||
dataValidatorBuilder
|
||||
.reset()
|
||||
.failWithCodeNoParameterAddedToErrorCode(
|
||||
"request.is.not.in.submitted.and.pending.state",
|
||||
"Loan reschedule request rejection is not allowed. "
|
||||
+ "Loan reschedule request is not in submitted and pending approval state.");
|
||||
}
|
||||
|
||||
if(!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.rescheduleloan.data;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
|
||||
public class LoanRescheduleRequestEnumerations {
|
||||
|
||||
public static EnumOptionData status(final LoanRescheduleRequestStatusEnumData status) {
|
||||
Long id = status.id();
|
||||
String code = status.code();
|
||||
String value = status.value();
|
||||
|
||||
return new EnumOptionData(id, code, value);
|
||||
}
|
||||
|
||||
public static LoanRescheduleRequestStatusEnumData status(final Integer statusId) {
|
||||
return status(LoanStatus.fromInt(statusId));
|
||||
}
|
||||
|
||||
public static LoanRescheduleRequestStatusEnumData status(final LoanStatus status) {
|
||||
LoanRescheduleRequestStatusEnumData enumData = new LoanRescheduleRequestStatusEnumData(
|
||||
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue().longValue(),
|
||||
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval");
|
||||
|
||||
switch(status) {
|
||||
case SUBMITTED_AND_PENDING_APPROVAL:
|
||||
enumData = new LoanRescheduleRequestStatusEnumData(
|
||||
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue().longValue(),
|
||||
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval");
|
||||
break;
|
||||
|
||||
case APPROVED:
|
||||
enumData = new LoanRescheduleRequestStatusEnumData(
|
||||
LoanStatus.APPROVED.getValue().longValue(),
|
||||
LoanStatus.APPROVED.getCode(), "Approved");
|
||||
break;
|
||||
|
||||
case REJECTED:
|
||||
enumData = new LoanRescheduleRequestStatusEnumData(
|
||||
LoanStatus.REJECTED.getValue().longValue(),
|
||||
LoanStatus.REJECTED.getCode(), "Rejected");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return enumData;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.rescheduleloan.data;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
|
||||
/**
|
||||
* Immutable data object represent loan reschedule request status enumerations.
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
public class LoanRescheduleRequestStatusEnumData {
|
||||
private final Long id;
|
||||
private final String code;
|
||||
private final String value;
|
||||
private final boolean pendingApproval;
|
||||
private final boolean approved;
|
||||
private final boolean rejected;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestStatusEnumData constructor
|
||||
*
|
||||
* Note: Same status types/states for loan accounts are used in here
|
||||
**/
|
||||
public LoanRescheduleRequestStatusEnumData(Long id, String code, String value) {
|
||||
this.id = id;
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.pendingApproval = Long.valueOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue()).equals(this.id);
|
||||
this.approved = Long.valueOf(LoanStatus.APPROVED.getValue()).equals(this.id);
|
||||
this.rejected = Long.valueOf(LoanStatus.REJECTED.getValue()).equals(this.id);
|
||||
}
|
||||
|
||||
public Long id() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String code() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public boolean isPendingApproval() {
|
||||
return this.pendingApproval;
|
||||
}
|
||||
|
||||
public boolean isApproved() {
|
||||
return this.approved;
|
||||
}
|
||||
|
||||
public boolean isRejected() {
|
||||
return this.rejected;
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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.rescheduleloan.data;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Immutable data object represent the timeline events of a loan reschedule request
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
public class LoanRescheduleRequestTimelineData {
|
||||
private final LocalDate submittedOnDate;
|
||||
private final String submittedByUsername;
|
||||
private final String submittedByFirstname;
|
||||
private final String submittedByLastname;
|
||||
|
||||
private final LocalDate approvedOnDate;
|
||||
private final String approvedByUsername;
|
||||
private final String approvedByFirstname;
|
||||
private final String approvedByLastname;
|
||||
|
||||
private final LocalDate rejectedOnDate;
|
||||
private final String rejectedByUsername;
|
||||
private final String rejectedByFirstname;
|
||||
private final String rejectedByLastname;
|
||||
|
||||
public LoanRescheduleRequestTimelineData(final LocalDate submittedOnDate, final String submittedByUsername, final String submittedByFirstname,
|
||||
final String submittedByLastname, final LocalDate approvedOnDate, final String approvedByUsername, final String approvedByFirstname,
|
||||
final String approvedByLastname, final LocalDate rejectedOnDate, final String rejectedByUsername, final String rejectedByFirstname,
|
||||
final String rejectedByLastname) {
|
||||
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
this.submittedByUsername = submittedByUsername;
|
||||
this.submittedByFirstname = submittedByFirstname;
|
||||
this.submittedByLastname = submittedByLastname;
|
||||
|
||||
this.approvedOnDate = approvedOnDate;
|
||||
this.approvedByUsername = approvedByUsername;
|
||||
this.approvedByFirstname = approvedByFirstname;
|
||||
this.approvedByLastname = approvedByLastname;
|
||||
|
||||
this.rejectedOnDate = rejectedOnDate;
|
||||
this.rejectedByUsername = rejectedByUsername;
|
||||
this.rejectedByFirstname = rejectedByFirstname;
|
||||
this.rejectedByLastname = rejectedByLastname;
|
||||
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.FlatInterestLoanScheduleGenerator;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
|
||||
public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory {
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext,
|
||||
final InterestMethod interestMethod, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = null;
|
||||
|
||||
switch (interestMethod) {
|
||||
case DECLINING_BALANCE:
|
||||
loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(
|
||||
mathContext, loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
break;
|
||||
|
||||
case FLAT:
|
||||
loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
break;
|
||||
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return loanRescheduleModel;
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_repayment_schedule_history")
|
||||
public class LoanRepaymentScheduleHistory extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "loan_id")
|
||||
private Loan loan;
|
||||
|
||||
@OneToOne(optional = true)
|
||||
@JoinColumn(name = "loan_reschedule_request_id")
|
||||
private LoanRescheduleRequest loanRescheduleRequest;
|
||||
|
||||
@Column(name = "installment", nullable = false)
|
||||
private Integer installmentNumber;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "fromdate", nullable = true)
|
||||
private Date fromDate;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "duedate", nullable = false)
|
||||
private Date dueDate;
|
||||
|
||||
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principal;
|
||||
|
||||
@Column(name = "principal_completed_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principalCompleted;
|
||||
|
||||
@Column(name = "principal_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principalWrittenOff;
|
||||
|
||||
@Column(name = "interest_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestCharged;
|
||||
|
||||
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestPaid;
|
||||
|
||||
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestWaived;
|
||||
|
||||
@Column(name = "interest_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestWrittenOff;
|
||||
|
||||
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesCharged;
|
||||
|
||||
@Column(name = "fee_charges_completed_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesPaid;
|
||||
|
||||
@Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesWrittenOff;
|
||||
|
||||
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesWaived;
|
||||
|
||||
@Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyCharges;
|
||||
|
||||
@Column(name = "penalty_charges_completed_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyChargesPaid;
|
||||
|
||||
@Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyChargesWrittenOff;
|
||||
|
||||
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyChargesWaived;
|
||||
|
||||
@Column(name = "total_paid_in_advance_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal totalPaidInAdvance;
|
||||
|
||||
@Column(name = "total_paid_late_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal totalPaidLate;
|
||||
|
||||
@Column(name = "completed_derived", nullable = false)
|
||||
private boolean obligationsMet;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "obligations_met_on_date")
|
||||
private Date obligationsMetOnDate;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "created_date")
|
||||
private Date createdOnDate;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "createdby_id")
|
||||
private AppUser createdByUser;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "lastmodifiedby_id")
|
||||
private AppUser lastModifiedByUser;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "lastmodified_date")
|
||||
private Date lastModifiedOnDate;
|
||||
|
||||
/**
|
||||
* LoanRepaymentScheduleHistory constructor
|
||||
**/
|
||||
protected LoanRepaymentScheduleHistory() {}
|
||||
|
||||
/**
|
||||
* LoanRepaymentScheduleHistory constructor
|
||||
**/
|
||||
private LoanRepaymentScheduleHistory(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final Integer installmentNumber, final Date fromDate, final Date dueDate, final BigDecimal principal,
|
||||
final BigDecimal principalCompleted, final BigDecimal principalWrittenOff, final BigDecimal interestCharged,
|
||||
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
||||
final BigDecimal feeChargesCharged, final BigDecimal feeChargesPaid, final BigDecimal feeChargesWrittenOff,
|
||||
final BigDecimal feeChargesWaived, final BigDecimal penaltyCharges, final BigDecimal penaltyChargesPaid,
|
||||
final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesWaived,
|
||||
final BigDecimal totalPaidInAdvance, final BigDecimal totalPaidLate, final boolean obligationsMet,
|
||||
final Date obligationsMetOnDate, final Date createdOnDate, final AppUser createdByUser,
|
||||
final AppUser lastModifiedByUser, final Date lastModifiedOnDate) {
|
||||
|
||||
this.loan = loan;
|
||||
this.loanRescheduleRequest = loanRescheduleRequest;
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
this.principal = principal;
|
||||
this.principalCompleted = principalCompleted;
|
||||
this.principalWrittenOff = principalWrittenOff;
|
||||
this.interestCharged = interestCharged;
|
||||
this.interestPaid = interestPaid;
|
||||
this.interestWaived = interestWaived;
|
||||
this.interestWrittenOff = interestWrittenOff;
|
||||
this.feeChargesCharged = feeChargesCharged;
|
||||
this.feeChargesPaid = feeChargesPaid;
|
||||
this.feeChargesWrittenOff = feeChargesWrittenOff;
|
||||
this.feeChargesWaived = feeChargesWaived;
|
||||
this.penaltyCharges = penaltyCharges;
|
||||
this.penaltyChargesPaid = penaltyChargesPaid;
|
||||
this.penaltyChargesWrittenOff = penaltyChargesWrittenOff;
|
||||
this.penaltyChargesWaived = penaltyChargesWaived;
|
||||
this.totalPaidInAdvance = totalPaidInAdvance;
|
||||
this.totalPaidLate = totalPaidLate;
|
||||
this.obligationsMet = obligationsMet;
|
||||
this.obligationsMetOnDate = obligationsMetOnDate;
|
||||
this.createdOnDate = createdOnDate;
|
||||
this.createdByUser = createdByUser;
|
||||
this.lastModifiedByUser = lastModifiedByUser;
|
||||
this.lastModifiedOnDate = lastModifiedOnDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an instance of the LoanRepaymentScheduleHistory class
|
||||
**/
|
||||
public static LoanRepaymentScheduleHistory instance(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final Integer installmentNumber, final Date fromDate, final Date dueDate, final BigDecimal principal,
|
||||
final BigDecimal principalCompleted, final BigDecimal principalWrittenOff, final BigDecimal interestCharged,
|
||||
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
||||
final BigDecimal feeChargesCharged, final BigDecimal feeChargesPaid, final BigDecimal feeChargesWrittenOff,
|
||||
final BigDecimal feeChargesWaived, final BigDecimal penaltyCharges, final BigDecimal penaltyChargesPaid,
|
||||
final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesWaived,
|
||||
final BigDecimal totalPaidInAdvance, final BigDecimal totalPaidLate, final boolean obligationsMet,
|
||||
final Date obligationsMetOnDate, final Date createdOnDate, final AppUser createdByUser,
|
||||
final AppUser lastModifiedByUser, final Date lastModifiedOnDate) {
|
||||
|
||||
return new LoanRepaymentScheduleHistory(loan, loanRescheduleRequest, installmentNumber, fromDate, dueDate, principal,
|
||||
principalCompleted, principalWrittenOff, interestCharged, interestPaid, interestWaived, interestWrittenOff,
|
||||
feeChargesCharged, feeChargesPaid, feeChargesWrittenOff, feeChargesWaived, penaltyCharges, penaltyChargesPaid,
|
||||
penaltyChargesWrittenOff, penaltyChargesWaived, totalPaidInAdvance, totalPaidLate, obligationsMet,
|
||||
obligationsMetOnDate, createdOnDate, createdByUser, lastModifiedByUser, lastModifiedOnDate);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void create(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final Integer installmentNumber, final Date fromDate, final Date dueDate, final BigDecimal principal,
|
||||
final BigDecimal principalCompleted, final BigDecimal principalWrittenOff, final BigDecimal interestCharged,
|
||||
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
||||
final BigDecimal feeChargesCharged, final BigDecimal feeChargesPaid, final BigDecimal feeChargesWrittenOff,
|
||||
final BigDecimal feeChargesWaived, final BigDecimal penaltyCharges, final BigDecimal penaltyChargesPaid,
|
||||
final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesWaived,
|
||||
final BigDecimal totalPaidInAdvance, final BigDecimal totalPaidLate, final boolean obligationsMet,
|
||||
final Date obligationsMetOnDate, final Date createdOnDate, final AppUser createdByUser,
|
||||
final AppUser lastModifiedByUser, final Date lastModifiedOnDate) {
|
||||
|
||||
new LoanRepaymentScheduleHistory(loan, loanRescheduleRequest, installmentNumber, fromDate, dueDate, principal,
|
||||
principalCompleted, principalWrittenOff, interestCharged, interestPaid, interestWaived, interestWrittenOff,
|
||||
feeChargesCharged, feeChargesPaid, feeChargesWrittenOff, feeChargesWaived, penaltyCharges, penaltyChargesPaid,
|
||||
penaltyChargesWrittenOff, penaltyChargesWaived, totalPaidInAdvance, totalPaidLate, obligationsMet,
|
||||
obligationsMetOnDate, createdOnDate, createdByUser, lastModifiedByUser, lastModifiedOnDate);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface LoanRepaymentScheduleHistoryRepository extends JpaRepository<LoanRepaymentScheduleHistory, Long>, JpaSpecificationExecutor<LoanRepaymentScheduleHistory> {
|
||||
|
||||
}
|
||||
+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.rescheduleloan.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
|
||||
public interface LoanRescheduleModalPeriod {
|
||||
|
||||
LoanSchedulePeriodData toData();
|
||||
|
||||
Integer periodNumber();
|
||||
|
||||
Integer oldPeriodNumber();
|
||||
|
||||
LocalDate periodFromDate();
|
||||
|
||||
LocalDate periodDueDate();
|
||||
|
||||
BigDecimal principalDue();
|
||||
|
||||
BigDecimal interestDue();
|
||||
|
||||
BigDecimal feeChargesDue();
|
||||
|
||||
BigDecimal penaltyChargesDue();
|
||||
|
||||
boolean isNew();
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
|
||||
public final class LoanRescheduleModel {
|
||||
|
||||
private final Collection<LoanRescheduleModelRepaymentPeriod> periods;
|
||||
private final Collection<LoanRepaymentScheduleHistory> oldPeriods;
|
||||
private final ApplicationCurrency applicationCurrency;
|
||||
private final int loanTermInDays;
|
||||
private final Money totalPrincipalDisbursed;
|
||||
private final BigDecimal totalPrincipalExpected;
|
||||
private final BigDecimal totalPrincipalPaid;
|
||||
private final BigDecimal totalInterestCharged;
|
||||
private final BigDecimal totalFeeChargesCharged;
|
||||
private final BigDecimal totalPenaltyChargesCharged;
|
||||
private final BigDecimal totalRepaymentExpected;
|
||||
private final BigDecimal totalOutstanding;
|
||||
|
||||
public LoanRescheduleModel(final Collection<LoanRescheduleModelRepaymentPeriod> periods, final Collection<LoanRepaymentScheduleHistory> oldPeriods,
|
||||
final ApplicationCurrency applicationCurrency, final int loanTermInDays, final Money principalDisbursed,
|
||||
final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged,
|
||||
final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected,
|
||||
final BigDecimal totalOutstanding) {
|
||||
this.periods = periods;
|
||||
this.oldPeriods = oldPeriods;
|
||||
this.applicationCurrency = applicationCurrency;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
this.totalPrincipalDisbursed = principalDisbursed;
|
||||
this.totalPrincipalExpected = totalPrincipalExpected;
|
||||
this.totalPrincipalPaid = totalPrincipalPaid;
|
||||
this.totalInterestCharged = totalInterestCharged;
|
||||
this.totalFeeChargesCharged = totalFeeChargesCharged;
|
||||
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
|
||||
this.totalRepaymentExpected = totalRepaymentExpected;
|
||||
this.totalOutstanding = totalOutstanding;
|
||||
}
|
||||
|
||||
public static LoanRescheduleModel instance(final Collection<LoanRescheduleModelRepaymentPeriod> periods, final Collection<LoanRepaymentScheduleHistory> oldPeriods,
|
||||
final ApplicationCurrency applicationCurrency, final int loanTermInDays, final Money principalDisbursed,
|
||||
final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged,
|
||||
final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected,
|
||||
final BigDecimal totalOutstanding) {
|
||||
|
||||
return new LoanRescheduleModel(periods, oldPeriods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected,
|
||||
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstanding);
|
||||
}
|
||||
|
||||
public LoanScheduleData toData() {
|
||||
|
||||
final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal();
|
||||
final Integer inMultiplesOf = this.totalPrincipalDisbursed.getCurrencyInMultiplesOf();
|
||||
final CurrencyData currency = this.applicationCurrency.toData(decimalPlaces, inMultiplesOf);
|
||||
|
||||
final Collection<LoanSchedulePeriodData> periodsData = new ArrayList<LoanSchedulePeriodData>();
|
||||
for (final LoanRescheduleModalPeriod modelPeriod : this.periods) {
|
||||
periodsData.add(modelPeriod.toData());
|
||||
}
|
||||
|
||||
final BigDecimal totalWaived = null;
|
||||
final BigDecimal totalWrittenOff = null;
|
||||
final BigDecimal totalRepayment = null;
|
||||
final BigDecimal totalPaidInAdvance = null;
|
||||
final BigDecimal totalPaidLate = null;
|
||||
|
||||
return new LoanScheduleData(currency, periodsData, this.loanTermInDays, this.totalPrincipalDisbursed.getAmount(),
|
||||
this.totalPrincipalExpected, this.totalPrincipalPaid, this.totalInterestCharged, this.totalFeeChargesCharged,
|
||||
this.totalPenaltyChargesCharged, totalWaived, totalWrittenOff, this.totalRepaymentExpected, totalRepayment,
|
||||
totalPaidInAdvance, totalPaidLate, this.totalOutstanding);
|
||||
}
|
||||
|
||||
public Collection<LoanRescheduleModelRepaymentPeriod> getPeriods() {
|
||||
return this.periods;
|
||||
}
|
||||
|
||||
public Collection<LoanRepaymentScheduleHistory> getOldPeriods() {
|
||||
return this.oldPeriods;
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
|
||||
public final class LoanRescheduleModelRepaymentPeriod implements LoanRescheduleModalPeriod {
|
||||
|
||||
private int periodNumber;
|
||||
private int oldPeriodNumber;
|
||||
private LocalDate fromDate;
|
||||
private LocalDate dueDate;
|
||||
private Money principalDue;
|
||||
private Money outstandingLoanBalance;
|
||||
private Money interestDue;
|
||||
private Money feeChargesDue;
|
||||
private Money penaltyChargesDue;
|
||||
private Money totalDue;
|
||||
private boolean isNew;
|
||||
|
||||
public LoanRescheduleModelRepaymentPeriod(final int periodNumber, final int oldPeriodNumber, LocalDate fromDate,
|
||||
final LocalDate dueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue,
|
||||
final Money feeChargesDue, final Money penaltyChargesDue, final Money totalDue, final boolean isNew) {
|
||||
this.periodNumber = periodNumber;
|
||||
this.oldPeriodNumber = oldPeriodNumber;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
this.principalDue = principalDue;
|
||||
this.outstandingLoanBalance = outstandingLoanBalance;
|
||||
this.interestDue = interestDue;
|
||||
this.feeChargesDue = feeChargesDue;
|
||||
this.penaltyChargesDue = penaltyChargesDue;
|
||||
this.totalDue = totalDue;
|
||||
this.isNew = isNew;
|
||||
}
|
||||
|
||||
public static LoanRescheduleModelRepaymentPeriod instance(final int periodNumber, final int oldPeriodNumber, LocalDate fromDate,
|
||||
final LocalDate dueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue,
|
||||
final Money feeChargesDue, final Money penaltyChargesDue, final Money totalDue, final boolean isNew) {
|
||||
|
||||
return new LoanRescheduleModelRepaymentPeriod(periodNumber, oldPeriodNumber, fromDate, dueDate, principalDue,
|
||||
outstandingLoanBalance, interestDue, feeChargesDue, penaltyChargesDue, totalDue, isNew);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanSchedulePeriodData toData() {
|
||||
return LoanSchedulePeriodData.repaymentOnlyPeriod(this.periodNumber, this.fromDate, this.dueDate, this.principalDue.getAmount(),
|
||||
this.outstandingLoanBalance.getAmount(), this.interestDue.getAmount(), this.feeChargesDue.getAmount(),
|
||||
this.penaltyChargesDue.getAmount(), this.totalDue.getAmount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer periodNumber() {
|
||||
return this.periodNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer oldPeriodNumber() {
|
||||
return this.oldPeriodNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate periodFromDate() {
|
||||
return this.fromDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate periodDueDate() {
|
||||
return this.dueDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal principalDue() {
|
||||
BigDecimal value = null;
|
||||
|
||||
if (this.principalDue != null) {
|
||||
value = this.principalDue.getAmount();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal interestDue() {
|
||||
BigDecimal value = null;
|
||||
|
||||
if (this.interestDue != null) {
|
||||
value = this.interestDue.getAmount();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal feeChargesDue() {
|
||||
BigDecimal value = null;
|
||||
|
||||
if (this.feeChargesDue != null) {
|
||||
value = this.feeChargesDue.getAmount();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal penaltyChargesDue() {
|
||||
BigDecimal value = null;
|
||||
|
||||
if (this.penaltyChargesDue != null) {
|
||||
value = this.penaltyChargesDue.getAmount();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return isNew;
|
||||
}
|
||||
|
||||
public void updatePeriodNumber(Integer periodNumber) {
|
||||
this.periodNumber = periodNumber;
|
||||
}
|
||||
|
||||
public void updateOldPeriodNumber(Integer oldPeriodNumber) {
|
||||
this.oldPeriodNumber = oldPeriodNumber;
|
||||
}
|
||||
|
||||
public void updatePeriodFromDate(LocalDate periodFromDate) {
|
||||
this.fromDate = periodFromDate;
|
||||
}
|
||||
|
||||
public void updatePeriodDueDate(LocalDate periodDueDate) {
|
||||
this.dueDate = periodDueDate;
|
||||
}
|
||||
|
||||
public void updatePrincipalDue(Money principalDue) {
|
||||
this.principalDue = principalDue;
|
||||
}
|
||||
|
||||
public void updateInterestDue(Money interestDue) {
|
||||
this.interestDue = interestDue;
|
||||
}
|
||||
|
||||
public void updateFeeChargesDue(Money feeChargesDue) {
|
||||
this.feeChargesDue = feeChargesDue;
|
||||
}
|
||||
|
||||
public void updatePenaltyChargesDue(Money penaltyChargesDue) {
|
||||
this.penaltyChargesDue = penaltyChargesDue;
|
||||
}
|
||||
|
||||
public void updateOutstandingLoanBalance(Money outstandingLoanBalance) {
|
||||
this.outstandingLoanBalance = outstandingLoanBalance;
|
||||
}
|
||||
|
||||
public void updateTotalDue(Money totalDue) {
|
||||
this.totalDue = totalDue;
|
||||
}
|
||||
|
||||
public void updateIsNew(boolean isNew) {
|
||||
this.isNew = isNew;
|
||||
}
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_reschedule_request")
|
||||
public class LoanRescheduleRequest extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "loan_id", nullable = false)
|
||||
private Loan loan;
|
||||
|
||||
@Column(name = "status_enum", nullable = false)
|
||||
private Integer statusEnum;
|
||||
|
||||
@Column(name = "reschedule_from_installment")
|
||||
private Integer rescheduleFromInstallment;
|
||||
|
||||
@Column(name = "grace_on_principal")
|
||||
private Integer graceOnPrincipal;
|
||||
|
||||
@Column(name = "grace_on_interest")
|
||||
private Integer graceOnInterest;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "reschedule_from_date")
|
||||
private Date rescheduleFromDate;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "adjusted_due_date")
|
||||
private Date adjustedDueDate;
|
||||
|
||||
@Column(name = "extra_terms")
|
||||
private Integer extraTerms;
|
||||
|
||||
@Column(name = "recalculate_interest")
|
||||
private Boolean recalculateInterest;
|
||||
|
||||
@Column(name = "interest_rate", scale = 6, precision = 19)
|
||||
private BigDecimal interestRate;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "reschedule_reason_cv_id")
|
||||
private CodeValue rescheduleReasonCodeValue;
|
||||
|
||||
@Column(name = "reschedule_reason_comment")
|
||||
private String rescheduleReasonComment;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "submitted_on_date")
|
||||
private Date submittedOnDate;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "submitted_by_user_id")
|
||||
private AppUser submittedByUser;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "approved_on_date")
|
||||
private Date approvedOnDate;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "approved_by_user_id")
|
||||
private AppUser approvedByUser;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "rejected_on_date")
|
||||
private Date rejectedOnDate;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "rejected_by_user_id")
|
||||
private AppUser rejectedByUser;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequest constructor
|
||||
**/
|
||||
protected LoanRescheduleRequest() {}
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequest constructor
|
||||
**/
|
||||
private LoanRescheduleRequest(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment,
|
||||
final Integer graceOnPrincipal, final Integer graceOnInterest, final Date rescheduleFromDate, final Date adjustedDueDate,
|
||||
final Integer extraTerms, final Boolean recalculateInterest, final BigDecimal interestRate, final CodeValue rescheduleReasonCodeValue,
|
||||
final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser,
|
||||
final Date approvedOnDate, final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
|
||||
this.loan = loan;
|
||||
this.statusEnum = statusEnum;
|
||||
this.rescheduleFromInstallment = rescheduleFromInstallment;
|
||||
this.graceOnPrincipal = graceOnPrincipal;
|
||||
this.graceOnInterest = graceOnInterest;
|
||||
this.rescheduleFromDate = rescheduleFromDate;
|
||||
this.extraTerms = extraTerms;
|
||||
this.interestRate = interestRate;
|
||||
this.rescheduleReasonCodeValue = rescheduleReasonCodeValue;
|
||||
this.rescheduleReasonComment = rescheduleReasonComment;
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
this.submittedByUser = submittedByUser;
|
||||
this.approvedOnDate = approvedOnDate;
|
||||
this.approvedByUser = approvedByUser;
|
||||
this.rejectedOnDate = rejectedOnDate;
|
||||
this.rejectedByUser = rejectedByUser;
|
||||
this.adjustedDueDate = adjustedDueDate;
|
||||
this.recalculateInterest = recalculateInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new instance of the LoanRescheduleRequest class
|
||||
**/
|
||||
public static LoanRescheduleRequest instance(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment,
|
||||
final Integer graceOnPrincipal, final Integer graceOnInterest, final Date rescheduleFromDate, final Date adjustedDueDate,
|
||||
final Integer extraTerms, final Boolean recalculateInterest, final BigDecimal interestRate, final CodeValue rescheduleReasonCodeValue,
|
||||
final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser,
|
||||
final Date approvedOnDate, final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
|
||||
|
||||
return new LoanRescheduleRequest(loan, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
|
||||
rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment, submittedOnDate,
|
||||
submittedByUser, approvedOnDate, approvedByUser, rejectedOnDate, rejectedByUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reschedule request loan object
|
||||
**/
|
||||
public Loan getLoan() {
|
||||
return this.loan;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the status enum
|
||||
**/
|
||||
public Integer getStatusEnum() {
|
||||
return this.statusEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return installment number of the rescheduling start point
|
||||
**/
|
||||
public Integer getRescheduleFromInstallment() {
|
||||
return this.rescheduleFromInstallment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the grace on principal
|
||||
**/
|
||||
public Integer getGraceOnPrincipal() {
|
||||
return this.graceOnPrincipal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the grace on interest
|
||||
**/
|
||||
public Integer getGraceOnInterest() {
|
||||
return this.graceOnInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return due date of the rescheduling start point
|
||||
**/
|
||||
public LocalDate getRescheduleFromDate() {
|
||||
|
||||
LocalDate localDate = null;
|
||||
|
||||
if(this.rescheduleFromDate != null) {
|
||||
localDate = new LocalDate(this.rescheduleFromDate);
|
||||
}
|
||||
|
||||
return localDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return due date of the first rescheduled installment
|
||||
**/
|
||||
public LocalDate getAdjustedDueDate() {
|
||||
|
||||
LocalDate localDate = null;
|
||||
|
||||
if(this.adjustedDueDate != null) {
|
||||
localDate = new LocalDate(this.adjustedDueDate);
|
||||
}
|
||||
|
||||
return localDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return extra terms to be added after the last loan installment
|
||||
**/
|
||||
public Integer getExtraTerms() {
|
||||
return this.extraTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the new interest rate to be applied to unpaid installments
|
||||
**/
|
||||
public BigDecimal getInterestRate() {
|
||||
return this.interestRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reschedule reason code value object
|
||||
**/
|
||||
public CodeValue getRescheduleReasonCodeValue() {
|
||||
return this.rescheduleReasonCodeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reschedule reason comment added by the "submittedByUser"
|
||||
**/
|
||||
public String getRescheduleReasonComment() {
|
||||
return this.rescheduleReasonComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the date the request was submitted
|
||||
**/
|
||||
public LocalDate getSubmittedOnDate() {
|
||||
LocalDate localDate = null;
|
||||
|
||||
if(this.submittedOnDate != null) {
|
||||
localDate = new LocalDate(this.submittedOnDate);
|
||||
}
|
||||
|
||||
return localDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the user that submitted the request
|
||||
**/
|
||||
public AppUser getSubmittedByUser() {
|
||||
return this.submittedByUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the date the request was approved
|
||||
**/
|
||||
public LocalDate getApprovedOnDate() {
|
||||
LocalDate localDate = null;
|
||||
|
||||
if(this.approvedOnDate != null) {
|
||||
localDate = new LocalDate(this.approvedOnDate);
|
||||
}
|
||||
|
||||
return localDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the user that approved the request
|
||||
**/
|
||||
public AppUser getApprovedByUser() {
|
||||
return this.approvedByUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the date the request was rejected
|
||||
**/
|
||||
public LocalDate getRejectedOnDate() {
|
||||
LocalDate localDate = null;
|
||||
|
||||
if(this.rejectedOnDate != null) {
|
||||
localDate = new LocalDate(this.rejectedOnDate);
|
||||
}
|
||||
|
||||
return localDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the recalculate interest option (true/false)
|
||||
**/
|
||||
public Boolean getRecalculateInterest() {
|
||||
boolean recalculateInterest = false;
|
||||
|
||||
if(this.recalculateInterest != null) {
|
||||
recalculateInterest = this.recalculateInterest;
|
||||
}
|
||||
|
||||
return recalculateInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the user that rejected the request
|
||||
**/
|
||||
public AppUser getRejectedByUser() {
|
||||
return this.approvedByUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* change the status of the loan reschedule request to approved, also updating the
|
||||
* approvedByUser and approvedOnDate properties
|
||||
*
|
||||
* @param approvedByUser the user who approved the request
|
||||
* @param approvedOnDate the date of the approval
|
||||
* @return void
|
||||
**/
|
||||
public void approve(final AppUser approvedByUser, final LocalDate approvedOnDate) {
|
||||
|
||||
if(approvedOnDate != null) {
|
||||
this.approvedByUser = approvedByUser;
|
||||
this.approvedOnDate = approvedOnDate.toDate();
|
||||
this.statusEnum = LoanStatus.APPROVED.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* change the status of the loan reschedule request to rejected, also updating the
|
||||
* approvedByUser and approvedOnDate properties
|
||||
*
|
||||
* @param approvedByUser the user who approved the request
|
||||
* @param approvedOnDate the date of the approval
|
||||
* @return void
|
||||
**/
|
||||
public void reject(final AppUser approvedByUser, final LocalDate approvedOnDate) {
|
||||
|
||||
if(approvedOnDate != null) {
|
||||
this.rejectedByUser = approvedByUser;
|
||||
this.rejectedOnDate = approvedOnDate.toDate();
|
||||
this.statusEnum = LoanStatus.REJECTED.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface LoanRescheduleRequestRepository extends JpaRepository<LoanRescheduleRequest, Long>, JpaSpecificationExecutor<LoanRescheduleRequest> {
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
|
||||
public interface LoanReschedulerFactory {
|
||||
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays);
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.rescheduleloan.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeException} thrown when loan reschedule request resources are not found.
|
||||
**/
|
||||
public class LoanRescheduleRequestNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestNotFoundException constructor
|
||||
*
|
||||
* @param requestId the loan reschedule request identifier
|
||||
* @return void
|
||||
**/
|
||||
public LoanRescheduleRequestNotFoundException(final Long requestId) {
|
||||
super("error.msg.loan.reschedule.request.id.invalid",
|
||||
"Loan reschedule request with identifier " + requestId + " does not exist", requestId);
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.rescheduleloan.handler;
|
||||
|
||||
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.rescheduleloan.service.LoanRescheduleRequestWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class ApproveLoanRescheduleRequestCommandHandler implements NewCommandSourceHandler {
|
||||
private final LoanRescheduleRequestWritePlatformService loanRescheduleRequestWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ApproveLoanRescheduleRequestCommandHandler(
|
||||
LoanRescheduleRequestWritePlatformService loanRescheduleRequestWritePlatformService) {
|
||||
this.loanRescheduleRequestWritePlatformService = loanRescheduleRequestWritePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
|
||||
return this.loanRescheduleRequestWritePlatformService.approve(jsonCommand);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.rescheduleloan.handler;
|
||||
|
||||
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.rescheduleloan.service.LoanRescheduleRequestWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class CreateLoanRescheduleRequestCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanRescheduleRequestWritePlatformService loanRescheduleRequestWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CreateLoanRescheduleRequestCommandHandler(
|
||||
LoanRescheduleRequestWritePlatformService loanRescheduleRequestWritePlatformService) {
|
||||
this.loanRescheduleRequestWritePlatformService = loanRescheduleRequestWritePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
|
||||
return this.loanRescheduleRequestWritePlatformService.create(jsonCommand);
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.rescheduleloan.handler;
|
||||
|
||||
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.rescheduleloan.service.LoanRescheduleRequestWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class RejectLoanRescheduleRequestCommandHandler implements NewCommandSourceHandler {
|
||||
private final LoanRescheduleRequestWritePlatformService loanRescheduleRequestWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public RejectLoanRescheduleRequestCommandHandler(
|
||||
LoanRescheduleRequestWritePlatformService loanRescheduleRequestWritePlatformService) {
|
||||
this.loanRescheduleRequestWritePlatformService = loanRescheduleRequestWritePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
|
||||
return this.loanRescheduleRequestWritePlatformService.reject(jsonCommand);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 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.rescheduleloan.service;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
|
||||
public interface LoanReschedulePreviewPlatformService {
|
||||
|
||||
public LoanRescheduleModel previewLoanReschedule(Long requestId);
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.rescheduleloan.service;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
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.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService {
|
||||
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
|
||||
@Autowired
|
||||
public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final ConfigurationDomainService configurationDomainService,
|
||||
final HolidayRepository holidayRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository) {
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel previewLoanReschedule(Long requestId) {
|
||||
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(requestId);
|
||||
|
||||
if(loanRescheduleRequest == null) {
|
||||
throw new LoanRescheduleRequestNotFoundException(requestId);
|
||||
}
|
||||
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||
loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
return loanRescheduleModel;
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.rescheduleloan.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestData;
|
||||
|
||||
public interface LoanRescheduleRequestReadPlatformService {
|
||||
|
||||
/**
|
||||
* get all loan reschedule requests by loan ID
|
||||
*
|
||||
* @param loanId the loan identifier
|
||||
* @return list of LoanRescheduleRequestData objects
|
||||
**/
|
||||
public List<LoanRescheduleRequestData> readLoanRescheduleRequests(Long loanId);
|
||||
|
||||
/**
|
||||
* get a single loan reschedule request by ID (primary key)
|
||||
*
|
||||
* @param requestId the loan reschedule request identifier
|
||||
* @return a LoanRescheduleRequestData object
|
||||
**/
|
||||
public LoanRescheduleRequestData readLoanRescheduleRequest(Long requestId);
|
||||
|
||||
/**
|
||||
* get all loan reschedule requests filter by loan ID and status enum
|
||||
*
|
||||
* @param loanId the loan identifier
|
||||
* @return list of LoanRescheduleRequestData objects
|
||||
**/
|
||||
public List<LoanRescheduleRequestData> readLoanRescheduleRequests(Long loanId, Integer statusEnum);
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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.rescheduleloan.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestData;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestEnumerations;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestStatusEnumData;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestTimelineData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanRescheduleRequestReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final LoanRepository loanRepository;
|
||||
private final LoanRescheduleRequestRowMapper loanRescheduleRequestRowMapper = new LoanRescheduleRequestRowMapper();
|
||||
|
||||
@Autowired
|
||||
public LoanRescheduleRequestReadPlatformServiceImpl(final RoutingDataSource dataSource,
|
||||
LoanRepository loanRepository) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.loanRepository = loanRepository;
|
||||
}
|
||||
|
||||
private static final class LoanRescheduleRequestRowMapper implements RowMapper<LoanRescheduleRequestData> {
|
||||
|
||||
private final String schema;
|
||||
|
||||
public LoanRescheduleRequestRowMapper() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(200);
|
||||
|
||||
sqlBuilder.append("lr.id as id, lr.loan_id as loanId, lr.status_enum as statusEnum, ");
|
||||
sqlBuilder.append("mc.display_name as clientName, ");
|
||||
sqlBuilder.append("mc.id as clientId, ");
|
||||
sqlBuilder.append("ml.account_no as loanAccountNumber, ");
|
||||
sqlBuilder.append("lr.reschedule_from_installment as rescheduleFromInstallment, ");
|
||||
sqlBuilder.append("lr.grace_on_principal as graceOnPrincipal, ");
|
||||
sqlBuilder.append("lr.grace_on_interest as graceOnInterest, ");
|
||||
sqlBuilder.append("lr.reschedule_from_date as rescheduleFromDate, ");
|
||||
sqlBuilder.append("lr.adjusted_due_date as adjustedDueDate, ");
|
||||
sqlBuilder.append("lr.extra_terms as extraTerms, ");
|
||||
sqlBuilder.append("lr.recalculate_interest as recalculateInterest, ");
|
||||
sqlBuilder.append("lr.interest_rate as interestRate, ");
|
||||
sqlBuilder.append("lr.reschedule_reason_cv_id as rescheduleReasonCvId, ");
|
||||
sqlBuilder.append("cv.code_value as rescheduleReasonCvValue, ");
|
||||
sqlBuilder.append("lr.reschedule_reason_comment as rescheduleReasonComment, ");
|
||||
|
||||
sqlBuilder.append("lr.submitted_on_date as submittedOnDate, ");
|
||||
sqlBuilder.append("sbu.username as submittedByUsername, ");
|
||||
sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
|
||||
sqlBuilder.append("sbu.lastname as submittedByLastname, ");
|
||||
|
||||
sqlBuilder.append("lr.approved_on_date as approvedOnDate, ");
|
||||
sqlBuilder.append("abu.username as approvedByUsername, ");
|
||||
sqlBuilder.append("abu.firstname as approvedByFirstname, ");
|
||||
sqlBuilder.append("abu.lastname as approvedByLastname, ");
|
||||
|
||||
sqlBuilder.append("lr.rejected_on_date as rejectedOnDate, ");
|
||||
sqlBuilder.append("rbu.username as rejectedByUsername, ");
|
||||
sqlBuilder.append("rbu.firstname as rejectedByFirstname, ");
|
||||
sqlBuilder.append("rbu.lastname as rejectedByLastname ");
|
||||
|
||||
sqlBuilder.append("from " + loanRescheduleRequestTableName() + " lr ");
|
||||
sqlBuilder.append("left join m_code_value cv on cv.id = lr.reschedule_reason_cv_id ");
|
||||
sqlBuilder.append("left join m_appuser sbu on sbu.id = lr.submitted_by_user_id ");
|
||||
sqlBuilder.append("left join m_appuser abu on abu.id = lr.approved_by_user_id ");
|
||||
sqlBuilder.append("left join m_appuser rbu on rbu.id = lr.rejected_by_user_id ");
|
||||
sqlBuilder.append("left join m_loan ml on ml.id = lr.loan_id ");
|
||||
sqlBuilder.append("left join m_client mc on mc.id = ml.client_id ");
|
||||
|
||||
this.schema = sqlBuilder.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
public String loanRescheduleRequestTableName() {
|
||||
return "m_loan_reschedule_request";
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public LoanRescheduleRequestData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final Long loanId = rs.getLong("loanId");
|
||||
final Integer statusEnumId = JdbcSupport.getInteger(rs, "statusEnum");
|
||||
final LoanRescheduleRequestStatusEnumData statusEnum = LoanRescheduleRequestEnumerations
|
||||
.status(statusEnumId);
|
||||
final String clientName = rs.getString("clientName");
|
||||
final String loanAccountNumber = rs.getString("loanAccountNumber");
|
||||
final Long clientId = rs.getLong("clientId");
|
||||
final Integer rescheduleFromInstallment = JdbcSupport.getInteger(rs, "rescheduleFromInstallment");
|
||||
final Integer graceOnPrincipal = JdbcSupport.getInteger(rs, "graceOnPrincipal");
|
||||
final Integer graceOnInterest = JdbcSupport.getInteger(rs, "graceOnInterest");
|
||||
final LocalDate rescheduleFromDate = JdbcSupport.getLocalDate(rs, "rescheduleFromDate");
|
||||
final LocalDate adjustedDueDate = JdbcSupport.getLocalDate(rs, "adjustedDueDate");
|
||||
final Integer extraTerms = JdbcSupport.getInteger(rs, "extraTerms");
|
||||
final BigDecimal interestRate = rs.getBigDecimal("interestRate");
|
||||
final Long rescheduleReasonCvId = JdbcSupport.getLong(rs, "rescheduleReasonCvId");
|
||||
final String rescheduleReasonCvValue = rs.getString("rescheduleReasonCvValue");
|
||||
final CodeValueData rescheduleReasonCodeValue = CodeValueData
|
||||
.instance(rescheduleReasonCvId, rescheduleReasonCvValue);
|
||||
final String rescheduleReasonComment = rs.getString("rescheduleReasonComment");
|
||||
final Boolean recalculateInterest = rs.getBoolean("recalculateInterest");
|
||||
|
||||
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
|
||||
final String submittedByUsername = rs.getString("submittedByUsername");
|
||||
final String submittedByFirstname = rs.getString("submittedByFirstname");
|
||||
final String submittedByLastname = rs.getString("submittedByLastname");
|
||||
|
||||
final LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedOnDate");
|
||||
final String approvedByUsername = rs.getString("approvedByUsername");
|
||||
final String approvedByFirstname = rs.getString("approvedByFirstname");
|
||||
final String approvedByLastname = rs.getString("approvedByLastname");
|
||||
|
||||
final LocalDate rejectedOnDate = JdbcSupport.getLocalDate(rs, "rejectedOnDate");
|
||||
final String rejectedByUsername = rs.getString("rejectedByUsername");
|
||||
final String rejectedByFirstname = rs.getString("rejectedByFirstname");
|
||||
final String rejectedByLastname = rs.getString("rejectedByLastname");
|
||||
|
||||
final LoanRescheduleRequestTimelineData timeline = new LoanRescheduleRequestTimelineData(submittedOnDate,
|
||||
submittedByUsername, submittedByFirstname, submittedByLastname, approvedOnDate, approvedByUsername,
|
||||
approvedByFirstname, approvedByLastname, rejectedOnDate, rejectedByUsername, rejectedByFirstname,
|
||||
rejectedByLastname);
|
||||
|
||||
return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, graceOnPrincipal,
|
||||
graceOnInterest, rescheduleFromDate, adjustedDueDate, extraTerms, interestRate, rescheduleReasonCodeValue,
|
||||
rescheduleReasonComment, timeline, clientName, loanAccountNumber, clientId, recalculateInterest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanRescheduleRequestData> readLoanRescheduleRequests(Long loanId) {
|
||||
final Loan loan = this.loanRepository.findOne(loanId);
|
||||
|
||||
if(loan == null) {
|
||||
throw new LoanNotFoundException(loanId);
|
||||
}
|
||||
|
||||
final String sql = "select " + this.loanRescheduleRequestRowMapper.schema() + " where lr.loan_id = ?";
|
||||
|
||||
return this.jdbcTemplate.query(sql, this.loanRescheduleRequestRowMapper, new Object[] { loanId });
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanRescheduleRequestData readLoanRescheduleRequest(Long requestId) {
|
||||
|
||||
try {
|
||||
final String sql = "select " + this.loanRescheduleRequestRowMapper.schema() + " where lr.id = ?";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, this.loanRescheduleRequestRowMapper, new Object[] { requestId });
|
||||
}
|
||||
|
||||
catch(final EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanRescheduleRequestData> readLoanRescheduleRequests(Long loanId, Integer statusEnum) {
|
||||
final Loan loan = this.loanRepository.findOne(loanId);
|
||||
|
||||
if(loan == null) {
|
||||
throw new LoanNotFoundException(loanId);
|
||||
}
|
||||
|
||||
final String sql = "select " + this.loanRescheduleRequestRowMapper.schema() + " where lr.loan_id = ?"
|
||||
+ " and lr.status_enum = ?";
|
||||
|
||||
return this.jdbcTemplate.query(sql, this.loanRescheduleRequestRowMapper, new Object[] { loanId, statusEnum });
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 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.rescheduleloan.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface LoanRescheduleRequestWritePlatformService {
|
||||
|
||||
CommandProcessingResult create(JsonCommand jsonCommand);
|
||||
|
||||
CommandProcessingResult approve(JsonCommand jsonCommand);
|
||||
|
||||
CommandProcessingResult reject(JsonCommand jsonCommand);
|
||||
}
|
||||
+479
@@ -0,0 +1,479 @@
|
||||
/**
|
||||
* 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.rescheduleloan.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
|
||||
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
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.core.exception.PlatformDataIntegrityException;
|
||||
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;
|
||||
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.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
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.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRepaymentScheduleHistoryRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
|
||||
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
private final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator;
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestWritePlatformServiceImpl constructor
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
@Autowired
|
||||
public LoanRescheduleRequestWritePlatformServiceImpl(LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
CodeValueRepositoryWrapper codeValueRepositoryWrapper,
|
||||
PlatformSecurityContext platformSecurityContext,
|
||||
LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
|
||||
LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
ConfigurationDomainService configurationDomainService,
|
||||
HolidayRepository holidayRepository,
|
||||
WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository) {
|
||||
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
||||
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
|
||||
this.platformSecurityContext = platformSecurityContext;
|
||||
this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object
|
||||
* and persist
|
||||
*
|
||||
* @return CommandProcessingResult object
|
||||
**/
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult create(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
// get the loan id from the JsonCommand object
|
||||
final Long loanId = jsonCommand.longValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.loanIdParamName);
|
||||
|
||||
// use the loan id to get a Loan entity object
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
||||
|
||||
// validate the request in the JsonCommand object passed as parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForCreateAction(jsonCommand, loan);
|
||||
|
||||
// get the reschedule reason code value id from the JsonCommand object
|
||||
final Long rescheduleReasonId = jsonCommand.longValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.rescheduleReasonIdParamName);
|
||||
|
||||
// use the reschedule reason code value id to get a CodeValue entity object
|
||||
final CodeValue rescheduleReasonCodeValue = this.codeValueRepositoryWrapper
|
||||
.findOneWithNotFoundDetection(rescheduleReasonId);
|
||||
|
||||
// get the grace on principal integer value from the JsonCommand object
|
||||
final Integer graceOnPrincipal = jsonCommand.integerValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.graceOnPrincipalParamName);
|
||||
|
||||
// get the grace on interest integer value from the JsonCommand object
|
||||
final Integer graceOnInterest = jsonCommand.integerValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.graceOnInterestParamName);
|
||||
|
||||
// get the extra terms to be added at the end of the new schedule from the JsonCommand object
|
||||
final Integer extraTerms = jsonCommand.integerValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.extraTermsParamName);
|
||||
|
||||
// get the new interest rate that would be applied to the new loan schedule
|
||||
final BigDecimal interestRate = jsonCommand.bigDecimalValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.newInterestRateParamName);
|
||||
|
||||
// get the reschedule reason comment text from the JsonCommand object
|
||||
final String rescheduleReasonComment = jsonCommand.stringValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.rescheduleReasonCommentParamName);
|
||||
|
||||
// get the recalculate interest option
|
||||
final Boolean recalculateInterest = jsonCommand.booleanObjectValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.recalculateInterestParamName);
|
||||
|
||||
// initialize set the value to null
|
||||
Date submittedOnDate = null;
|
||||
|
||||
// check if the parameter is in the JsonCommand object
|
||||
if(jsonCommand.hasParameter(RescheduleLoansApiConstants.submittedOnDateParamName)) {
|
||||
// create a LocalDate object from the "submittedOnDate" Date string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.submittedOnDateParamName);
|
||||
|
||||
if(localDate != null) {
|
||||
// update the value of the "submittedOnDate" variable
|
||||
submittedOnDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
// initially set the value to null
|
||||
Date rescheduleFromDate = null;
|
||||
|
||||
// start point of the rescheduling exercise
|
||||
Integer rescheduleFromInstallment = null;
|
||||
|
||||
// initially set the value to null
|
||||
Date adjustedDueDate = null;
|
||||
|
||||
// check if the parameter is in the JsonCommand object
|
||||
if(jsonCommand.hasParameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)) {
|
||||
// create a LocalDate object from the "rescheduleFromDate" Date string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.rescheduleFromDateParamName);
|
||||
|
||||
if(localDate != null) {
|
||||
// get installment by due date
|
||||
LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(localDate);
|
||||
rescheduleFromInstallment = installment.getInstallmentNumber();
|
||||
|
||||
// update the value of the "rescheduleFromDate" variable
|
||||
rescheduleFromDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
if(jsonCommand.hasParameter(RescheduleLoansApiConstants.adjustedDueDateParamName)) {
|
||||
// create a LocalDate object from the "adjustedDueDate" Date string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.adjustedDueDateParamName);
|
||||
|
||||
if(localDate != null) {
|
||||
// update the value of the "adjustedDueDate"variable
|
||||
adjustedDueDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = LoanRescheduleRequest.instance(
|
||||
loan, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), rescheduleFromInstallment, graceOnPrincipal,
|
||||
graceOnInterest, rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue,
|
||||
rescheduleReasonComment, submittedOnDate, this.platformSecurityContext.authenticatedUser(),
|
||||
null, null, null, null);
|
||||
|
||||
// create a new entry in the m_loan_reschedule_request table
|
||||
this.loanRescheduleRequestRepository.save(loanRescheduleRequest);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(jsonCommand.commandId())
|
||||
.withEntityId(loanRescheduleRequest.getId())
|
||||
.withLoanId(loan.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
catch(final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleCreateActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult approve(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
final Long loanRescheduleRequestId = jsonCommand.entityId();
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(loanRescheduleRequestId);
|
||||
|
||||
if(loanRescheduleRequest == null) {
|
||||
throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId);
|
||||
}
|
||||
|
||||
// validate the request in the JsonCommand object passed as parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForApproveAction(jsonCommand, loanRescheduleRequest);
|
||||
|
||||
final AppUser appUser = this.platformSecurityContext.authenticatedUser();
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
|
||||
LocalDate approvedOnDate = jsonCommand.localDateValueOfParameterNamed("approvedOnDate");
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat
|
||||
.forPattern(jsonCommand.dateFormat())
|
||||
.withLocale(jsonCommand.extractLocale());
|
||||
|
||||
changes.put("locale", jsonCommand.locale());
|
||||
changes.put("dateFormat", jsonCommand.dateFormat());
|
||||
changes.put("approvedOnDate", approvedOnDate.toString(dateTimeFormatter));
|
||||
changes.put("approvedByUserId", appUser.getId());
|
||||
|
||||
if(!changes.isEmpty()) {
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||
loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
|
||||
for(LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if(period.isNew()) {
|
||||
LoanRepaymentScheduleInstallment repaymentScheduleInstallment = new LoanRepaymentScheduleInstallment(
|
||||
loan, period.periodNumber(), period.periodFromDate(), period.periodDueDate(), period.principalDue(),
|
||||
period.interestDue(), BigDecimal.ZERO, BigDecimal.ZERO, false);
|
||||
|
||||
repaymentScheduleInstallments.add(repaymentScheduleInstallment);
|
||||
}
|
||||
|
||||
else {
|
||||
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
|
||||
if(repaymentScheduleInstallment.getInstallmentNumber().equals(period.oldPeriodNumber())) {
|
||||
|
||||
repaymentScheduleInstallment.updateInstallmentNumber(period.periodNumber());
|
||||
repaymentScheduleInstallment.updateFromDate(period.periodFromDate());
|
||||
repaymentScheduleInstallment.updateDueDate(period.periodDueDate());
|
||||
repaymentScheduleInstallment.updatePrincipal(period.principalDue());
|
||||
repaymentScheduleInstallment.updateInterestCharged(period.interestDue());
|
||||
|
||||
if(Money.of(currency, period.principalDue()).isZero() &&
|
||||
Money.of(currency, period.interestDue()).isZero() &&
|
||||
repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isZero() &&
|
||||
repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isZero() &&
|
||||
repaymentScheduleInstallment.isNotFullyPaidOff()) {
|
||||
|
||||
repaymentScheduleInstallment.updateObligationMet(true);
|
||||
repaymentScheduleInstallment.updateObligationMetOnDate(new LocalDate());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = loanRescheduleModel.getOldPeriods();
|
||||
|
||||
for(LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
|
||||
this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
|
||||
}
|
||||
|
||||
loan.updateRescheduledByUser(appUser);
|
||||
loan.updateRescheduledOnDate(new LocalDate());
|
||||
|
||||
// update the Loan summary
|
||||
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(),
|
||||
true);
|
||||
|
||||
// update the total number of schedule repayments
|
||||
loan.updateNumberOfRepayments(periods.size());
|
||||
|
||||
// update the loan term frequency (loan term frequency = number of repayments)
|
||||
loan.updateTermFrequency(periods.size());
|
||||
|
||||
// update the status of the request
|
||||
loanRescheduleRequest.approve(appUser, approvedOnDate);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(jsonCommand.commandId())
|
||||
.withEntityId(loanRescheduleRequestId)
|
||||
.withLoanId(loanRescheduleRequest.getLoan().getId())
|
||||
.with(changes)
|
||||
.build();
|
||||
}
|
||||
|
||||
catch(final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleApproveActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult reject(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
final Long loanRescheduleRequestId = jsonCommand.entityId();
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = loanRescheduleRequestRepository
|
||||
.findOne(loanRescheduleRequestId);
|
||||
|
||||
if(loanRescheduleRequest == null) {
|
||||
throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId);
|
||||
}
|
||||
|
||||
// validate the request in the JsonCommand object passed as parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForRejectAction(jsonCommand, loanRescheduleRequest);
|
||||
|
||||
final AppUser appUser = this.platformSecurityContext.authenticatedUser();
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
|
||||
LocalDate rejectedOnDate = jsonCommand.localDateValueOfParameterNamed("rejectedOnDate");
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat
|
||||
.forPattern(jsonCommand.dateFormat())
|
||||
.withLocale(jsonCommand.extractLocale());
|
||||
|
||||
changes.put("locale", jsonCommand.locale());
|
||||
changes.put("dateFormat", jsonCommand.dateFormat());
|
||||
changes.put("rejectedOnDate", rejectedOnDate.toString(dateTimeFormatter));
|
||||
changes.put("rejectedByUserId", appUser.getId());
|
||||
|
||||
if(!changes.isEmpty()) {
|
||||
loanRescheduleRequest.reject(appUser, rejectedOnDate);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(jsonCommand.commandId())
|
||||
.withEntityId(loanRescheduleRequestId)
|
||||
.withLoanId(loanRescheduleRequest.getLoan().getId())
|
||||
.with(changes)
|
||||
.build();
|
||||
}
|
||||
|
||||
catch(final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleRejectActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the create method
|
||||
*
|
||||
* @param jsonCommand JSON command object
|
||||
* @param dve data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleCreateActionDataIntegrityViolation(final JsonCommand jsonCommand,
|
||||
final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the reject method
|
||||
*
|
||||
* @param jsonCommand JSON command object
|
||||
* @param dve data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleRejectActionDataIntegrityViolation(final JsonCommand jsonCommand,
|
||||
final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the approve method
|
||||
*
|
||||
* @param jsonCommand JSON command object
|
||||
* @param dve data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleApproveActionDataIntegrityViolation(final JsonCommand jsonCommand,
|
||||
final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
}
|
||||
+30
@@ -5,6 +5,10 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
/**
|
||||
@@ -13,9 +17,35 @@ import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
*/
|
||||
public interface LoanProductMinimumRepaymentScheduleRelatedDetail {
|
||||
|
||||
MonetaryCurrency getCurrency();
|
||||
|
||||
Money getPrincipal();
|
||||
|
||||
Integer graceOnInterestCharged();
|
||||
|
||||
Integer graceOnInterestPayment();
|
||||
|
||||
Integer graceOnPrincipalPayment();
|
||||
|
||||
Money getInArrearsTolerance();
|
||||
|
||||
BigDecimal getNominalInterestRatePerPeriod();
|
||||
|
||||
PeriodFrequencyType getInterestPeriodFrequencyType();
|
||||
|
||||
BigDecimal getAnnualNominalInterestRate();
|
||||
|
||||
InterestMethod getInterestMethod();
|
||||
|
||||
InterestCalculationPeriodMethod getInterestCalculationPeriodMethod();
|
||||
|
||||
Integer getRepayEvery();
|
||||
|
||||
PeriodFrequencyType getRepaymentPeriodFrequencyType();
|
||||
|
||||
Integer getNumberOfRepayments();
|
||||
|
||||
AmortizationMethod getAmortizationMethod();
|
||||
|
||||
Integer getGraceOnDueDate();
|
||||
}
|
||||
|
||||
+33
-16
@@ -169,51 +169,62 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
return defaultTo;
|
||||
}
|
||||
|
||||
public MonetaryCurrency getCurrency() {
|
||||
@Override
|
||||
public MonetaryCurrency getCurrency() {
|
||||
return this.currency.copy();
|
||||
}
|
||||
|
||||
public Money getPrincipal() {
|
||||
@Override
|
||||
public Money getPrincipal() {
|
||||
return Money.of(this.currency, this.principal);
|
||||
}
|
||||
|
||||
public void setPrincipal(BigDecimal principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public Integer graceOnInterestCharged() {
|
||||
|
||||
@Override
|
||||
public Integer graceOnInterestCharged() {
|
||||
return this.graceOnInterestCharged;
|
||||
}
|
||||
|
||||
public Integer graceOnInterestPayment() {
|
||||
|
||||
@Override
|
||||
public Integer graceOnInterestPayment() {
|
||||
return this.graceOnInterestPayment;
|
||||
}
|
||||
|
||||
public Integer graceOnPrincipalPayment() {
|
||||
|
||||
@Override
|
||||
public Integer graceOnPrincipalPayment() {
|
||||
return this.graceOnPrincipalPayment;
|
||||
}
|
||||
|
||||
public Money getInArrearsTolerance() {
|
||||
@Override
|
||||
public Money getInArrearsTolerance() {
|
||||
return Money.of(this.currency, this.inArrearsTolerance);
|
||||
}
|
||||
|
||||
public BigDecimal getNominalInterestRatePerPeriod() {
|
||||
@Override
|
||||
public BigDecimal getNominalInterestRatePerPeriod() {
|
||||
return BigDecimal.valueOf(Double.valueOf(this.nominalInterestRatePerPeriod.stripTrailingZeros().toString()));
|
||||
}
|
||||
|
||||
public PeriodFrequencyType getInterestPeriodFrequencyType() {
|
||||
@Override
|
||||
public PeriodFrequencyType getInterestPeriodFrequencyType() {
|
||||
return this.interestPeriodFrequencyType;
|
||||
}
|
||||
|
||||
public BigDecimal getAnnualNominalInterestRate() {
|
||||
@Override
|
||||
public BigDecimal getAnnualNominalInterestRate() {
|
||||
return BigDecimal.valueOf(Double.valueOf(this.annualNominalInterestRate.stripTrailingZeros().toString()));
|
||||
}
|
||||
|
||||
public InterestMethod getInterestMethod() {
|
||||
@Override
|
||||
public InterestMethod getInterestMethod() {
|
||||
return this.interestMethod;
|
||||
}
|
||||
|
||||
public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() {
|
||||
@Override
|
||||
public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() {
|
||||
return this.interestCalculationPeriodMethod;
|
||||
}
|
||||
|
||||
@@ -232,7 +243,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
return this.numberOfRepayments;
|
||||
}
|
||||
|
||||
public AmortizationMethod getAmortizationMethod() {
|
||||
@Override
|
||||
public AmortizationMethod getAmortizationMethod() {
|
||||
return this.amortizationMethod;
|
||||
}
|
||||
|
||||
@@ -466,7 +478,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
this.interestPeriodFrequencyType = interestPeriodFrequencyType;
|
||||
}
|
||||
|
||||
public Integer getGraceOnDueDate() {
|
||||
@Override
|
||||
public Integer getGraceOnDueDate() {
|
||||
return this.graceOnArrearsAgeing;
|
||||
}
|
||||
|
||||
@@ -485,4 +498,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
public void updateIsInterestRecalculationEnabled(final boolean isInterestRecalculationEnabled) {
|
||||
this.isInterestRecalculationEnabled = isInterestRecalculationEnabled;
|
||||
}
|
||||
|
||||
public void updateNumberOfRepayments(Integer numberOfRepayments) {
|
||||
this.numberOfRepayments = numberOfRepayments;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user