diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 3aac0da3c..e69f76042 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -1041,6 +1041,46 @@
Update a Collateral |
Delete a Collateral |
+
+ | Loan Rescheduling |
+ rescheduleloans |
+ Create new loan reschedule request |
+ |
+ |
+ |
+
+
+ |
+ rescheduleloans/{requestId} |
+ |
+ Retrieve a Loan Reschedule Request |
+ |
+ |
+
+
+ |
+ rescheduleloans/{requestId}?command=previewLoanReschedule |
+ |
+ Retrieve a Preview of The New Loan Repayment Schedule |
+ |
+ |
+
+
+ |
+ rescheduleloans/{requestId}?command=reject |
+ Reject Loan Reschedule Request |
+ |
+ |
+ |
+
+
+ |
+ rescheduleloans/{requestId}?command=approve |
+ Approve Loan Reschedule Request |
+ |
+ |
+ |
+
@@ -9860,6 +9900,382 @@ No Request Body:
+
+
+
+
+
+
Loan Rescheduling
+
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.
+
+
+ Field Descriptions |
+
+
+ | loanId |
+
+
+ | The ID of the loan to be rescheduled. |
+
+
+ | rescheduleFromDate |
+
+
+ | Due date of the start point (installment) of the rescheduling. |
+
+
+ | graceOnPrincipal |
+
+
+ | Number of installments to be added with zero principal. |
+
+
+ | graceOnInterest |
+
+
+ | Number of installments to be added with zero interest. |
+
+
+ | extraTerms |
+
+
+ | Number of installments to be added after the last loan repayment schedule installment. |
+
+
+ | recalculateInterest |
+
+
+ | If recalculateInterest="true", the total interest amount of the loan is recalculated. If recalculateInterest="false", the total interest amount remains the same. |
+
+
+ | newInterestRate |
+
+
+ | New interest rate to be used in calculating the interest amount for each rescheduled period/installment. |
+
+
+ | adjustedDueDate |
+
+
+ | New due date for the start point (installment) of the rescheduling. |
+
+
+ | rescheduleReasonId |
+
+
+ | The ID of the code value that indicates the reason for rescheduling the loan. |
+
+
+ | rescheduleReasonComment |
+
+
+ | Text provided as extra reason for the rescheduling of the loan. |
+
+
+ | submittedOnDate |
+
+
+ | The date on which the loan reschedule request was made. |
+
+
+
+
+
+
+
+
+
Create a Loan Reschedule Request
+
+
+ Mandatory Fields |
+
+
+ loanId, rescheduleFromDate, rescheduleReasonId, submittedOnDate,
+ graceOnPrincipal OR graceOnInterest OR extraTerms OR newInterestRate OR adjustedDueDate
+ |
+
+
+
+
+
+ Optional Fields |
+
+
+ | recalculateInterest, rescheduleReasonComment |
+
+
+
+
+
+POST https://DomainName/api/v1/rescheduleloans
+
+
+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
+}
+
+
+{
+ "loanId": 1,
+ "resourceId": 1
+}
+
+
+
+
+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
+}
+
+
+{
+ "loanId": 1,
+ "resourceId": 1
+}
+
+
+
+
+
+
+
Retrieve a Loan Reschedule Request
+
Example Requests:
+
rescheduleloans/1
+
+
+
+GET https://DomainName/api/v1/rescheduleloans/{requestId}
+
+
+{
+ "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": ""
+}
+
+
+
+
+
+
+
Retrieve a Preview of The New Loan Repayment Schedule
+
Example Requests:
+
rescheduleloans/1?command=previewLoanReschedule
+
+
+
+GET https://DomainName/api/v1/rescheduleloans/{requestId}?command=previewLoanReschedule
+
+
+
+ "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
+ }
+ ]
+}
+
+
+
+
+
+
+
Reject a Loan Reschedule Request
+
+
+ POST https://DomainName/api/v1/rescheduleloans/{requestId}?command=reject
+ POST rescheduleloans/1?command=reject
+Content-Type: application/json
+Request Body:
+{
+ "locale": "en",
+ "dateFormat": "dd MMMM yyyy",
+ "rejectedOnDate": "11 September 2014"
+}
+
+
+{
+ "loanId": 1,
+ "resourceId": 1,
+ "changes": {
+ "locale": "en",
+ "dateFormat": "dd MMMM yyyy",
+ "rejectedOnDate": "11 September 2014",
+ "rejectedByUserId": 1
+ }
+}
+
+
+
+
+
+
+
Approve a Loan Reschedule Request
+
Rescheduling of a loan happens once a loan reschedule request is approved.
+
+
+ POST https://DomainName/api/v1/rescheduleloans/{requestId}?command=approve
+ POST rescheduleloans/1?command=approve
+Content-Type: application/json
+Request Body:
+{
+ "locale": "en",
+ "dateFormat": "dd MMMM yyyy",
+ "approvedOnDate": "11 September 2014"
+}
+
+
+{
+ "loanId": 1,
+ "resourceId": 1,
+ "changes": {
+ "locale": "en",
+ "dateFormat": "dd MMMM yyyy",
+ "approvedOnDate": "11 September 2014",
+ "approvedByUserId": 1
+ }
+}
+
+
+
@@ -32215,6 +32631,46 @@ No Request Body:
Update a Collateral |
Delete a Collateral |
+
+ | Loan Rescheduling |
+ rescheduleloans |
+ Create a Loan Reschedule Request |
+ |
+ |
+ |
+
+
+ |
+ rescheduleloans/{requestId} |
+ |
+ Retrieve a Loan Reschedule Request |
+ |
+ |
+
+
+ |
+ rescheduleloans/{requestId}?command=previewLoanReschedule |
+ |
+ Retrieve a Preview of The New Loan Repayment Schedule |
+ |
+ |
+
+
+ |
+ rescheduleloans/{requestId}?command=reject |
+ Reject Loan Reschedule Request |
+ |
+ |
+ |
+
+
+ |
+ rescheduleloans/{requestId}?command=approve |
+ Approve Loan Reschedule Request |
+ |
+ |
+ |
+
| General Ledger Account |
glaccounts |
diff --git a/mifosng-db/migrations/core_db/V178_2__loan_rescheduling_tables_and_permissions.sql b/mifosng-db/migrations/core_db/V178_2__loan_rescheduling_tables_and_permissions.sql
new file mode 100644
index 000000000..95ea664ab
--- /dev/null
+++ b/mifosng-db/migrations/core_db/V178_2__loan_rescheduling_tables_and_permissions.sql
@@ -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');
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanRescheduleRequestTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanRescheduleRequestTest.java
new file mode 100644
index 000000000..502736eca
--- /dev/null
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanRescheduleRequestTest.java
@@ -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 + ")");
+ }
+}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/BatchHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/BatchHelper.java
index 74ffa6d31..1fc973b65 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/BatchHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/BatchHelper.java
@@ -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);
}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ClientHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ClientHelper.java
index 1defecfef..d0b51fc74 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ClientHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ClientHelper.java
@@ -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);
}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GlobalConfigurationHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GlobalConfigurationHelper.java
index 6e4f4ae8f..c603b373c 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GlobalConfigurationHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GlobalConfigurationHelper.java
@@ -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 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");
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java
index 2d64a4de2..f7d5fa2c2 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java
@@ -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 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 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 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()));
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/HolidayHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/HolidayHelper.java
index 63a0c2545..ef6ddd972 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/HolidayHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/HolidayHelper.java
@@ -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;
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/LoanRescheduleRequestHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/LoanRescheduleRequestHelper.java
new file mode 100644
index 000000000..812bd1736
--- /dev/null
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/LoanRescheduleRequestHelper.java
@@ -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);
+ }
+}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/SchedulerJobHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/SchedulerJobHelper.java
index ac62fee84..331d49603 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/SchedulerJobHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/SchedulerJobHelper.java
@@ -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);
}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/SurveyHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/SurveyHelper.java
index ca5b14b00..f5af9830f 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/SurveyHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/SurveyHelper.java
@@ -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);
}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/accounting/AccountHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/accounting/AccountHelper.java
index 757ba51f8..5a9e69978 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/accounting/AccountHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/accounting/AccountHelper.java
@@ -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;
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java
index 8b8dfaefb..553e66b07 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java
@@ -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 disbursementData = null;
private List charges = new ArrayList();
@@ -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 disbursementData) {
this.disbursementData = disbursementData;
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanRescheduleRequestTestBuilder.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanRescheduleRequestTestBuilder.java
new file mode 100644
index 000000000..a5e112b4e
--- /dev/null
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanRescheduleRequestTestBuilder.java
@@ -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 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 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 map = new HashMap<>();
+ map.put("locale", "en");
+ map.put("dateFormat", "dd MMMM yyyy");
+ map.put("approvedOnDate", submittedOnDate);
+ return new Gson().toJson(map);
+ }
+}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanStatusChecker.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanStatusChecker.java
index 7703818ea..0c7bc5ac7 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanStatusChecker.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanStatusChecker.java
@@ -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");
}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java
index a9dd88220..a44589215 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java
@@ -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) {
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/xbrl/XBRLIntegrationTestHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/xbrl/XBRLIntegrationTestHelper.java
index f83f7589b..117ffe6d7 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/xbrl/XBRLIntegrationTestHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/xbrl/XBRLIntegrationTestHelper.java
@@ -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;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
index a8e37ef92..ca44abcfe 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
@@ -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");
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
index c26deb0db..6334373b6 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
@@ -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;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
index 4b61b29bc..6bc24b2b2 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
@@ -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());
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
index 5503a487f..34a1e6b77 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
@@ -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 {
@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 {
return this.isNpa;
}
+ /**
+ * @return List of loan repayments schedule objects
+ **/
+ public List 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 getDisbursmentData() {
+ Iterator iterator = this.getDisbursementDetails().iterator();
+ List disbursementData = new ArrayList();
+
+ 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 = getDisbursmentData();
+
+ final List loanVariationTermsData = new ArrayList();
+ 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;
+ }
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanDisbursementDetails.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanDisbursementDetails.java
index 68c8252e3..99b387168 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanDisbursementDetails.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanDisbursementDetails.java
@@ -84,6 +84,10 @@ public class LoanDisbursementDetails extends AbstractPersistable {
public BigDecimal principal() {
return this.principal;
}
+
+ public BigDecimal approvedPrincipal() {
+ return this.approvedPrincipal;
+ }
public void updatePrincipal(BigDecimal principal) {
this.principal = principal;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
index 73cfde635..1861ed2fc 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
@@ -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 installmentNumberComparator
+ = new Comparator() {
+
+ @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;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java
index 442a99f13..2e04f05f5 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java
@@ -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;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index ce0c66592..0f18580fe 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -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 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 loanRepaymentScheduleHistoryList = createLoanScheduleArchive(loanRescheduleRequest);
+
+ // get the initial list of repayment installments
+ List repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
+
+ // sort list by installment number in ASC order
+ Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator);
+
+ final Collection periods = new ArrayList();
+
+ 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 createLoanScheduleArchive(final LoanRescheduleRequest loanRescheduleRequest) {
+ final Loan loan = loanRescheduleRequest.getLoan();
+ final List repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
+ final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
+ final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
+ List loanRepaymentScheduleHistoryList = new ArrayList();
+
+ 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;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
index 8aa9c6c00..17587859b 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -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 {
* Note: 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.
*
*/
- 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 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;
+ }
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
index 7d819ad1b..4450461ee 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
@@ -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 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 holidays, final WorkingDays workingDays);
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/RescheduleLoansApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/RescheduleLoansApiConstants.java
new file mode 100644
index 000000000..8866d46a0
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/RescheduleLoansApiConstants.java
@@ -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 CREATE_REQUEST_DATA_PARAMETERS = new HashSet(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 REJECT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName,
+ dateFormatParamName, rejectedOnDateParam));
+
+ // approve action request parameters
+ public static final String approvedOnDateParam = "approvedOnDate";
+
+ public static final Set APPROVE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName,
+ dateFormatParamName, approvedOnDateParam));
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/api/RescheduleLoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/api/RescheduleLoansApiResource.java
new file mode 100644
index 000000000..cf3c53150
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/api/RescheduleLoansApiResource.java
@@ -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 loanRescheduleRequestToApiJsonSerializer;
+ private final DefaultToApiJsonSerializer 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 loanRescheduleRequestToApiJsonSerializer,
+ final PlatformSecurityContext platformSecurityContext,
+ final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
+ final LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService,
+ final ApiRequestParameterHelper apiRequestParameterHelper,
+ final DefaultToApiJsonSerializer 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());
+ }
+
+ 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);
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestData.java
new file mode 100644
index 000000000..e0e4d2eba
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestData.java
@@ -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;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java
new file mode 100644
index 000000000..a3869bd55
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java
@@ -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