MIFOSX-213: support pending, active, closed status for clients.

This commit is contained in:
Keith Woodlock
2013-04-17 13:05:24 +01:00
parent 3001cc3582
commit 1180200ca3
61 changed files with 1596 additions and 1256 deletions
+280 -147
View File
@@ -176,6 +176,14 @@
<td></td> <td></td>
<td><a href="#clients_retrieve">Retrieve a Client</a></td> <td><a href="#clients_retrieve">Retrieve a Client</a></td>
<td><a href="#clients_update">Update a Client</a></td> <td><a href="#clients_update">Update a Client</a></td>
<td><a href="#clients_delete">Delete a Client</a></td>
</tr>
<tr class="alt">
<td></td>
<td>clients/{clientId}?command=activate</td>
<td><a href="#clients_activate">Activate a Client</a></td>
<td></td>
<td></td>
<td></td> <td></td>
</tr> </tr>
<tr class="alt"> <tr class="alt">
@@ -1697,8 +1705,8 @@ function executeAjaxRequest(url, verbType, jsonData, basicAuthKey, successFuncti
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h3>Clients</h3> <h3>Clients</h3>
<p>Clients are people and businesses that have applied (or may <p>Clients are people and businesses that have applied (or may apply) to an MFI for loans.</p>
apply) to an MFI for loans.</p> <p>Clients can be created in <b>Pending</b> state (as opposed to <b>Active</b> state) if global configuration for <i>allow-pending-client-status</i> is enabled.</p>
<table class=matrixHeading> <table class=matrixHeading>
<tr class="matrixHeadingBG"> <tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td> <td><div class="mifosXHeading2">Field Descriptions</div></td>
@@ -1712,6 +1720,29 @@ function executeAjaxRequest(url, verbType, jsonData, basicAuthKey, successFuncti
auto generated account no. is put in place based on the auto generated account no. is put in place based on the
configured strategy.</td> configured strategy.</td>
</tr> </tr>
<tr class=alt>
<td>externalId</td>
</tr>
<tr>
<td class=fielddesc>A place to put an external reference for
this client e.g. The ID another system uses.<br> If
provided, it must be unique.
</td>
</tr>
<tr class=alt>
<td>active</td>
</tr>
<tr>
<td class=fielddesc>Indicates whether this client is to be created as <b>active</b> client. If active=true, then <b>activationDate</b> must be provided. If active=false, then the client is created as <b>pending</b> but this must be allowed through configuration.
</td>
</tr>
<tr class=alt>
<td>activationDate</td>
</tr>
<tr>
<td class=fielddesc>The date on which the client became <b>active</b>.
</td>
</tr>
<tr class=alt> <tr class=alt>
<td>firstname</td> <td>firstname</td>
</tr> </tr>
@@ -1741,63 +1772,11 @@ function executeAjaxRequest(url, verbType, jsonData, basicAuthKey, successFuncti
business that doesnt suit the firstname,middlename,lastname business that doesnt suit the firstname,middlename,lastname
structure.</td> structure.</td>
</tr> </tr>
<tr class=alt>
<td>externalId</td>
</tr>
<tr>
<td class=fielddesc>A place to put an external reference for
this client e.g. The ID another system uses.<br> If
provided, it must be unique.
</td>
</tr>
</table> </table>
</div> </div>
</div> </div>
<a id="client_list" name="clients_list" class="old-syle-anchor">&nbsp;</a> <a id="clients_template" name="clients_template" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>List Clients</h4>
<p>Example Requests:</p>
<div class=apiClick>clients</div>
<br>
<br>
<div class=apiClick>clients?fields=displayName,officeName,joinedDate</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/clients
</code>
<code class="method-response">
[
{
"id": 2,
"accountNo": "000000002",
"externalId": "one/78",
"fullname": "New Business to Create",
"displayName": "New Business to Create",
"officeId": 1,
"officeName": "Head Office",
"joinedDate": [2006,6,13]
},
{
"id": 1,
"accountNo": "000000001",
"externalId": "786YYH7",
"firstname": "Petra",
"lastname": "Yton",
"displayName": "Petra Yton",
"officeId": 1,
"officeName": "Head Office",
"joinedDate": [2009,3,4]
}
]
</code>
</div>
</div>
<a id="clients_template" name="clients_template"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h4>Retrieve Client Details Template</h4> <h4>Retrieve Client Details Template</h4>
@@ -1819,58 +1798,20 @@ GET https://DomainName/api/v1/clients/template
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"officeId": 1, "activationDate": [
"joinedDate": [ 2013,
2013, 4,
2, 17
5 ],
], "officeId": 1,
"allowedOffices": [ "clientPendingApprovalAllowed": false,
{ "officeOptions": [
"id": 1, {
"name": "Head Office", "id": 1,
"nameDecorated": "Head Office" "name": "Head Office",
} "nameDecorated": "Head Office"
] }
} ]
</code>
</div>
</div>
<a id="clients_retrieve" name="clients_retrieve"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve a Client</h4>
<p>Example Requests:</p>
<div class=apiClick>clients/1</div>
<br>
<br>
<div class=apiClick>clients/1?template=true</div>
<br>
<br>
<div class=apiClick>clients/1?fields=id,displayName,officeName</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/clients/{clientId}
</code>
<code class="method-response">
{
"id": 1,
"accountNo": "000000001",
"externalId": "786YYH7",
"firstname": "Petra",
"lastname": "Yton",
"displayName": "Petra Yton",
"officeId": 1,
"officeName": "Head Office",
"joinedDate": [
2009,
3,
4
]
} }
</code> </code>
</div> </div>
@@ -1891,11 +1832,19 @@ GET https://DomainName/api/v1/clients/{clientId}
<td><div class="mifosXHeading2">Mandatory Fields</div></td> <td><div class="mifosXHeading2">Mandatory Fields</div></td>
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>firstname and lastname OR fullname<br>, joinedDate, <td>firstname and lastname OR fullname,<br> officeId,<br> active=true and activationDate OR active=false,<br>
officeId
</td> </td>
</tr> </tr>
</table> </table>
<br />
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>groupId, externalId, accountNo</td>
</tr>
</table>
</div> </div>
<div class="method-example"> <div class="method-example">
<code class="method-declaration"> <code class="method-declaration">
@@ -1905,13 +1854,14 @@ POST https://DomainName/api/v1/clients
POST clients POST clients
Content-Type: application/json Request Body: Content-Type: application/json Request Body:
{ {
"officeId": 1, "officeId": 1,
"firstname": "Petra", "firstname": "Petra",
"lastname": "Yton", "lastname": "Yton",
"externalId": "786YYH7", "externalId": "786YYH7",
"dateFormat": "dd MMMM yyyy", "dateFormat": "dd MMMM yyyy",
"locale": "en", "locale": "en",
"joinedDate": "04 March 2009" "active": true,
"activationDate": "04 March 2009"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -1928,28 +1878,164 @@ POST clients
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"officeId": 1, "officeId": 1,
"fullname": "New Business to Create", "fullname": "Client of group",
"externalId": "one/78", "groupId": 1,
"dateFormat": "dd MMMM yyyy", "dateFormat": "dd MMMM yyyy",
"locale": "en", "locale": "en",
"joinedDate": "13 June 2006" "active": true,
"activationDate": "04 March 2009"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"officeId": 1, "officeId": 1,
"clientId": 2, "groupId": 1,
"resourceId": 2 "clientId": 2,
"resourceId": 2
} }
</code> </code>
</div> </div>
</div> </div>
<a id="clients_activate" name="clients_activate" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Activate a Client</h2>
<p>Clients can be created in a <i>Pending</i> state if configuration is enabled. This API exists to enable client activation (for when a client becomes approved member of microfinance institue.</p>
<p>If the client happens to be alrady active this API will result in an error.</p>
</div>
<div class="method-example">
<code class="method-declaration">POST https://Domain Name/api/v1/clients/{clientId}?command=activate</code>
<code class="method-request">POST clients/1?command=activate
Content-Type: application/json
Request Body:
{
"locale": "en",
"dateFormat": "dd MMMM yyyy",
"activationDate": "01 March 2011"
}
</code>
<code class="method-response">
{
"officeId": 1,
"clientId": 1,
"resourceId": 1
}
</code>
</div>
</div>
<a id="clients_retrieve" name="clients_retrieve" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve a Client</h4>
<p>Example Requests:</p>
<div class=apiClick>clients/1</div>
<br>
<br>
<div class=apiClick>clients/1?template=true</div>
<br>
<br>
<div class=apiClick>clients/1?fields=id,displayName,officeName</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/clients/{clientId}
</code>
<code class="method-response">
{
"id": 2,
"accountNo": "000000002",
"status": {
"id": 300,
"code": "clientStatusType.active",
"value": "Active"
},
"activationDate": [
2009,
3,
4
],
"fullname": "Client of group",
"displayName": "Client of group",
"officeId": 1,
"officeName": "Head Office",
"groups": [
{
"id": 1,
"name": "Example Group"
}
]
}
</code>
</div>
</div>
<a id="client_list" name="clients_list" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>List Clients</h4>
<p>Example Requests:</p>
<div class=apiClick>clients</div>
<br>
<br>
<div class=apiClick>clients?fields=displayName,officeName,joinedDate</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/clients
</code>
<code class="method-response">
[
{
"id": 2,
"accountNo": "000000002",
"status": {
"id": 300,
"code": "clientStatusType.active",
"value": "Active"
},
"activationDate": [
2009,
3,
4
],
"fullname": "Client of group",
"displayName": "Client of group",
"officeId": 1,
"officeName": "Head Office"
},
{
"id": 1,
"accountNo": "000000001",
"externalId": "786YYH7",
"status": {
"id": 300,
"code": "clientStatusType.active",
"value": "Active"
},
"activationDate": [
2009,
3,
4
],
"firstname": "Petra",
"lastname": "Yton",
"displayName": "Petra Yton",
"officeId": 1,
"officeName": "Head Office"
}
]
</code>
</div>
</div>
<a id="clients_update" name="clients_update" class="old-syle-anchor">&nbsp;</a> <a id="clients_update" name="clients_update" class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h4>Update a Client</h4> <h4>Update a Client</h4>
<p>You can update any attribute of a client except for its association with <i>groups</i>. The relationship between a client and a group must be removed through the <b>Groups</b> API.</p>
</div> </div>
<div class="method-example"> <div class="method-example">
<code class="method-declaration"> <code class="method-declaration">
@@ -1960,10 +2046,7 @@ PUT clients/1
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"externalId": "786444UUUYYH7", "externalId": "786444UUUYYH7"
"dateFormat": "dd MMMM yyyy",
"locale": "en",
"joinedDate": "09 June 2006"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -1972,18 +2055,41 @@ Request Body:
"clientId": 1, "clientId": 1,
"resourceId": 1, "resourceId": 1,
"changes": { "changes": {
"externalId": "786444UUUYYH7", "externalId": "786444UUUYYH7"
"joinedDate": "09 June 2006",
"dateFormat": "dd MMMM yyyy",
"locale": "en"
} }
} }
</code> </code>
</div> </div>
</div> </div>
<a id="clients_loansummary" name="clients_loansummary" <a id="clients_delete" name="clients_delete" class="old-syle-anchor">&nbsp;</a>
class="old-syle-anchor">&nbsp;</a> <div class="method-section">
<div class="method-description">
<h4>Delete a Client</h4>
<p>If a client is in <i>Pending</i> state you are allowed to <b>Delete</b> it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with it, you cannot delete the client but you may <b>Close</b> the client if they have died or exited the program.</p>
</div>
<div class="method-example">
<code class="method-declaration">
DELETE https://DomainName/api/v1/clients/{clientId}
</code>
<code class="method-request">
DELETE clients/3
Content-Type: application/json
Request Body:
{
}
</code>
<code class="method-response">
{
"officeId": 1,
"clientId": 3,
"resourceId": 3
}
</code>
</div>
</div>
<a id="clients_loansummary" name="clients_loansummary" class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h4>Retrieve a Client's Loan Account Summary</h4> <h4>Retrieve a Client's Loan Account Summary</h4>
@@ -7066,13 +7172,16 @@ Request Body:
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h3>Global Configuration</h3> <h3>Global Configuration</h3>
<p>Global configuration related to set of supported <p>Global configuration related to set of supported enable/disable configurations:</p>
enable/disable configurations e.g. maker-checker.</p> <ol>
<li><b>maker-checker</b> - defaults to false - if true turns on maker-checker functionality</li>
<li><b>allow-pending-client-status</b> - defaults to false - if true allows clients to be created in 'Pending' state</li>
<li><b>allow-pending-group-status</b> - defaults to false - if true allows groups to be created in 'Pending' state</li>
</ol>
</div> </div>
</div> </div>
<a id="configs_globalconfig_retrieve" <a id="configs_globalconfig_retrieve" name="configs_globalconfig_retrieve" class="old-syle-anchor">&nbsp;</a>
name="configs_globalconfig_retrieve" class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h4>Retrieve Global Configuration</h4> <h4>Retrieve Global Configuration</h4>
@@ -7086,12 +7195,20 @@ GET https://DomainName/api/v1/configurations
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"globalConfiguration": [ "globalConfiguration": [
{ {
"name": "maker-checker", "name": "maker-checker",
"enabled": false "enabled": false
} },
] {
"name": "allow-pending-client-status",
"enabled": false
},
{
"name": "allow-pending-group-status",
"enabled": false
}
]
} }
</code> </code>
</div> </div>
@@ -7105,14 +7222,22 @@ GET https://DomainName/api/v1/configurations
</div> </div>
<div class="method-example"> <div class="method-example">
<code class="method-declaration"> <code class="method-declaration">
PUT https://DomainName/api/v1/configurations?maker-checker=false PUT https://DomainName/api/v1/configurations?allow-pending-client-status=false
</code> </code>
<code class="method-request"> <code class="method-request">
PUT configurations PUT configurations
Content-Type: application/json Content-Type: application/json
Request Body: { } Request Body: { }
</code> </code>
<code class="method-response">{ "changes": {} } </code> <code class="method-response">
{
"changes": {
"globalConfiguration": {
"allow-pending-client-status": false
}
}
}
</code>
</div> </div>
</div> </div>
@@ -12884,6 +13009,14 @@ GET https://DomainName/api/v1/search?query=000000111
<td></td> <td></td>
<td><a href="#clients_retrieve">Retrieve a Client</a></td> <td><a href="#clients_retrieve">Retrieve a Client</a></td>
<td><a href="#clients_update">Update a Client</a></td> <td><a href="#clients_update">Update a Client</a></td>
<td><a href="#clients_delete">Delete a Client</a></td>
</tr>
<tr class="alt">
<td></td>
<td>clients/{clientId}?command=activate</td>
<td><a href="#clients_activate">Activate a Client</a></td>
<td></td>
<td></td>
<td></td> <td></td>
</tr> </tr>
<tr class="alt"> <tr class="alt">
@@ -0,0 +1,8 @@
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'ACTIVATE_CLIENT', 'CLIENT', 'ACTIVATE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'ACTIVATE_CLIENT_CHECKER', 'CLIENT', 'ACTIVATE', 0);
ALTER TABLE `m_client`
CHANGE COLUMN `external_id` `external_id` VARCHAR(100) NULL DEFAULT NULL AFTER `account_no`,
CHANGE COLUMN `status_enum` `status_enum` INT(5) NOT NULL DEFAULT '300' AFTER `external_id`,
CHANGE COLUMN `joined_date` `activation_date` DATE NULL DEFAULT NULL AFTER `status_enum`;
@@ -1,29 +1,28 @@
package org.mifosplatform.integrationtests; package org.mifosplatform.integrationtests;
import com.jayway.restassured.builder.RequestSpecBuilder; import java.util.HashMap;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mifosplatform.integrationtests.common.*; import org.mifosplatform.integrationtests.common.ClientHelper;
import org.mifosplatform.integrationtests.common.Utils;
import org.mifosplatform.integrationtests.common.accounting.Account; import org.mifosplatform.integrationtests.common.accounting.Account;
import org.mifosplatform.integrationtests.common.accounting.JournalEntry;
import org.mifosplatform.integrationtests.common.accounting.AccountHelper; import org.mifosplatform.integrationtests.common.accounting.AccountHelper;
import org.mifosplatform.integrationtests.common.accounting.JournalEntry;
import org.mifosplatform.integrationtests.common.accounting.JournalEntryHelper; import org.mifosplatform.integrationtests.common.accounting.JournalEntryHelper;
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder; import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder; import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker; import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker;
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper; import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
import java.util.HashMap; import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import static org.junit.Assert.assertThat; import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
@SuppressWarnings("rawtypes")
public class AccountingScenario { public class AccountingScenario {
private RequestSpecification requestSpec; private RequestSpecification requestSpec;
private ResponseSpecification responseSpec; private ResponseSpecification responseSpec;
@@ -170,12 +169,12 @@ public class AccountingScenario {
System.out.println("Repayment 5 Done ......"); System.out.println("Repayment 5 Done ......");
} }
private Integer createLoanProduct(Account ... accounts) { private Integer createLoanProduct(final Account ... accounts) {
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
String loanProductJSON = new LoanProductTestBuilder().withPrincipal(LP_PRINCIPAL.toString()).withRepaymentTypeAsMonth() String loanProductJSON = new LoanProductTestBuilder().withPrincipal(LP_PRINCIPAL.toString()).withRepaymentTypeAsMonth()
.withRepaymentAfterEvery(LP_REPAYMENT_PERIOD).withNumberOfRepayments(LP_REPAYMENTS).withRepaymentTypeAsMonth() .withRepaymentAfterEvery(LP_REPAYMENT_PERIOD).withNumberOfRepayments(LP_REPAYMENTS).withRepaymentTypeAsMonth()
.withinterestRatePerPeriod(LP_INTEREST_RATE).withInterestRateFrequencyTypeAsMonths() .withinterestRatePerPeriod(LP_INTEREST_RATE).withInterestRateFrequencyTypeAsMonths()
.withAmortizationTypeAsEqualPrinciplePayment().withInterestTypeAsFlat() .withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat()
.withAccountingRuleAsAccrualBased(accounts) .withAccountingRuleAsAccrualBased(accounts)
.build(); .build();
return loanTransactionHelper.getLoanProductId(loanProductJSON); return loanTransactionHelper.getLoanProductId(loanProductJSON);
@@ -1,17 +1,15 @@
package org.mifosplatform.integrationtests; package org.mifosplatform.integrationtests;
//import static org.junit.Assert.assertEquals;
import java.util.HashMap; import java.util.HashMap;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mifosplatform.integrationtests.common.ClientHelper; import org.mifosplatform.integrationtests.common.ClientHelper;
import org.mifosplatform.integrationtests.common.Utils;
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder; import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder; import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker; import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker;
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper; import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
import org.mifosplatform.integrationtests.common.Utils;
import com.jayway.restassured.builder.RequestSpecBuilder; import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder; import com.jayway.restassured.builder.ResponseSpecBuilder;
@@ -19,13 +17,11 @@ import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.specification.RequestSpecification; import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification; import com.jayway.restassured.specification.ResponseSpecification;
import static org.junit.Assert.assertEquals;
/** /**
* Client Loan Integration Test for checking Loan Disbursement with Waive * Client Loan Integration Test for checking Loan Disbursement with Waive
* Interest and Write-Off. * Interest and Write-Off.
*/ */
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes" })
public class LoanWithWaiveInterestAndWriteOffIntegrationTest { public class LoanWithWaiveInterestAndWriteOffIntegrationTest {
private ResponseSpecification responseSpec; private ResponseSpecification responseSpec;
@@ -120,7 +116,8 @@ public class LoanWithWaiveInterestAndWriteOffIntegrationTest {
String loanProductJSON = new LoanProductTestBuilder().withPrincipal(LP_PRINCIPAL).withRepaymentTypeAsMonth() String loanProductJSON = new LoanProductTestBuilder().withPrincipal(LP_PRINCIPAL).withRepaymentTypeAsMonth()
.withRepaymentAfterEvery(LP_REPAYMENT_PERIOD).withNumberOfRepayments(LP_REPAYMENTS).withRepaymentTypeAsMonth() .withRepaymentAfterEvery(LP_REPAYMENT_PERIOD).withNumberOfRepayments(LP_REPAYMENTS).withRepaymentTypeAsMonth()
.withinterestRatePerPeriod(LP_INTEREST_RATE).withInterestRateFrequencyTypeAsMonths() .withinterestRatePerPeriod(LP_INTEREST_RATE).withInterestRateFrequencyTypeAsMonths()
.withAmortizationTypeAsEqualPrinciplePayment().withInterestTypeAsFlat().build(); .withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().build();
return loanTransactionHelper.getLoanProductId(loanProductJSON); return loanTransactionHelper.getLoanProductId(loanProductJSON);
} }
@@ -1,22 +1,25 @@
package org.mifosplatform.integrationtests; package org.mifosplatform.integrationtests;
import java.util.HashMap;
import org.junit.Before;
import org.junit.Test;
import org.mifosplatform.integrationtests.common.ClientHelper;
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.LoanStatusChecker;
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
import com.jayway.restassured.builder.RequestSpecBuilder; import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder; import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType; import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.specification.RequestSpecification; import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification; import com.jayway.restassured.specification.ResponseSpecification;
import org.junit.Before;
import org.junit.Test;
import org.mifosplatform.integrationtests.common.*;
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker;
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
import java.util.HashMap;
@SuppressWarnings("rawtypes")
public class LoanWithdrawnByApplicant { public class LoanWithdrawnByApplicant {
private ResponseSpecification responseSpec; private ResponseSpecification responseSpec;
private RequestSpecification requestSpec; private RequestSpecification requestSpec;
private LoanTransactionHelper loanTransactionHelper; private LoanTransactionHelper loanTransactionHelper;
@@ -28,32 +31,29 @@ public class LoanWithdrawnByApplicant {
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
loanTransactionHelper = new LoanTransactionHelper(requestSpec,responseSpec); loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
} }
@Test @Test
public void loanWithdrawnByApplicant(){ public void loanWithdrawnByApplicant() {
Integer clientID = ClientHelper.createClient(requestSpec,responseSpec,"01 January 2012"); Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2012");
Integer loanProductID = loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder().build()); Integer loanProductID = loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder().build());
Integer loanID = applyForLoanApplication(clientID, loanProductID); Integer loanID = applyForLoanApplication(clientID, loanProductID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID); HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
loanTransactionHelper.withdrawLoanApplicationByClient("03 April 2012",loanID); loanTransactionHelper.withdrawLoanApplicationByClient("03 April 2012", loanID);
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec,responseSpec,loanID); loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
LoanStatusChecker.verifyLoanAccountIsNotActive(loanStatusHashMap); LoanStatusChecker.verifyLoanAccountIsNotActive(loanStatusHashMap);
} }
private Integer applyForLoanApplication(Integer clientID, Integer loanProductID){ private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID) {
String loanApplication = new LoanApplicationTestBuilder() String loanApplication = new LoanApplicationTestBuilder().withPrincipal("5000").withLoanTermFrequency("5")
.withPrincipal("5000").withLoanTermFrequency("5").withLoanTermFrequencyAsMonths() .withLoanTermFrequencyAsMonths().withNumberOfRepayments("5").withRepaymentEveryAfter("1")
.withNumberOfRepayments("5").withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsMonths() .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2").withExpectedDisbursementDate("04 April 2012")
.withInterestRatePerPeriod("2") .withSubmittedOnDate("02 April 2012").build(clientID.toString(), loanProductID.toString());
.withExpectedDisbursementDate("04 April 2012")
.withSubmittedOnDate("02 April 2012")
.build(clientID.toString(),loanProductID.toString());
return loanTransactionHelper.getLoanId(loanApplication); return loanTransactionHelper.getLoanId(loanApplication);
} }
} }
@@ -1,81 +1,92 @@
package org.mifosplatform.integrationtests.common.accounting; package org.mifosplatform.integrationtests.common.accounting;
import com.google.gson.Gson;
import org.mifosplatform.integrationtests.common.ClientHelper;
import org.mifosplatform.integrationtests.common.Utils;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap; import java.util.HashMap;
import org.mifosplatform.integrationtests.common.Utils;
import com.google.gson.Gson;
public class GLAccountBuilder { public class GLAccountBuilder {
public static final String ASSET_ACCOUNT="1"; public static final String ASSET_ACCOUNT = "1";
public static final String LIABILITY_ACCOUNT="2"; public static final String LIABILITY_ACCOUNT = "2";
public static final String EQUITY_ACCOUNT="3"; public static final String EQUITY_ACCOUNT = "3";
public static final String INCOME_ACCOUNT="4"; public static final String INCOME_ACCOUNT = "4";
public static final String EXPENSE_ACCOUNT="5"; public static final String EXPENSE_ACCOUNT = "5";
private static final String ACCOUNT_USAGE_DETAIL="1"; private static final String ACCOUNT_USAGE_DETAIL = "1";
private static final String ACCOUNT_USAGE_HEADER="2"; private static final String ACCOUNT_USAGE_HEADER = "2";
private static final String MANUAL_ENTRIES_ALLOW="true"; private static final String MANUAL_ENTRIES_ALLOW = "true";
private static final String MANUAL_ENTRIES_NOT_ALLOW="false"; private static final String MANUAL_ENTRIES_NOT_ALLOW = "false";
private static String name = Utils.randomStringGenerator("ACCOUNT_NAME_", 5); private static String name = Utils.randomStringGenerator("ACCOUNT_NAME_", 5);
private static String GLCode=""; private static String GLCode = "";
private static String accountType=""; private static String accountType = "";
private static String accountUsage=ACCOUNT_USAGE_DETAIL; private static String accountUsage = ACCOUNT_USAGE_DETAIL;
private static String manualEntriesAllowed=MANUAL_ENTRIES_ALLOW; private static String manualEntriesAllowed = MANUAL_ENTRIES_ALLOW;
private static String description = "DEFAULT_DESCRIPTION"; private static String description = "DEFAULT_DESCRIPTION";
public String build(){ public String build() {
HashMap<String, String> map = new HashMap<String, String>(); HashMap<String, String> map = new HashMap<String, String>();
map.put("name",this.name); map.put("name", GLAccountBuilder.name);
map.put("glCode",this.GLCode); map.put("glCode", GLAccountBuilder.GLCode);
map.put("manualEntriesAllowed",this.manualEntriesAllowed); map.put("manualEntriesAllowed", GLAccountBuilder.manualEntriesAllowed);
map.put("type",this.accountType); map.put("type", GLAccountBuilder.accountType);
map.put("usage",this.accountUsage); map.put("usage", GLAccountBuilder.accountUsage);
map.put("description",this.description); map.put("description", GLAccountBuilder.description);
return new Gson().toJson(map); return new Gson().toJson(map);
} }
public GLAccountBuilder withAccountTypeAsAsset(){ public GLAccountBuilder withAccountTypeAsAsset() {
this.accountType= ASSET_ACCOUNT; GLAccountBuilder.accountType = ASSET_ACCOUNT;
this.GLCode= Utils.randomStringGenerator("ASSET_",2); GLAccountBuilder.GLCode = Utils.randomStringGenerator("ASSET_", 2);
this.GLCode+= Calendar.getInstance().getTimeInMillis()+""; //Added unique timestamp for avoiding random collisions GLAccountBuilder.GLCode += Calendar.getInstance().getTimeInMillis() + ""; // Added
return this; // unique
} // timestamp
public GLAccountBuilder withAccountTypeAsLiability(){ // for
this.accountType=LIABILITY_ACCOUNT; // avoiding
this.GLCode=Utils.randomStringGenerator("LIABILITY_",2); // random
this.GLCode+= Calendar.getInstance().getTimeInMillis()+""; // collisions
return this;
}
public GLAccountBuilder withAccountTypeAsAsEquity(){
this.accountType=EQUITY_ACCOUNT;
this.GLCode=Utils.randomStringGenerator("EQUITY_",2);
this.GLCode+= Calendar.getInstance().getTimeInMillis()+"";
return this;
}
public GLAccountBuilder withAccountTypeAsIncome(){
this.accountType=INCOME_ACCOUNT;
this.GLCode=Utils.randomStringGenerator("INCOME_",2);
this.GLCode+= Calendar.getInstance().getTimeInMillis()+"";
return this;
}
public GLAccountBuilder withAccountTypeAsExpense(){
this.accountType=EXPENSE_ACCOUNT;
this.GLCode=Utils.randomStringGenerator("EXPENSE_",2);
this.GLCode+= Calendar.getInstance().getTimeInMillis()+"";
return this;
}
public GLAccountBuilder withAccountUsageAsHeader(){
this.accountUsage = ACCOUNT_USAGE_HEADER;
return this; return this;
} }
public GLAccountBuilder withMaualEntriesNotAllowed(){ public GLAccountBuilder withAccountTypeAsLiability() {
this.manualEntriesAllowed = MANUAL_ENTRIES_NOT_ALLOW; GLAccountBuilder.accountType = LIABILITY_ACCOUNT;
GLAccountBuilder.GLCode = Utils.randomStringGenerator("LIABILITY_", 2);
GLAccountBuilder.GLCode += Calendar.getInstance().getTimeInMillis() + "";
return this;
}
public GLAccountBuilder withAccountTypeAsAsEquity() {
GLAccountBuilder.accountType = EQUITY_ACCOUNT;
GLAccountBuilder.GLCode = Utils.randomStringGenerator("EQUITY_", 2);
GLAccountBuilder.GLCode += Calendar.getInstance().getTimeInMillis() + "";
return this;
}
public GLAccountBuilder withAccountTypeAsIncome() {
GLAccountBuilder.accountType = INCOME_ACCOUNT;
GLAccountBuilder.GLCode = Utils.randomStringGenerator("INCOME_", 2);
GLAccountBuilder.GLCode += Calendar.getInstance().getTimeInMillis() + "";
return this;
}
public GLAccountBuilder withAccountTypeAsExpense() {
GLAccountBuilder.accountType = EXPENSE_ACCOUNT;
GLAccountBuilder.GLCode = Utils.randomStringGenerator("EXPENSE_", 2);
GLAccountBuilder.GLCode += Calendar.getInstance().getTimeInMillis() + "";
return this;
}
public GLAccountBuilder withAccountUsageAsHeader() {
GLAccountBuilder.accountUsage = ACCOUNT_USAGE_HEADER;
return this;
}
public GLAccountBuilder withMaualEntriesNotAllowed() {
GLAccountBuilder.manualEntriesAllowed = MANUAL_ENTRIES_NOT_ALLOW;
return this; return this;
} }
} }
@@ -1,48 +1,50 @@
package org.mifosplatform.integrationtests.common.accounting; package org.mifosplatform.integrationtests.common.accounting;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
import org.mifosplatform.integrationtests.common.Utils;
import java.util.ArrayList;
import java.util.HashMap;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.HashMap;
import org.mifosplatform.integrationtests.common.Utils;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
@SuppressWarnings("rawtypes")
public class JournalEntryHelper { public class JournalEntryHelper {
private RequestSpecification requestSpec; private final RequestSpecification requestSpec;
private ResponseSpecification responseSpec; private final ResponseSpecification responseSpec;
public JournalEntryHelper(RequestSpecification requestSpec, ResponseSpecification responseSpec) { public JournalEntryHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
this.requestSpec = requestSpec; this.requestSpec = requestSpec;
this.responseSpec = responseSpec; this.responseSpec = responseSpec;
} }
public void checkJournalEntryForExpenseAccount(Account expenseAccount, String date, JournalEntry accountEntries) { public void checkJournalEntryForExpenseAccount(final Account expenseAccount, final String date, final JournalEntry accountEntries) {
checkJournalEntry(expenseAccount, Utils.convertDateToURLFormat(date),accountEntries); checkJournalEntry(expenseAccount, Utils.convertDateToURLFormat(date),accountEntries);
} }
public void checkJournalEntryForAssetAccount(Account assetAccount, String date, JournalEntry... accountEntries) { public void checkJournalEntryForAssetAccount(final Account assetAccount, final String date, final JournalEntry... accountEntries) {
checkJournalEntry(assetAccount,Utils.convertDateToURLFormat(date),accountEntries); checkJournalEntry(assetAccount,Utils.convertDateToURLFormat(date),accountEntries);
} }
public void checkJournalEntryForIncomeAccount(Account incomeAccount, String date, JournalEntry accountEntries) { public void checkJournalEntryForIncomeAccount(final Account incomeAccount, final String date, final JournalEntry accountEntries) {
checkJournalEntry(incomeAccount, Utils.convertDateToURLFormat(date), accountEntries); checkJournalEntry(incomeAccount, Utils.convertDateToURLFormat(date), accountEntries);
} }
private String getEntryValueFromJournalEntry(ArrayList<HashMap> entryResponse, int entryNumber) { private String getEntryValueFromJournalEntry(final ArrayList<HashMap> entryResponse, final int entryNumber) {
HashMap map = (HashMap) entryResponse.get(entryNumber).get("entryType"); HashMap map = (HashMap) entryResponse.get(entryNumber).get("entryType");
return (String) map.get("value"); return (String) map.get("value");
} }
private Float getTransactionAmountFromJournalEntry(ArrayList<HashMap> entryResponse, int entryNumber) { private Float getTransactionAmountFromJournalEntry(final ArrayList<HashMap> entryResponse, final int entryNumber) {
return (Float) entryResponse.get(entryNumber).get("amount"); return (Float) entryResponse.get(entryNumber).get("amount");
} }
private void checkJournalEntry(Account account, String date, JournalEntry... accountEntries){ private void checkJournalEntry(final Account account, final String date, final JournalEntry... accountEntries){
String url = createURLForGettingAccountEntries(account,date); String url = createURLForGettingAccountEntries(account,date);
ArrayList<HashMap> response = Utils.performServerGet(requestSpec, responseSpec, url, ""); ArrayList<HashMap> response = Utils.performServerGet(requestSpec, responseSpec, url, "");
for (int i = 0; i < accountEntries.length; i++) { for (int i = 0; i < accountEntries.length; i++) {
@@ -51,7 +53,7 @@ public class JournalEntryHelper {
} }
} }
private String createURLForGettingAccountEntries (Account account,String date){ private String createURLForGettingAccountEntries (final Account account,final String date){
return new String ("/mifosng-provider/api/v1/journalentries?glAccountId=" + account.getAccountID() + "&type=" +account.getAccountType() return new String ("/mifosng-provider/api/v1/journalentries?glAccountId=" + account.getAccountID() + "&type=" +account.getAccountType()
+ "&fromDate=" + date + "&toDate=" + date + "&tenantIdentifier=default"); + "&fromDate=" + date + "&toDate=" + date + "&tenantIdentifier=default");
} }
@@ -12,7 +12,7 @@ public class LoanApplicationTestBuilder {
private static final String YEARS = "3"; private static final String YEARS = "3";
private static final String DECLINING_BALANCE = "0"; private static final String DECLINING_BALANCE = "0";
private static final String FLAT_BALANCE = "1"; private static final String FLAT_BALANCE = "1";
private static final String EQUAL_PRINCIPLE_PAYMENTS = "0"; private static final String EQUAL_PRINCIPAL_PAYMENTS = "0";
private static final String EQUAL_INSTALLMENTS = "1"; private static final String EQUAL_INSTALLMENTS = "1";
private static final String CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD = "1"; private static final String CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD = "1";
private static final String MIFOS_STANDARD_STRATEGY = "1"; private static final String MIFOS_STANDARD_STRATEGY = "1";
@@ -26,7 +26,7 @@ public class LoanApplicationTestBuilder {
private String interestRate = "2"; private String interestRate = "2";
private String interestType = FLAT_BALANCE; private String interestType = FLAT_BALANCE;
private String amortizationType = EQUAL_PRINCIPLE_PAYMENTS; private String amortizationType = EQUAL_PRINCIPAL_PAYMENTS;
private String interestCalculationPeriodType = CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD; private String interestCalculationPeriodType = CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD;
private final String transactionProcessingID = MIFOS_STANDARD_STRATEGY; private final String transactionProcessingID = MIFOS_STANDARD_STRATEGY;
private String expectedDisbursmentDate = ""; private String expectedDisbursmentDate = "";
@@ -131,7 +131,7 @@ public class LoanApplicationTestBuilder {
} }
public LoanApplicationTestBuilder withAmortizationTypeAsEqualPrincipalPayments() { public LoanApplicationTestBuilder withAmortizationTypeAsEqualPrincipalPayments() {
this.amortizationType = EQUAL_PRINCIPLE_PAYMENTS; this.amortizationType = EQUAL_PRINCIPAL_PAYMENTS;
return this; return this;
} }
@@ -16,7 +16,7 @@ public class LoanProductTestBuilder {
private static final String MONTHS = "2"; private static final String MONTHS = "2";
private static final String YEARS = "3"; private static final String YEARS = "3";
private static final String CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD="1"; private static final String CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD="1";
private static final String EQUAL_PRINCIPLE_PAYMENTS="0"; private static final String EQUAL_PRINCIPAL_PAYMENTS="0";
private static final String EQUAL_INSTALLMENTS="1"; private static final String EQUAL_INSTALLMENTS="1";
private static final String DECLINING_BALANCE="0"; private static final String DECLINING_BALANCE="0";
private static final String FLAT_BALANCE="1"; private static final String FLAT_BALANCE="1";
@@ -137,8 +137,8 @@ public class LoanProductTestBuilder {
return this; return this;
} }
public LoanProductTestBuilder withAmortizationTypeAsEqualPrinciplePayment(){ public LoanProductTestBuilder withAmortizationTypeAsEqualPrincipalPayment(){
this.amortizationType = EQUAL_PRINCIPLE_PAYMENTS; this.amortizationType = EQUAL_PRINCIPAL_PAYMENTS;
return this; return this;
} }
@@ -1,20 +1,21 @@
package org.mifosplatform.integrationtests.common.loans; package org.mifosplatform.integrationtests.common.loans;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import org.mifosplatform.integrationtests.common.Utils;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.jayway.restassured.specification.RequestSpecification; import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification; import com.jayway.restassured.specification.ResponseSpecification;
import org.mifosplatform.integrationtests.common.Utils;
import static org.junit.Assert.assertEquals;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class LoanTransactionHelper { public class LoanTransactionHelper {
private RequestSpecification requestSpec; private final RequestSpecification requestSpec;
private ResponseSpecification responseSpec; private final ResponseSpecification responseSpec;
private static final String CREATE_LOAN_PRODUCT_URL = "/mifosng-provider/api/v1/loanproducts?tenantIdentifier=default"; 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 APPLY_LOAN_URL = "/mifosng-provider/api/v1/loans?tenantIdentifier=default";
@@ -28,7 +29,7 @@ public class LoanTransactionHelper {
private static final String WITHDRAW_LOAN_APPLICATION_COMMAND = "withdrawnByApplicant"; private static final String WITHDRAW_LOAN_APPLICATION_COMMAND = "withdrawnByApplicant";
public LoanTransactionHelper(RequestSpecification requestSpec,ResponseSpecification responseSpec){ public LoanTransactionHelper(final RequestSpecification requestSpec,final ResponseSpecification responseSpec){
this.requestSpec = requestSpec; this.requestSpec = requestSpec;
this.responseSpec = responseSpec; this.responseSpec = responseSpec;
} }
@@ -182,6 +183,7 @@ public class LoanTransactionHelper {
return (HashMap) response.get("status"); return (HashMap) response.get("status");
} }
@SuppressWarnings("unchecked")
public void verifyRepaymentScheduleEntryFor(final int repaymentNumber, final float expectedPrincipalOutstanding, final Integer loanID) { public void verifyRepaymentScheduleEntryFor(final int repaymentNumber, final float expectedPrincipalOutstanding, final Integer loanID) {
System.out.println("---------------------------GETTING LOAN REPAYMENT SCHEDULE--------------------------------"); System.out.println("---------------------------GETTING LOAN REPAYMENT SCHEDULE--------------------------------");
ArrayList<HashMap> repaymentPeriods = getLoanRepaymentSchedule(requestSpec, responseSpec, loanID); ArrayList<HashMap> repaymentPeriods = getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
@@ -22,7 +22,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
} }
@Override @Override
public void createJournalEntriesForLoan(LoanDTO loanDTO) { public void createJournalEntriesForLoan(final LoanDTO loanDTO) {
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(loanDTO.getOfficeId()); final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(loanDTO.getOfficeId());
final Office office = this.helper.getOfficeById(loanDTO.getOfficeId()); final Office office = this.helper.getOfficeById(loanDTO.getOfficeId());
for (final LoanTransactionDTO loanTransactionDTO : loanDTO.getNewLoanTransactions()) { for (final LoanTransactionDTO loanTransactionDTO : loanDTO.getNewLoanTransactions()) {
@@ -175,7 +175,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
BigDecimal totalDebitAmount = new BigDecimal(0); BigDecimal totalDebitAmount = new BigDecimal(0);
// handle principle payment or writeOff (and reversals) // handle principal payment or writeOff (and reversals)
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(principalAmount); totalDebitAmount = totalDebitAmount.add(principalAmount);
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId, helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId,
@@ -25,7 +25,6 @@ import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.mifosplatform.commands.data.AuditData; import org.mifosplatform.commands.data.AuditData;
import org.mifosplatform.commands.data.AuditSearchData; import org.mifosplatform.commands.data.AuditSearchData;
import org.mifosplatform.commands.data.CommandSourceData;
import org.mifosplatform.commands.service.AuditReadPlatformService; import org.mifosplatform.commands.service.AuditReadPlatformService;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
@@ -43,183 +42,146 @@ import org.springframework.stereotype.Component;
@Scope("singleton") @Scope("singleton")
public class MakercheckersApiResource { public class MakercheckersApiResource {
private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>( private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "actionName", "entityName", "resourceId",
Arrays.asList("id", "actionName", "entityName", "resourceId", "subresourceId", "maker", "madeOnDate", "checker", "checkedOnDate", "processingResult", "commandAsJson", "officeName",
"subresourceId", "maker", "madeOnDate", "checker", "groupLevelName", "groupName", "clientName", "loanAccountNo", "savingsAccountNo"));
"checkedOnDate", "processingResult", "commandAsJson",
"officeName", "groupLevelName", "groupName", "clientName",
"loanAccountNo", "savingsAccountNo"));
//Next line removed because read permissions checks part of read services private final AuditReadPlatformService readPlatformService;
//private final String resourceNameForPermissions = "MAKERCHECKER"; private final DefaultToApiJsonSerializer<AuditData> toApiJsonSerializerAudit;
private final DefaultToApiJsonSerializer<AuditSearchData> toApiJsonSerializerSearchTemplate;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService writePlatformService;
private final AuditReadPlatformService readPlatformService; @Autowired
private final DefaultToApiJsonSerializer<CommandSourceData> toApiJsonSerializer; public MakercheckersApiResource(final AuditReadPlatformService readPlatformService,
private final DefaultToApiJsonSerializer<AuditData> toApiJsonSerializerAudit; final DefaultToApiJsonSerializer<AuditData> toApiJsonSerializerAudit,
private final DefaultToApiJsonSerializer<AuditSearchData> toApiJsonSerializerSearchTemplate; final DefaultToApiJsonSerializer<AuditSearchData> toApiJsonSerializerSearchTemplate,
private final ApiRequestParameterHelper apiRequestParameterHelper; final ApiRequestParameterHelper apiRequestParameterHelper, final PortfolioCommandSourceWritePlatformService writePlatformService) {
private final PortfolioCommandSourceWritePlatformService writePlatformService; this.readPlatformService = readPlatformService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.toApiJsonSerializerAudit = toApiJsonSerializerAudit;
this.toApiJsonSerializerSearchTemplate = toApiJsonSerializerSearchTemplate;
this.writePlatformService = writePlatformService;
}
@Autowired @GET
public MakercheckersApiResource( @Consumes({ MediaType.APPLICATION_JSON })
final AuditReadPlatformService readPlatformService, @Produces({ MediaType.APPLICATION_JSON })
final DefaultToApiJsonSerializer<CommandSourceData> toApiJsonSerializer, public String retrieveCommands(@Context final UriInfo uriInfo, @QueryParam("actionName") final String actionName,
final DefaultToApiJsonSerializer<AuditData> toApiJsonSerializerAudit, @QueryParam("entityName") final String entityName, @QueryParam("resourceId") final Long resourceId,
final DefaultToApiJsonSerializer<AuditSearchData> toApiJsonSerializerSearchTemplate, @QueryParam("makerId") final Long makerId, @QueryParam("makerDateTimeFrom") final String makerDateTimeFrom,
final ApiRequestParameterHelper apiRequestParameterHelper, @QueryParam("makerDateTimeTo") final String makerDateTimeTo, @QueryParam("officeId") final Integer officeId,
final PortfolioCommandSourceWritePlatformService writePlatformService) { @QueryParam("groupId") final Integer groupId, @QueryParam("clientId") final Integer clientId,
this.readPlatformService = readPlatformService; @QueryParam("loanid") final Integer loanId, @QueryParam("savingsAccountId") final Integer savingsAccountId) {
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.toApiJsonSerializerAudit = toApiJsonSerializerAudit;
this.toApiJsonSerializerSearchTemplate = toApiJsonSerializerSearchTemplate;
this.writePlatformService = writePlatformService;
}
@GET final String extraCriteria = getExtraCriteria(actionName, entityName, resourceId, makerId, makerDateTimeFrom, makerDateTimeTo,
@Consumes({ MediaType.APPLICATION_JSON }) officeId, groupId, clientId, loanId, savingsAccountId);
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveCommands(@Context final UriInfo uriInfo,
@QueryParam("actionName") final String actionName,
@QueryParam("entityName") final String entityName,
@QueryParam("resourceId") final Long resourceId,
@QueryParam("makerId") final Long makerId,
@QueryParam("makerDateTimeFrom") final String makerDateTimeFrom,
@QueryParam("makerDateTimeTo") final String makerDateTimeTo,
@QueryParam("officeId") final Integer officeId,
@QueryParam("groupId") final Integer groupId,
@QueryParam("clientId") final Integer clientId,
@QueryParam("loanid") final Integer loanId,
@QueryParam("savingsAccountId") final Integer savingsAccountId) {
final String extraCriteria = getExtraCriteria(actionName, entityName, final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
resourceId, makerId, makerDateTimeFrom, makerDateTimeTo,
officeId, groupId, clientId, loanId, savingsAccountId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper final Collection<AuditData> entries = this.readPlatformService.retrieveAllEntriesToBeChecked(extraCriteria,
.process(uriInfo.getQueryParameters()); settings.isIncludeJson());
final Collection<AuditData> entries = this.readPlatformService return this.toApiJsonSerializerAudit.serialize(settings, entries, RESPONSE_DATA_PARAMETERS);
.retrieveAllEntriesToBeChecked(extraCriteria, }
settings.isIncludeJson());
return this.toApiJsonSerializerAudit.serialize(settings, entries, @GET
RESPONSE_DATA_PARAMETERS); @Path("/searchtemplate")
} @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAuditSearchTemplate(@Context final UriInfo uriInfo) {
@GET final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@Path("/searchtemplate")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAuditSearchTemplate(@Context final UriInfo uriInfo) {
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper final AuditSearchData auditSearchData = this.readPlatformService.retrieveSearchTemplate("makerchecker");
.process(uriInfo.getQueryParameters());
final AuditSearchData auditSearchData = this.readPlatformService final Set<String> RESPONSE_DATA_PARAMETERS_SEARCH_TEMPLATE = new HashSet<String>(Arrays.asList("appUsers", "actionNames",
.retrieveSearchTemplate("makerchecker"); "entityNames"));
final Set<String> RESPONSE_DATA_PARAMETERS_SEARCH_TEMPLATE = new HashSet<String>( return this.toApiJsonSerializerSearchTemplate.serialize(settings, auditSearchData, RESPONSE_DATA_PARAMETERS_SEARCH_TEMPLATE);
Arrays.asList("appUsers", "actionNames", "entityNames")); }
return this.toApiJsonSerializerSearchTemplate.serialize(settings, @POST
auditSearchData, RESPONSE_DATA_PARAMETERS_SEARCH_TEMPLATE); @Path("{auditId}")
} @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String approveMakerCheckerEntry(@PathParam("auditId") final Long auditId, @QueryParam("command") final String commandParam) {
@POST CommandProcessingResult result = null;
@Path("{auditId}") if (is(commandParam, "approve")) {
@Consumes({ MediaType.APPLICATION_JSON }) result = this.writePlatformService.approveEntry(auditId);
@Produces({ MediaType.APPLICATION_JSON }) } else {
public String approveMakerCheckerEntry( throw new UnrecognizedQueryParamException("command", commandParam);
@PathParam("auditId") final Long auditId, }
@QueryParam("command") final String commandParam) {
CommandProcessingResult result = null; return this.toApiJsonSerializerAudit.serialize(result);
if (is(commandParam, "approve")) { }
result = this.writePlatformService.approveEntry(auditId);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
return this.toApiJsonSerializer.serialize(result); private boolean is(final String commandParam, final String commandValue) {
} return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
private boolean is(final String commandParam, final String commandValue) { @DELETE
return StringUtils.isNotBlank(commandParam) @Path("{auditId}")
&& commandParam.trim().equalsIgnoreCase(commandValue); @Consumes({ MediaType.APPLICATION_JSON })
} @Produces({ MediaType.APPLICATION_JSON })
public String deleteMakerCheckerEntry(@PathParam("auditId") final Long auditId) {
@DELETE final Long id = this.writePlatformService.deleteEntry(auditId);
@Path("{auditId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String deleteMakerCheckerEntry(
@PathParam("auditId") final Long auditId) {
final Long id = this.writePlatformService.deleteEntry(auditId); return this.toApiJsonSerializerAudit.serialize(CommandProcessingResult.commandOnlyResult(id));
}
return this.toApiJsonSerializer.serialize(CommandProcessingResult private String getExtraCriteria(final String actionName, final String entityName, final Long resourceId, final Long makerId,
.commandOnlyResult(id)); final String makerDateTimeFrom, final String makerDateTimeTo, final Integer officeId, final Integer groupId,
} final Integer clientId, final Integer loanId, final Integer savingsAccountId) {
private String getExtraCriteria(final String actionName, String extraCriteria = "";
final String entityName, final Long resourceId, final Long makerId,
final String makerDateTimeFrom, final String makerDateTimeTo,
final Integer officeId, final Integer groupId,
final Integer clientId, final Integer loanId,
final Integer savingsAccountId) {
String extraCriteria = ""; if (actionName != null) {
extraCriteria += " and aud.action_name = " + ApiParameterHelper.sqlEncodeString(actionName);
}
if (entityName != null) {
extraCriteria += " and aud.entity_name like " + ApiParameterHelper.sqlEncodeString(entityName + "%");
}
if (actionName != null) { if (resourceId != null) {
extraCriteria += " and aud.action_name = " extraCriteria += " and aud.resource_id = " + resourceId;
+ ApiParameterHelper.sqlEncodeString(actionName); }
} if (makerId != null) {
if (entityName != null) { extraCriteria += " and aud.maker_id = " + makerId;
extraCriteria += " and aud.entity_name like " }
+ ApiParameterHelper.sqlEncodeString(entityName + "%"); if (makerDateTimeFrom != null) {
} extraCriteria += " and aud.made_on_date >= " + ApiParameterHelper.sqlEncodeString(makerDateTimeFrom);
}
if (makerDateTimeTo != null) {
extraCriteria += " and aud.made_on_date <= " + ApiParameterHelper.sqlEncodeString(makerDateTimeTo);
}
if (resourceId != null) { if (officeId != null) {
extraCriteria += " and aud.resource_id = " + resourceId; extraCriteria += " and aud.office_id = " + officeId;
} }
if (makerId != null) {
extraCriteria += " and aud.maker_id = " + makerId;
}
if (makerDateTimeFrom != null) {
extraCriteria += " and aud.made_on_date >= "
+ ApiParameterHelper.sqlEncodeString(makerDateTimeFrom);
}
if (makerDateTimeTo != null) {
extraCriteria += " and aud.made_on_date <= "
+ ApiParameterHelper.sqlEncodeString(makerDateTimeTo);
}
if (officeId != null) { if (groupId != null) {
extraCriteria += " and aud.office_id = " + officeId; extraCriteria += " and aud.group_id = " + groupId;
} }
if (groupId != null) { if (clientId != null) {
extraCriteria += " and aud.group_id = " + groupId; extraCriteria += " and aud.client_id = " + clientId;
} }
if (clientId != null) { if (loanId != null) {
extraCriteria += " and aud.client_id = " + clientId; extraCriteria += " and aud.loan_id = " + loanId;
} }
if (loanId != null) { if (savingsAccountId != null) {
extraCriteria += " and aud.loan_id = " + loanId; extraCriteria += " and aud.savings_account_id = " + savingsAccountId;
} }
if (savingsAccountId != null) { if (StringUtils.isNotBlank(extraCriteria)) {
extraCriteria += " and aud.savings_account_id = " extraCriteria = extraCriteria.substring(4);
+ savingsAccountId; }
}
if (StringUtils.isNotBlank(extraCriteria)) { return extraCriteria;
extraCriteria = extraCriteria.substring(4); }
}
return extraCriteria;
}
} }
@@ -10,6 +10,7 @@ import org.joda.time.LocalDate;
/** /**
* Immutable data object representing maker-checker entry * Immutable data object representing maker-checker entry
*/ */
@Deprecated
final public class CommandSourceData { final public class CommandSourceData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -22,7 +23,7 @@ final public class CommandSourceData {
private final Long entityId; private final Long entityId;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final String entityHref; private final String entityHref;
private String commandJson; private final String commandJson;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final LocalDate madeOnDate; private final LocalDate madeOnDate;
@@ -261,6 +261,10 @@ public class CommandWrapper {
return this.entityName.equalsIgnoreCase("CLIENT"); return this.entityName.equalsIgnoreCase("CLIENT");
} }
public boolean isClientActivation() {
return this.actionName.equalsIgnoreCase("ACTIVATE") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isClientIdentifierResource() { public boolean isClientIdentifierResource() {
return this.entityName.equals("CLIENTIDENTIFIER"); return this.entityName.equals("CLIENTIDENTIFIER");
} }
@@ -483,5 +487,4 @@ public class CommandWrapper {
public boolean isReportResource() { public boolean isReportResource() {
return this.entityName.equalsIgnoreCase("REPORT"); return this.entityName.equalsIgnoreCase("REPORT");
} }
} }
@@ -328,10 +328,20 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder activateClient(final Long clientId) {
this.actionName = "ACTIVATE";
this.entityName = "CLIENT";
this.entityId = clientId;
this.clientId = clientId;
this.href = "/clients/" + clientId + "?command=activate&template=true";
return this;
}
public CommandWrapperBuilder updateClient(final Long clientId) { public CommandWrapperBuilder updateClient(final Long clientId) {
this.actionName = "UPDATE"; this.actionName = "UPDATE";
this.entityName = "CLIENT"; this.entityName = "CLIENT";
this.entityId = clientId; this.entityId = clientId;
this.clientId = clientId;
this.href = "/clients/" + clientId; this.href = "/clients/" + clientId;
return this; return this;
} }
@@ -340,6 +350,7 @@ public class CommandWrapperBuilder {
this.actionName = "DELETE"; this.actionName = "DELETE";
this.entityName = "CLIENT"; this.entityName = "CLIENT";
this.entityId = clientId; this.entityId = clientId;
this.clientId = clientId;
this.href = "/clients/" + clientId; this.href = "/clients/" + clientId;
this.json = "{}"; this.json = "{}";
return this; return this;
@@ -878,26 +889,26 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder createCenter() { public CommandWrapperBuilder createCenter() {
this.actionName = "CREATE"; this.actionName = "CREATE";
this.entityName = "CENTER"; this.entityName = "CENTER";
this.href = "/centers/template"; this.href = "/centers/template";
return this; return this;
} }
public CommandWrapperBuilder updateCenter(Long centerId) { public CommandWrapperBuilder updateCenter(final Long centerId) {
this.actionName = "UPDATE"; this.actionName = "UPDATE";
this.entityName = "CENTER"; this.entityName = "CENTER";
this.entityId = centerId; this.entityId = centerId;
this.href = "/centers/" + centerId; this.href = "/centers/" + centerId;
return this; return this;
} }
public CommandWrapperBuilder deleteCenter(Long centerId) { public CommandWrapperBuilder deleteCenter(final Long centerId) {
this.actionName = "DELETE"; this.actionName = "DELETE";
this.entityName = "CENTER"; this.entityName = "CENTER";
this.entityId = centerId; this.entityId = centerId;
this.href = "/centers/" + centerId; this.href = "/centers/" + centerId;
return this; return this;
} }
} }
@@ -1,17 +0,0 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.commands.service;
import java.util.Collection;
import org.mifosplatform.commands.data.CommandSourceData;
public interface PortfolioCommandsReadPlatformService {
CommandSourceData retrieveById(Long id);
Collection<CommandSourceData> retrieveUnprocessChangesByEntityNameAndId(String entityName, Long entityId);
}
@@ -1,72 +0,0 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.commands.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.commands.data.CommandSourceData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class PortfolioCommandsReadPlatformServiceImpl implements PortfolioCommandsReadPlatformService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public PortfolioCommandsReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class CommandSourceMapper implements RowMapper<CommandSourceData> {
public String schema() {
return " mc.id as id, mc.action_name as actionName, mc.entity_name as entityName, mc.resource_id as entityId, mc.api_get_url as getHref, "
+ "mc.command_as_json as taskJson, mc.made_on_date as madeOnDate from m_portfolio_command_source mc ";
}
@Override
public CommandSourceData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String actionName = rs.getString("actionName");
final String entityName = rs.getString("entityName");
final Long entityId = JdbcSupport.getLong(rs, "entityId");
final String taskJson = rs.getString("taskJson");
final String getHref = rs.getString("getHref");
final LocalDate madeOnDate = JdbcSupport.getLocalDate(rs, "madeOnDate");
return new CommandSourceData(id, actionName, entityName, entityId, getHref, taskJson, madeOnDate);
}
}
@Override
public CommandSourceData retrieveById(final Long id) {
final CommandSourceMapper rm = new CommandSourceMapper();
final String sql = "select " + rm.schema() + " where mc.id=?";
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { id });
}
@Override
public Collection<CommandSourceData> retrieveUnprocessChangesByEntityNameAndId(final String entityName, final Long entityId) {
final CommandSourceMapper rm = new CommandSourceMapper();
final String sql = "select "
+ rm.schema()
+ " where mc.entity_name like ? and mc.resource_id = ? and mc.checker_id is null and mc.processing_result_enum = 2 order by mc.made_on_date DESC, mc.action_name ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { entityName, entityId });
}
}
@@ -149,6 +149,10 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
handler = applicationContext.getBean("updateClientCommandHandler", NewCommandSourceHandler.class); handler = applicationContext.getBean("updateClientCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) { } else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteClientCommandHandler", NewCommandSourceHandler.class); handler = applicationContext.getBean("deleteClientCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isClientActivation()) {
handler = applicationContext.getBean("activateClientCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
} }
// end of client // end of client
} else if (wrapper.isUpdateRolePermissions()) { } else if (wrapper.isUpdateRolePermissions()) {
@@ -9,4 +9,5 @@ public interface ConfigurationDomainService {
boolean isMakerCheckerEnabledForTask(String taskPermissionCode); boolean isMakerCheckerEnabledForTask(String taskPermissionCode);
boolean isClientPendingApprovalAllowedEnabled();
} }
@@ -39,4 +39,13 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
return thisTask.hasMakerCheckerEnabled() && property.isEnabled(); return thisTask.hasMakerCheckerEnabled() && property.isEnabled();
} }
@Override
public boolean isClientPendingApprovalAllowedEnabled() {
final String propertyKey = "allow-pending-client-status";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByName(propertyKey);
if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(propertyKey); }
return property.isEnabled();
}
} }
@@ -35,12 +35,10 @@ public class ApiRequestParameterHelper {
} }
final boolean prettyPrint = ApiParameterHelper.prettyPrint(queryParameters); final boolean prettyPrint = ApiParameterHelper.prettyPrint(queryParameters);
final boolean template = ApiParameterHelper.template(queryParameters); final boolean template = ApiParameterHelper.template(queryParameters);
final Long commandId = ApiParameterHelper.commandId(queryParameters);
final boolean makerCheckerable = ApiParameterHelper.makerCheckerable(queryParameters); final boolean makerCheckerable = ApiParameterHelper.makerCheckerable(queryParameters);
final boolean includeJson = ApiParameterHelper.includeJson(queryParameters); final boolean includeJson = ApiParameterHelper.includeJson(queryParameters);
return ApiRequestJsonSerializationSettings return ApiRequestJsonSerializationSettings.from(prettyPrint, responseParameters, template, makerCheckerable, includeJson);
.from(prettyPrint, responseParameters, template, commandId, makerCheckerable, includeJson);
} }
public ApiRequestJsonSerializationSettings process(final MultivaluedMap<String, String> queryParameters) { public ApiRequestJsonSerializationSettings process(final MultivaluedMap<String, String> queryParameters) {
@@ -48,11 +46,9 @@ public class ApiRequestParameterHelper {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(queryParameters); final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(queryParameters);
final boolean prettyPrint = ApiParameterHelper.prettyPrint(queryParameters); final boolean prettyPrint = ApiParameterHelper.prettyPrint(queryParameters);
final boolean template = ApiParameterHelper.template(queryParameters); final boolean template = ApiParameterHelper.template(queryParameters);
final Long commandId = ApiParameterHelper.commandId(queryParameters);
final boolean makerCheckerable = ApiParameterHelper.makerCheckerable(queryParameters); final boolean makerCheckerable = ApiParameterHelper.makerCheckerable(queryParameters);
final boolean includeJson = ApiParameterHelper.includeJson(queryParameters); final boolean includeJson = ApiParameterHelper.includeJson(queryParameters);
return ApiRequestJsonSerializationSettings return ApiRequestJsonSerializationSettings.from(prettyPrint, responseParameters, template, makerCheckerable, includeJson);
.from(prettyPrint, responseParameters, template, commandId, makerCheckerable, includeJson);
} }
} }
@@ -75,6 +75,15 @@ public class DataValidatorBuilder {
return this; return this;
} }
public void failWithCode(final String errorCode) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(this.parameter)
.append(errorCode);
StringBuilder defaultEnglishMessage = new StringBuilder("Failed data validation due to: ").append(errorCode).append(".");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
this.parameter, this.value);
dataValidationErrors.add(error);
}
public DataValidatorBuilder equalToParameter(final String linkedParameterName, final Object linkedValue) { public DataValidatorBuilder equalToParameter(final String linkedParameterName, final Object linkedValue) {
if (value == null && linkedValue == null && ignoreNullValue) { return this; } if (value == null && linkedValue == null && ignoreNullValue) { return this; }
@@ -16,28 +16,25 @@ public class ApiRequestJsonSerializationSettings {
private final boolean prettyPrint; private final boolean prettyPrint;
private final Set<String> parametersForPartialResponse; private final Set<String> parametersForPartialResponse;
private final boolean template; private final boolean template;
private final Long commandId;
private final boolean makerCheckerable; private final boolean makerCheckerable;
private final boolean includeJson; private final boolean includeJson;
public ApiRequestJsonSerializationSettings(final boolean prettyPrint, final Set<String> parametersForPartialResponse, public ApiRequestJsonSerializationSettings(final boolean prettyPrint, final Set<String> parametersForPartialResponse,
final boolean template, final Long commandId, final boolean makerCheckerable, final boolean includeJson) { final boolean template, final boolean makerCheckerable, final boolean includeJson) {
this.prettyPrint = prettyPrint; this.prettyPrint = prettyPrint;
this.parametersForPartialResponse = parametersForPartialResponse; this.parametersForPartialResponse = parametersForPartialResponse;
this.template = template; this.template = template;
this.commandId = commandId;
this.makerCheckerable = makerCheckerable; this.makerCheckerable = makerCheckerable;
this.includeJson = includeJson; this.includeJson = includeJson;
} }
public static ApiRequestJsonSerializationSettings from(final boolean prettyPrint, final Set<String> parametersForPartialResponse, public static ApiRequestJsonSerializationSettings from(final boolean prettyPrint, final Set<String> parametersForPartialResponse,
final boolean template, final Long commandId, final boolean makerCheckerable, final boolean includeJson) { final boolean template, final boolean makerCheckerable, final boolean includeJson) {
// FIXME - KW - rather than always creating new objects for this could // FIXME - KW - rather than always creating new objects for this could
// just send by common ones like, prettyprint=false, empty response // just send by common ones like, prettyprint=false, empty response
// parameters // parameters
return new ApiRequestJsonSerializationSettings(prettyPrint, parametersForPartialResponse, template, commandId, makerCheckerable, return new ApiRequestJsonSerializationSettings(prettyPrint, parametersForPartialResponse, template, makerCheckerable, includeJson);
includeJson);
} }
public boolean isPrettyPrint() { public boolean isPrettyPrint() {
@@ -56,10 +53,6 @@ public class ApiRequestJsonSerializationSettings {
return this.includeJson; return this.includeJson;
} }
public Long getCommandId() {
return this.commandId;
}
public Set<String> getParametersForPartialResponse() { public Set<String> getParametersForPartialResponse() {
return this.parametersForPartialResponse; return this.parametersForPartialResponse;
} }
@@ -67,8 +60,4 @@ public class ApiRequestJsonSerializationSettings {
public boolean isPartialResponseRequired() { public boolean isPartialResponseRequired() {
return !this.parametersForPartialResponse.isEmpty(); return !this.parametersForPartialResponse.isEmpty();
} }
public boolean isCommandIdPassed() {
return this.commandId != null;
}
} }
@@ -8,13 +8,14 @@ package org.mifosplatform.organisation.office.data;
/** /**
* Immutable data object used for office lookups (will remove eventually and just use {@link OfficeData}. * Immutable data object used for office lookups (will remove eventually and just use {@link OfficeData}.
*/ */
@Deprecated
public class OfficeLookup { public class OfficeLookup {
private Long id; private final Long id;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private String name; private final String name;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private String nameDecorated; private final String nameDecorated;
public OfficeLookup(final Long id, final String name, final String nameDecorated) { public OfficeLookup(final Long id, final String name, final String nameDecorated) {
this.id = id; this.id = id;
@@ -21,6 +21,7 @@ public interface OfficeReadPlatformService {
* Deprecated so cane eventually remove need for OfficeLookup type - so use * Deprecated so cane eventually remove need for OfficeLookup type - so use
* retrieveAllOfficesForDropdown instead. * retrieveAllOfficesForDropdown instead.
*/ */
@Deprecated
Collection<OfficeLookup> retrieveAllOfficesForLookup(); Collection<OfficeLookup> retrieveAllOfficesForLookup();
OfficeData retrieveOffice(Long officeId); OfficeData retrieveOffice(Long officeId);
@@ -0,0 +1,63 @@
package org.mifosplatform.portfolio.client.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.mifosplatform.portfolio.client.data.ClientData;
public class ClientApiConstants {
public static final String CLIENT_RESOURCE_NAME = "client";
// general
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
// request parameters
public static final String idParamName = "id";
public static final String groupIdParamName = "groupId";
public static final String accountNoParamName = "accountNo";
public static final String externalIdParamName = "externalId";
public static final String firstnameParamName = "firstname";
public static final String middlenameParamName = "middlename";
public static final String lastnameParamName = "lastname";
public static final String fullnameParamName = "fullname";
public static final String officeIdParamName = "officeId";
public static final String staffIdParamName = "staffId";
public static final String activeParamName = "active";
public static final String activationDateParamName = "activationDate";
// response parameters
public static final String statusParamName = "status";
public static final String hierarchyParamName = "hierarchy";
public static final String displayNameParamName = "displayName";
public static final String officeNameParamName = "officeName";
public static final String staffNameParamName = "staffName";
public static final String imageKeyParamName = "imageKey";
public static final String imagePresentParamName = "imagePresent";
// associations related part of response
public static final String parentGroupsParamName = "groups";
// template related part of response
public static final String officeOptionsParamName = "officeOptions";
public static final String staffOptionsParamName = "staffOptions";
public static final Set<String> CLIENT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName, firstnameParamName, middlenameParamName,
lastnameParamName, fullnameParamName, officeIdParamName, staffIdParamName, activeParamName, activationDateParamName));
/**
* These parameters will match the class level parameters of
* {@link ClientData}. Where possible, we try to get response parameters to
* match those of request parameters.
*/
public static final Set<String> CLIENT_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName, accountNoParamName,
externalIdParamName, statusParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName,
displayNameParamName, officeIdParamName, officeNameParamName, staffIdParamName, staffNameParamName, hierarchyParamName,
officeOptionsParamName, staffOptionsParamName));
public static final Set<String> ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, activationDateParamName));
}
@@ -111,7 +111,7 @@ public class ClientImagesApiResource {
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
final ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId); final ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); } if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
@@ -134,7 +134,7 @@ public class ClientImagesApiResource {
public Response downloadClientImage(@PathParam("clientId") final Long clientId) { public Response downloadClientImage(@PathParam("clientId") final Long clientId) {
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
final ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId); final ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); } if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
@@ -26,24 +26,21 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.mifosplatform.commands.data.CommandSourceData;
import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.commands.service.PortfolioCommandsReadPlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; 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.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeLookup; import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.mifosplatform.portfolio.client.command.ClientCommand;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData;
import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.serialization.ClientCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -53,185 +50,84 @@ import org.springframework.stereotype.Component;
@Scope("singleton") @Scope("singleton")
public class ClientsApiResource { public class ClientsApiResource {
private final Set<String> CLIENT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "accountNo", "externalId", "firstname",
"middlename", "lastname", "fullname", "displayName", "officeId", "officeName", "joinedDate", "imageKey", "imagePresent",
"parentGroups", "allowedOffices", "currentChange", "allChanges"));
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final ClientReadPlatformService clientReadPlatformService; private final ClientReadPlatformService clientReadPlatformService;
private final OfficeReadPlatformService officeReadPlatformService; private final OfficeReadPlatformService officeReadPlatformService;
private final ToApiJsonSerializer<ClientData> toApiJsonSerializer; private final ToApiJsonSerializer<ClientData> toApiJsonSerializer;
private final ToApiJsonSerializer<ClientAccountSummaryCollectionData> clientAccountSummaryToApiJsonSerializer; private final ToApiJsonSerializer<ClientAccountSummaryCollectionData> clientAccountSummaryToApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper; private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandsReadPlatformService commandSourceReadPlatformService;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final ClientCommandFromApiJsonDeserializer clientCommandFromApiJsonDeserializer;
@Autowired @Autowired
public ClientsApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService, public ClientsApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService,
final OfficeReadPlatformService officeReadPlatformService, final ToApiJsonSerializer<ClientData> toApiJsonSerializer, final OfficeReadPlatformService officeReadPlatformService, final ToApiJsonSerializer<ClientData> toApiJsonSerializer,
final ClientCommandFromApiJsonDeserializer clientCommandFromApiJsonDeserializer,
final ToApiJsonSerializer<ClientAccountSummaryCollectionData> clientAccountSummaryToApiJsonSerializer, final ToApiJsonSerializer<ClientAccountSummaryCollectionData> clientAccountSummaryToApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper, final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandsReadPlatformService commandsReadPlatformService,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.context = context; this.context = context;
this.clientReadPlatformService = readPlatformService; this.clientReadPlatformService = readPlatformService;
this.officeReadPlatformService = officeReadPlatformService; this.officeReadPlatformService = officeReadPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer; this.toApiJsonSerializer = toApiJsonSerializer;
this.clientCommandFromApiJsonDeserializer = clientCommandFromApiJsonDeserializer;
this.clientAccountSummaryToApiJsonSerializer = clientAccountSummaryToApiJsonSerializer; this.clientAccountSummaryToApiJsonSerializer = clientAccountSummaryToApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper; this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandSourceReadPlatformService = commandsReadPlatformService;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
} }
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllIndividualClients(@Context final UriInfo uriInfo, @QueryParam("sqlSearch") final String sqlSearch,
@QueryParam("officeId") final Long officeId, @QueryParam("externalId") final String externalId,
@QueryParam("displayName") final String displayName, @QueryParam("firstName") final String firstName,
@QueryParam("lastName") final String lastName, @QueryParam("underHierarchy") final String hierarchy) {
context.authenticatedUser().validateHasReadPermission("CLIENT");
// FIXME - kw - rather than exposing SQL through the api can we not just
// pass parameter we want to search by and build up sql like is done for
// other parameters?
final String extraCriteria = buildSqlStringFromClientCriteria(sqlSearch, officeId, externalId, displayName, firstName, lastName,
hierarchy);
final Collection<ClientData> clients = this.clientReadPlatformService.retrieveAllIndividualClients(extraCriteria);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, clients, CLIENT_DATA_PARAMETERS);
}
private String buildSqlStringFromClientCriteria(final String sqlSearch, final Long officeId, final String externalId,
final String displayName, final String firstName, final String lastName, final String hierarchy) {
String extraCriteria = "";
if (sqlSearch != null) {
extraCriteria = " and (" + sqlSearch + ")";
}
if (officeId != null) {
extraCriteria += " and office_id = " + officeId;
}
if (externalId != null) {
extraCriteria += " and external_id like " + ApiParameterHelper.sqlEncodeString(externalId);
}
if (displayName != null) {
extraCriteria += " and concat(ifnull(firstname, ''), if(firstname > '',' ', '') , ifnull(lastname, '')) like "
+ ApiParameterHelper.sqlEncodeString(displayName);
}
if (firstName != null) {
extraCriteria += " and firstname like " + ApiParameterHelper.sqlEncodeString(firstName);
}
if (lastName != null) {
extraCriteria += " and lastname like " + ApiParameterHelper.sqlEncodeString(lastName);
}
if (hierarchy != null) {
extraCriteria += " and o.hierarchy like " + ApiParameterHelper.sqlEncodeString(hierarchy + "%");
}
if (StringUtils.isNotBlank(extraCriteria)) {
extraCriteria = extraCriteria.substring(4);
}
return extraCriteria;
}
@GET
@Path("{clientId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveClient(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission("CLIENT");
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId);
if (settings.isTemplate()) {
// FIXME - KW - no need for special OfficeLookup object, just use
// OfficeData and only populate with id, name, nameDecorated
final List<OfficeLookup> allowedOffices = new ArrayList<OfficeLookup>(officeReadPlatformService.retrieveAllOfficesForLookup());
clientData = ClientData.templateOnTop(clientData, allowedOffices);
}
if (settings.isCommandIdPassed()) {
clientData = handleRequestToIntegrateProposedChangesFromCommand(clientId, settings.getCommandId(), clientData);
}
// pick up possibility of changes by default - might push down a layer
// into the retrieve client method
final Collection<ClientData> dataChanges = retrieveAllUnprocessedDataChanges(clientId);
clientData = ClientData.integrateChanges(clientData, clientData.currentChange(), dataChanges);
return this.toApiJsonSerializer.serialize(settings, clientData, CLIENT_DATA_PARAMETERS);
}
private ClientData handleRequestToIntegrateProposedChangesFromCommand(final Long clientId, final Long commandId,
final ClientData clientData) {
final CommandSourceData entry = this.commandSourceReadPlatformService.retrieveById(commandId);
final ClientCommand command = this.clientCommandFromApiJsonDeserializer.commandFromApiJson(entry.json());
final ClientData currentChange = ClientData.dataChangeInstance(clientId, command.getOfficeId(), command.getExternalId(),
command.getFirstname(), command.getMiddlename(), command.getLastname(), command.getFullname(), command.getJoiningDate());
final Collection<ClientData> dataChanges = null; // retrieveAllUnprocessedDataChanges(clientId);
return ClientData.integrateChanges(clientData, currentChange, dataChanges);
}
private Collection<ClientData> retrieveAllUnprocessedDataChanges(final Long clientId) {
Collection<ClientData> dataChanges = new ArrayList<ClientData>();
Collection<CommandSourceData> unprocessedChanges = this.commandSourceReadPlatformService.retrieveUnprocessChangesByEntityNameAndId(
"CLIENT", clientId);
for (CommandSourceData commandSourceData : unprocessedChanges) {
final ClientCommand command = this.clientCommandFromApiJsonDeserializer.commandFromApiJson(commandSourceData.json());
final ClientData change = ClientData
.dataChangeInstance(clientId, command.getOfficeId(), command.getExternalId(), command.getFirstname(),
command.getMiddlename(), command.getLastname(), command.getFullname(), command.getJoiningDate());
dataChanges.add(change);
}
if (dataChanges.isEmpty()) {
dataChanges = null;
}
return dataChanges;
}
@GET @GET
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String newClientDetails(@Context final UriInfo uriInfo) { public String retrieveTemplate(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission("CLIENT"); context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
final ClientData clientData = this.clientReadPlatformService.retrieveTemplate();
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("sqlSearch") final String sqlSearch,
@QueryParam("officeId") final Long officeId, @QueryParam("externalId") final String externalId,
@QueryParam("displayName") final String displayName, @QueryParam("firstName") final String firstname,
@QueryParam("lastName") final String lastname, @QueryParam("underHierarchy") final String hierarchy) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
lastname, hierarchy);
final Collection<ClientData> clients = this.clientReadPlatformService.retrieveAll(searchParameters);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, clients, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{clientId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveOne(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
ClientData clientData = this.clientReadPlatformService.retrieveNewClientDetails(); ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
if (settings.isCommandIdPassed()) { if (settings.isTemplate()) {
clientData = handleRequestToIntegrateProposedChangesFromCommand(null, settings.getCommandId(), clientData); final List<OfficeData> allowedOffices = new ArrayList<OfficeData>(officeReadPlatformService.retrieveAllOfficesForDropdown());
clientData = ClientData.templateOnTop(clientData, allowedOffices);
} }
return this.toApiJsonSerializer.serialize(settings, clientData, CLIENT_DATA_PARAMETERS); return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
} }
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String createClient(final String apiRequestBodyAsJson) { public String create(final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder() // final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.createClient() // .createClient() //
@@ -247,7 +143,7 @@ public class ClientsApiResource {
@Path("{clientId}") @Path("{clientId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String updateClient(@PathParam("clientId") final Long clientId, final String apiRequestBodyAsJson) { public String update(@PathParam("clientId") final Long clientId, final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder() // final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.updateClient(clientId) // .updateClient(clientId) //
@@ -263,7 +159,7 @@ public class ClientsApiResource {
@Path("{clientId}") @Path("{clientId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String deleteClient(@PathParam("clientId") final Long clientId) { public String delete(@PathParam("clientId") final Long clientId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder() // final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.deleteClient(clientId) // .deleteClient(clientId) //
@@ -274,26 +170,49 @@ public class ClientsApiResource {
return this.toApiJsonSerializer.serialize(result); return this.toApiJsonSerializer.serialize(result);
} }
@POST
@Path("{clientId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String activate(@PathParam("clientId") final Long clientId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson) {
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandProcessingResult result = null;
if (is(commandParam, "activate")) {
final CommandWrapper commandRequest = builder.activateClient(clientId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
if (result == null) {
//
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate" });
}
return this.toApiJsonSerializer.serialize(result);
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
@GET @GET
@Path("{clientId}/loans") @Path("{clientId}/loans")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveClientAccounts(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) { public String retrieveAssociatedAccounts(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission("CLIENT"); context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
final ClientAccountSummaryCollectionData clientAccount = this.clientReadPlatformService.retrieveClientAccountDetails(clientId); final ClientAccountSummaryCollectionData clientAccount = this.clientReadPlatformService.retrieveClientAccountDetails(clientId);
final Set<String> CLIENT_ACCOUNTS_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("pendingApprovalLoans", final Set<String> CLIENT_ACCOUNTS_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("pendingApprovalLoans",
"awaitingDisbursalLoans", "openLoans", "closedLoans", "anyLoanCount", "pendingApprovalLoanCount", "awaitingDisbursalLoans", "openLoans", "closedLoans", "anyLoanCount", "pendingApprovalLoanCount",
"awaitingDisbursalLoanCount", "activeLoanCount", "closedLoanCount", "pendingApprovalDepositAccountsCount", "awaitingDisbursalLoanCount", "activeLoanCount", "closedLoanCount", "pendingApprovalSavingAccountsCount",
"pendingApprovalDepositAccounts", "approvedDepositAccountsCount", "approvedDepositAccounts", "pendingApprovalSavingAccounts", "approvedSavingAccountsCount", "approvedSavingAccounts",
"withdrawnByClientDepositAccountsCount", "withdrawnByClientDepositAccounts", "closedDepositAccountsCount", "withdrawnByClientSavingAccountsCount", "withdrawnByClientSavingAccounts", "rejectedSavingAccountsCount",
"closedDepositAccounts", "maturedDepositAccountsCount", "maturedDepositAccounts", "rejectedDepositAccountsCount", "rejectedSavingAccounts", "closedSavingAccountsCount", "closedSavingAccounts"));
"rejectedDepositAccounts", "preclosedDepositAccountsCount", "preclosedDepositAccounts",
"pendingApprovalSavingAccountsCount", "pendingApprovalSavingAccounts", "approvedSavingAccountsCount",
"approvedSavingAccounts", "withdrawnByClientSavingAccountsCount", "withdrawnByClientSavingAccounts",
"rejectedSavingAccountsCount", "rejectedSavingAccounts", "closedSavingAccountsCount", "closedSavingAccounts"));
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount, CLIENT_ACCOUNTS_DATA_PARAMETERS); return this.clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount, CLIENT_ACCOUNTS_DATA_PARAMETERS);
@@ -13,7 +13,8 @@ import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.HashCodeBuilder;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.organisation.office.data.OfficeLookup; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.data.GroupGeneralData;
/** /**
@@ -24,78 +25,70 @@ final public class ClientData implements Comparable<ClientData> {
private final Long id; private final Long id;
private final String accountNo; private final String accountNo;
private final String externalId; private final String externalId;
private final EnumOptionData status;
private final LocalDate activationDate;
private final String firstname; private final String firstname;
private final String middlename; private final String middlename;
private final String lastname; private final String lastname;
private final String fullname; private final String fullname;
private final String displayName; private final String displayName;
private final Long officeId; private final Long officeId;
private final String officeName; private final String officeName;
private final LocalDate joinedDate;
private final String imageKey; private final String imageKey;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Boolean imagePresent; private final Boolean imagePresent;
private final Boolean clientPendingApprovalAllowed;
// associations // associations
private final Collection<GroupGeneralData> parentGroups; private final Collection<GroupGeneralData> groups;
// template // template
private final List<OfficeLookup> allowedOffices; private final Collection<OfficeData> officeOptions;
private final ClientData currentChange;
private final Collection<ClientData> allChanges;
public static ClientData dataChangeInstance(final Long id, final Long officeId, final String externalId, final String firstname, public static ClientData template(final Long officeId, final LocalDate joinedDate, final Boolean clientPendingApprovalAllowed,
final String middlename, final String lastname, final String fullname, final LocalDate joiningDate) { final Collection<OfficeData> allowedOffices) {
return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null,
String localDisplayName = null; clientPendingApprovalAllowed, allowedOffices, null);
return new ClientData(null, officeId, null, id, firstname, middlename, lastname, fullname, localDisplayName, externalId,
joiningDate, null, null, null, null, null);
} }
public static ClientData integrateChanges(final ClientData clientData, final ClientData currentChange, public static ClientData templateOnTop(final ClientData clientData, final List<OfficeData> allowedOffices) {
final Collection<ClientData> allChanges) {
return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname,
clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId,
clientData.joinedDate, clientData.imageKey, clientData.allowedOffices, currentChange, allChanges, clientData.parentGroups);
}
public static ClientData template(final Long officeId, final LocalDate joinedDate, final List<OfficeLookup> allowedOffices) { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id,
return new ClientData(null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, allowedOffices, null, null, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName,
null); clientData.externalId, clientData.activationDate, clientData.imageKey, clientData.clientPendingApprovalAllowed,
} allowedOffices, clientData.groups);
public static ClientData templateOnTop(final ClientData clientData, final List<OfficeLookup> allowedOffices) {
return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname,
clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId,
clientData.joinedDate, clientData.imageKey, allowedOffices, clientData.currentChange, clientData.allChanges,
clientData.parentGroups);
} }
public static ClientData setParentGroups(final ClientData clientData, final Collection<GroupGeneralData> parentGroups) { public static ClientData setParentGroups(final ClientData clientData, final Collection<GroupGeneralData> parentGroups) {
return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname, return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id,
clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName,
clientData.joinedDate, clientData.imageKey, clientData.allowedOffices, clientData.currentChange, clientData.allChanges, clientData.externalId, clientData.activationDate, clientData.imageKey, clientData.clientPendingApprovalAllowed,
parentGroups); clientData.officeOptions, parentGroups);
} }
public static ClientData clientIdentifier(final Long id, final String accountIdentifier, final String firstname, public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname,
final String middlename, final String lastname, final String fullname, final String displayName, final Long officeId, final String middlename, final String lastname, final String fullname, final String displayName, final Long officeId,
final String officeName) { final String officeName) {
return new ClientData(accountIdentifier, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, null, return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, null,
null, null, null, null, null, null); null, null, null, null, null);
} }
public static ClientData lookup(final Long id, final String displayName, final Long officeId, final String officeName) { public static ClientData lookup(final Long id, final String displayName, final Long officeId, final String officeName) {
return new ClientData(null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, null, null); return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, null);
} }
public ClientData(final String accountNo, final Long officeId, final String officeName, final Long id, final String firstname, public ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id,
final String middlename, final String lastname, final String fullname, final String displayName, final String externalId, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName,
final LocalDate joinedDate, final String imageKey, final List<OfficeLookup> allowedOffices, final ClientData currentChange, final String externalId, final LocalDate activationDate, final String imageKey, final Boolean clientPendingApprovalAllowed,
final Collection<ClientData> allChanges, final Collection<GroupGeneralData> parentGroups) { final Collection<OfficeData> allowedOffices, final Collection<GroupGeneralData> groups) {
this.accountNo = accountNo; this.accountNo = accountNo;
this.status = status;
this.officeId = officeId; this.officeId = officeId;
this.officeName = officeName; this.officeName = officeName;
this.id = id; this.id = id;
@@ -105,17 +98,49 @@ final public class ClientData implements Comparable<ClientData> {
this.fullname = StringUtils.defaultIfEmpty(fullname, null); this.fullname = StringUtils.defaultIfEmpty(fullname, null);
this.displayName = StringUtils.defaultIfEmpty(displayName, null); this.displayName = StringUtils.defaultIfEmpty(displayName, null);
this.externalId = StringUtils.defaultIfEmpty(externalId, null); this.externalId = StringUtils.defaultIfEmpty(externalId, null);
this.joinedDate = joinedDate; this.activationDate = activationDate;
this.imageKey = imageKey; this.imageKey = imageKey;
if (imageKey != null) { if (imageKey != null) {
this.imagePresent = Boolean.TRUE; this.imagePresent = Boolean.TRUE;
} else { } else {
this.imagePresent = null; this.imagePresent = null;
} }
this.allowedOffices = allowedOffices;
this.currentChange = currentChange; this.clientPendingApprovalAllowed = clientPendingApprovalAllowed;
this.allChanges = allChanges;
this.parentGroups = parentGroups; // associations
this.groups = groups;
// template
this.officeOptions = allowedOffices;
}
public Long id() {
return this.id;
}
public String displayName() {
return this.displayName;
}
public Long officeId() {
return this.officeId;
}
public String officeName() {
return this.officeName;
}
public String imageKey() {
return this.imageKey;
}
public boolean imageKeyDoesNotExist() {
return !imageKeyExists();
}
private boolean imageKeyExists() {
return StringUtils.isNotBlank(this.imageKey);
} }
@Override @Override
@@ -147,38 +172,7 @@ final public class ClientData implements Comparable<ClientData> {
.toHashCode(); .toHashCode();
} }
public String displayName() { // TODO - kw - look into removing usage of the getters below
return this.displayName;
}
public String officeName() {
return this.officeName;
}
private boolean imageKeyExists() {
return StringUtils.isNotBlank(this.imageKey);
}
public boolean imageKeyDoesNotExist() {
return !imageKeyExists();
}
public String imageKey() {
return this.imageKey;
}
public Long id() {
return this.id;
}
public Long officeId() {
return this.officeId;
}
public ClientData currentChange() {
return this.currentChange;
}
public String getExternalId() { public String getExternalId() {
return this.externalId; return this.externalId;
} }
@@ -191,15 +185,7 @@ final public class ClientData implements Comparable<ClientData> {
return this.lastname; return this.lastname;
} }
public Long getOfficeId() { public LocalDate getActivationDate() {
return this.officeId; return this.activationDate;
}
public String getOfficeName() {
return this.officeName;
}
public LocalDate getJoinedDate() {
return this.joinedDate;
} }
} }
@@ -0,0 +1,186 @@
/**
* 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.client.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName;
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.client.api.ClientApiConstants;
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 final class ClientDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public ClientDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json, final boolean isPendingApprovalEnabled) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_REQUEST_DATA_PARAMETERS);
final JsonElement element = fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ClientApiConstants.CLIENT_RESOURCE_NAME);
if (fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) {
final String accountNo = fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) {
final String externalId = fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.externalIdParamName).value(externalId).ignoreIfNull()
.notExceedingLengthOf(100);
}
final Long officeId = fromApiJsonHelper.extractLongNamed(ClientApiConstants.officeIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.officeIdParamName).value(officeId).notNull().integerGreaterThanZero();
if (fromApiJsonHelper.parameterExists(ClientApiConstants.groupIdParamName, element)) {
final Long groupId = fromApiJsonHelper.extractLongNamed(ClientApiConstants.groupIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.groupIdParamName).value(groupId).notNull().integerGreaterThanZero();
}
final Boolean active = fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element);
if (active != null) {
if (active.booleanValue()) {
final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.activationDateParamName).value(joinedDate).notNull();
} else {
if (!isPendingApprovalEnabled) {
baseDataValidator.reset().parameter(ClientApiConstants.activeParamName).failWithCode(".pending.status.not.allowed");
}
}
} else {
baseDataValidator.reset().parameter(ClientApiConstants.activeParamName).value(active).trueOrFalseRequired(false);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(final String json, final boolean isPendingApprovalEnabled) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_REQUEST_DATA_PARAMETERS);
final JsonElement element = fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ClientApiConstants.CLIENT_RESOURCE_NAME);
boolean atLeastOneParameterPassedForUpdate = false;
if (fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String accountNo = fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String externalId = fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.externalIdParamName).value(externalId).notExceedingLengthOf(100);
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.officeIdParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
final Long officeId = fromApiJsonHelper.extractLongNamed(ClientApiConstants.officeIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.officeIdParamName).value(officeId).notNull().integerGreaterThanZero();
}
final Boolean active = fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element);
if (active != null) {
if (active.booleanValue()) {
final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.activationDateParamName).value(joinedDate).notNull();
} else {
if (!isPendingApprovalEnabled) {
baseDataValidator.reset().parameter(ClientApiConstants.activeParamName).failWithCode(".pending.status.not.allowed");
}
}
} else {
baseDataValidator.reset().parameter(ClientApiConstants.activeParamName).value(active).trueOrFalseRequired(false);
}
if (!atLeastOneParameterPassedForUpdate) {
final Object forceError = null;
baseDataValidator.reset().anyOfNotNull(forceError);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateActivation(final JsonCommand command) {
final String json = command.json();
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.ACTIVATION_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ClientApiConstants.CLIENT_RESOURCE_NAME);
final JsonElement element = command.parsedJson();
final LocalDate activationDate = fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element);
baseDataValidator.reset().parameter(activationDateParamName).value(activationDate).notNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
//
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
}
}
@@ -27,18 +27,21 @@ import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator; import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator;
import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.portfolio.client.api.ClientApiConstants;
import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.Group;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity @Entity
@Table(name = "m_client", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "account_no_UNIQUE") }) @Table(name = "m_client", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "account_no_UNIQUE") })
public class Client extends AbstractPersistable<Long> { public final class Client extends AbstractPersistable<Long> {
@Column(name = "account_no", length = 20, unique = true, nullable = false) @Column(name = "account_no", length = 20, unique = true, nullable = false)
private String accountNumber; private String accountNumber;
@@ -47,6 +50,12 @@ public class Client extends AbstractPersistable<Long> {
@JoinColumn(name = "office_id", nullable = false) @JoinColumn(name = "office_id", nullable = false)
private Office office; private Office office;
/**
* A value from {@link ClientStatus}.
*/
@Column(name = "status_enum", nullable = false)
private Integer status;
@Column(name = "firstname", length = 50) @Column(name = "firstname", length = 50)
private String firstname; private String firstname;
@@ -60,61 +69,80 @@ public class Client extends AbstractPersistable<Long> {
private String fullname; private String fullname;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Column(name = "display_name", length = 100) @Column(name = "display_name", length = 100, nullable = false)
private String displayName; private String displayName;
@Column(name = "joined_date") @Column(name = "activation_date", nullable = true)
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
private Date joinedDate; private Date activationDate;
@Column(name = "external_id", length = 100, unique = true) @Column(name = "external_id", length = 100, nullable = true, unique = true)
private String externalId; private String externalId;
@Column(name = "is_deleted", nullable = false) @Column(name = "is_deleted", nullable = false)
private boolean deleted = false; private boolean deleted = false;
@Column(name = "image_key", length = 500) @Column(name = "image_key", length = 500, nullable = true)
private String imageKey; private String imageKey;
@ManyToMany @ManyToMany
@JoinTable(name = "m_group_client", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "group_id")) @JoinTable(name = "m_group_client", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "group_id"))
private Set<Group> parentGroups; private Set<Group> groups;
@Transient @Transient
private boolean accountNumberRequiresAutoGeneration = false; private boolean accountNumberRequiresAutoGeneration = false;
public static Client fromJson(final Office clientOffice, final Group clientParentGroup, final JsonCommand command) { public static Client createNew(final Office clientOffice, final Group clientParentGroup, final JsonCommand command,
final boolean isPendingStateAllowed) {
final String accountNo = command.stringValueOfParameterNamed("accountNo"); final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
final String firstname = command.stringValueOfParameterNamed("firstname"); final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
final String middlename = command.stringValueOfParameterNamed("middlename");
final String lastname = command.stringValueOfParameterNamed("lastname");
final String fullname = command.stringValueOfParameterNamed("fullname");
final LocalDate joiningDate = command.localDateValueOfParameterNamed("joinedDate");
final String externalId = command.stringValueOfParameterNamed("externalId");
return new Client(clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, joiningDate, externalId); final String firstname = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
final String middlename = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
final String lastname = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
final String fullname = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName);
ClientStatus status = ClientStatus.ACTIVE;
boolean active = true;
if (isPendingStateAllowed && command.hasParameter("active")) {
active = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.activeParamName);
}
LocalDate activationDate = null;
if (active) {
activationDate = command.localDateValueOfParameterNamed(ClientApiConstants.activationDateParamName);
} else {
status = ClientStatus.PENDING;
}
return new Client(status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, activationDate,
externalId);
} }
protected Client() { protected Client() {
//
} }
private Client(final Office office, final Group clientParentGroup, final String accountNo, final String firstname, private Client(final ClientStatus status, final Office office, final Group clientParentGroup, final String accountNo,
final String middlename, final String lastname, final String fullname, final LocalDate openingDate, final String externalId) { final String firstname, final String middlename, final String lastname, final String fullname, final LocalDate activationDate,
final String externalId) {
if (StringUtils.isBlank(accountNo)) { if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate(); this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true; this.accountNumberRequiresAutoGeneration = true;
} else { } else {
this.accountNumber = accountNo; this.accountNumber = accountNo;
} }
this.status = status.getValue();
this.office = office; this.office = office;
if (StringUtils.isNotBlank(externalId)) { if (StringUtils.isNotBlank(externalId)) {
this.externalId = externalId.trim(); this.externalId = externalId.trim();
} else { } else {
this.externalId = null; this.externalId = null;
} }
this.joinedDate = openingDate.toDateMidnight().toDate(); if (activationDate != null) {
this.activationDate = activationDate.toDateMidnight().toDate();
}
if (StringUtils.isNotBlank(firstname)) { if (StringUtils.isNotBlank(firstname)) {
this.firstname = firstname.trim(); this.firstname = firstname.trim();
} else { } else {
@@ -140,8 +168,8 @@ public class Client extends AbstractPersistable<Long> {
} }
if (clientParentGroup != null) { if (clientParentGroup != null) {
this.parentGroups = new HashSet<Group>(); this.groups = new HashSet<Group>();
this.parentGroups.add(clientParentGroup); this.groups.add(clientParentGroup);
} }
deriveDisplayName(); deriveDisplayName();
@@ -173,10 +201,65 @@ public class Client extends AbstractPersistable<Long> {
this.accountNumberRequiresAutoGeneration = false; this.accountNumberRequiresAutoGeneration = false;
} }
public void activate(final DateTimeFormatter formatter, final LocalDate activationLocalDate) {
if (isActive()) {
final String defaultUserMessage = "Cannot activate client. Client is already active.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.already.active", defaultUserMessage,
"activationDate", activationLocalDate.toString(formatter));
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
if (isDateInTheFuture(activationLocalDate)) {
final String defaultUserMessage = "Activation date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.activationDate.in.the.future",
defaultUserMessage, "activationDate", activationLocalDate);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
this.activationDate = activationLocalDate.toDate();
this.status = ClientStatus.ACTIVE.getValue();
}
public boolean isNotActive() {
return !isActive();
}
public boolean isActive() {
return ClientStatus.fromInt(this.status).isActive();
}
public boolean isNotPending() {
return !isPending();
}
public boolean isPending() {
return ClientStatus.fromInt(this.status).isPending();
}
private boolean isDateInTheFuture(final LocalDate localDate) {
return localDate.isAfter(DateUtils.getLocalDateOfTenant());
}
public Map<String, Object> update(final JsonCommand command) { public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(9); final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(9);
final String statusEnumParamName = "status";
if (command.isChangeInIntegerParameterNamed(statusEnumParamName, this.status)) {
final Integer newValue = command.integerValueOfParameterNamed(statusEnumParamName);
actualChanges.put(statusEnumParamName, ClientEnumerations.status(newValue));
this.status = ClientStatus.fromInt(newValue).getValue();
}
final String accountNoParamName = "accountNo"; final String accountNoParamName = "accountNo";
if (command.isChangeInStringParameterNamed(accountNoParamName, this.accountNumber)) { if (command.isChangeInStringParameterNamed(accountNoParamName, this.accountNumber)) {
final String newValue = command.stringValueOfParameterNamed(accountNoParamName); final String newValue = command.stringValueOfParameterNamed(accountNoParamName);
@@ -238,7 +321,7 @@ public class Client extends AbstractPersistable<Long> {
actualChanges.put("locale", localeAsInput); actualChanges.put("locale", localeAsInput);
final LocalDate newValue = command.localDateValueOfParameterNamed(joiningDateParamName); final LocalDate newValue = command.localDateValueOfParameterNamed(joiningDateParamName);
this.joinedDate = newValue.toDate(); this.activationDate = newValue.toDate();
} }
deriveDisplayName(); deriveDisplayName();
@@ -297,8 +380,8 @@ public class Client extends AbstractPersistable<Long> {
private LocalDate getJoiningLocalDate() { private LocalDate getJoiningLocalDate() {
LocalDate joiningLocalDate = null; LocalDate joiningLocalDate = null;
if (this.joinedDate != null) { if (this.activationDate != null) {
joiningLocalDate = LocalDate.fromDateFields(this.joinedDate); joiningLocalDate = LocalDate.fromDateFields(this.activationDate);
} }
return joiningLocalDate; return joiningLocalDate;
} }
@@ -335,7 +418,7 @@ public class Client extends AbstractPersistable<Long> {
} }
public Date getJoiningDate() { public Date getJoiningDate() {
return joinedDate; return activationDate;
} }
public String getExternalId() { public String getExternalId() {
@@ -0,0 +1,36 @@
/**
* 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.client.domain;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
public class ClientEnumerations {
public static EnumOptionData status(final Integer statusId) {
return status(ClientStatus.fromInt(statusId));
}
public static EnumOptionData status(final ClientStatus status) {
EnumOptionData optionData = new EnumOptionData(ClientStatus.INVALID.getValue().longValue(), ClientStatus.INVALID.getCode(),
"Invalid");
switch (status) {
case INVALID:
optionData = new EnumOptionData(ClientStatus.INVALID.getValue().longValue(), ClientStatus.INVALID.getCode(), "Invalid");
break;
case PENDING:
optionData = new EnumOptionData(ClientStatus.PENDING.getValue().longValue(), ClientStatus.PENDING.getCode(), "Pending");
break;
case ACTIVE:
optionData = new EnumOptionData(ClientStatus.ACTIVE.getValue().longValue(), ClientStatus.ACTIVE.getCode(), "Active");
break;
case CLOSED:
optionData = new EnumOptionData(ClientStatus.CLOSED.getValue().longValue(), ClientStatus.CLOSED.getCode(), "Closed");
break;
}
return optionData;
}
}
@@ -27,8 +27,19 @@ public class ClientRepositoryWrapper {
public Client findOneWithNotFoundDetection(final Long id) { public Client findOneWithNotFoundDetection(final Long id) {
final Client client = this.repository.findOne(id); final Client client = this.repository.findOne(id);
if (client == null) { throw new ClientNotFoundException(id); } if (client == null || client.isDeleted()) { throw new ClientNotFoundException(id); }
return client; return client;
} }
public void save(final Client client) {
this.repository.save(client);
}
public void saveAndFlush(final Client client) {
this.repository.saveAndFlush(client);
}
public void delete(final Client client) {
this.repository.delete(client);
}
} }
@@ -0,0 +1,66 @@
/**
* 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.client.domain;
/**
* Enum representation of client status states.
*/
public enum ClientStatus {
INVALID(0, "clientStatusType.invalid"), //
PENDING(100, "clientStatusType.pending"), //
ACTIVE(300, "clientStatusType.active"), //
CLOSED(600, "clientStatusType.closed");
private final Integer value;
private final String code;
public static ClientStatus fromInt(final Integer statusValue) {
ClientStatus enumeration = ClientStatus.INVALID;
switch (statusValue) {
case 100:
enumeration = ClientStatus.PENDING;
break;
case 300:
enumeration = ClientStatus.ACTIVE;
break;
case 600:
enumeration = ClientStatus.CLOSED;
break;
}
return enumeration;
}
private ClientStatus(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public boolean hasStateOf(final ClientStatus state) {
return this.value.equals(state.getValue());
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return code;
}
public boolean isPending() {
return this.value.equals(ClientStatus.PENDING.getValue());
}
public boolean isActive() {
return this.value.equals(ClientStatus.ACTIVE.getValue());
}
public boolean isClosed() {
return this.value.equals(ClientStatus.CLOSED.getValue());
}
}
@@ -11,12 +11,9 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceN
* A {@link RuntimeException} thrown when client Identifier resources are not * A {@link RuntimeException} thrown when client Identifier resources are not
* found. * found.
*/ */
public class ClientIdentifierNotFoundException extends public class ClientIdentifierNotFoundException extends AbstractPlatformResourceNotFoundException {
AbstractPlatformResourceNotFoundException {
public ClientIdentifierNotFoundException(Long id) { public ClientIdentifierNotFoundException(final Long id) {
super("error.msg.clientIdentifier.id.invalid", super("error.msg.clientIdentifier.id.invalid", "Client Identifier with the primary key " + id + " does not exist", id);
"Client Identifier with the primary key " + id }
+ " does not exist", id);
}
} }
@@ -0,0 +1,19 @@
/**
* 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.client.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when attempting to delete clients
*/
public class ClientMustBePendingToBeDeletedException extends AbstractPlatformDomainRuleException {
public ClientMustBePendingToBeDeletedException(final Long id) {
super("error.msg.clients.cannot.be.deleted",
"Client with identifier " + id + " cannot be deleted as it is not in `Pending` state.", id);
}
}
@@ -12,7 +12,7 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceN
*/ */
public class ClientNotFoundException extends AbstractPlatformResourceNotFoundException { public class ClientNotFoundException extends AbstractPlatformResourceNotFoundException {
public ClientNotFoundException(Long id) { public ClientNotFoundException(final Long id) {
super("error.msg.client.id.invalid", "Client with identifier " + id + " does not exist", id); super("error.msg.client.id.invalid", "Client with identifier " + id + " does not exist", id);
} }
} }
@@ -0,0 +1,32 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.client.handler;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.portfolio.client.service.ClientWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ActivateClientCommandHandler implements NewCommandSourceHandler {
private final ClientWritePlatformService clientWritePlatformService;
@Autowired
public ActivateClientCommandHandler(final ClientWritePlatformService clientWritePlatformService) {
this.clientWritePlatformService = clientWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.clientWritePlatformService.activateClient(command.entityId(), command);
}
}
@@ -1,196 +0,0 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.client.serialization;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.client.command.ClientCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
/**
* Implementation of {@link FromApiJsonDeserializer} for {@link ClientCommand}
* 's.
*/
@Component
public final class ClientCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<ClientCommand> {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "accountNo", "externalId", "firstname",
"middlename", "lastname", "fullname", "officeId", "joinedDate", "locale", "dateFormat", "groupId"));
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public ClientCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
@Override
public ClientCommand commandFromApiJson(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final String accountNo = fromApiJsonHelper.extractStringNamed("accountNo", element);
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
final String firstname = fromApiJsonHelper.extractStringNamed("firstname", element);
final String middlename = fromApiJsonHelper.extractStringNamed("middlename", element);
final String lastname = fromApiJsonHelper.extractStringNamed("lastname", element);
final String fullname = fromApiJsonHelper.extractStringNamed("fullname", element);
final Long officeId = fromApiJsonHelper.extractLongNamed("officeId", element);
final LocalDate joiningDate = fromApiJsonHelper.extractLocalDateNamed("joinedDate", element);
return new ClientCommand(accountNo, externalId, firstname, middlename, lastname, fullname, officeId, joiningDate);
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client");
boolean atLeastOneParameterPassedForUpdate = false;
final String accountNoParameterName = "accountNo";
if (fromApiJsonHelper.parameterExists(accountNoParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String accountNo = fromApiJsonHelper.extractStringNamed(accountNoParameterName, element);
baseDataValidator.reset().parameter(accountNoParameterName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
final String externalIdParameterName = "externalId";
if (fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String externalId = fromApiJsonHelper.extractStringNamed(externalIdParameterName, element);
baseDataValidator.reset().parameter(externalIdParameterName).value(externalId).ignoreIfNull().notExceedingLengthOf(100);
}
final String fullnameParameterName = "fullname";
if (fromApiJsonHelper.parameterExists(fullnameParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
final String lastNameParameterName = "lastname";
if (fromApiJsonHelper.parameterExists(lastNameParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
final String middleNameParameterName = "middlename";
if (fromApiJsonHelper.parameterExists(middleNameParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
final String firstNameParameterName = "firstname";
if (fromApiJsonHelper.parameterExists(firstNameParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
final String officeIdParameterName = "officeId";
if (fromApiJsonHelper.parameterExists(officeIdParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final Long officeId = fromApiJsonHelper.extractLongNamed(officeIdParameterName, element);
baseDataValidator.reset().parameter(officeIdParameterName).value(officeId).notNull().integerGreaterThanZero();
}
final String joinedDateParameterName = "joinedDate";
if (fromApiJsonHelper.parameterExists(joinedDateParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String joinedDateStr = fromApiJsonHelper.extractStringNamed(joinedDateParameterName, element);
baseDataValidator.reset().parameter(joinedDateParameterName).value(joinedDateStr).notBlank();
final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(joinedDateParameterName, element);
baseDataValidator.reset().parameter(joinedDateParameterName).value(joinedDate).notNull();
}
if (!atLeastOneParameterPassedForUpdate) {
final Object forceError = null;
baseDataValidator.reset().anyOfNotNull(forceError);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client");
final String accountNoParameterName = "accountNo";
if (fromApiJsonHelper.parameterExists(accountNoParameterName, element)) {
final String accountNo = fromApiJsonHelper.extractStringNamed(accountNoParameterName, element);
baseDataValidator.reset().parameter(accountNoParameterName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
final String externalIdParameterName = "externalId";
if (fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
final String externalId = fromApiJsonHelper.extractStringNamed(externalIdParameterName, element);
baseDataValidator.reset().parameter(externalIdParameterName).value(externalId).ignoreIfNull().notExceedingLengthOf(100);
}
final String officeIdParameterName = "officeId";
if (fromApiJsonHelper.parameterExists(officeIdParameterName, element)) {
final Long officeId = fromApiJsonHelper.extractLongNamed(officeIdParameterName, element);
baseDataValidator.reset().parameter(officeIdParameterName).value(officeId).notNull().integerGreaterThanZero();
}
final String groupIdParameterName = "groupId";
if (fromApiJsonHelper.parameterExists(groupIdParameterName, element)) {
final Long groupId = fromApiJsonHelper.extractLongNamed(groupIdParameterName, element);
baseDataValidator.reset().parameter(groupIdParameterName).value(groupId).notNull().integerGreaterThanZero();
}
final String joinedDateParameterName = "joinedDate";
if (fromApiJsonHelper.parameterExists(joinedDateParameterName, element)) {
final String joinedDateStr = fromApiJsonHelper.extractStringNamed(joinedDateParameterName, element);
baseDataValidator.reset().parameter(joinedDateParameterName).value(joinedDateStr).notBlank();
final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(joinedDateParameterName, element);
baseDataValidator.reset().parameter(joinedDateParameterName).value(joinedDate).notNull();
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -10,18 +10,19 @@ import java.util.Collection;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData;
import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.group.service.SearchParameters;
public interface ClientReadPlatformService { public interface ClientReadPlatformService {
Collection<ClientData> retrieveAllIndividualClients(String extraCriteria); ClientData retrieveTemplate();
ClientData retrieveIndividualClient(Long clientId); Collection<ClientData> retrieveAll(SearchParameters searchParameters);
ClientData retrieveNewClientDetails(); ClientData retrieveOne(Long clientId);
Collection<ClientData> retrieveAllIndividualClientsForLookup(String extraCriteria); Collection<ClientData> retrieveAllForLookup(String extraCriteria);
Collection<ClientData> retrieveAllIndividualClientsForLookupByOfficeId(Long officeId); Collection<ClientData> retrieveAllForLookupByOfficeId(Long officeId);
ClientAccountSummaryCollectionData retrieveClientAccountDetails(Long clientId); ClientAccountSummaryCollectionData retrieveClientAccountDetails(Long clientId);
@@ -13,16 +13,21 @@ import java.util.List;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeLookup; import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData;
import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.data.GroupGeneralData;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData; import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
@@ -37,61 +42,126 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final ConfigurationDomainService configurationDomainService;
private final OfficeReadPlatformService officeReadPlatformService; private final OfficeReadPlatformService officeReadPlatformService;
// data mappers
private final ClientMapper clientMapper = new ClientMapper();
private final ClientLookupMapper lookupMapper = new ClientLookupMapper(); private final ClientLookupMapper lookupMapper = new ClientLookupMapper();
private final ClientMembersOfGroupMapper membersOfGroupMapper = new ClientMembersOfGroupMapper(); private final ClientMembersOfGroupMapper membersOfGroupMapper = new ClientMembersOfGroupMapper();
private final ParentGroupsMapper clientGroupsMapper = new ParentGroupsMapper();
@Autowired @Autowired
public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource, public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource,
final OfficeReadPlatformService officeReadPlatformService) { final ConfigurationDomainService configurationDomainService, final OfficeReadPlatformService officeReadPlatformService) {
this.context = context; this.context = context;
this.configurationDomainService = configurationDomainService;
this.officeReadPlatformService = officeReadPlatformService; this.officeReadPlatformService = officeReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
} }
@Override @Override
public Collection<ClientData> retrieveAllIndividualClients(final String extraCriteria) { public ClientData retrieveTemplate() {
AppUser currentUser = context.authenticatedUser(); final AppUser currentUser = context.authenticatedUser();
String hierarchy = currentUser.getOffice().getHierarchy();
String hierarchySearchString = hierarchy + "%";
ClientMapper rm = new ClientMapper(); final Collection<OfficeData> offices = officeReadPlatformService.retrieveAllOfficesForDropdown();
String sql = "select " + rm.clientSchema(); final boolean clientPendingApprovalAllowed = this.configurationDomainService.isClientPendingApprovalAllowedEnabled();
final Long officeId = currentUser.getOffice().getId();
return ClientData.template(officeId, new LocalDate(), clientPendingApprovalAllowed, offices);
}
@Override
public Collection<ClientData> retrieveAll(final SearchParameters searchParameters) {
final AppUser currentUser = context.authenticatedUser();
final String hierarchy = currentUser.getOffice().getHierarchy();
final String hierarchySearchString = hierarchy + "%";
String sql = "select " + this.clientMapper.schema() + " where o.hierarchy like ? and c.is_deleted=0";
final String extraCriteria = buildSqlStringFromClientCriteria(searchParameters);
if (StringUtils.isNotBlank(extraCriteria)) sql += " and (" + extraCriteria + ")"; if (StringUtils.isNotBlank(extraCriteria)) sql += " and (" + extraCriteria + ")";
sql += " order by c.display_name ASC, c.account_no ASC"; sql += " order by c.display_name ASC, c.account_no ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { hierarchySearchString }); return this.jdbcTemplate.query(sql, clientMapper, new Object[] { hierarchySearchString });
}
private String buildSqlStringFromClientCriteria(final SearchParameters searchParameters) {
final String sqlSearch = searchParameters.getSqlSearch();
final Long officeId = searchParameters.getOfficeId();
final String externalId = searchParameters.getExternalId();
final String displayName = searchParameters.getName();
final String firstname = searchParameters.getFirstname();
final String lastname = searchParameters.getLastname();
final String hierarchy = searchParameters.getHierarchy();
String extraCriteria = "";
if (sqlSearch != null) {
extraCriteria = " and (" + sqlSearch + ")";
}
if (officeId != null) {
extraCriteria += " and office_id = " + officeId;
}
if (externalId != null) {
extraCriteria += " and external_id like " + ApiParameterHelper.sqlEncodeString(externalId);
}
if (displayName != null) {
extraCriteria += " and concat(ifnull(firstname, ''), if(firstname > '',' ', '') , ifnull(lastname, '')) like "
+ ApiParameterHelper.sqlEncodeString(displayName);
}
if (firstname != null) {
extraCriteria += " and firstname like " + ApiParameterHelper.sqlEncodeString(firstname);
}
if (lastname != null) {
extraCriteria += " and lastname like " + ApiParameterHelper.sqlEncodeString(lastname);
}
if (hierarchy != null) {
extraCriteria += " and o.hierarchy like " + ApiParameterHelper.sqlEncodeString(hierarchy + "%");
}
if (StringUtils.isNotBlank(extraCriteria)) {
extraCriteria = extraCriteria.substring(4);
}
return extraCriteria;
} }
@Override @Override
public ClientData retrieveIndividualClient(final Long clientId) { public ClientData retrieveOne(final Long clientId) {
try { try {
AppUser currentUser = context.authenticatedUser(); AppUser currentUser = context.authenticatedUser();
String hierarchy = currentUser.getOffice().getHierarchy(); String hierarchy = currentUser.getOffice().getHierarchy();
String hierarchySearchString = hierarchy + "%"; String hierarchySearchString = hierarchy + "%";
ClientMapper rm = new ClientMapper(); String sql = "select " + this.clientMapper.schema() + " where o.hierarchy like ? and c.is_deleted=0 and c.id = ?";
String sql = "select " + rm.clientSchema() + " and c.id = " + clientId; ClientData clientData = this.jdbcTemplate.queryForObject(sql, this.clientMapper,
ClientData clientData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { hierarchySearchString }); new Object[] { hierarchySearchString, clientId });
ParentGroupsMapper cgrm = new ParentGroupsMapper(); String clientGroupsSql = "select " + this.clientGroupsMapper.parentGroupsSchema();
String cgSql = "select " + cgrm.parentGroupsSchema();
Collection<GroupGeneralData> parentGroups = this.jdbcTemplate.query(cgSql, cgrm, new Object[] { clientId });
return ClientData.setParentGroups(clientData , parentGroups);
Collection<GroupGeneralData> parentGroups = this.jdbcTemplate.query(clientGroupsSql, this.clientGroupsMapper, new Object[] { clientId });
return ClientData.setParentGroups(clientData, parentGroups);
} catch (EmptyResultDataAccessException e) { } catch (EmptyResultDataAccessException e) {
throw new ClientNotFoundException(clientId); throw new ClientNotFoundException(clientId);
} }
} }
@Override @Override
public Collection<ClientData> retrieveAllIndividualClientsForLookup(final String extraCriteria) { public Collection<ClientData> retrieveAllForLookup(final String extraCriteria) {
String sql = "select " + this.lookupMapper.schema(); String sql = "select " + this.lookupMapper.schema();
@@ -103,24 +173,13 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
} }
@Override @Override
public Collection<ClientData> retrieveAllIndividualClientsForLookupByOfficeId(final Long officeId) { public Collection<ClientData> retrieveAllForLookupByOfficeId(final Long officeId) {
final String sql = "select " + this.lookupMapper.schema() + " and c.office_id = ?"; final String sql = "select " + this.lookupMapper.schema() + " and c.office_id = ?";
return this.jdbcTemplate.query(sql, this.lookupMapper, new Object[] { officeId }); return this.jdbcTemplate.query(sql, this.lookupMapper, new Object[] { officeId });
} }
@Override
public ClientData retrieveNewClientDetails() {
final AppUser currentUser = context.authenticatedUser();
final List<OfficeLookup> offices = new ArrayList<OfficeLookup>(officeReadPlatformService.retrieveAllOfficesForLookup());
final Long officeId = currentUser.getOffice().getId();
return ClientData.template(officeId, new LocalDate(), offices);
}
@Override @Override
public Collection<ClientData> retrieveClientMembersOfGroup(final Long groupId) { public Collection<ClientData> retrieveClientMembersOfGroup(final Long groupId) {
@@ -145,7 +204,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
sqlBuilder.append("c.office_id as officeId, o.name as officeName, "); sqlBuilder.append("c.office_id as officeId, o.name as officeName, ");
sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "); sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, "); sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
sqlBuilder.append("c.joined_date as joinedDate, c.image_key as imagekey "); sqlBuilder.append("c.activation_date as activationDate, c.image_key as imagekey ");
sqlBuilder.append("from m_client c "); sqlBuilder.append("from m_client c ");
sqlBuilder.append("join m_office o on o.id = c.office_id "); sqlBuilder.append("join m_office o on o.id = c.office_id ");
sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id"); sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id");
@@ -161,6 +220,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
public ClientData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { public ClientData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final String accountNo = rs.getString("accountNo"); final String accountNo = rs.getString("accountNo");
final EnumOptionData status = null;
final Long officeId = JdbcSupport.getLong(rs, "officeId"); final Long officeId = JdbcSupport.getLong(rs, "officeId");
final Long id = JdbcSupport.getLong(rs, "id"); final Long id = JdbcSupport.getLong(rs, "id");
final String firstname = rs.getString("firstname"); final String firstname = rs.getString("firstname");
@@ -169,29 +231,46 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final String fullname = rs.getString("fullname"); final String fullname = rs.getString("fullname");
final String displayName = rs.getString("displayName"); final String displayName = rs.getString("displayName");
final String externalId = rs.getString("externalId"); final String externalId = rs.getString("externalId");
final LocalDate joinedDate = JdbcSupport.getLocalDate(rs, "joinedDate"); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
final String imageKey = rs.getString("imageKey"); final String imageKey = rs.getString("imageKey");
final String officeName = rs.getString("officeName"); final String officeName = rs.getString("officeName");
return new ClientData(accountNo, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, externalId, return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
joinedDate, imageKey, null, null, null, null); externalId, activationDate, imageKey, null, null, null);
} }
} }
private static final class ClientMapper implements RowMapper<ClientData> { private static final class ClientMapper implements RowMapper<ClientData> {
public String clientSchema() { private final String schema;
return "c.account_no as accountNo, c.office_id as officeId, o.name as officeName, c.id as id, "
+ "c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, " public ClientMapper() {
+ "c.fullname as fullname, c.display_name as displayName, " StringBuilder builder = new StringBuilder(400);
+ "c.external_id as externalId, c.joined_date as joinedDate, c.image_key as imagekey from m_client c join m_office o on o.id = c.office_id "
+ " where o.hierarchy like ? and c.is_deleted=0 "; builder.append("c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum, ");
builder.append("c.office_id as officeId, o.name as officeName, ");
builder.append("c.office_id as officeId, o.name as officeName, ");
builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
builder.append("c.fullname as fullname, c.display_name as displayName, ");
builder.append("c.activation_date as activationDate, c.image_key as imagekey ");
builder.append("from m_client c ");
builder.append("join m_office o on o.id = c.office_id ");
this.schema = builder.toString();
}
public String schema() {
return this.schema;
} }
@Override @Override
public ClientData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { public ClientData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final String accountNo = rs.getString("accountNo"); final String accountNo = rs.getString("accountNo");
final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum");
final EnumOptionData status = ClientEnumerations.status(statusEnum);
final Long officeId = JdbcSupport.getLong(rs, "officeId"); final Long officeId = JdbcSupport.getLong(rs, "officeId");
final Long id = JdbcSupport.getLong(rs, "id"); final Long id = JdbcSupport.getLong(rs, "id");
final String firstname = rs.getString("firstname"); final String firstname = rs.getString("firstname");
@@ -200,12 +279,12 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final String fullname = rs.getString("fullname"); final String fullname = rs.getString("fullname");
final String displayName = rs.getString("displayName"); final String displayName = rs.getString("displayName");
final String externalId = rs.getString("externalId"); final String externalId = rs.getString("externalId");
final LocalDate joinedDate = JdbcSupport.getLocalDate(rs, "joinedDate"); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
final String imageKey = rs.getString("imageKey"); final String imageKey = rs.getString("imageKey");
final String officeName = rs.getString("officeName"); final String officeName = rs.getString("officeName");
return new ClientData(accountNo, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, externalId, return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
joinedDate, imageKey, null, null, null ,null); externalId, activationDate, imageKey, null, null, null);
} }
} }
@@ -215,7 +294,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
public String parentGroupsSchema() { public String parentGroupsSchema() {
return "gp.id As groupId , gp.name As groupName from m_client cl JOIN m_group_client gc ON cl.id = gc.client_id " return "gp.id As groupId , gp.name As groupName from m_client cl JOIN m_group_client gc ON cl.id = gc.client_id "
+ "JOIN m_group gp ON gp.id = gc.group_id WHERE cl.id = ? AND gp.is_deleted = 0 "; + "JOIN m_group gp ON gp.id = gc.group_id WHERE cl.id = ? AND gp.is_deleted = 0 ";
} }
@Override @Override
public GroupGeneralData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { public GroupGeneralData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
@@ -266,7 +345,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
this.context.authenticatedUser(); this.context.authenticatedUser();
// Check if client exists // Check if client exists
retrieveIndividualClient(clientId); retrieveOne(clientId);
List<ClientAccountSummaryData> pendingApprovalLoans = new ArrayList<ClientAccountSummaryData>(); List<ClientAccountSummaryData> pendingApprovalLoans = new ArrayList<ClientAccountSummaryData>();
List<ClientAccountSummaryData> awaitingDisbursalLoans = new ArrayList<ClientAccountSummaryData>(); List<ClientAccountSummaryData> awaitingDisbursalLoans = new ArrayList<ClientAccountSummaryData>();
@@ -332,7 +411,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
this.context.authenticatedUser(); this.context.authenticatedUser();
// Check if client exists // Check if client exists
retrieveIndividualClient(clientId); retrieveOne(clientId);
ClientLoanAccountSummaryDataMapper rm = new ClientLoanAccountSummaryDataMapper(); ClientLoanAccountSummaryDataMapper rm = new ClientLoanAccountSummaryDataMapper();
@@ -417,9 +496,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
private static final class ClientIdentifierMapper implements RowMapper<ClientData> { private static final class ClientIdentifierMapper implements RowMapper<ClientData> {
public String clientLookupByIdentifierSchema() { public String clientLookupByIdentifierSchema() {
return "c.id as id, c.account_no as accountNo, c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, " return "c.id as id, c.account_no as accountNo, c.status_enum as statusEnum, c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "
+ "c.fullname as fullname, c.display_name as displayName," + "c.office_id as officeId, o.name as officeName " + "c.fullname as fullname, c.display_name as displayName,"
+ " from m_client c, m_office o, m_client_identifier ci " + "where o.id = c.office_id and c.id=ci.client_id " + "c.office_id as officeId, o.name as officeName "
+ " from m_client c, m_office o, m_client_identifier ci "
+ "where o.id = c.office_id and c.id=ci.client_id "
+ "and ci.document_type_id= ? and ci.document_key like ?"; + "and ci.document_type_id= ? and ci.document_key like ?";
} }
@@ -428,6 +509,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final Long id = rs.getLong("id"); final Long id = rs.getLong("id");
final String accountNo = rs.getString("accountNo"); final String accountNo = rs.getString("accountNo");
final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum");
final EnumOptionData status = ClientEnumerations.status(statusEnum);
final String firstname = rs.getString("firstname"); final String firstname = rs.getString("firstname");
final String middlename = rs.getString("middlename"); final String middlename = rs.getString("middlename");
final String lastname = rs.getString("lastname"); final String lastname = rs.getString("lastname");
@@ -437,7 +522,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final Long officeId = rs.getLong("officeId"); final Long officeId = rs.getLong("officeId");
final String officeName = rs.getString("officeName"); final String officeName = rs.getString("officeName");
return ClientData.clientIdentifier(id, accountNo, firstname, middlename, lastname, fullname, displayName, officeId, officeName); return ClientData.clientIdentifier(id, accountNo, status, firstname, middlename, lastname, fullname, displayName, officeId,
officeName);
} }
} }
} }
@@ -18,6 +18,8 @@ public interface ClientWritePlatformService {
CommandProcessingResult updateClient(Long clientId, JsonCommand command); CommandProcessingResult updateClient(Long clientId, JsonCommand command);
CommandProcessingResult activateClient(Long clientId, JsonCommand command);
CommandProcessingResult deleteClient(Long clientId); CommandProcessingResult deleteClient(Long clientId);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')")
@@ -28,5 +30,4 @@ public interface ClientWritePlatformService {
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_CLIENTIMAGE')") @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_CLIENTIMAGE')")
CommandProcessingResult deleteClientImage(Long clientId); CommandProcessingResult deleteClientImage(Long clientId);
} }
@@ -8,9 +8,15 @@ package org.mifosplatform.portfolio.client.service;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -22,15 +28,19 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository; import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.mifosplatform.portfolio.client.api.ClientApiConstants;
import org.mifosplatform.portfolio.client.data.ClientDataValidator;
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator; import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory; import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepository; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.client.exception.ClientMustBePendingToBeDeletedException;
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
import org.mifosplatform.portfolio.client.serialization.ClientCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepository; import org.mifosplatform.portfolio.group.domain.GroupRepository;
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException; import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -44,37 +54,51 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private final static Logger logger = LoggerFactory.getLogger(ClientWritePlatformServiceJpaRepositoryImpl.class); private final static Logger logger = LoggerFactory.getLogger(ClientWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final ClientRepository clientRepository; private final ClientRepositoryWrapper clientRepository;
private final OfficeRepository officeRepository; private final OfficeRepository officeRepository;
private final NoteRepository noteRepository;
private final GroupRepository groupRepository; private final GroupRepository groupRepository;
private final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final ClientDataValidator fromApiJsonDeserializer;
private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory; private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory;
private final ConfigurationDomainService configurationDomainService;
@Autowired @Autowired
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ClientRepository clientRepository, public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final OfficeRepository officeRepository, final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer, final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final GroupRepository groupRepository) { final ClientDataValidator fromApiJsonDeserializer,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final GroupRepository groupRepository,
final ConfigurationDomainService configurationDomainService) {
this.context = context; this.context = context;
this.clientRepository = clientRepository; this.clientRepository = clientRepository;
this.officeRepository = officeRepository; this.officeRepository = officeRepository;
this.noteRepository = noteRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory; this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory;
this.groupRepository = groupRepository; this.groupRepository = groupRepository;
this.configurationDomainService = configurationDomainService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult deleteClient(final Long clientId) { public CommandProcessingResult deleteClient(final Long clientId) {
final Client client = this.clientRepository.findOne(clientId); final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
final Long officeId = client.getOffice().getId(); final Long officeId = client.getOffice().getId();
client.delete(); if (client.isNotPending()) { throw new ClientMustBePendingToBeDeletedException(clientId); }
this.clientRepository.save(client);
return new CommandProcessingResultBuilder().withOfficeId(officeId).withClientId(clientId).withEntityId(clientId).build(); List<Note> relatedNotes = this.noteRepository.findByClientId(clientId);
this.noteRepository.deleteInBatch(relatedNotes);
// client.delete();
this.clientRepository.delete(client);
return new CommandProcessingResultBuilder() //
.withOfficeId(officeId) //
.withClientId(clientId) //
.withEntityId(clientId) //
.build();
} }
/* /*
@@ -107,23 +131,24 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
try { try {
context.authenticatedUser(); context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreate(command.json()); final boolean isPendingApprovalEnabled = this.configurationDomainService.isClientPendingApprovalAllowedEnabled();
this.fromApiJsonDeserializer.validateForCreate(command.json(), isPendingApprovalEnabled);
final Long officeId = command.longValueOfParameterNamed("officeId"); final Long officeId = command.longValueOfParameterNamed(ClientApiConstants.officeIdParamName);
final Office clientOffice = this.officeRepository.findOne(officeId); final Office clientOffice = this.officeRepository.findOne(officeId);
if (clientOffice == null) { throw new OfficeNotFoundException(officeId); } if (clientOffice == null) { throw new OfficeNotFoundException(officeId); }
final Long groupId = command.longValueOfParameterNamed("groupId"); final Long groupId = command.longValueOfParameterNamed(ClientApiConstants.groupIdParamName);
Group clientParentGroup = null; Group clientParentGroup = null;
if (groupId != null) { if (groupId != null) {
clientParentGroup = this.groupRepository.findOne(groupId); clientParentGroup = this.groupRepository.findOne(groupId);
if (clientParentGroup == null) { throw new GroupNotFoundException(groupId); } if (clientParentGroup == null) { throw new GroupNotFoundException(groupId); }
} }
final Client newClient = Client.fromJson(clientOffice, clientParentGroup, command); final Client newClient = Client.createNew(clientOffice, clientParentGroup, command, isPendingApprovalEnabled);
this.clientRepository.save(newClient); this.clientRepository.save(newClient);
if (newClient.isAccountNumberRequiresAutoGeneration()) { if (newClient.isAccountNumberRequiresAutoGeneration()) {
@@ -137,6 +162,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
.withOfficeId(clientOffice.getId()) // .withOfficeId(clientOffice.getId()) //
.withClientId(newClient.getId()) // .withClientId(newClient.getId()) //
.withGroupId(groupId) //
.withEntityId(newClient.getId()) // .withEntityId(newClient.getId()) //
.build(); .build();
} catch (DataIntegrityViolationException dve) { } catch (DataIntegrityViolationException dve) {
@@ -152,10 +178,10 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
try { try {
context.authenticatedUser(); context.authenticatedUser();
this.fromApiJsonDeserializer.validateForUpdate(command.json()); final boolean isPendingApprovalEnabled = this.configurationDomainService.isClientPendingApprovalAllowedEnabled();
this.fromApiJsonDeserializer.validateForUpdate(command.json(), isPendingApprovalEnabled);
final Client clientForUpdate = this.clientRepository.findOne(clientId); final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId);
if (clientForUpdate == null || clientForUpdate.isDeleted()) { throw new ClientNotFoundException(clientId); }
final Map<String, Object> changes = clientForUpdate.update(command); final Map<String, Object> changes = clientForUpdate.update(command);
@@ -173,7 +199,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
.withOfficeId(clientForUpdate.getOffice().getId()) // .withOfficeId(clientForUpdate.officeId()) //
.withClientId(clientId) // .withClientId(clientId) //
.withEntityId(clientId) // .withEntityId(clientId) //
.with(changes) // .with(changes) //
@@ -186,9 +212,37 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Transactional @Transactional
@Override @Override
public CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream) { public CommandProcessingResult activateClient(final Long clientId, final JsonCommand command) {
try { try {
final Client client = this.clientRepository.findOne(clientId); this.fromApiJsonDeserializer.validateActivation(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate");
client.activate(fmt, activationDate);
this.clientRepository.saveAndFlush(client);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(client.officeId()) //
.withClientId(clientId) //
.withEntityId(clientId) //
.build();
} catch (DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return new CommandProcessingResult(Long.valueOf(-1));
}
}
@Transactional
@Override
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream) {
try {
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
String imageUploadLocation = setupForClientImageUpdate(clientId, client); String imageUploadLocation = setupForClientImageUpdate(clientId, client);
String imageLocation = FileUtils.saveToFileSystem(inputStream, imageUploadLocation, imageName); String imageLocation = FileUtils.saveToFileSystem(inputStream, imageUploadLocation, imageName);
@@ -204,8 +258,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Override @Override
public CommandProcessingResult deleteClientImage(final Long clientId) { public CommandProcessingResult deleteClientImage(final Long clientId) {
final Client client = this.clientRepository.findOne(clientId); final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
// delete image from the file system // delete image from the file system
if (StringUtils.isNotEmpty(client.getImageKey())) { if (StringUtils.isNotEmpty(client.getImageKey())) {
@@ -217,7 +270,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Override @Override
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final Base64EncodedImage encodedImage) { public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final Base64EncodedImage encodedImage) {
try { try {
final Client client = this.clientRepository.findOne(clientId); final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
final String imageUploadLocation = setupForClientImageUpdate(clientId, client); final String imageUploadLocation = setupForClientImageUpdate(clientId, client);
final String imageLocation = FileUtils.saveToFileSystem(encodedImage, imageUploadLocation, "image"); final String imageLocation = FileUtils.saveToFileSystem(encodedImage, imageUploadLocation, "image");
@@ -255,5 +308,4 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve); logger.error(dve.getMessage(), dve);
} }
} }
@@ -252,7 +252,7 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService
} }
Collection<ClientData> clientOptions = this.clientReadPlatformService Collection<ClientData> clientOptions = this.clientReadPlatformService
.retrieveAllIndividualClientsForLookupByOfficeId(centerOfficeId); .retrieveAllForLookupByOfficeId(centerOfficeId);
if (CollectionUtils.isEmpty(clientOptions)) { if (CollectionUtils.isEmpty(clientOptions)) {
clientOptions = null; clientOptions = null;
} }
@@ -82,7 +82,7 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
} }
Collection<ClientData> clientOptions = this.clientReadPlatformService Collection<ClientData> clientOptions = this.clientReadPlatformService
.retrieveAllIndividualClientsForLookupByOfficeId(defaultOfficeId); .retrieveAllForLookupByOfficeId(defaultOfficeId);
if (CollectionUtils.isEmpty(clientOptions)) { if (CollectionUtils.isEmpty(clientOptions)) {
clientOptions = null; clientOptions = null;
} }
@@ -7,18 +7,28 @@ public class SearchParameters {
private final String externalId; private final String externalId;
private final String name; private final String name;
private final String hierarchy; private final String hierarchy;
private final String firstname;
private final String lastname;
public static SearchParameters from(final String sqlSearch, final Long officeId, final String externalId, final String name, public static SearchParameters from(final String sqlSearch, final Long officeId, final String externalId, final String name,
final String hierarchy) { final String hierarchy) {
return new SearchParameters(sqlSearch, officeId, externalId, name, hierarchy); return new SearchParameters(sqlSearch, officeId, externalId, name, hierarchy, null, null);
} }
private SearchParameters(final String sqlSearch, final Long officeId, final String externalId, final String name, final String hierarchy) { public static SearchParameters forClients(final String sqlSearch, final Long officeId, final String externalId,
final String displayName, final String firstname, final String lastname, final String hierarchy) {
return new SearchParameters(sqlSearch, officeId, externalId, displayName, hierarchy, firstname, lastname);
}
private SearchParameters(final String sqlSearch, final Long officeId, final String externalId, final String name,
final String hierarchy, final String firstname, final String lastname) {
this.sqlSearch = sqlSearch; this.sqlSearch = sqlSearch;
this.officeId = officeId; this.officeId = officeId;
this.externalId = externalId; this.externalId = externalId;
this.name = name; this.name = name;
this.hierarchy = hierarchy; this.hierarchy = hierarchy;
this.firstname = firstname;
this.lastname = lastname;
} }
public String getSqlSearch() { public String getSqlSearch() {
@@ -40,4 +50,12 @@ public class SearchParameters {
public String getHierarchy() { public String getHierarchy() {
return this.hierarchy; return this.hierarchy;
} }
public String getFirstname() {
return this.firstname;
}
public String getLastname() {
return this.lastname;
}
} }
@@ -67,24 +67,24 @@ public class GuarantorData {
allowedClientRelationshipTypes); allowedClientRelationshipTypes);
} }
public static GuarantorData mergeClientData(ClientData clientData, GuarantorData guarantorData) { public static GuarantorData mergeClientData(final ClientData clientData, final GuarantorData guarantorData) {
return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId, return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId,
guarantorData.guarantorType, clientData.getFirstname(), clientData.getLastname(), null, null, null, null, null, null, null, guarantorData.guarantorType, clientData.getFirstname(), clientData.getLastname(), null, null, null, null, null, null, null,
null, null, null, clientData.getOfficeName(), clientData.getJoinedDate(), clientData.getExternalId(), null, null, null, null, clientData.officeName(), clientData.getActivationDate(), clientData.getExternalId(), null,
guarantorData.allowedClientRelationshipTypes); guarantorData.allowedClientRelationshipTypes);
} }
public static GuarantorData mergeStaffData(StaffData staffData, GuarantorData guarantorData) { public static GuarantorData mergeStaffData(final StaffData staffData, final GuarantorData guarantorData) {
return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId, return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId,
guarantorData.guarantorType, staffData.getFirstname(), staffData.getLastname(), null, null, null, null, null, null, null, guarantorData.guarantorType, staffData.getFirstname(), staffData.getLastname(), null, null, null, null, null, null, null,
null, null, null, staffData.getOfficeName(), null, null, null, guarantorData.allowedClientRelationshipTypes); null, null, null, staffData.getOfficeName(), null, null, null, guarantorData.allowedClientRelationshipTypes);
} }
public GuarantorData(Long id, Long loanId, final CodeValueData clientRelationshipType, Long entityId, EnumOptionData guarantorType, public GuarantorData(final Long id, final Long loanId, final CodeValueData clientRelationshipType, final Long entityId, final EnumOptionData guarantorType,
String firstname, String lastname, LocalDate dob, String addressLine1, String addressLine2, String city, String state, final String firstname, final String lastname, final LocalDate dob, final String addressLine1, final String addressLine2, final String city, final String state,
String zip, String country, String mobileNumber, String housePhoneNumber, String comment, String officeName, final String zip, final String country, final String mobileNumber, final String housePhoneNumber, final String comment, final String officeName,
LocalDate joinedDate, String externalId, List<EnumOptionData> guarantorTypeOptions, final LocalDate joinedDate, final String externalId, final List<EnumOptionData> guarantorTypeOptions,
Collection<CodeValueData> allowedClientRelationshipTypes) { final Collection<CodeValueData> allowedClientRelationshipTypes) {
this.id = id; this.id = id;
this.loanId = loanId; this.loanId = loanId;
this.clientRelationshipType = clientRelationshipType; this.clientRelationshipType = clientRelationshipType;
@@ -127,7 +127,7 @@ public class GuarantorReadPlatformServiceImpl implements GuarantorReadPlatformSe
*/ */
private GuarantorData mergeDetailsForClientOrStaffGuarantor(GuarantorData guarantorData) { private GuarantorData mergeDetailsForClientOrStaffGuarantor(GuarantorData guarantorData) {
if (guarantorData.isExistingClient()) { if (guarantorData.isExistingClient()) {
ClientData clientData = clientReadPlatformService.retrieveIndividualClient(guarantorData.getEntityId()); ClientData clientData = clientReadPlatformService.retrieveOne(guarantorData.getEntityId());
return GuarantorData.mergeClientData(clientData, guarantorData); return GuarantorData.mergeClientData(clientData, guarantorData);
} else if (guarantorData.isStaffMember()) { } else if (guarantorData.isStaffMember()) {
StaffData staffData = staffReadPlatformService.retrieveStaff(guarantorData.getEntityId()); StaffData staffData = staffReadPlatformService.retrieveStaff(guarantorData.getEntityId());
@@ -84,7 +84,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(guarantor.getOfficeId()) return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(guarantor.getOfficeId())
.withEntityId(guarantor.getId()).withLoanId(loanId).build(); .withEntityId(guarantor.getId()).withLoanId(loanId).build();
} catch (DataIntegrityViolationException dve) { } catch (DataIntegrityViolationException dve) {
handleGuarantorDataIntegrityIssues(command, dve); handleGuarantorDataIntegrityIssues(dve);
return CommandProcessingResult.empty(); return CommandProcessingResult.empty();
} }
} }
@@ -124,7 +124,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(guarantorForUpdate.getOfficeId()) return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(guarantorForUpdate.getOfficeId())
.withEntityId(guarantorForUpdate.getId()).withOfficeId(guarantorForUpdate.getLoanId()).with(changesOnly).build(); .withEntityId(guarantorForUpdate.getId()).withOfficeId(guarantorForUpdate.getLoanId()).with(changesOnly).build();
} catch (DataIntegrityViolationException dve) { } catch (DataIntegrityViolationException dve) {
handleGuarantorDataIntegrityIssues(command, dve); handleGuarantorDataIntegrityIssues(dve);
return CommandProcessingResult.empty(); return CommandProcessingResult.empty();
} }
} }
@@ -142,32 +142,22 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
.withOfficeId(guarantorForDelete.getOfficeId()).build(); .withOfficeId(guarantorForDelete.getOfficeId()).build();
} }
/** private void validateGuarantorBusinessRules(final Guarantor guarantor) {
* @param loanId
* @param loan
* @param guarantor
*/
private void validateGuarantorBusinessRules(Guarantor guarantor) {
// validate guarantor conditions // validate guarantor conditions
if (guarantor.isExistingCustomer()) { if (guarantor.isExistingCustomer()) {
// check client exists // check client exists
clientRepositoryWrapper.findOneWithNotFoundDetection(guarantor.getEntityId()); clientRepositoryWrapper.findOneWithNotFoundDetection(guarantor.getEntityId());
// validate that the client is not set as a self guarantor // validate that the client is not set as a self guarantor
if (guarantor.getClientId().equals(guarantor.getEntityId())) { throw new InvalidGuarantorException(guarantor.getEntityId(), if (guarantor.getClientId().equals(guarantor.getEntityId())) {
guarantor.getLoanId()); } //
throw new InvalidGuarantorException(guarantor.getEntityId(), guarantor.getLoanId());
}
} else if (guarantor.isExistingEmployee()) { } else if (guarantor.isExistingEmployee()) {
staffRepositoryWrapper.findOneWithNotFoundDetection(guarantor.getEntityId()); staffRepositoryWrapper.findOneWithNotFoundDetection(guarantor.getEntityId());
} }
} }
/** private void handleGuarantorDataIntegrityIssues(final DataIntegrityViolationException dve) {
* Throws an exception for any data Integrity issue
*
* @param command
* @param dve
*/
@SuppressWarnings("unused")
private void handleGuarantorDataIntegrityIssues(final JsonCommand command, DataIntegrityViolationException dve) {
Throwable realCause = dve.getMostSpecificCause(); Throwable realCause = dve.getMostSpecificCause();
logger.error(dve.getMessage(), dve); logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.guarantor.unknown.data.integrity.issue", throw new PlatformDataIntegrityException("error.msg.guarantor.unknown.data.integrity.issue",
@@ -148,7 +148,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
context.authenticatedUser(); context.authenticatedUser();
final ClientData clientAccount = this.clientReadPlatformService.retrieveIndividualClient(clientId); final ClientData clientAccount = this.clientReadPlatformService.retrieveOne(clientId);
final LocalDate expectedDisbursementDate = DateUtils.getLocalDateOfTenant(); final LocalDate expectedDisbursementDate = DateUtils.getLocalDateOfTenant();
LoanAccountData loanTemplateDetails = LoanAccountData.clientDefaults(clientAccount.id(), clientAccount.displayName(), LoanAccountData loanTemplateDetails = LoanAccountData.clientDefaults(clientAccount.id(), clientAccount.displayName(),
clientAccount.officeId(), expectedDisbursementDate); clientAccount.officeId(), expectedDisbursementDate);
@@ -666,14 +666,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
if (fromDate == null) { if (fromDate == null) {
fromDate = this.lastDueDate; fromDate = this.lastDueDate;
} }
final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue); final BigDecimal outstandingPrincipalBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue);
// update based on current period values // update based on current period values
this.lastDueDate = dueDate; this.lastDueDate = dueDate;
outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue); outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue);
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate,
dueDate, principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan, dueDate, principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
interestExpectedDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesExpectedDue, interestExpectedDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesExpectedDue,
feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesExpectedDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesExpectedDue,
penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod,
@@ -147,7 +147,7 @@ public class LoanEnumerations {
break; break;
case EQUAL_PRINCIPAL: case EQUAL_PRINCIPAL:
optionData = new EnumOptionData(AmortizationMethod.EQUAL_PRINCIPAL.getValue().longValue(), optionData = new EnumOptionData(AmortizationMethod.EQUAL_PRINCIPAL.getValue().longValue(),
AmortizationMethod.EQUAL_PRINCIPAL.getCode(), "Equal principle payments"); AmortizationMethod.EQUAL_PRINCIPAL.getCode(), "Equal principal payments");
break; break;
default: default:
optionData = new EnumOptionData(AmortizationMethod.INVALID.getValue().longValue(), AmortizationMethod.INVALID.getCode(), optionData = new EnumOptionData(AmortizationMethod.INVALID.getValue().longValue(), AmortizationMethod.INVALID.getCode(),
@@ -295,7 +295,10 @@ public class SavingsAccountDataValidator {
} }
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", if (!dataValidationErrors.isEmpty()) {
"Validation errors exist.", dataValidationErrors); } //
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
} }
} }
@@ -82,7 +82,10 @@ public class SavingsAccountTransactionDataValidator {
} }
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", if (!dataValidationErrors.isEmpty()) {
"Validation errors exist.", dataValidationErrors); } //
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
} }
} }
@@ -207,7 +207,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
ClientData client = null; ClientData client = null;
if (clientId != null) { if (clientId != null) {
client = this.clientReadPlatformService.retrieveIndividualClient(clientId); client = this.clientReadPlatformService.retrieveOne(clientId);
} }
GroupGeneralData group = null; GroupGeneralData group = null;
@@ -5,7 +5,6 @@
*/ */
package org.mifosplatform.useradministration.api; package org.mifosplatform.useradministration.api;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@@ -22,23 +21,17 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.mifosplatform.commands.data.CommandSourceData;
import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.commands.service.PortfolioCommandsReadPlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.useradministration.command.PermissionsCommand;
import org.mifosplatform.useradministration.command.RoleCommand;
import org.mifosplatform.useradministration.data.PermissionData; import org.mifosplatform.useradministration.data.PermissionData;
import org.mifosplatform.useradministration.data.RoleData; import org.mifosplatform.useradministration.data.RoleData;
import org.mifosplatform.useradministration.data.RolePermissionsData; import org.mifosplatform.useradministration.data.RolePermissionsData;
import org.mifosplatform.useradministration.serialization.PermissionsCommandFromApiJsonDeserializer;
import org.mifosplatform.useradministration.serialization.RoleCommandFromApiJsonDeserializer;
import org.mifosplatform.useradministration.service.PermissionReadPlatformService; import org.mifosplatform.useradministration.service.PermissionReadPlatformService;
import org.mifosplatform.useradministration.service.RoleReadPlatformService; import org.mifosplatform.useradministration.service.RoleReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -67,33 +60,24 @@ public class RolesApiResource {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final RoleReadPlatformService roleReadPlatformService; private final RoleReadPlatformService roleReadPlatformService;
private final PermissionReadPlatformService permissionReadPlatformService; private final PermissionReadPlatformService permissionReadPlatformService;
private final RoleCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final PermissionsCommandFromApiJsonDeserializer permissionsFromApiJsonDeserializer;
private final DefaultToApiJsonSerializer<RoleData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<RoleData> toApiJsonSerializer;
private final DefaultToApiJsonSerializer<RolePermissionsData> permissionsToApiJsonSerializer; private final DefaultToApiJsonSerializer<RolePermissionsData> permissionsToApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper; private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandsReadPlatformService commandSourceReadPlatformService;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired @Autowired
public RolesApiResource(final PlatformSecurityContext context, final RoleReadPlatformService readPlatformService, public RolesApiResource(final PlatformSecurityContext context, final RoleReadPlatformService readPlatformService,
final PermissionReadPlatformService permissionReadPlatformService, final PermissionReadPlatformService permissionReadPlatformService,
final RoleCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final PermissionsCommandFromApiJsonDeserializer permissionsFromApiJsonDeserializer,
final DefaultToApiJsonSerializer<RoleData> toApiJsonSerializer, final DefaultToApiJsonSerializer<RoleData> toApiJsonSerializer,
final DefaultToApiJsonSerializer<RolePermissionsData> permissionsToApiJsonSerializer, final DefaultToApiJsonSerializer<RolePermissionsData> permissionsToApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper, final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandsReadPlatformService commandSourceReadPlatformService,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.context = context; this.context = context;
this.roleReadPlatformService = readPlatformService; this.roleReadPlatformService = readPlatformService;
this.permissionReadPlatformService = permissionReadPlatformService; this.permissionReadPlatformService = permissionReadPlatformService;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.permissionsFromApiJsonDeserializer = permissionsFromApiJsonDeserializer;
this.toApiJsonSerializer = toApiJsonSerializer; this.toApiJsonSerializer = toApiJsonSerializer;
this.permissionsToApiJsonSerializer = permissionsToApiJsonSerializer; this.permissionsToApiJsonSerializer = permissionsToApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper; this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandSourceReadPlatformService = commandSourceReadPlatformService;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
} }
@@ -135,13 +119,7 @@ public class RolesApiResource {
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
RoleData role = this.roleReadPlatformService.retrieveOne(roleId); final RoleData role = this.roleReadPlatformService.retrieveOne(roleId);
if (settings.isCommandIdPassed()) {
final CommandSourceData entry = this.commandSourceReadPlatformService.retrieveById(settings.getCommandId());
final RoleCommand changesOnly = this.fromApiJsonDeserializer.commandFromApiJson(entry.json());
RoleData currentChanges = RoleData.changes(changesOnly.getName(), changesOnly.getDescription());
role = RoleData.integrateChanges(role, currentChanges);
}
return this.toApiJsonSerializer.serialize(settings, role, RESPONSE_DATA_PARAMETERS); return this.toApiJsonSerializer.serialize(settings, role, RESPONSE_DATA_PARAMETERS);
} }
@@ -174,18 +152,7 @@ public class RolesApiResource {
final RoleData role = this.roleReadPlatformService.retrieveOne(roleId); final RoleData role = this.roleReadPlatformService.retrieveOne(roleId);
final Collection<PermissionData> permissionUsageData = this.permissionReadPlatformService.retrieveAllRolePermissions(roleId); final Collection<PermissionData> permissionUsageData = this.permissionReadPlatformService.retrieveAllRolePermissions(roleId);
Collection<PermissionData> currentChanges = null; final RolePermissionsData permissionsData = role.toRolePermissionData(permissionUsageData);
if (settings.isCommandIdPassed()) {
final CommandSourceData entry = this.commandSourceReadPlatformService.retrieveById(settings.getCommandId());
PermissionsCommand command = permissionsFromApiJsonDeserializer.commandFromApiJson(entry.json());
currentChanges = new ArrayList<PermissionData>();
for (String key : command.getPermissions().keySet()) {
currentChanges.add(PermissionData.from(key, command.getPermissions().get(key)));
}
}
final RolePermissionsData permissionsData = role.toRolePermissionData(permissionUsageData, currentChanges);
return this.permissionsToApiJsonSerializer.serialize(settings, permissionsData, PERMISSIONS_RESPONSE_DATA_PARAMETERS); return this.permissionsToApiJsonSerializer.serialize(settings, permissionsData, PERMISSIONS_RESPONSE_DATA_PARAMETERS);
} }
@@ -15,27 +15,15 @@ public class RoleData {
private final Long id; private final Long id;
private final String name; private final String name;
private final String description; private final String description;
@SuppressWarnings("unused")
private final RoleData currentChanges;
public RolePermissionsData toRolePermissionData(final Collection<PermissionData> permissionUsageData, public RolePermissionsData toRolePermissionData(final Collection<PermissionData> permissionUsageData) {
final Collection<PermissionData> currentChanges) { return new RolePermissionsData(id, name, description, permissionUsageData);
return new RolePermissionsData(id, name, description, permissionUsageData, currentChanges);
} }
public static RoleData changes(final String name, final String description) { public RoleData(final Long id, final String name, final String description) {
return new RoleData(null, name, description, null);
}
public static RoleData integrateChanges(final RoleData role, final RoleData currentChanges) {
return new RoleData(role.id, role.name, role.description, currentChanges);
}
public RoleData(final Long id, final String name, final String description, final RoleData currentChanges) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.description = description; this.description = description;
this.currentChanges = currentChanges;
} }
@Override @Override
@@ -21,15 +21,12 @@ public class RolePermissionsData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<PermissionData> permissionUsageData; private final Collection<PermissionData> permissionUsageData;
@SuppressWarnings("unused")
private final Collection<PermissionData> currentChanges;
public RolePermissionsData(final Long id, final String name, final String description, public RolePermissionsData(final Long id, final String name, final String description,
final Collection<PermissionData> permissionUsageData, final Collection<PermissionData> currentChanges) { final Collection<PermissionData> permissionUsageData) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.description = description; this.description = description;
this.permissionUsageData = permissionUsageData; this.permissionUsageData = permissionUsageData;
this.currentChanges = currentChanges;
} }
} }
@@ -36,7 +36,7 @@ public class Role extends AbstractPersistable<Long> {
@ManyToMany(fetch = FetchType.EAGER) @ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "m_role_permission", joinColumns = @JoinColumn(name = "role_id"), inverseJoinColumns = @JoinColumn(name = "permission_id")) @JoinTable(name = "m_role_permission", joinColumns = @JoinColumn(name = "role_id"), inverseJoinColumns = @JoinColumn(name = "permission_id"))
private Set<Permission> permissions = new HashSet<Permission>(); private final Set<Permission> permissions = new HashSet<Permission>();
public static Role fromJson(final JsonCommand command) { public static Role fromJson(final JsonCommand command) {
final String name = command.stringValueOfParameterNamed("name"); final String name = command.stringValueOfParameterNamed("name");
@@ -109,6 +109,6 @@ public class Role extends AbstractPersistable<Long> {
} }
public RoleData toData() { public RoleData toData() {
return new RoleData(this.getId(), this.name, this.description, null); return new RoleData(this.getId(), this.name, this.description);
} }
} }
@@ -18,21 +18,15 @@ import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.useradministration.command.RoleCommand;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
/**
* Implementation of {@link FromApiJsonDeserializer} for {@link RoleCommand}'s.
*/
@Component @Component
public final class RoleCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<RoleCommand> { public final class RoleCommandValidator {
/** /**
* The parameters supported for this command. * The parameters supported for this command.
@@ -42,25 +36,10 @@ public final class RoleCommandFromApiJsonDeserializer extends AbstractFromApiJso
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@Autowired @Autowired
public RoleCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) { public RoleCommandValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
} }
@Override
public RoleCommand commandFromApiJson(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final String name = fromApiJsonHelper.extractStringNamed("name", element);
final String description = fromApiJsonHelper.extractStringNamed("description", element);
return new RoleCommand(name, description);
}
public void validateForCreate(final String json) { public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
@@ -106,7 +85,10 @@ public final class RoleCommandFromApiJsonDeserializer extends AbstractFromApiJso
} }
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", if (!dataValidationErrors.isEmpty()) {
"Validation errors exist.", dataValidationErrors); } //
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
} }
} }
@@ -59,7 +59,7 @@ public class RoleReadPlatformServiceImpl implements RoleReadPlatformService {
final String name = rs.getString("name"); final String name = rs.getString("name");
final String description = rs.getString("description"); final String description = rs.getString("description");
return new RoleData(id, name, description, null); return new RoleData(id, name, description);
} }
public String schema() { public String schema() {
@@ -22,7 +22,7 @@ import org.mifosplatform.useradministration.domain.RoleRepository;
import org.mifosplatform.useradministration.exception.PermissionNotFoundException; import org.mifosplatform.useradministration.exception.PermissionNotFoundException;
import org.mifosplatform.useradministration.exception.RoleNotFoundException; import org.mifosplatform.useradministration.exception.RoleNotFoundException;
import org.mifosplatform.useradministration.serialization.PermissionsCommandFromApiJsonDeserializer; import org.mifosplatform.useradministration.serialization.PermissionsCommandFromApiJsonDeserializer;
import org.mifosplatform.useradministration.serialization.RoleCommandFromApiJsonDeserializer; import org.mifosplatform.useradministration.serialization.RoleCommandValidator;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -37,12 +37,12 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final RoleRepository roleRepository; private final RoleRepository roleRepository;
private final PermissionRepository permissionRepository; private final PermissionRepository permissionRepository;
private final RoleCommandFromApiJsonDeserializer roleCommandFromApiJsonDeserializer; private final RoleCommandValidator roleCommandFromApiJsonDeserializer;
private final PermissionsCommandFromApiJsonDeserializer permissionsFromApiJsonDeserializer; private final PermissionsCommandFromApiJsonDeserializer permissionsFromApiJsonDeserializer;
@Autowired @Autowired
public RoleWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final RoleRepository roleRepository, public RoleWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final RoleRepository roleRepository,
final PermissionRepository permissionRepository, final RoleCommandFromApiJsonDeserializer roleCommandFromApiJsonDeserializer, final PermissionRepository permissionRepository, final RoleCommandValidator roleCommandFromApiJsonDeserializer,
final PermissionsCommandFromApiJsonDeserializer fromApiJsonDeserializer) { final PermissionsCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
this.context = context; this.context = context;
this.roleRepository = roleRepository; this.roleRepository = roleRepository;