Merge branch 'develop'

This commit is contained in:
Keith Woodlock
2013-06-20 10:40:49 +01:00
75 changed files with 3084 additions and 474 deletions
+42
View File
@@ -6,6 +6,7 @@ See VERSIONING (https://github.com/openMF/mifosx/blob/master/VERSIONING.md) for
Releases
===============
1. 20 Jun 2013 - 1.4.0.RELEASE
1. 10 Jun 2013 - 1.3.0.RELEASE
1. 06 Jun 2013 - 1.2.1.RELEASE
1. 27 May 2013 - 1.2.0.RELEASE
@@ -24,6 +25,47 @@ Releases
1. 05 April 2013 - 0.11.1.beta
1. 05 April 2013 - 0.11.0.beta
1.4.0.RELEASE
=============
Feature Release
This feature release focussed on moving groups functionality from BETA to public API.
Along with Group releated work, key improvements was the addition of a UI and API for adding 'additional data' and staff relationship to clients.
Platform & API
Bug
- [MIFOSX-420] - In "Change loan application" not able to save the modifications made because of error message getting displayed on charges.
- [MIFOSX-424] - In new loan application, loan is getting approved for the Principal amount beyond the range (Minimum and Maximum) defined in the loan product.
- [MIFOSX-429] - App user of one branch can edit client of another branch
- [MIFOSX-446] - New loan application is not validating Min-Max Principal defined in the product defination
- [MIFOSX-447] - New loan application is not validating Min-Max for "# Of Repayments" defined in the product defination
- [MIFOSX-448] - New loan application is not validating Min-Max "Nominal interest rate %" defined in the product defination
- [MIFOSX-468] - In "Change loan application" error message is displaying for the second charge which was applied before.
- [MIFOSX-474] - Adjusting an existing loan transaction fails when 'note' information is passed
New Features & Improvements
- [MIFOSX-365] - Simplify "Data Tables" functionality
- [MIFOSX-421] - Ability to create and associate Group Roles
- [MIFOSX-434] - Allow linking of staff to Clients
- [MIFOSX-445] - Show loanofficers at higher hierarchical level in product template
- [MIFOSX-449] - Group and JLG loans repayment schedule should match group meeting dates if loan repayment is synched with group meeting
- [MIFOSX-457] - Update API param names for accounting rules to be consistent with rest of the accounting services
- [MIFOSX-458] - Unable to create accounting rules with Tag Id, multiple debits/credits etc
Reference App
- [MIFOSX-378] - TEVI: Produce 'role-based' dashboards as entry/landing page for users that login
- [MIFOSX-455] - CodeValues functionality broken on the reference Client app
- [MIFOSX-469] - In Groups unable to activate the created group
- [MIFOSX-464] - Unable to edit the created charge in "charge details" page
BETA (functionality related to BETA functionality
- [MIFOSX-456] - CRUD services and UI for creating Holidays
- [MIFOSX-411] - While creating new savings account, If "Interest posting period" is defined Annually, it is getting posted monthly.
1.3.0.RELEASE
=============
Feature Release
+1 -1
View File
@@ -26,7 +26,7 @@ The mifos-platform (project named 'Mifos X') has reached <b>public release versi
That means there is a published and stable API. This API is documented in the api-docs under <b>Full API Matrix</b>.
Latest stable release: <a target="_blank" href="https://github.com/openMF/mifosx/tree/mifosplatform-1.3.0" title="mifosplatform-1.3.0">mifosplatform-1.3.0</a>
Latest stable release: <a target="_blank" href="https://github.com/openMF/mifosx/tree/mifosplatform-1.4.0" title="mifosplatform-1.4.0">mifosplatform-1.4.0</a>
License
=============
+521 -98
View File
@@ -90,7 +90,7 @@
<body>
<div id="page-wrapper">
<div id="flybar">
<div id="nav-logo">1.3.0.RELEASE API</div>
<div id="nav-logo">1.4.0.RELEASE API</div>
<div class="flybar-nav">
<h2 class="flybar-button">Overview</h2>
<div class="flybar-menu-overview">
@@ -449,6 +449,106 @@
</div>
</div>
<div class="flybar-nav">
<h2 class="flybar-button">Group</h2>
<div id="toc-menu-loan" class="flybar-menu">
<div class="tocMatrix">
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td colspan=2 valign="top"><div class="matrixHeadingVerbs">
<table>
<tr>
<td>RESOURCES</td>
</tr>
</table>
</div></td>
<td><table>
<tr>
<td><div class="matrixHeadingVerbs">POST</div></td>
</tr>
<tr>
<td><div class=matrixHeadingCommands>create/complex
update</div></td>
</tr>
</table></td>
<td><table>
<tr>
<td><div class="matrixHeadingVerbs">GET</div></td>
</tr>
<tr>
<td><div class=matrixHeadingCommands>read</div></td>
</tr>
</table></td>
<td><table>
<tr>
<td><div class="matrixHeadingVerbs">PUT</div></td>
</tr>
<tr>
<td><div class=matrixHeadingCommands>update</div></td>
</tr>
</table></td>
<td><table>
<tr>
<td><div class="matrixHeadingVerbs">DELETE</div></td>
</tr>
<tr>
<td><div class=matrixHeadingCommands>delete</div></td>
</tr>
</table></td>
</tr>
<tr class="alt">
<td><a href="#groups">Group</a></td>
<td>groups</td>
<td><a href="#groups_create">Create a Group</a></td>
<td><a href="#groups_list">List Groups</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/template</td>
<td></td>
<td><a href="#groups_template">Retrieve Group Template</a></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>groups/{groupId}</td>
<td></td>
<td><a href="#groups_retrieve">Retrieve a Group</a></td>
<td><a href="#groups_update">Update a Group</a></td>
<td><a href="#groups_delete">Delete a Group</a></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=activate</td>
<td><a href="#groups_activate">Activate a Group</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=associateClients</td>
<td><a href="#groups_associate_clients">Associate Clients</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=disassociateClients</td>
<td><a href="#groups_disassociate_clients">Disassociate Clients</a></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
</div>
</div>
<!-- disabled Groupings API for release 1.0.0 as its beta -->
<!--
<div class="flybar-nav">
@@ -1272,6 +1372,10 @@
<td>clients/template</td>
<td><a href="#clients_template">Retrieve Client Template</a></td>
</tr>
<tr class="alt">
<td>groups/template</td>
<td><a href="#groups_template">Retrieve Group Template</a></td>
</tr>
<tr >
<td>loanproducts/template</td>
<td><a href="#loanproducts_template">Retrieve Loan Product Template</a></td>
@@ -3253,6 +3357,33 @@ GET https://DomainName/mifosng-provider/api/v1/centers
<div class="method-section">
<div class="method-description">
<h4>Retrieve Group Template</h4>
<p>This is a convenience resource. It can be useful when
building maintenance user interface screens for client
applications. The template data returned consists of any or all
of:
<ul>
<li class=normalli>Field Defaults</li>
<li class=normalli>Allowed Value Lists</li>
</ul>
</p>
<h5>Arguments</h5>
<dl class="argument-list">
<dt>officeId</dt>
<dd>
Integer <span>optional</span>
</dd>
<dt>centerId</dt>
<dd>
Integer <span>optional</span>
</dd>
<dt>staffInSelectedOfficeOnly</dt>
<dd>
Boolean <span>optional</span>
</dd>
<dd>Defaults to false if not provided. If <strong>staffInSelectedOfficeOnly=true</strong> only staff
who are associated with the selected branch are returned.</dd>
</dl>
<p>Example Requests:</p>
<div class=apiClick>groups/template</div>
<br/>
@@ -3260,7 +3391,8 @@ GET https://DomainName/mifosng-provider/api/v1/centers
<br/>
<div class=apiClick>groups/template?centerId=1</div>
<br/>
<div class=apiClick>groups/template?centerId=1&staffInSelectedOfficeOnly=true</div>
<br/>
</div>
<div class="method-example">
<p>Template to create a standard group</p>
@@ -3901,12 +4033,14 @@ GET https://DomainName/mifosng-provider/api/v1/groups
<td class=fielddesc>The type of transaction processing strategy to be used.<br>
At present some MFI specific ones exist. This is an important point of change for many MFIs.
<br>Responsible for things like payment order and how early or late repayments should be processed.<br>
Out of the box, four available:
Out of the box options available are:
<ol>
<li>id=1, code=mifos-standard-strategy, name=Mifos style</li>
<li>id=2, code=heavensfamily-strategy, name=Heavensfamily</li>
<li>id=3, code=creocore-strategy, name=Creocore</li>
<li>id=4, code=rbi-india-strategy, name=RBI (India)</li>
<li>id=5, code=principal-interest-penalties-fees-order-strategy, name=Principal Interest Penalties Fees Order</li>
<li>id=6, code=interest-principal-penalties-fees-order-strategy, name=Interest Principal Penalties Fees Order</li>
</ol>
</td>
</tr>
@@ -3973,6 +4107,13 @@ GET https://DomainName/mifosng-provider/api/v1/groups
</dd>
<dd>If entered, productId, productName and selectedProduct
fields are returned.</dd>
<dt>staffInSelectedOfficeOnly</dt>
<dd>
Boolean <span>optional</span>
</dd>
<dd>Defaults to false if not provided. If <strong>staffInSelectedOfficeOnly=true</strong> only loan officers
who are associated with the selected branch are returned.</dd>
</dl>
<p>Example Requests:</p>
<div class=apiClick>loans/template?templateType=individual&clientId=1</div>
@@ -4900,21 +5041,20 @@ Request Body:
"locale": "en_GB",
"clientId": 1,
"productId": 1,
"principal": "100,000.00",
"principal": "10,000.00",
"loanTermFrequency": 12,
"loanTermFrequencyType": 2,
"loanType": "individual"
"numberOfRepayments": 12,
"loanType": "individual",
"numberOfRepayments": 10,
"repaymentEvery": 1,
"repaymentFrequencyType": 2,
"interestRatePerPeriod": 2,
"interestRatePerPeriod": 10,
"amortizationType": 1,
"interestType": 0,
"interestCalculationPeriodType": 1,
"transactionProcessingStrategyId": 1,
"expectedDisbursementDate": "20 September 2011",
"submittedOnDate": "20 September 2011",
"loanType": "individual"
"expectedDisbursementDate": "10 Jun 2013",
"submittedOnDate": "10 Jun 2013"
}
</code>
<code class="method-response">
@@ -6995,12 +7135,14 @@ Request Body:
<td class=fielddesc>The type of transaction processing strategy to be used.<br>
At present some MFI specific ones exist. This is an important point of change for many MFIs.
<br>Responsible for things like payment order and how early or late repayments should be processed.<br>
Out of the box, four available:
Out of the box options available are:
<ol>
<li>id=1, code=mifos-standard-strategy, name=Mifos style</li>
<li>id=2, code=heavensfamily-strategy, name=Heavensfamily</li>
<li>id=3, code=creocore-strategy, name=Creocore</li>
<li>id=4, code=rbi-india-strategy, name=RBI (India)</li>
<li>id=5, code=principal-interest-penalties-fees-order-strategy, name=Principal Interest Penalties Fees Order</li>
<li>id=6, code=interest-principal-penalties-fees-order-strategy, name=Interest Principal Penalties Fees Order</li>
</ol>
</td>
</tr>
@@ -7590,7 +7732,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, paymentChannelToFundSourceMappings</td>
<td>inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings</td>
</tr>
<tr class="matrixHeadingBG">
@@ -7673,14 +7815,27 @@ Request Body:
"writeOffAccountId":"41",
"paymentChannelToFundSourceMappings": [
{
"paymentTypeId": "14",
"fundSourceAccountId": "2"
"paymentTypeId": "11",
"fundSourceAccountId": "11"
}
],
"feeToIncomeAccountMappings": [
{
"chargeId": "1",
"incomeAccountId": "5"
},
{
"paymentTypeId": "14",
"fundSourceAccountId": "2"
"chargeId": "2",
"incomeAccountId": "8"
}
],
"penaltyToIncomeAccountMappings": [
{
"chargeId": "6",
"incomeAccountId": "9"
}
]
}
</code>
<code class="method-response">
@@ -9455,11 +9610,30 @@ Content-Type: application/json
<div class="method-description">
<h3>Accounting Rules</h3>
<p>
Its additional option for MFIs to define their own accounting rules.
Its typically used to perform daily accounting postings of journal entries.
It is typical scenario in MFI's that non accountants pass journal entries on a regular basis.
For Ex: A branch office might deposit their entire cash at hand to their Bank account at the end of a
working day. The branch office users might not understand enough of accounting to figure out which
account needs to get credited and which account needs to be debited to represent this transaction.
</p>
<p>For an accounting rule debit account and credit account is fixed, so that we can avoid
the selection of credit and debit accounts every time for default journal entries</p>
<p>Enter accounting rules, an abstraction on top of manual Journal entires for enabling
simpler data entry.
An accounting rule can define any of the following abstractions
<ul>
<li>A Simple journal entry where both the credit and debit account have been preselected</li>
<li>A Simple journal entry where either credit or debit accounts have been limited to a
pre-selected list of accounts (Ex: Debit account should be one of "Bank of America" of "JP Morgan"
and credit account should be "Cash")
</li>
<li>A Compound journal entry where multiple debits and / or multiple credits may be made
amongst a set of preselected list of accounts (Ex: Credit account should be either "Bank Of America"
or "Cash" and debit account can be "Employee Salary" and/or "Miscellenous Expenses")
</li>
</ul>
</p>
<p>An accounting rule can also be optionally associated with a branch, so that only a particular Branch's
users have access to the rule</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td>
@@ -9474,49 +9648,51 @@ Content-Type: application/json
<td>description</td>
</tr>
<tr>
<td class=fielddesc>The description about the accounting rule.</td>
<td class=fielddesc>A description of the accounting rule.</td>
</tr>
<tr class=alt>
<td>officeId</td>
</tr>
<tr>
<td class=fielddesc>The officeId of the office/branch for this accounting rule is applicable.</td>
<td class=fielddesc>An Optional Office for this accounting rule is applicable.</td>
</tr>
<tr class=alt>
<td>debitAccountHead</td>
<td>accountToDebit</td>
</tr>
<tr>
<td class=fielddesc>Debit account for creating accounting rule</td>
<td class=fielddesc>ID of the target account to be Debited</td>
</tr>
<tr class=alt>
<td>creditAccountHead</td>
<td>accountToCredit</td>
</tr>
<tr>
<td class=fielddesc>Credit account for creating accounting rule</td>
<td class=fielddesc>ID of the target account to be Credited</td>
</tr>
<tr class=alt>
<td>debitTags</td>
</tr>
<tr>
<td class=fielddesc>Debit tags for creating accounting rule, These tags fetches the all chart of accounts that are tagged with these tagIds</td>
<td class=fielddesc>A list of accounting Tags. Any Ledger account with this tag can serve as the
account to be debited.<br> This parameter is optional, if <b>accountToDebit</b> present in the request params</td>
</tr>
<tr class=alt>
<td>creditTags</td>
</tr>
<tr>
<td class=fielddesc>Credit tags for creating accounting rule, These tags fetches the all chart of accounts that are tagged with these tagIds</td>
<td class=fielddesc>A list of accounting Tags. Any Ledger account with this tag can serve as the
account to be credited.<br> This parameter is optional, if <b>accountToCredit</b> present in the request params</td></td>
</tr>
<tr class=alt>
<td>allowMultipleDebitEntries</td>
</tr>
<tr>
<td class=fielddesc>Allows to add multiple debits if the value is true, while creating journal entries with this rule</td>
<td class=fielddesc>Allows passing multiple debit entries for the accounting rule</td>
</tr>
<tr class=alt>
<td>allowMultipleCreditEntries</td>
</tr>
<tr>
<td class=fielddesc>Allows to add multiple credits if the value is true, while creating journal entries with this rule</td>
<td class=fielddesc>Allows passing multiple credit entries for the accounting rule</td>
</tr>
</table>
</div>
@@ -9608,6 +9784,24 @@ GET https://DomainName/api/v1/accountingrules/template
<div class="method-description">
<h4>Create/Define a Accounting rule</h4>
<p>Define a new Accounting rule.</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
</tr>
<tr class=alt>
<td>name, officeId,<br> accountToDebit OR debitTags,<br> accountToCredit OR creditTags.<br>
</td>
</tr>
</table>
<br />
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>description</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">
@@ -9620,8 +9814,8 @@ Request Body:
{
"name": "test",
"officeId": "1",
"debitAccountHead": "21",
"creditAccountHead": "9",
"accountToDebit": "21",
"accountToCredit": "9",
"description": "Employee salary"
}
</code>
@@ -9658,7 +9852,7 @@ Request Body:
{
"name": "test123",
"officeId": "1",
"debitAccountHead": "21",
"accountToDebit": "21",
"creditTags": ["12", "13"],
"allowMultipleCreditEntries": "false",
"description": "Employee salary"
@@ -9677,9 +9871,9 @@ Request Body:
{
"name": "fordocstest",
"officeId": "1",
"debitTags": ["10", "11"],
"debitTags": ["10","11"],
"allowMultipleDebitEntries": "false",
"creditAccountHead": "9",
"accountToCredit": "9",
"description": "for api docs"
}
</code>
@@ -11433,6 +11627,83 @@ No Request Body:
<h4>Create Data Table</h4>
<p>Create a new data table and registers it with the Mifos X Core application
table.</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong> datatableName</td>
</tr>
<tr>
<td class=fielddesc>The name of the Data Table.</td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong> apptableName</td>
</tr>
<tr>
<td class=fielddesc>
Application table name. Must be one of the following:
<ul class="field">
<li>m_client</li>
<li>m_group</li>
<li>m_loan</li>
<li>m_office</li>
<li>m_saving_account</li>
</ul>
</td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong>columns</td>
</tr>
<tr>
<td class=fielddesc>An array of columns in the new Data Table.</td>
</tr>
</table>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions - columns</div></td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong>name</td>
</tr>
<tr>
<td class=fielddesc>Name of the created column. Can contain only alphanumeric characters, underscores and spaces, but cannot start with a number. Cannot start or end with an underscore or space.</td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong>type</td>
</tr>
<tr>
<td class=fielddesc>
Column type. Must be one of the following:
<ul class="field">
<li>Date</li>
<li>Decimal</li>
<li>Dropdown</li>
<li>Number</li>
<li>String</li>
<li>Text</li>
</ul>
</td>
</tr>
<tr class=alt>
<td><strong>Mandatory [type = Dropdown] - </strong>code</td>
</tr>
<tr>
<td class=fielddesc>Used in Code Value fields. Column name becomes: <tt>code_cd_name</tt>. Mandatory if using type Dropdown, otherwise an error is returned.</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>mandatory</td>
</tr>
<tr>
<td class=fielddesc>Determines whether this column must have a value in every entry. Optional, defaults to <tt>false</tt>.</td>
</tr>
<tr class=alt>
<td><strong>Mandatory [type = String] - </strong>length</td>
</tr>
<tr>
<td class=fielddesc>Length of the text field. Mandatory if type String is used, otherwise an error is returned.</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">
@@ -11619,6 +11890,153 @@ GET https://DomainName/api/v1/datatables/{datatable}
<h4>Update Data Table</h4>
<p>Modifies fields of a data table. If the apptableName parameter is passed,
data table is deregistered and registered with the new application table.</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td>
</tr>
<tr class=alt>
<td><em>Optional - </em> apptableName</td>
</tr>
<tr>
<td class=fielddesc>
Application table name. Only necessary if changing the application table this Data Table is registered to. Must be one of the following:
<ul class="field">
<li>m_client</li>
<li>m_group</li>
<li>m_loan</li>
<li>m_office</li>
<li>m_saving_account</li>
</ul>
</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>dropColumns</td>
</tr>
<tr>
<td class=fielddesc>An array of columns to be deleted from the Data Table.</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>addColumns</td>
</tr>
<tr>
<td class=fielddesc>An array of columns to be added to the Data Table.</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>changeColumns</td>
</tr>
<tr>
<td class=fielddesc>An array of columns to be changed in the Data Table.</td>
</tr>
</table>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions - dropColumns</div></td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong>name</td>
</tr>
<tr>
<td class=fielddesc>Requires a full name of the deleted column to be provided.</td>
</tr>
</table>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions - addColumns</div></td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong>name</td>
</tr>
<tr>
<td class=fielddesc>Name of the created column. Can contain only alphanumeric characters, underscores and spaces, but cannot start with a number. Cannot start or end with an underscore or space.</td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong>type</td>
</tr>
<tr>
<td class=fielddesc>
Column type. Must be one of the following:
<ul class="field">
<li>Date</li>
<li>Decimal</li>
<li>Dropdown</li>
<li>Number</li>
<li>String</li>
<li>Text</li>
</ul>
</td>
</tr>
<tr class=alt>
<td><strong>Mandatory [type = Dropdown] - </strong>code</td>
</tr>
<tr>
<td class=fielddesc>Used in Code Value fields. Column name becomes: <tt>code_cd_name</tt>. Mandatory if using type Dropdown, otherwise an error is returned.</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>mandatory</td>
</tr>
<tr>
<td class=fielddesc>Determines whether this column must have a value in every entry. Optional, defaults to <tt>false</tt>.</td>
</tr>
<tr class=alt>
<td><strong>Mandatory [type = String] - </strong>length</td>
</tr>
<tr>
<td class=fielddesc>Length of the text field. Mandatory if type String is used, otherwise an error is returned.</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>after</td>
</tr>
<tr>
<td class=fielddesc>Only used when re-ordering Data Table columns. Requires a full column name to be provided.</td>
</tr>
</table>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions - changeColumns</div></td>
</tr>
<tr class=alt>
<td><strong>Mandatory - </strong>name</td>
</tr>
<tr>
<td class=fielddesc>Name of the created column. Can contain only alphanumeric characters, underscores and spaces, but cannot start with a number. Cannot start or end with an underscore or space.</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>newName</td>
</tr>
<tr>
<td class=fielddesc>New name of the created column. Can contain only alphanumeric characters, underscores and spaces, but cannot start with a number. Cannot start or end with an underscore or space.</td>
</tr>
<tr class=alt>
<td><strong>Mandatory [type = Dropdown] - </strong>code</td>
</tr>
<tr>
<td class=fielddesc>Used in Code Value fields. Column name becomes: <tt>code_cd_name</tt>. Mandatory if using type Dropdown, otherwise an error is returned.</td>
</tr>
<tr class=alt>
<td><em>Optional [type = Dropdown] - </em>newCode</td>
</tr>
<tr>
<td class=fielddesc>Used in Code Value fields. Column name becomes: <tt>code_cd_name</tt>. Optional if using type Dropdown, otherwise an error is returned.</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>mandatory</td>
</tr>
<tr>
<td class=fielddesc>Determines whether this column must have a value in every entry. Optional, defaults to <tt>false</tt>.</td>
</tr>
<tr class=alt>
<td><strong>Mandatory [type = String] - </strong>length</td>
</tr>
<tr>
<td class=fielddesc>Length of the text field. Mandatory if type String is used, otherwise an error is returned.</td>
</tr>
<tr class=alt>
<td><em>Optional - </em>after</td>
</tr>
<tr>
<td class=fielddesc>Only used when re-ordering Data Table columns. Requires a full column name to be provided.</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">
@@ -11631,26 +12049,31 @@ Content-Type: application/json
Request Body:
{
"apptableName": "m_client",
"columns": [
"dropColumns": [
{
"name": "Gender",
"name": "Gender_cd_Question"
}
],
"addColumns": [
{
"name": "Question",
"type": "Dropdown",
"code": "Gender"
},
{
"name": "Some Decimal",
"type": "Decimal",
"code": "Gender",
"mandatory": true
},
{
"name": "Birth Date",
"type": "Date"
},
"name": "Some Number",
"type": "Number",
"after": "Some Field"
}
],
"changeColumns": [
{
"name": "Question 2",
"type": "String",
"length": 100,
"mandatory": false
"name": "Question",
"newName": "Question 2",
"mandatory": true,
"code": "Gender",
"newCode": "Gender2"
}
]
}
@@ -11668,7 +12091,7 @@ Request Body:
<div class="method-section">
<div class="method-description">
<h4>Delete Data Table</h4>
<p>Deletes a data table and unregisters it from the Mifos X Core
<p>Deletes a data table and deregisters it from the Mifos X Core
application table.</p>
</div>
<div class="method-example">
@@ -15386,6 +15809,54 @@ GET https://DomainName/api/v1/search?query=000000111
</td>
<td></td>
</tr>
<tr class="alt">
<td><a href="#groups">Group</a></td>
<td>groups</td>
<td><a href="#groups_create">Create a Group</a></td>
<td><a href="#groups_list">List Groups</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/template</td>
<td></td>
<td><a href="#groups_template">Retrieve Group Template</a></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>groups/{groupId}</td>
<td></td>
<td><a href="#groups_retrieve">Retrieve a Group</a></td>
<td><a href="#groups_update">Update a Group</a></td>
<td><a href="#groups_delete">Delete a Group</a></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=activate</td>
<td><a href="#groups_activate">Activate a Group</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=associateClients</td>
<td><a href="#groups_associate_clients">Associate Clients</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=disassociateClients</td>
<td><a href="#groups_disassociate_clients">Disassociate Clients</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td><a href="#loanproducts">Loan Product</a></td>
<td>loanproducts</td>
@@ -16128,54 +16599,6 @@ GET https://DomainName/api/v1/search?query=000000111
<td></td>
<td></td>
</tr>
<tr class="alt">
<td><a href="#groups">Group</a></td>
<td>groups</td>
<td><a href="#groups_create">Create a Group</a></td>
<td><a href="#groups_list">List Groups</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/template</td>
<td></td>
<td><a href="#groups_template">Retrieve Group Template</a></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>groups/{groupId}</td>
<td></td>
<td><a href="#groups_retrieve">Retrieve a Group</a></td>
<td><a href="#groups_update">Update a Group</a></td>
<td><a href="#groups_delete">Delete a Group</a></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=activate</td>
<td><a href="#groups_activate">Activate a Group</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=associateClients</td>
<td><a href="#groups_associate_clients">Associate Clients</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>groups/{groupId}?command=disassociateClients</td>
<td><a href="#groups_disassociate_clients">Disassociate Clients</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td><a href="#savingsproducts">Savings Product</a></td>
<td>savingsproducts</td>
+13
View File
@@ -724,3 +724,16 @@ strong { font-weight: bold; }
.stripe-version {
float: left;
}
ul.field {
margin: 5px 10px;
}
ul.field li {
list-style: square inside none;
font-style: italic;
}
tt {
font-size: 9.5pt;
}
@@ -0,0 +1,3 @@
ALTER TABLE `acc_accounting_rule`
CHANGE COLUMN `debit_account_id` `debit_account_id` BIGINT(20) NULL DEFAULT NULL AFTER `office_id`,
CHANGE COLUMN `credit_account_id` `credit_account_id` BIGINT(20) NULL DEFAULT NULL AFTER `allow_multiple_debits`;
@@ -0,0 +1,22 @@
create table `m_holiday`(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`from_date` DATETIME NOT NULL,
`to_date` DATETIME NOT NULL,
`description` varchar(100) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `holiday_name` (`name`)
);
CREATE TABLE `m_holiday_office` (
`holiday_id` BIGINT(20) NOT NULL,
`office_id` BIGINT(20) NOT NULL,
PRIMARY KEY (`holiday_id`, `office_id`),
INDEX `m_holiday_id_ibfk_1` (`holiday_id`),
INDEX `m_office_id_ibfk_2` (`office_id`),
CONSTRAINT `m_holiday_id_ibfk_1` FOREIGN KEY (`holiday_id`) REFERENCES `m_holiday` (`id`),
CONSTRAINT `m_office_id_ibfk_2` FOREIGN KEY (`office_id`) REFERENCES `m_office` (`id`)
);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('organisation', 'CREATE_HOLIDAY', 'HOLIDAY', 'CREATE', 0);
@@ -0,0 +1,22 @@
INSERT INTO `m_code` (`code_name`, `is_system_defined`) VALUES ('GROUPROLE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'ASSIGNROLE_GROUP', 'GROUP', 'ASSIGNROLE', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UNASSIGNROLE_GROUP', 'GROUP', 'UNASSIGNROLE', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UPDATEROLE_GROUP', 'GROUP', 'UPDATEROLE', 0);
CREATE TABLE `m_group_roles` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`client_id` BIGINT(20) NULL DEFAULT NULL,
`group_id` BIGINT(20) NULL DEFAULT NULL,
`role_cv_id` INT(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `FKGroupRoleClientId` (`client_id`),
INDEX `FKGroupRoleGroupId` (`group_id`),
INDEX `FK_grouprole_m_codevalue` (`role_cv_id`),
UNIQUE INDEX `UNIQUE_GROUP_ROLES` (`client_id`, `group_id`, `role_cv_id`),
CONSTRAINT `FKGroupRoleClientId` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`),
CONSTRAINT `FKGroupRoleGroupId` FOREIGN KEY (`group_id`) REFERENCES `m_group` (`id`),
CONSTRAINT `FK_grouprole_m_codevalue` FOREIGN KEY (`role_cv_id`) REFERENCES `m_code_value` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
@@ -0,0 +1,320 @@
/* Leaving as out-of-box (but non-core reports) for now
could be removed later if people think too many non-generic
reports are being added
used in quipo TEVI dashboard (landing pages) */
DELETE FROM `stretchy_report` where report_name = 'FieldAgentStats';
DELETE FROM `stretchy_report` where report_name = 'FieldAgentPrograms';
DELETE FROM `stretchy_report` where report_name = 'ProgramDetails';
DELETE FROM `stretchy_report` where report_name = 'ChildrenStaffList';
DELETE FROM `stretchy_report` where report_name = 'CoordinatorStats';
DELETE FROM `stretchy_report` where report_name = 'BranchManagerStats';
DELETE FROM `stretchy_report` where report_name = 'ProgramDirectorStats';
DELETE FROM `stretchy_report` where report_name = 'ProgramStats';
DELETE FROM `m_permission` where entity_name = 'FieldAgentStats';
DELETE FROM `m_permission` where entity_name = 'FieldAgentPrograms';
DELETE FROM `m_permission` where entity_name = 'ProgramDetails';
DELETE FROM `m_permission` where entity_name = 'ChildrenStaffList';
DELETE FROM `m_permission` where entity_name = 'CoordinatorStats';
DELETE FROM `m_permission` where entity_name = 'BranchManagerStats';
DELETE FROM `m_permission` where entity_name = 'ProgramDirectorStats';
DELETE FROM `m_permission` where entity_name = 'ProgramStats';
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('FieldAgentStats', 'Table', 'Quipo', false, false, 'Field Agent Statistics',
"
select ifnull(cur.display_symbol, l.currency_code) as Currency,
/*This query will return more than one entry if more than one currency is used */
count(distinct(c.id)) as activeClients, count(*) as activeLoans,
sum(l.principal_disbursed_derived) as disbursedAmount,
sum(l.principal_outstanding_derived) as loanOutstandingAmount,
round((sum(l.principal_outstanding_derived) * 100) / sum(l.principal_disbursed_derived),2) as loanOutstandingPC,
'???' as loanOverPaymentAmount,
sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) as portfolioAtRisk,
round((sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) * 100) / sum(l.principal_outstanding_derived), 2) as portfolioAtRiskPC,
count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) as clientsInDefault,
round((count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) * 100) / count(distinct(c.id)),2) as clientsInDefaultPC,
(sum(l.principal_disbursed_derived) / count(*)) as averageLoanAmount
from m_staff fa
join m_office o on o.id = fa.office_id
and o.hierarchy like concat('${currentUserHierarchy}', '%')
join m_group pgm on pgm.staff_id = fa.id
join m_loan l on l.group_id = pgm.id and l.client_id is not null
left join m_currency cur on cur.code = l.currency_code
left join m_loan_arrears_aging laa on laa.loan_id = l.id
join m_client c on c.id = l.client_id
where fa.id = ${staffId}
and l.loan_status_id = 300
group by l.currency_code
");
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('FieldAgentPrograms', 'Table', 'Quipo', false, false, 'List of Field Agent Programs',
"
select pgm.id, pgm.display_name as `name`, sts.enum_message_property as status
from m_group pgm
join m_office o on o.id = pgm.office_id
and o.hierarchy like concat('${currentUserHierarchy}', '%')
left join r_enum_value sts on sts.enum_name = 'status_enum' and sts.enum_id = pgm.status_enum
where pgm.staff_id = ${staffId}
");
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('ProgramDetails', 'Table', 'Quipo', false, false, 'List of Loans in a Program',
"
select l.id as loanId, l.account_no as loanAccountNo, c.id as clientId, c.account_no as clientAccountNo,
pgm.display_name as programName,
(select count(*)
from m_loan cy
where cy.group_id = pgm.id and cy.client_id =c.id
and cy.disbursedon_date <= l.disbursedon_date) as loanCycleNo,
c.display_name as clientDisplayName,
ifnull(cur.display_symbol, l.currency_code) as Currency,
ifnull(l.principal_repaid_derived,0.0) as loanRepaidAmount,
ifnull(l.principal_outstanding_derived, 0.0) as loanOutstandingAmount,
'???' as loanInAdvanceAmount,
ifnull(laa.principal_overdue_derived, 0.0) as loanInArrearsAmount,
if(ifnull(laa.principal_overdue_derived, 0.00) > 0, 'Yes', 'No') as inDefault,
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0) as portfolioAtRisk
from m_group pgm
join m_office o on o.id = pgm.office_id
and o.hierarchy like concat('${currentUserHierarchy}', '%')
join m_loan l on l.group_id = pgm.id and l.client_id is not null
left join m_currency cur on cur.code = l.currency_code
join m_client c on c.id = l.client_id
left join m_loan_arrears_aging laa on laa.loan_id = l.id
where pgm.id = ${programId}
and l.loan_status_id = 300
order by c.display_name, l.account_no
");
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('ChildrenStaffList', 'Table', 'Quipo', false, false, 'Get Next Level Down Staff',
"
select s.id, s.display_name,
s.firstname, s.lastname, s.organisational_role_enum,
s.organisational_role_parent_staff_id,
sp.display_name as `organisational_role_parent_staff_display_name`
from m_staff s
join m_staff sp on s.organisational_role_parent_staff_id = sp.id
where s.organisational_role_parent_staff_id = ${staffId}
");
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('CoordinatorStats', 'Table', 'Quipo', false, false, 'Coordinator Statistics',
"
select ifnull(cur.display_symbol, l.currency_code) as Currency,
/*This query will return more than one entry if more than one currency is used */
count(distinct(c.id)) as activeClients, count(*) as activeLoans,
sum(l.principal_disbursed_derived) as disbursedAmount,
sum(l.principal_outstanding_derived) as loanOutstandingAmount,
round((sum(l.principal_outstanding_derived) * 100) / sum(l.principal_disbursed_derived),2) as loanOutstandingPC,
'???' as loanOverPaymentAmount,
sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) as portfolioAtRisk,
round((sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) * 100) / sum(l.principal_outstanding_derived), 2) as portfolioAtRiskPC,
count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) as clientsInDefault,
round((count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) * 100) / count(distinct(c.id)),2) as clientsInDefaultPC,
(sum(l.principal_disbursed_derived) / count(*)) as averageLoanAmount
from m_staff coord
join m_staff fa on fa.organisational_role_parent_staff_id = coord.id
join m_office o on o.id = fa.office_id
and o.hierarchy like concat('${currentUserHierarchy}', '%')
join m_group pgm on pgm.staff_id = fa.id
join m_loan l on l.group_id = pgm.id and l.client_id is not null
left join m_currency cur on cur.code = l.currency_code
left join m_loan_arrears_aging laa on laa.loan_id = l.id
join m_client c on c.id = l.client_id
where coord.id = ${staffId}
and l.loan_status_id = 300
group by l.currency_code
");
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('BranchManagerStats', 'Table', 'Quipo', false, false, 'Branch Manager Statistics',
"
select ifnull(cur.display_symbol, l.currency_code) as Currency,
/*This query will return more than one entry if more than one currency is used */
count(distinct(c.id)) as activeClients, count(*) as activeLoans,
sum(l.principal_disbursed_derived) as disbursedAmount,
sum(l.principal_outstanding_derived) as loanOutstandingAmount,
round((sum(l.principal_outstanding_derived) * 100) / sum(l.principal_disbursed_derived),2) as loanOutstandingPC,
'???' as loanOverPaymentAmount,
sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) as portfolioAtRisk,
round((sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) * 100) / sum(l.principal_outstanding_derived), 2) as portfolioAtRiskPC,
count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) as clientsInDefault,
round((count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) * 100) / count(distinct(c.id)),2) as clientsInDefaultPC,
(sum(l.principal_disbursed_derived) / count(*)) as averageLoanAmount
from m_staff bm
join m_staff coord on coord.organisational_role_parent_staff_id = bm.id
join m_staff fa on fa.organisational_role_parent_staff_id = coord.id
join m_office o on o.id = fa.office_id
and o.hierarchy like concat('${currentUserHierarchy}', '%')
join m_group pgm on pgm.staff_id = fa.id
join m_loan l on l.group_id = pgm.id and l.client_id is not null
left join m_currency cur on cur.code = l.currency_code
left join m_loan_arrears_aging laa on laa.loan_id = l.id
join m_client c on c.id = l.client_id
where bm.id = ${staffId}
and l.loan_status_id = 300
group by l.currency_code
");
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('ProgramDirectorStats', 'Table', 'Quipo', false, false, 'Program DirectorStatistics',
"
select ifnull(cur.display_symbol, l.currency_code) as Currency,
/*This query will return more than one entry if more than one currency is used */
count(distinct(c.id)) as activeClients, count(*) as activeLoans,
sum(l.principal_disbursed_derived) as disbursedAmount,
sum(l.principal_outstanding_derived) as loanOutstandingAmount,
round((sum(l.principal_outstanding_derived) * 100) / sum(l.principal_disbursed_derived),2) as loanOutstandingPC,
'???' as loanOverPaymentAmount,
sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) as portfolioAtRisk,
round((sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) * 100) / sum(l.principal_outstanding_derived), 2) as portfolioAtRiskPC,
count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) as clientsInDefault,
round((count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) * 100) / count(distinct(c.id)),2) as clientsInDefaultPC,
(sum(l.principal_disbursed_derived) / count(*)) as averageLoanAmount
from m_staff pd
join m_staff bm on bm.organisational_role_parent_staff_id = pd.id
join m_staff coord on coord.organisational_role_parent_staff_id = bm.id
join m_staff fa on fa.organisational_role_parent_staff_id = coord.id
join m_office o on o.id = fa.office_id
and o.hierarchy like concat('${currentUserHierarchy}', '%')
join m_group pgm on pgm.staff_id = fa.id
join m_loan l on l.group_id = pgm.id and l.client_id is not null
left join m_currency cur on cur.code = l.currency_code
left join m_loan_arrears_aging laa on laa.loan_id = l.id
join m_client c on c.id = l.client_id
where pd.id = ${staffId}
and l.loan_status_id = 300
group by l.currency_code
");
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('ProgramStats', 'Table', 'Quipo', false, false, 'Program Statistics',
"
select ifnull(cur.display_symbol, l.currency_code) as Currency,
/*This query will return more than one entry if more than one currency is used */
count(distinct(c.id)) as activeClients, count(*) as activeLoans,
sum(l.principal_disbursed_derived) as disbursedAmount,
sum(l.principal_outstanding_derived) as loanOutstandingAmount,
round((sum(l.principal_outstanding_derived) * 100) / sum(l.principal_disbursed_derived),2) as loanOutstandingPC,
'???' as loanOverPaymentAmount,
sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) as portfolioAtRisk,
round((sum(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
l.principal_outstanding_derived,0)) * 100) / sum(l.principal_outstanding_derived), 2) as portfolioAtRiskPC,
count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) as clientsInDefault,
round((count(distinct(
if(date_sub(curdate(), interval 28 day) > ifnull(laa.overdue_since_date_derived, curdate()),
c.id,null))) * 100) / count(distinct(c.id)),2) as clientsInDefaultPC,
(sum(l.principal_disbursed_derived) / count(*)) as averageLoanAmount
from m_group pgm
join m_office o on o.id = pgm.office_id
and o.hierarchy like concat('${currentUserHierarchy}', '%')
join m_loan l on l.group_id = pgm.id and l.client_id is not null
left join m_currency cur on cur.code = l.currency_code
left join m_loan_arrears_aging laa on laa.loan_id = l.id
join m_client c on c.id = l.client_id
where pgm.id = ${programId}
and l.loan_status_id = 300
group by l.currency_code
");
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_FieldAgentStats', 'FieldAgentStats', 'READ', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_FieldAgentPrograms', 'FieldAgentPrograms', 'READ', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_ProgramDetails', 'ProgramDetails', 'READ', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_ChildrenStaffList', 'ChildrenStaffList', 'READ', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_CoordinatorStats', 'CoordinatorStats', 'READ', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_BranchManagerStats', 'BranchManagerStats', 'READ', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_ProgramDirectorStats', 'ProgramDirectorStats', 'READ', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_ProgramStats', 'ProgramStats', 'READ', 0);
@@ -0,0 +1,124 @@
/* Leaving as out-of-box (but non-core report) for now
could be removed later if people think too many non-generic
reports are being added
similar example used in small MFI Elevate Africa */
delete from stretchy_report_parameter
where report_id = (select r.id from stretchy_report r where r.report_name = 'TxnRunningBalances');
DELETE FROM `stretchy_report` where report_name = 'TxnRunningBalances';
DELETE FROM `m_permission` where entity_name = 'TxnRunningBalances';
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `core_report`, `use_report`, `description`, `report_sql`)
VALUES ('TxnRunningBalances', 'Table', 'Transaction', false, false,
"Running Balance Txn report for Individual Lending.
Suitable for small MFI's. Larger could use it using the branch or other parameters.
Basically, suck it and see if its quick enough for you out-of-te box or whether it needs performance work in your situation.
",
"
select date('${startDate}') as 'Transaction Date', 'Opening Balance' as `Transaction Type`, null as Office,
null as 'Loan Officer', null as `Loan Account No`, null as `Loan Product`, null as `Currency`,
null as `Client Account No`, null as Client,
null as Amount, null as Principal, null as Interest,
@totalOutstandingPrincipal :=
ifnull(round(sum(
if (txn.transaction_type_enum = 1 /* disbursement */,
ifnull(txn.amount,0.00),
ifnull(txn.principal_portion_derived,0.00) * -1))
,2),0.00) as 'Outstanding Principal',
@totalInterestIncome :=
ifnull(round(sum(
if (txn.transaction_type_enum in (2,5,8) /* repayment, repayment at disbursal, recovery repayment */,
ifnull(txn.interest_portion_derived,0.00),
0))
,2),0.00) as 'Interest Income',
@totalWriteOff :=
ifnull(round(sum(
if (txn.transaction_type_enum = 6 /* write-off */,
ifnull(txn.principal_portion_derived,0.00),
0))
,2),0.00) as 'Principal Write Off'
from m_office o
join m_office ounder on ounder.hierarchy like concat(o.hierarchy, '%')
and ounder.hierarchy like concat('${currentUserHierarchy}', '%')
join m_client c on c.office_id = ounder.id
join m_loan l on l.client_id = c.id
join m_product_loan lp on lp.id = l.product_id
join m_loan_transaction txn on txn.loan_id = l.id
left join m_currency cur on cur.code = l.currency_code
where txn.is_reversed = false
and txn.transaction_type_enum not in (10,11)
and o.id = ${officeId}
and txn.transaction_date < date('${startDate}')
union all
select x.`Transaction Date`, x.`Transaction Type`, x.Office, x.`Loan Officer`, x.`Loan Account No`, x.`Loan Product`, x.`Currency`,
x.`Client Account No`, x.Client, x.Amount, x.Principal, x.Interest,
cast(round(
if (x.transaction_type_enum = 1 /* disbursement */,
@totalOutstandingPrincipal := @totalOutstandingPrincipal + x.`Amount`,
@totalOutstandingPrincipal := @totalOutstandingPrincipal - x.`Principal`)
,2) as decimal(19,2)) as 'Outstanding Principal',
cast(round(
if (x.transaction_type_enum in (2,5,8) /* repayment, repayment at disbursal, recovery repayment */,
@totalInterestIncome := @totalInterestIncome + x.`Interest`,
@totalInterestIncome)
,2) as decimal(19,2)) as 'Interest Income',
cast(round(
if (x.transaction_type_enum = 6 /* write-off */,
@totalWriteOff := @totalWriteOff + x.`Principal`,
@totalWriteOff)
,2) as decimal(19,2)) as 'Principal Write Off'
from
(select txn.transaction_type_enum, txn.id as txn_id, txn.transaction_date as 'Transaction Date',
cast(
ifnull(re.enum_message_property, concat('Unknown Transaction Type Value: ' , txn.transaction_type_enum))
as char) as 'Transaction Type',
ounder.`name` as Office, lo.display_name as 'Loan Officer',
l.account_no as 'Loan Account No', lp.`name` as 'Loan Product',
ifnull(cur.display_symbol, l.currency_code) as Currency,
c.account_no as 'Client Account No', c.display_name as 'Client',
ifnull(txn.amount,0.00) as Amount,
ifnull(txn.principal_portion_derived,0.00) as Principal,
ifnull(txn.interest_portion_derived,0.00) as Interest
from m_office o
join m_office ounder on ounder.hierarchy like concat(o.hierarchy, '%')
and ounder.hierarchy like concat('${currentUserHierarchy}', '%')
join m_client c on c.office_id = ounder.id
join m_loan l on l.client_id = c.id
left join m_staff lo on lo.id = l.loan_officer_id
join m_product_loan lp on lp.id = l.product_id
join m_loan_transaction txn on txn.loan_id = l.id
left join m_currency cur on cur.code = l.currency_code
left join r_enum_value re on re.enum_name = 'transaction_type_enum'
and re.enum_id = txn.transaction_type_enum
where txn.is_reversed = false
and txn.transaction_type_enum not in (10,11)
and (ifnull(l.loan_officer_id, -10) = '${loanOfficerId}' or '-1' = '${loanOfficerId}')
and o.id = ${officeId}
and txn.transaction_date >= date('${startDate}')
and txn.transaction_date <= date('${endDate}')
order by txn.transaction_date, txn.id) x
");
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('report', 'READ_TxnRunningBalances', 'TxnRunningBalances', 'READ', 0);
insert into stretchy_report_parameter (report_id, parameter_id)
select r.id, p.id
from stretchy_report r,
stretchy_parameter p
where r.report_name = 'TxnRunningBalances'
and p.parameter_name in ('startDateSelect', 'endDateSelect', 'OfficeIdSelectOne',
'loanOfficerIdSelectAll');
@@ -0,0 +1,5 @@
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UNASSIGNSTAFF_CLIENT', 'CLIENT', 'UNASSIGNSTAFF', 0);
ALTER TABLE m_client
ADD COLUMN `staff_id` BIGINT(20) NULL DEFAULT NULL AFTER `office_id`,
ADD INDEX `client_staff_id` (`staff_id`),
ADD CONSTRAINT `FK_m_client_m_staff` FOREIGN KEY (`staff_id`) REFERENCES `m_staff` (`id`);
+1 -1
View File
@@ -1 +1 @@
releaseVersion=1.3.0.RELEASE
releaseVersion=1.4.0.RELEASE
@@ -12,7 +12,7 @@ import java.util.Set;
* Enum of all parameters passed in while creating/updating a loan product
***/
public enum AccountingRuleJsonInputParams {
ID("id"), OFFICE_ID("officeId"), ACCOUNT_TO_DEBIT("debitAccountHead"), ACCOUNT_TO_CREDIT("creditAccountHead"), NAME("name"), DESCRIPTION(
ID("id"), OFFICE_ID("officeId"), ACCOUNT_TO_DEBIT("accountToDebit"), ACCOUNT_TO_CREDIT("accountToCredit"), NAME("name"), DESCRIPTION(
"description"), SYSTEM_DEFINED("systemDefined"), DEBIT_ACCOUNT_TAGS("debitTags"), CREDIT_ACCOUNT_TAGS("creditTags"), ALLOW_MULTIPLE_CREDIT_ENTRIES(
"allowMultipleCreditEntries"), ALLOW_MULTIPLE_DEBIT_ENTRIES("allowMultipleDebitEntries");
@@ -109,18 +109,15 @@ public class CommandWrapper {
}
public boolean isCreateDatatable() {
return (this.actionName.equalsIgnoreCase("CREATE") && this.href.startsWith("/datatables/")
&& this.entityId == null);
return (this.actionName.equalsIgnoreCase("CREATE") && this.href.startsWith("/datatables/") && this.entityId == null);
}
public boolean isDeleteDatatable() {
return (this.actionName.equalsIgnoreCase("DELETE") && this.href.startsWith("/datatables/")
&& this.entityId == null);
return (this.actionName.equalsIgnoreCase("DELETE") && this.href.startsWith("/datatables/") && this.entityId == null);
}
public boolean isUpdateDatatable() {
return (this.actionName.equalsIgnoreCase("UPDATE") && this.href.startsWith("/datatables/")
&& this.entityId == null);
return (this.actionName.equalsIgnoreCase("UPDATE") && this.href.startsWith("/datatables/") && this.entityId == null);
}
public String getTaskPermissionName() {
@@ -415,7 +412,7 @@ public class CommandWrapper {
}
public boolean isUpdateOfOwnUserDetails(final Long loggedInUserId) {
return this.isUserResource() && isUpdate() && loggedInUserId.equals(this.entityId);
return isUserResource() && isUpdate() && loggedInUserId.equals(this.entityId);
}
public boolean isDepositProductResource() {
@@ -499,8 +496,8 @@ public class CommandWrapper {
public boolean isGroupResource() {
return this.entityName.equalsIgnoreCase("GROUP");
}
public boolean isSaveGroupCollectionSheet(){
public boolean isSaveGroupCollectionSheet() {
return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("SAVECOLLECTIONSHEET");
}
@@ -512,10 +509,10 @@ public class CommandWrapper {
return this.entityName.equalsIgnoreCase("CENTER");
}
public boolean isSaveCenterCollectionSheet(){
public boolean isSaveCenterCollectionSheet() {
return this.entityName.equalsIgnoreCase("CENTER") && this.actionName.equalsIgnoreCase("SAVECOLLECTIONSHEET");
}
public boolean isReportResource() {
return this.entityName.equalsIgnoreCase("REPORT");
}
@@ -528,7 +525,28 @@ public class CommandWrapper {
return this.actionName.equalsIgnoreCase("DISASSOCIATECLIENTS");
}
public boolean isAccountingRuleResource() {
return this.entityName.equalsIgnoreCase("ACCOUNTINGRULE");
}
public boolean isHolidayResource() {
return this.entityName.equalsIgnoreCase("HOLIDAY");
}
public boolean isAccountingRuleResource() {
return this.entityName.equalsIgnoreCase("ACCOUNTINGRULE");
}
public boolean isClientUnassignStaff() {
return this.actionName.equalsIgnoreCase("UNASSIGNSTAFF") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isAssignRole() {
return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("ASSIGNROLE");
}
public boolean isUnAssignRole() {
return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("UNASSIGNROLE");
}
public boolean isUpdateRole() {
return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("UPDATEROLE");
}
}
@@ -1018,4 +1018,48 @@ public class CommandWrapperBuilder {
this.href = "/accountingrules/" + accountingRuleId;
return this;
}
public CommandWrapperBuilder createHoliday() {
this.actionName = "CREATE";
this.entityName = "HOLIDAY";
this.entityId = null;
this.href = "/holidays/template";
return this;
}
public CommandWrapperBuilder assignRole(Long groupId) {
this.actionName = "ASSIGNROLE";
this.entityName = "GROUP";
this.groupId = groupId;
this.entityId = null;
this.href = "/groups/"+groupId+"?command=assignRole";
return this;
}
public CommandWrapperBuilder unassignRole(Long groupId, Long roleId) {
this.actionName = "UNASSIGNROLE";
this.entityName = "GROUP";
this.groupId = groupId;
this.entityId = roleId;
this.href = "/groups/"+groupId+"?command=unassignRole";
return this;
}
public CommandWrapperBuilder updateRole(Long groupId, Long roleId) {
this.actionName = "UPDATEROLE";
this.entityName = "GROUP";
this.groupId = groupId;
this.entityId = roleId;
this.href = "/groups/"+groupId+"?command=updateRole";
return this;
}
public CommandWrapperBuilder unassignClientStaff(Long clientId) {
this.actionName = "UNASSIGNSTAFF";
this.entityName = "CLIENT";
this.entityId = clientId;
this.clientId = clientId;
this.href = "/clients/" + clientId + "?command=unassignStaff";
return this;
}
}
@@ -59,7 +59,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
final NewCommandSourceHandler handler = findCommandHandler(wrapper);
final CommandProcessingResult result = handler.processCommand(command);
final AppUser maker = context.authenticatedUser();
final AppUser maker = this.context.authenticatedUser();
CommandSource commandSourceResult = null;
if (command.commandId() != null) {
@@ -83,7 +83,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
}
if (commandSourceResult.hasJson()) {
commandSourceRepository.save(commandSourceResult);
this.commandSourceRepository.save(commandSourceResult);
}
if (rollbackTransaction) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(commandSourceResult); }
@@ -96,7 +96,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
public CommandProcessingResult logCommand(final CommandSource commandSourceResult) {
commandSourceResult.markAsAwaitingApproval();
commandSourceRepository.save(commandSourceResult);
this.commandSourceRepository.save(commandSourceResult);
return new CommandProcessingResultBuilder().withCommandId(commandSourceResult.getId())
.withEntityId(commandSourceResult.getResourceId()).build();
@@ -106,333 +106,342 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
NewCommandSourceHandler handler = null;
if (wrapper.isConfigurationResource()) {
handler = applicationContext.getBean("updateGlobalConfigurationCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateGlobalConfigurationCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDatatableResource()) {
if (wrapper.isCreateDatatable()) {
handler = applicationContext.getBean("createDatatableCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDeleteDatatable()) {
handler = applicationContext.getBean("deleteDatatableCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdateDatatable()) {
handler = applicationContext.getBean("updateDatatableCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isCreate()) {
handler = applicationContext.getBean("createDatatableEntryCommandHandler", NewCommandSourceHandler.class);
if (wrapper.isCreateDatatable()) {
handler = this.applicationContext.getBean("createDatatableCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDeleteDatatable()) {
handler = this.applicationContext.getBean("deleteDatatableCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdateDatatable()) {
handler = this.applicationContext.getBean("updateDatatableCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isCreate()) {
handler = this.applicationContext.getBean("createDatatableEntryCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdateMultiple()) {
handler = applicationContext.getBean("updateOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdateOneToOne()) {
handler = applicationContext.getBean("updateOneToOneDatatableEntryCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateOneToOneDatatableEntryCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDeleteMultiple()) {
handler = applicationContext.getBean("deleteOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDeleteOneToOne()) {
handler = applicationContext.getBean("deleteOneToOneDatatableEntryCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteOneToOneDatatableEntryCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isNoteResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createNoteCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createNoteCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateNoteCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateNoteCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteNoteCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteNoteCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isClientIdentifierResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createClientIdentifierCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createClientIdentifierCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateClientIdentifierCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateClientIdentifierCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteClientIdentifierCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteClientIdentifierCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isClientResource() && !wrapper.isClientNoteResource() && !wrapper.isClientIdentifierResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createClientCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createClientCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateClientCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateClientCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteClientCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteClientCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isClientActivation()) {
handler = applicationContext.getBean("activateClientCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("activateClientCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isClientUnassignStaff()) {
handler = applicationContext.getBean("unassignClientStaffCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
// end of client
} else if (wrapper.isUpdateRolePermissions()) {
handler = applicationContext.getBean("updateRolePermissionsCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateRolePermissionsCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isPermissionResource()) {
handler = applicationContext.getBean("updateMakerCheckerPermissionsCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateMakerCheckerPermissionsCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isRoleResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createRoleCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createRoleCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateRoleCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateRoleCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isUserResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createUserCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createUserCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateUserCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateUserCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteUserCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteUserCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isStaffResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createStaffCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createStaffCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateStaffCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateStaffCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isGuarantorResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createGuarantorCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createGuarantorCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateGuarantorCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateGuarantorCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteGuarantorCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteGuarantorCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isCollateralResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createCollateralCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createCollateralCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateCollateralCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateCollateralCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteCollateralCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteCollateralCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isCodeResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createCodeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createCodeCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateCodeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateCodeCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteCodeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteCodeCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isCodeValueResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createCodeValueCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createCodeValueCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateCodeValueCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateCodeValueCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteCodeValueCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteCodeValueCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isCurrencyResource()) {
handler = applicationContext.getBean("updateCurrencyCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateCurrencyCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isFundResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createFundCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createFundCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateFundCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateFundCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isOfficeResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createOfficeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createOfficeCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateOfficeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateOfficeCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isOfficeTransactionResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createOfficeTransactionCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createOfficeTransactionCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteOfficeTransactionCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteOfficeTransactionCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isChargeDefinitionResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createChargeDefinitionCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createChargeDefinitionCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateChargeDefinitionCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateChargeDefinitionCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteChargeDefinitionCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteChargeDefinitionCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isLoanProductResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createLoanProductCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createLoanProductCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateLoanProductCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateLoanProductCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isLoanResource()) {
if (wrapper.isApproveLoanApplication()) {
handler = applicationContext.getBean("loanApplicationApprovalCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanApplicationApprovalCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUndoApprovalOfLoanApplication()) {
handler = applicationContext.getBean("loanApplicationApprovalUndoCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanApplicationApprovalUndoCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isApplicantWithdrawalFromLoanApplication()) {
handler = applicationContext.getBean("loanApplicationWithdrawnByApplicantCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanApplicationWithdrawnByApplicantCommandHandler",
NewCommandSourceHandler.class);
} else if (wrapper.isRejectionOfLoanApplication()) {
handler = applicationContext.getBean("loanApplicationRejectedCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanApplicationRejectedCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDisbursementOfLoan()) {
handler = applicationContext.getBean("disburseLoanCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("disburseLoanCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUndoDisbursementOfLoan()) {
handler = applicationContext.getBean("undoDisbursalLoanCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("undoDisbursalLoanCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isLoanRepayment()) {
handler = applicationContext.getBean("loanRepaymentCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanRepaymentCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isLoanRepaymentAdjustment()) {
handler = applicationContext.getBean("loanRepaymentAdjustmentCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanRepaymentAdjustmentCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isWaiveInterestPortionOnLoan()) {
handler = applicationContext.getBean("waiveInterestPortionOnLoanCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("waiveInterestPortionOnLoanCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isLoanWriteOff()) {
handler = applicationContext.getBean("writeOffLoanCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("writeOffLoanCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isCloseLoanAsObligationsMet()) {
handler = applicationContext.getBean("closeLoanCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("closeLoanCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isCloseLoanAsRescheduled()) {
handler = applicationContext.getBean("closeLoanAsRescheduledCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("closeLoanAsRescheduledCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdateLoanOfficer()) {
handler = applicationContext.getBean("updateLoanOfficerCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateLoanOfficerCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isRemoveLoanOfficer()) {
handler = applicationContext.getBean("removeLoanOfficerCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("removeLoanOfficerCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isBulkUpdateLoanOfficer()) {
handler = applicationContext.getBean("bulkUpdateLoanOfficerCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("bulkUpdateLoanOfficerCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isCreate()) {
handler = applicationContext.getBean("loanApplicationSubmittalCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanApplicationSubmittalCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("loanApplicationModificationCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanApplicationModificationCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("loanApplicationDeletionCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("loanApplicationDeletionCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isLoanChargeResource()) {
if (wrapper.isAddLoanCharge()) {
handler = applicationContext.getBean("addLoanChargeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("addLoanChargeCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDeleteLoanCharge()) {
handler = applicationContext.getBean("deleteLoanChargeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteLoanChargeCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdateLoanCharge()) {
handler = applicationContext.getBean("updateLoanChargeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateLoanChargeCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isWaiveLoanCharge()) {
handler = applicationContext.getBean("waiveLoanChargeCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("waiveLoanChargeCommandHandler", NewCommandSourceHandler.class);
}
} else if (wrapper.isGLAccountResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createGLAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createGLAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateGLAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateGLAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteGLAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteGLAccountCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isGLClosureResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createGLClosureCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createGLClosureCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateGLClosureCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateGLClosureCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteGLClosureCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteGLClosureCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isJournalEntryResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createJournalEntryCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createJournalEntryCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isRevertJournalEntry()) {
handler = applicationContext.getBean("reverseJournalEntryCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("reverseJournalEntryCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isSavingsProductResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createSavingsProductCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createSavingsProductCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateSavingsProductCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateSavingsProductCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteSavingsProductCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteSavingsProductCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isSavingsAccountResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountDeposit()) {
handler = applicationContext.getBean("depositSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("depositSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountWithdrawal()) {
handler = applicationContext.getBean("withdrawSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("withdrawSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountActivation()) {
handler = applicationContext.getBean("activateSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("activateSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountInterestCalculation()) {
handler = applicationContext.getBean("calculateInterestSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("calculateInterestSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountInterestPosting()) {
handler = applicationContext.getBean("postInterestSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("postInterestSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isCalendarResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createCalendarCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createCalendarCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateCalendarCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateCalendarCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteCalendarCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteCalendarCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isGroupResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createGroupCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createGroupCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateGroupCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateGroupCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUnassignStaff()) {
handler = applicationContext.getBean("unassignGroupStaffCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("unassignGroupStaffCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteGroupCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteGroupCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isGroupActivation()) {
handler = applicationContext.getBean("activateGroupCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("activateGroupCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isAssociateClients()) {
handler = applicationContext.getBean("associateClientsToGroupCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("associateClientsToGroupCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDisassociateClients()) {
handler = applicationContext.getBean("disassociateClientsFromGroupCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("disassociateClientsFromGroupCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSaveGroupCollectionSheet()) {
handler = applicationContext.getBean("saveGroupCollectionSheetCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("saveGroupCollectionSheetCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isAssignRole()) {
handler = this.applicationContext.getBean("assignRoleCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUnAssignRole()) {
handler = this.applicationContext.getBean("unassignRoleCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdateRole()) {
handler = this.applicationContext.getBean("updateGroupRoleCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isCenterResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createCenterCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createCenterCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateCenterCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateCenterCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteCenterCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteCenterCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isCenterActivation()) {
handler = applicationContext.getBean("activateCenterCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("activateCenterCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSaveCenterCollectionSheet()) {
handler = applicationContext.getBean("saveCenterCollectionSheetCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("saveCenterCollectionSheetCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isCollectionSheetResource()) {
if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateCollectionSheetCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateCollectionSheetCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
@@ -440,11 +449,11 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
} else if (wrapper.isReportResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createReportCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createReportCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateReportCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateReportCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteReportCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteReportCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
@@ -452,16 +461,21 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
} else if (wrapper.isAccountingRuleResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createAccountingRuleCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("createAccountingRuleCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateAccountingRuleCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("updateAccountingRuleCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = applicationContext.getBean("deleteAccountingRuleCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("deleteAccountingRuleCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
}else {
} else if (wrapper.isHolidayResource()) {
if (wrapper.isCreate()) {
handler = applicationContext.getBean("createHolidayCommandHandler", NewCommandSourceHandler.class);
}
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
@@ -78,4 +78,5 @@ public class CommandProcessingResultBuilder {
this.transactionId = withTransactionId;
return this;
}
}
@@ -79,7 +79,7 @@ public class DataValidatorBuilder {
public void failWithCode(final String errorCode) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(this.parameter)
.append(errorCode);
.append(".").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);
@@ -0,0 +1,32 @@
package org.mifosplatform.infrastructure.core.exception;
/**
* A {@link RuntimeException} thrown when the authenticated user don't have
* enough permissions to access.
*/
public abstract class AbstractPlatformAccessDeniedException extends RuntimeException {
private final String globalisationMessageCode;
private final String defaultUserMessage;
private final Object[] defaultUserMessageArgs;
public AbstractPlatformAccessDeniedException(final String globalisationMessageCode, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
this.globalisationMessageCode = globalisationMessageCode;
this.defaultUserMessage = defaultUserMessage;
this.defaultUserMessageArgs = defaultUserMessageArgs;
}
public String getGlobalisationMessageCode() {
return this.globalisationMessageCode;
}
public String getDefaultUserMessage() {
return this.defaultUserMessage;
}
public Object[] getDefaultUserMessageArgs() {
return this.defaultUserMessageArgs;
}
}
@@ -43,7 +43,7 @@ public class DatatableCommandFromApiJsonDeserializer {
private final Set<String> supportedParametersForAddColumns = new HashSet<String>(Arrays.asList(
"name", "type", "length", "mandatory", "after", "code"));
private final Set<String> supportedParametersForChangeColumns = new HashSet<String>(Arrays.asList(
"name", "newName", "mandatory", "after", "code", "newCode"));
"name", "newName", "length", "mandatory", "after", "code", "newCode"));
private final Set<String> supportedParametersForDropColumns = new HashSet<String>(Arrays.asList(
"name"));
private final Object[] supportedColumnTypes = { "String", "Number", "Decimal", "Date", "Text", "Dropdown" };
@@ -61,25 +61,34 @@ public class DatatableCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("type").value(type).notBlank()
.isOneOfTheseValues(supportedColumnTypes);
final Integer length = fromApiJsonHelper.extractIntegerSansLocaleNamed("length", column);
if (type != null && type.equals("String")) {
if (length != null) {
baseDataValidator.reset().parameter("length").value(length).notBlank()
.zeroOrPositiveAmount();
if (fromApiJsonHelper.parameterExists("length", column)) {
final String lengthStr = fromApiJsonHelper.extractStringNamed("length", column);
if (lengthStr != null && !StringUtils.isWhitespace(lengthStr)
&& StringUtils.isNumeric(lengthStr) && StringUtils.isNotBlank(lengthStr)) {
Integer length = Integer.parseInt(lengthStr);
baseDataValidator.reset().parameter("length").value(length).positiveAmount();
} else if (StringUtils.isBlank(lengthStr) || StringUtils.isWhitespace(lengthStr)) {
baseDataValidator.reset().parameter("length").failWithCode("must.be.provided.when.type.is.String");
} else if (!StringUtils.isNumeric(lengthStr)) {
baseDataValidator.reset().parameter("length").failWithCode("not.greater.than.zero");
}
} else {
baseDataValidator.reset().parameter("length").value(length)
.cantBeBlankWhenParameterProvidedIs("type", type);
baseDataValidator.reset().parameter("length").failWithCode("must.be.provided.when.type.is.String");
}
} else {
baseDataValidator.reset().parameter("length").value(length)
.mustBeBlankWhenParameterProvidedIs("type", type);
baseDataValidator.reset().parameter("length").mustBeBlankWhenParameterProvidedIs("type", type);
}
final String code = fromApiJsonHelper.extractStringNamed("code", column);
if (type != null && type.equals("Dropdown")) {
baseDataValidator.reset().parameter("code").value(code).notBlank()
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
if (code != null) {
baseDataValidator.reset().parameter("code").value(code).notBlank()
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
} else {
baseDataValidator.reset().parameter("code").value(code)
.cantBeBlankWhenParameterProvidedIs("type", type);
}
} else {
baseDataValidator.reset().parameter("code").value(code).mustBeBlankWhenParameterProvided("type", type);
}
@@ -95,15 +104,16 @@ public class DatatableCommandFromApiJsonDeserializer {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
final JsonElement element = fromApiJsonHelper.parse(json);
final String datatableName = fromApiJsonHelper.extractStringNamed("datatableName", element);
baseDataValidator.reset().parameter("datatableName").value(datatableName).notBlank().notExceedingLengthOf(50)
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
final String apptableName = fromApiJsonHelper.extractStringNamed("apptableName", element);
baseDataValidator.reset().parameter("apptableName").value(apptableName).notBlank().notExceedingLengthOf(50)
.isOneOfTheseValues(supportedApptableNames);
final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : "";
final String datatableName = fromApiJsonHelper.extractStringNamed("datatableName", element);
baseDataValidator.reset().parameter("datatableName").value(datatableName).notBlank().notExceedingLengthOf(50)
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
final Boolean multiRow = fromApiJsonHelper.extractBooleanNamed("multiRow", element);
baseDataValidator.reset().parameter("multiRow").value(multiRow).ignoreIfNull().notBlank()
.isOneOfTheseValues(true, false);
@@ -119,9 +129,6 @@ public class DatatableCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("name").value(name).notBlank()
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
final String type = fromApiJsonHelper.extractStringNamed("type", column);
baseDataValidator.reset().parameter("type").value(type).notBlank().isOneOfTheseValues(supportedColumnTypes);
validateType(baseDataValidator, column);
final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column);
@@ -170,6 +177,18 @@ public class DatatableCommandFromApiJsonDeserializer {
.notExceedingLengthOf(50).isNotOneOfTheseValues("id", fkColumnName)
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
if (fromApiJsonHelper.parameterExists("length", column)) {
final String lengthStr = fromApiJsonHelper.extractStringNamed("length", column);
if (StringUtils.isWhitespace(lengthStr) || !StringUtils.isNumeric(lengthStr)
|| StringUtils.isBlank(lengthStr)) {
baseDataValidator.reset().parameter("length").failWithCode("not.greater.than.zero");
} else {
Integer length = Integer.parseInt(lengthStr);
baseDataValidator.reset().parameter("length").value(length).ignoreIfNull().notBlank()
.positiveAmount();
}
}
final String code = fromApiJsonHelper.extractStringNamed("code", column);
baseDataValidator.reset().parameter("code").value(code).ignoreIfNull().notBlank()
.notExceedingLengthOf(100).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
@@ -24,6 +24,7 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.serialization.DatatableCommandFromApiJsonDeserializer;
@@ -297,6 +298,11 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
return (code + "_cd_" + columnName);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
private void parseDatatableColumnObjectForCreate(final JsonObject column, StringBuilder sqlBuilder) {
String name = (column.has("name")) ? column.get("name").getAsString() : null;
@@ -357,7 +363,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
validateAppTable(apptableName);
String fkColumnName = apptableName.substring(2) + "_id";
String fkName = datatableName.toLowerCase().replaceAll("\\s", "_");
String fkName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + fkColumnName;
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` (");
@@ -380,7 +386,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (multiRow) {
sqlBuilder = sqlBuilder
.append(", PRIMARY KEY (`id`)")
.append(", KEY `fk_" + apptableName + "` (`" + fkColumnName + "`)")
.append(", KEY `fk_" + apptableName.substring(2) + "_id` (`" + fkColumnName + "`)")
.append(", CONSTRAINT `fk_" + fkName + "` ")
.append("FOREIGN KEY (`" + fkColumnName + "`) ")
.append("REFERENCES `" + apptableName + "` (`id`)");
@@ -398,11 +404,17 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
registerDatatable(datatableName, apptableName);
} catch (SQLGrammarException e) {
Throwable realCause = e.getCause();
if (realCause.getMessage().contains("Table") && realCause.getMessage().contains("already exists")) {
throw new PlatformDataIntegrityException(
"error.msg.datatable.already.exists", realCause.getMessage());
}
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
if (realCause.getMessage().toLowerCase().contains("duplicate column name")) {
baseDataValidator.reset().parameter("name").failWithCode("duplicate.column.name");
} else if (realCause.getMessage().contains("Table") && realCause.getMessage().contains("already exists")) {
baseDataValidator.reset().parameter("datatableName").value(datatableName)
.failWithCode("datatable.already.exists");
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
return new CommandProcessingResultBuilder()
@@ -415,6 +427,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition, StringBuilder sqlBuilder) {
String name = (column.has("name")) ? column.get("name").getAsString() : null;
String lengthStr = (column.has("length")) ? column.get("length").getAsString() : null;
Integer length = (StringUtils.isNotBlank(lengthStr)) ? Integer.parseInt(lengthStr) : null;
String newName = (column.has("newName")) ? column.get("newName").getAsString() : name;
Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false;
String after = (column.has("after")) ? column.get("after").getAsString() : null;
@@ -437,13 +451,15 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
}
String type = mapColumnNameDefinition.get(name).getColumnType();
Long length = mapColumnNameDefinition.get(name).getColumnLength();
if (length == null && type.toLowerCase().equals("varchar")) {
length = mapColumnNameDefinition.get(name).getColumnLength().intValue();
}
sqlBuilder = sqlBuilder.append(", CHANGE `" + name + "` `" + newName + "` " + type);
if (length != null && length > 0) {
if (type.equals("DECIMAL")) {
if (type.toLowerCase().equals("decimal")) {
sqlBuilder.append("(19,6)");
} else if (type.equals("VARCHAR")) {
} else if (type.toLowerCase().equals("varchar")) {
sqlBuilder.append("(" + length + ")");
}
}
@@ -531,6 +547,34 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
String oldApptableName = this.queryForApplicationTableName(datatableName);
if (!StringUtils.equals(oldApptableName, apptableName)) {
String oldFKName = oldApptableName.substring(2) + "_id";
String newFKName = apptableName.substring(2) + "_id";
String oldConstraintName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + oldFKName;
String newConstraintName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + newFKName;
StringBuilder sqlBuilder = new StringBuilder();
if (mapColumnNameDefinition.containsKey("id")) {
sqlBuilder = sqlBuilder
.append("ALTER TABLE `" + datatableName + "` ")
.append("DROP KEY `fk_" + oldFKName + "`,")
.append("DROP FOREIGN KEY `fk_" + oldConstraintName + "`,")
.append("CHANGE COLUMN `" + oldFKName + "` `" + newFKName + "` BIGINT(20) NOT NULL,")
.append("ADD KEY `fk_" + newFKName + "` (`" + newFKName + "`),")
.append("ADD CONSTRAINT `fk_" + newConstraintName + "` ")
.append("FOREIGN KEY (`" + newFKName + "`) ")
.append("REFERENCES `" + apptableName + "` (`id`)");
} else {
sqlBuilder = sqlBuilder
.append("ALTER TABLE `" + datatableName + "` ")
.append("DROP FOREIGN KEY `fk_" + oldConstraintName + "`,")
.append("CHANGE COLUMN `" + oldFKName + "` `" + newFKName + "` BIGINT(20) NOT NULL,")
.append("ADD CONSTRAINT `fk_" + newConstraintName + "` ")
.append("FOREIGN KEY (`" + newFKName + "`) ")
.append("REFERENCES `" + apptableName + "` (`id`)");
}
this.jdbcTemplate.execute(sqlBuilder.toString());
deregisterDatatable(datatableName);
registerDatatable(datatableName, apptableName);
}
@@ -590,17 +634,18 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
}
} catch (SQLGrammarException e) {
Throwable realCause = e.getCause();
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
if (realCause.getMessage().toLowerCase().contains("unknown column")) {
throw new PlatformDataIntegrityException(
"error.msg.datatable.column.missing.update", realCause.getMessage());
baseDataValidator.reset().parameter("name").failWithCode("does.not.exist");
} else if (realCause.getMessage().toLowerCase().contains("can't drop")) {
throw new PlatformDataIntegrityException(
"error.msg.datatable.column.missing.drop", realCause.getMessage());
baseDataValidator.reset().parameter("name").failWithCode("does.not.exist");
} else if (realCause.getMessage().toLowerCase().contains("duplicate column")) {
throw new PlatformDataIntegrityException(
"error.msg.datatable.duplicate.column", realCause.getMessage());
baseDataValidator.reset().parameter("name").failWithCode("column.already.exists");
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
}
@@ -618,11 +663,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
this.jdbcTemplate.execute(sql);
} catch (SQLGrammarException e) {
Throwable realCause = e.getCause();
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
if (realCause.getMessage().contains("Unknown table")) {
throw new PlatformDataIntegrityException(
"error.msg.datatable.missing", realCause.getMessage());
baseDataValidator.reset().parameter("datatableName").failWithCode("does.not.exist");
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
}
@@ -0,0 +1,11 @@
package org.mifosplatform.infrastructure.security.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformAccessDeniedException;
public class PermissionDeniedException extends AbstractPlatformAccessDeniedException {
public PermissionDeniedException(final String code, final String defaultMessage, final Long userId) {
super(code, defaultMessage, userId);
}
}
@@ -0,0 +1,25 @@
package org.mifosplatform.organisation.holiday.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class HolidayApiConstants {
public static final String HOLIDAY_RESOURCE_NAME = "holiday";
// general
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
//request parameters
public static final String name = "name";
public static final String fromDate = "fromDate";
public static final String toDate = "toDate";
public static final String description = "description";
public static final String offices = "offices";
public static final String officeId = "officeId";
public static final Set<String> HOLIDAY_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, name, fromDate, toDate, description, offices));
}
@@ -0,0 +1,45 @@
package org.mifosplatform.organisation.holiday.api;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/holidays")
@Component
@Scope("singleton")
public class HolidaysApiResource {
private final DefaultToApiJsonSerializer<Object> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public HolidaysApiResource(final DefaultToApiJsonSerializer<Object> toApiJsonSerializer, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.toApiJsonSerializer = toApiJsonSerializer;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createNewHoliday(final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createHoliday().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
}
@@ -0,0 +1,84 @@
package org.mifosplatform.organisation.holiday.data;
import java.lang.reflect.Type;
import java.util.ArrayList;
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.FromJsonHelper;
import org.mifosplatform.organisation.holiday.api.HolidayApiConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
@Component
public class HolidayDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public HolidayDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
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, HolidayApiConstants.HOLIDAY_CREATE_REQUEST_DATA_PARAMETERS);
final JsonElement element = fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(HolidayApiConstants.HOLIDAY_RESOURCE_NAME);
final String name = this.fromApiJsonHelper.extractStringNamed(HolidayApiConstants.name, element);
baseDataValidator.reset().parameter(HolidayApiConstants.name).value(name).notNull().notExceedingLengthOf(100);
final LocalDate fromDate = this.fromApiJsonHelper.extractLocalDateNamed(HolidayApiConstants.fromDate, element);
baseDataValidator.reset().parameter(HolidayApiConstants.fromDate).value(fromDate).notNull();
final LocalDate toDate = this.fromApiJsonHelper.extractLocalDateNamed(HolidayApiConstants.toDate, element);
baseDataValidator.reset().parameter(HolidayApiConstants.toDate).value(toDate).notNull();
Set<Long> offices = null;
final JsonObject topLevelJsonElement = element.getAsJsonObject();
if (topLevelJsonElement.has(HolidayApiConstants.offices) && topLevelJsonElement.get(HolidayApiConstants.offices).isJsonArray()) {
final JsonArray array = topLevelJsonElement.get(HolidayApiConstants.offices).getAsJsonArray();
if (array.size() > 0) {
offices = new HashSet<Long>(array.size());
for (int i = 0; i < array.size(); i++) {
final JsonObject officeElement = array.get(i).getAsJsonObject();
final Long officeId = this.fromApiJsonHelper.extractLongNamed(HolidayApiConstants.officeId, officeElement);
baseDataValidator.reset().parameter(HolidayApiConstants.offices).value(officeId).notNull();
offices.add(officeId);
}
}
}
baseDataValidator.reset().parameter(HolidayApiConstants.offices).value(offices).notNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
//
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -0,0 +1,88 @@
/**
* 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.organisation.holiday.domain;
import java.util.Date;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.holiday.api.HolidayApiConstants;
import org.mifosplatform.organisation.office.domain.Office;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_holiday", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "holiday_name") })
public class Holiday extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@Column(name = "name", unique = true, nullable = false, length = 100)
private String name;
@SuppressWarnings("unused")
@Column(name = "from_date", nullable = false)
@Temporal(TemporalType.DATE)
private Date fromDate;
@SuppressWarnings("unused")
@Column(name = "to_date", nullable = false)
@Temporal(TemporalType.DATE)
private Date toDate;
@SuppressWarnings("unused")
@Column(name = "description", length = 100)
private String description;
@SuppressWarnings("unused")
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "m_holiday_office", joinColumns = @JoinColumn(name = "holiday_id"), inverseJoinColumns = @JoinColumn(name = "office_id"))
private Set<Office> offices;
public static Holiday createNew(final Set<Office> offices, final JsonCommand command) {
final String name = command.stringValueOfParameterNamed(HolidayApiConstants.name);
final LocalDate fromDate = command.localDateValueOfParameterNamed(HolidayApiConstants.fromDate);
final LocalDate toDate = command.localDateValueOfParameterNamed(HolidayApiConstants.toDate);
final String description = command.stringValueOfParameterNamed(HolidayApiConstants.description);
return new Holiday(name, fromDate, toDate, description, offices);
}
private Holiday(final String name, final LocalDate fromDate, final LocalDate toDate, final String description, final Set<Office> offices) {
if (StringUtils.isNotBlank(name)) {
this.name = name.trim();
}
if (fromDate != null) {
this.fromDate = fromDate.toDate();
}
if (toDate != null) {
this.toDate = toDate.toDate();
}
if (StringUtils.isNotBlank(name)) {
this.description = description.trim();
} else {
this.description = null;
}
if (offices != null) {
this.offices = offices;
}
}
}
@@ -0,0 +1,15 @@
/**
* 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.organisation.holiday.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface HolidayRepository extends JpaRepository<Holiday, Long>,
JpaSpecificationExecutor<Holiday> {
// no added behaviour
}
@@ -0,0 +1,27 @@
package org.mifosplatform.organisation.holiday.handler;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.organisation.holiday.service.HolidayWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CreateHolidayCommandHandler implements NewCommandSourceHandler {
private final HolidayWritePlatformService holidayWritePlatformService;
@Autowired
public CreateHolidayCommandHandler(final HolidayWritePlatformService holidayWritePlatformService) {
this.holidayWritePlatformService = holidayWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.holidayWritePlatformService.createHoliday(command);
}
}
@@ -0,0 +1,5 @@
package org.mifosplatform.organisation.holiday.service;
public class HolidayReadPlatformServiceImpl {
}
@@ -0,0 +1,9 @@
package org.mifosplatform.organisation.holiday.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface HolidayWritePlatformService {
CommandProcessingResult createHoliday(JsonCommand command);
}
@@ -0,0 +1,119 @@
package org.mifosplatform.organisation.holiday.service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.holiday.api.HolidayApiConstants;
import org.mifosplatform.organisation.holiday.data.HolidayDataValidator;
import org.mifosplatform.organisation.holiday.domain.Holiday;
import org.mifosplatform.organisation.holiday.domain.HolidayRepository;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@Service
public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(HolidayWritePlatformServiceJpaRepositoryImpl.class);
private final HolidayDataValidator fromApiJsonDeserializer;
private final HolidayRepository holidayRepository;
private final PlatformSecurityContext context;
private final OfficeRepository officeRepository;
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public HolidayWritePlatformServiceJpaRepositoryImpl(final HolidayDataValidator fromApiJsonDeserializer,
final HolidayRepository holidayRepository, final PlatformSecurityContext context, final OfficeRepository officeRepository,
final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.holidayRepository = holidayRepository;
this.context = context;
this.officeRepository = officeRepository;
this.fromApiJsonHelper = fromApiJsonHelper;
}
@Transactional
@Override
public CommandProcessingResult createHoliday(final JsonCommand command) {
try {
this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreate(command.json());
validateToDateBeforeFromDate(command);
Set<Office> offices = null;
final JsonObject topLevelJsonElement = this.fromApiJsonHelper.parse(command.json()).getAsJsonObject();
if (topLevelJsonElement.has(HolidayApiConstants.offices) && topLevelJsonElement.get(HolidayApiConstants.offices).isJsonArray()) {
final JsonArray array = topLevelJsonElement.get(HolidayApiConstants.offices).getAsJsonArray();
offices = new HashSet<Office>(array.size());
for (int i = 0; i < array.size(); i++) {
final JsonObject officeElement = array.get(i).getAsJsonObject();
final Long officeId = this.fromApiJsonHelper.extractLongNamed(HolidayApiConstants.officeId, officeElement);
final Office office = this.officeRepository.findOne(officeId);
if (office == null) { throw new OfficeNotFoundException(officeId); }
offices.add(office);
}
}
final Holiday holiday = Holiday.createNew(offices, command);
this.holidayRepository.save(holiday);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(holiday.getId()).build();
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("holiday_name")) {
final String name = command.stringValueOfParameterNamed("name");
throw new PlatformDataIntegrityException("error.msg.holiday.duplicate.name", "Holiday with name `" + name + "` already exists",
"name", name);
}
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.office.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
private void validateToDateBeforeFromDate(final JsonCommand command) {
final LocalDate fromDate = command.localDateValueOfParameterNamed(HolidayApiConstants.fromDate);
final LocalDate toDate = command.localDateValueOfParameterNamed(HolidayApiConstants.toDate);
if (toDate.isBefore(fromDate)) {
final String defaultUserMessage = "ToDate date cannot be before the FromDate.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.holiday.toDate.cannot be.before.the.FromDate",
defaultUserMessage, "ToDate");
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -53,7 +53,7 @@ public class OfficesApiResource {
private final OfficeReadPlatformService readPlatformService;
private final DefaultToApiJsonSerializer<OfficeData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public OfficesApiResource(final PlatformSecurityContext context, final OfficeReadPlatformService readPlatformService,
@@ -22,7 +22,6 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
@@ -81,8 +80,7 @@ public class StaffApiResource {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
final String extraCriteria = getStaffCriteria(sqlSearch, officeId);
final Collection<StaffData> staff = this.readPlatformService.retrieveAllStaff(extraCriteria);
final Collection<StaffData> staff = this.readPlatformService.retrieveAllStaff(sqlSearch, officeId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, staff, RESPONSE_DATA_PARAMETERS);
@@ -131,22 +129,4 @@ public class StaffApiResource {
return this.toApiJsonSerializer.serialize(result);
}
private String getStaffCriteria(final String sqlSearch, final Long officeId) {
String extraCriteria = "";
if (sqlSearch != null) {
extraCriteria = " and (" + sqlSearch + ")";
}
if (officeId != null) {
extraCriteria += " and office_id = " + officeId;
}
if (StringUtils.isNotBlank(extraCriteria)) {
extraCriteria = extraCriteria.substring(4);
}
return extraCriteria;
}
}
@@ -13,9 +13,15 @@ public interface StaffReadPlatformService {
StaffData retrieveStaff(Long staffId);
Collection<StaffData> retrieveAllStaff(final String extraCriteria);
Collection<StaffData> retrieveAllStaffForDropdown(Long officeId);
Collection<StaffData> retrieveAllLoanOfficersByOffice(final Long officeId);
Collection<StaffData> retrieveAllLoanOfficersInOfficeById(final Long officeId);
/**
* returns all staff in offices that are above the provided
* <code>officeId</code>.
*/
Collection<StaffData> retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(Long officeId, boolean loanOfficersOnly);
Collection<StaffData> retrieveAllStaff(String sqlSearch, Long officeId);
}
@@ -26,6 +26,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
private final StaffLookupMapper lookupMapper = new StaffLookupMapper();
private final StaffInOfficeHierarchyMapper staffInOfficeHierarchyMapper = new StaffInOfficeHierarchyMapper();
@Autowired
public StaffReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) {
@@ -56,6 +57,42 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService {
}
}
private static final class StaffInOfficeHierarchyMapper implements RowMapper<StaffData> {
public String schema(final boolean loanOfficersOnly) {
StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("s.id as id, s.office_id as officeId, o.name as officeName,");
sqlBuilder.append("s.firstname as firstname, s.lastname as lastname,");
sqlBuilder.append("s.display_name as displayName, s.is_loan_officer as isLoanOfficer ");
sqlBuilder.append("from m_office o ");
sqlBuilder.append("join m_office ohierarchy on o.hierarchy like concat(ohierarchy.hierarchy, '%') ");
sqlBuilder.append("join m_staff s on s.office_id = ohierarchy.id ");
if (loanOfficersOnly) {
sqlBuilder.append("and s.is_loan_officer is true ");
}
sqlBuilder.append("where o.id = ? ");
return sqlBuilder.toString();
}
@Override
public StaffData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String firstname = rs.getString("firstname");
final String lastname = rs.getString("lastname");
final String displayName = rs.getString("displayName");
final Long officeId = rs.getLong("officeId");
final String officeName = rs.getString("officeName");
final boolean isLoanOfficer = rs.getBoolean("isLoanOfficer");
return StaffData.instance(id, firstname, lastname, displayName, officeId, officeName, isLoanOfficer);
}
}
private static final class StaffLookupMapper implements RowMapper<StaffData> {
private final String schemaSql;
@@ -84,19 +121,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService {
}
@Override
public Collection<StaffData> retrieveAllStaff(final String extraCriteria) {
final StaffMapper rm = new StaffMapper();
String sql = "select " + rm.schema();
if (StringUtils.isNotBlank(extraCriteria)) {
sql += " where " + extraCriteria;
}
sql = sql + " order by s.lastname";
return this.jdbcTemplate.query(sql, rm, new Object[] {});
}
@Override
public Collection<StaffData> retrieveAllLoanOfficersByOffice(final Long officeId) {
public Collection<StaffData> retrieveAllLoanOfficersInOfficeById(final Long officeId) {
return retrieveAllStaff(" office_id=" + officeId + " and is_loan_officer=1");
}
@@ -132,4 +157,48 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService {
throw new StaffNotFoundException(staffId);
}
}
@Override
public Collection<StaffData> retrieveAllStaff(final String sqlSearch, final Long officeId) {
final String extraCriteria = getStaffCriteria(sqlSearch, officeId);
return retrieveAllStaff(extraCriteria);
}
private Collection<StaffData> retrieveAllStaff(final String extraCriteria) {
final StaffMapper rm = new StaffMapper();
String sql = "select " + rm.schema();
if (StringUtils.isNotBlank(extraCriteria)) {
sql += " where " + extraCriteria;
}
sql = sql + " order by s.lastname";
return this.jdbcTemplate.query(sql, rm, new Object[] {});
}
private String getStaffCriteria(final String sqlSearch, final Long officeId) {
String extraCriteria = "";
if (sqlSearch != null) {
extraCriteria = " and (" + sqlSearch + ")";
}
if (officeId != null) {
extraCriteria += " and office_id = " + officeId;
}
if (StringUtils.isNotBlank(extraCriteria)) {
extraCriteria = extraCriteria.substring(4);
}
return extraCriteria;
}
@Override
public Collection<StaffData> retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(final Long officeId, final boolean loanOfficersOnly) {
String sql = "select " + this.staffInOfficeHierarchyMapper.schema(loanOfficersOnly);
sql = sql + " order by s.lastname";
return this.jdbcTemplate.query(sql, this.staffInOfficeHierarchyMapper, new Object[] { officeId });
}
}
@@ -168,7 +168,7 @@ public class Charge extends AbstractPersistable<Long> {
this.amount = newValue;
}
final String chargeTimeParamName = "chargeTime";
final String chargeTimeParamName = "chargeTimeType";
if (command.isChangeInIntegerParameterNamed(chargeTimeParamName, this.chargeTime)) {
final Integer newValue = command.integerValueOfParameterNamed(chargeTimeParamName);
actualChanges.put(chargeTimeParamName, newValue);
@@ -184,7 +184,7 @@ public class Charge extends AbstractPersistable<Long> {
this.chargeAppliesTo = ChargeAppliesTo.fromInt(newValue).getValue();
}
final String chargeCalculationParamName = "chargeCalculation";
final String chargeCalculationParamName = "chargeCalculationType";
if (command.isChangeInIntegerParameterNamed(chargeCalculationParamName, this.chargeCalculation)) {
final Integer newValue = command.integerValueOfParameterNamed(chargeCalculationParamName);
actualChanges.put(chargeCalculationParamName, newValue);
@@ -31,6 +31,7 @@ public class ClientApiConstants {
public static final String officeIdParamName = "officeId";
public static final String activeParamName = "active";
public static final String activationDateParamName = "activationDate";
public static final String staffIdParamName = "staffId";
// response parameters
public static final String statusParamName = "status";
@@ -45,14 +46,15 @@ public class ClientApiConstants {
// template related part of response
public static final String officeOptionsParamName = "officeOptions";
public static final String staffOptionsParamName = "staffOptions";
public static final Set<String> CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName, firstnameParamName, middlenameParamName,
lastnameParamName, fullnameParamName, officeIdParamName, activeParamName, activationDateParamName));
lastnameParamName, fullnameParamName, officeIdParamName, activeParamName, activationDateParamName, staffIdParamName));
public static final Set<String> CLIENT_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, accountNoParamName, externalIdParamName, firstnameParamName, middlenameParamName,
lastnameParamName, fullnameParamName, activeParamName, activationDateParamName));
lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName));
/**
* These parameters will match the class level parameters of
@@ -62,7 +64,7 @@ public class ClientApiConstants {
public static final Set<String> CLIENT_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName, accountNoParamName,
externalIdParamName, statusParamName, activeParamName, activationDateParamName, firstnameParamName, middlenameParamName,
lastnameParamName, fullnameParamName, displayNameParamName, officeIdParamName, officeNameParamName, hierarchyParamName,
imageKeyParamName, imagePresentParamName, groupsParamName, officeOptionsParamName));
imageKeyParamName, imagePresentParamName, groupsParamName, officeOptionsParamName, staffOptionsParamName));
public static final Set<String> ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, activationDateParamName));
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.client.api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -37,6 +38,8 @@ import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
@@ -44,6 +47,7 @@ import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@Path("/clients")
@Component
@@ -57,13 +61,15 @@ public class ClientsApiResource {
private final ToApiJsonSerializer<ClientAccountSummaryCollectionData> clientAccountSummaryToApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final StaffReadPlatformService staffReadPlatformService;
@Autowired
public ClientsApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService,
final OfficeReadPlatformService officeReadPlatformService, final ToApiJsonSerializer<ClientData> toApiJsonSerializer,
final ToApiJsonSerializer<ClientAccountSummaryCollectionData> clientAccountSummaryToApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final StaffReadPlatformService staffReadPlatformService) {
this.context = context;
this.clientReadPlatformService = readPlatformService;
this.officeReadPlatformService = officeReadPlatformService;
@@ -71,17 +77,18 @@ public class ClientsApiResource {
this.clientAccountSummaryToApiJsonSerializer = clientAccountSummaryToApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.staffReadPlatformService = staffReadPlatformService;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@Context final UriInfo uriInfo) {
public String retrieveTemplate(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
final ClientData clientData = this.clientReadPlatformService.retrieveTemplate();
final ClientData clientData = this.clientReadPlatformService.retrieveTemplate(officeId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
@@ -121,7 +128,11 @@ public class ClientsApiResource {
ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
if (settings.isTemplate()) {
final List<OfficeData> allowedOffices = new ArrayList<OfficeData>(officeReadPlatformService.retrieveAllOfficesForDropdown());
clientData = ClientData.templateOnTop(clientData, allowedOffices);
Collection<StaffData> staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(clientData.officeId());
if (CollectionUtils.isEmpty(staffOptions)) {
staffOptions = null;
}
clientData = ClientData.templateOnTop(clientData, allowedOffices, staffOptions);
}
return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
@@ -186,11 +197,14 @@ public class ClientsApiResource {
if (is(commandParam, "activate")) {
final CommandWrapper commandRequest = builder.activateClient(clientId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "unassignStaff")) {
final CommandWrapper commandRequest = builder.unassignClientStaff(clientId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
if (result == null) {
//
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate" });
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "unassignStaff" });
}
return this.toApiJsonSerializer.serialize(result);
@@ -15,6 +15,7 @@ import org.apache.commons.lang.builder.HashCodeBuilder;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
/**
@@ -43,28 +44,34 @@ final public class ClientData implements Comparable<ClientData> {
private final Long imageId;
private final Boolean imagePresent;
private final Long staffId;
private final String staffName;
// associations
private final Collection<GroupGeneralData> groups;
// template
private final Collection<OfficeData> officeOptions;
private final Collection<StaffData> staffOptions;
public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection<OfficeData> officeOptions) {
return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, officeOptions, null);
public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection<OfficeData> officeOptions,
final Collection<StaffData> staffOptions) {
return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, null, null, officeOptions, null, staffOptions);
}
public static ClientData templateOnTop(final ClientData clientData, final List<OfficeData> allowedOffices) {
public static ClientData templateOnTop(final ClientData clientData, final List<OfficeData> allowedOffices, final Collection<StaffData> staffOptions) {
return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id,
clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName,
clientData.externalId, clientData.activationDate, clientData.imageId, allowedOffices, clientData.groups);
clientData.externalId, clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName,
allowedOffices, clientData.groups, staffOptions);
}
public static ClientData setParentGroups(final ClientData clientData, final Collection<GroupGeneralData> parentGroups) {
return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id,
clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName,
clientData.externalId, clientData.activationDate, clientData.imageId, clientData.officeOptions, parentGroups);
clientData.externalId, clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName,
clientData.officeOptions, parentGroups, clientData.staffOptions);
}
public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname,
@@ -72,24 +79,25 @@ final public class ClientData implements Comparable<ClientData> {
final String officeName) {
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) {
return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null);
return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, null, null, null);
}
public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName,
final Long id, final String firstname, final String middlename, final String lastname, final String fullname,
final String displayName, final String externalId, final LocalDate activationDate, final Long imageId) {
final String displayName, final String externalId, final LocalDate activationDate, final Long imageId, final Long staffId,
final String staffName) {
return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
externalId, activationDate, imageId, null, null);
externalId, activationDate, imageId, staffId, staffName, null, null, null);
}
private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id,
final String firstname, final String middlename, final String lastname, final String fullname, final String displayName,
final String externalId, final LocalDate activationDate, final Long imageId, final Collection<OfficeData> allowedOffices,
final Collection<GroupGeneralData> groups) {
final String externalId, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName,
final Collection<OfficeData> allowedOffices, final Collection<GroupGeneralData> groups, final Collection<StaffData> staffOptions) {
this.accountNo = accountNo;
this.status = status;
if (status != null) {
@@ -113,12 +121,15 @@ final public class ClientData implements Comparable<ClientData> {
} else {
this.imagePresent = Boolean.FALSE;
}
this.staffId = staffId;
this.staffName = staffName;
// associations
this.groups = groups;
// template
this.officeOptions = allowedOffices;
this.staffOptions = staffOptions;
}
public Long id() {
@@ -9,8 +9,11 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activa
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;
@@ -42,24 +45,29 @@ public final class ClientDataValidator {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_CREATE_REQUEST_DATA_PARAMETERS);
final JsonElement element = fromApiJsonHelper.parse(json);
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_CREATE_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ClientApiConstants.CLIENT_RESOURCE_NAME);
final Long officeId = fromApiJsonHelper.extractLongNamed(ClientApiConstants.officeIdParamName, element);
final Long officeId = this.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);
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.groupIdParamName, element)) {
final Long groupId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.groupIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.groupIdParamName).value(groupId).notNull().integerGreaterThanZero();
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) {
final String accountNo = fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element);
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.staffIdParamName, element)) {
final Long staffId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.staffIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.staffIdParamName).value(staffId).ignoreIfNull().longGreaterThanZero();
}
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) {
final String accountNo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
@@ -103,16 +111,17 @@ public final class ClientDataValidator {
}
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) {
final String externalId = fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element);
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) {
final String externalId = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.externalIdParamName).value(externalId).ignoreIfNull()
.notExceedingLengthOf(100);
}
final Boolean active = fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element);
final Boolean active = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element);
if (active != null) {
if (active.booleanValue()) {
final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, element);
final LocalDate joinedDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName,
element);
baseDataValidator.reset().parameter(ClientApiConstants.activationDateParamName).value(joinedDate).notNull();
}
} else {
@@ -123,67 +132,67 @@ public final class ClientDataValidator {
}
private void validateIndividualNamePartsCannotBeUsedWithFullname(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
final String firstnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element);
final String firstnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element);
if (StringUtils.isNotBlank(firstnameParam)) {
final String fullnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
final String fullnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.fullnameParamName).value(fullnameParam)
.mustBeBlankWhenParameterProvided(ClientApiConstants.firstnameParamName, firstnameParam);
}
final String middlenameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element);
final String middlenameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element);
if (StringUtils.isNotBlank(middlenameParam)) {
final String fullnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
final String fullnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.fullnameParamName).value(fullnameParam)
.mustBeBlankWhenParameterProvided(ClientApiConstants.middlenameParamName, middlenameParam);
}
final String lastnameParamName = fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element);
final String lastnameParamName = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element);
if (StringUtils.isNotBlank(lastnameParamName)) {
final String fullnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
final String fullnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.fullnameParamName).value(fullnameParam)
.mustBeBlankWhenParameterProvided(ClientApiConstants.lastnameParamName, lastnameParamName);
}
}
private void validateRequiredIndividualNamePartsExist(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
final String firstnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element);
final String firstnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.firstnameParamName).value(firstnameParam).notBlank()
.notExceedingLengthOf(50);
final String middlenameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element);
final String middlenameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.middlenameParamName).value(middlenameParam).ignoreIfNull()
.notExceedingLengthOf(50);
final String lastnameParamName = fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element);
final String lastnameParamName = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.lastnameParamName).value(lastnameParamName).notBlank()
.notExceedingLengthOf(50);
}
private void fullnameCannotBeBlank(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
final String fullnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
final String fullnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.fullnameParamName).value(fullnameParam).notBlank().notExceedingLengthOf(100);
}
private boolean isIndividualNamePartParameterPassed(final JsonElement element) {
return fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element)
|| fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element)
|| fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element);
return this.fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element)
|| this.fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element)
|| this.fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element);
}
private boolean isFullnameParameterPassed(final JsonElement element) {
return fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element);
return this.fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element);
}
private boolean isIndividualNameProvided(final JsonElement element) {
final String firstname = fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element);
final String middlename = fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element);
final String lastname = fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element);
final String firstname = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element);
final String middlename = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element);
final String lastname = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element);
return StringUtils.isNotBlank(firstname) || StringUtils.isNotBlank(middlename) || StringUtils.isNotBlank(lastname);
}
private boolean isFullnameProvided(final JsonElement element) {
final String fullname = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
final String fullname = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element);
return StringUtils.isNotBlank(fullname);
}
@@ -192,8 +201,8 @@ public final class ClientDataValidator {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_UPDATE_REQUEST_DATA_PARAMETERS);
final JsonElement element = fromApiJsonHelper.parse(json);
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_UPDATE_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
@@ -201,9 +210,9 @@ public final class ClientDataValidator {
.resource(ClientApiConstants.CLIENT_RESOURCE_NAME);
boolean atLeastOneParameterPassedForUpdate = false;
if (fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) {
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String accountNo = fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element);
final String accountNo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
@@ -247,36 +256,42 @@ public final class ClientDataValidator {
}
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element)) {
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element)) {
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element)) {
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element)) {
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
}
if (fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) {
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String externalId = fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element);
final String externalId = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.externalIdParamName).value(externalId).notExceedingLengthOf(100);
}
final Boolean active = fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element);
final Boolean active = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element);
if (active != null) {
if (active.booleanValue()) {
final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, element);
final LocalDate joinedDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName,
element);
baseDataValidator.reset().parameter(ClientApiConstants.activationDateParamName).value(joinedDate).notNull();
}
}
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.staffIdParamName, element)) {
final Long staffId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.staffIdParamName, element);
baseDataValidator.reset().parameter(ClientApiConstants.staffIdParamName).value(staffId).ignoreIfNull().longGreaterThanZero();
}
if (!atLeastOneParameterPassedForUpdate) {
final Object forceError = null;
baseDataValidator.reset().anyOfNotNull(forceError);
@@ -291,7 +306,7 @@ public final class ClientDataValidator {
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);
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.ACTIVATION_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
@@ -299,7 +314,7 @@ public final class ClientDataValidator {
final JsonElement element = command.parsedJson();
final LocalDate activationDate = fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element);
final LocalDate activationDate = this.fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element);
baseDataValidator.reset().parameter(activationDateParamName).value(activationDate).notNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
@@ -311,4 +326,28 @@ public final class ClientDataValidator {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public void validateForUnassignStaff(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParametersUnassignStaff = new HashSet<String>(Arrays.asList(ClientApiConstants.staffIdParamName));
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersUnassignStaff);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ClientApiConstants.CLIENT_RESOURCE_NAME);
final String staffIdParameterName = ClientApiConstants.staffIdParamName;
final Long staffId = this.fromApiJsonHelper.extractLongNamed(staffIdParameterName, element);
baseDataValidator.reset().parameter(staffIdParameterName).value(staffId).notNull().longGreaterThanZero();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -5,6 +5,27 @@
*/
package org.mifosplatform.portfolio.client.domain;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormatter;
@@ -16,14 +37,11 @@ import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.documentmanagement.domain.Image;
import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.portfolio.client.api.ClientApiConstants;
import org.mifosplatform.portfolio.group.domain.Group;
import org.springframework.data.jpa.domain.AbstractPersistable;
import javax.persistence.*;
import java.util.*;
@Entity
@Table(name = "m_client", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "account_no_UNIQUE") })
public final class Client extends AbstractPersistable<Long> {
@@ -67,6 +85,10 @@ public final class Client extends AbstractPersistable<Long> {
@Column(name = "external_id", length = 100, nullable = true, unique = true)
private String externalId;
@ManyToOne
@JoinColumn(name = "staff_id")
private Staff staff;
@ManyToMany
@JoinTable(name = "m_group_client", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "group_id"))
private Set<Group> groups;
@@ -74,7 +96,7 @@ public final class Client extends AbstractPersistable<Long> {
@Transient
private boolean accountNumberRequiresAutoGeneration = false;
public static Client createNew(final Office clientOffice, final Group clientParentGroup, final JsonCommand command) {
public static Client createNew(final Office clientOffice, final Group clientParentGroup, final Staff staff, final JsonCommand command) {
final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
@@ -97,7 +119,7 @@ public final class Client extends AbstractPersistable<Long> {
}
return new Client(status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, activationDate,
externalId);
externalId, staff);
}
protected Client() {
@@ -106,7 +128,7 @@ public final class Client extends AbstractPersistable<Long> {
private Client(final ClientStatus status, final Office office, final Group clientParentGroup, final String accountNo,
final String firstname, final String middlename, final String lastname, final String fullname, final LocalDate activationDate,
final String externalId) {
final String externalId, final Staff staff) {
if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true;
@@ -152,6 +174,8 @@ public final class Client extends AbstractPersistable<Long> {
this.groups.add(clientParentGroup);
}
this.staff = staff;
deriveDisplayName();
validate();
}
@@ -280,6 +304,11 @@ public final class Client extends AbstractPersistable<Long> {
this.fullname = newValue;
}
if (command.isChangeInLongParameterNamed(ClientApiConstants.staffIdParamName, staffId())) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
actualChanges.put(ClientApiConstants.staffIdParamName, newValue);
}
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
@@ -326,7 +355,7 @@ public final class Client extends AbstractPersistable<Long> {
private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) {
if (this.activationDate != null) {
if (office.isOpeningDateAfter(getActivationLocalDate())) {
if (this.office.isOpeningDateAfter(getActivationLocalDate())) {
final String defaultUserMessage = "Client activation date cannot be a date before the office opening date.";
final ApiParameterError error = ApiParameterError.parameterError(
"error.msg.clients.activationDate.cannot.be.before.office.activation.date", defaultUserMessage,
@@ -374,7 +403,7 @@ public final class Client extends AbstractPersistable<Long> {
return this.office.getId();
}
public void setImage(Image image) {
public void setImage(final Image image) {
this.image = image;
}
@@ -386,8 +415,32 @@ public final class Client extends AbstractPersistable<Long> {
return this.displayName;
}
public void setDisplayName(String displayName) {
public void setDisplayName(final String displayName) {
this.displayName = displayName;
}
public Office getOffice() {
return this.office;
}
private Long staffId() {
Long staffId = null;
if (this.staff != null) {
staffId = this.staff.getId();
}
return staffId;
}
public void updateStaff(final Staff staff) {
this.staff = staff;
}
public Staff getStaff() {
return this.staff;
}
public void unassignStaff() {
this.staff = null;
}
}
@@ -0,0 +1,11 @@
package org.mifosplatform.portfolio.client.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ClientHasNoStaffException extends AbstractPlatformResourceNotFoundException {
public ClientHasNoStaffException(final Long clientId) {
super("error.msg.client.has.no.staff", "Client with identifier " + clientId + " does not have staff", clientId);
}
}
@@ -0,0 +1,27 @@
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 UnassignClientStaffCommandHandler implements NewCommandSourceHandler {
private final ClientWritePlatformService clientWritePlatformService;
@Autowired
public UnassignClientStaffCommandHandler(final ClientWritePlatformService clientWritePlatformService) {
this.clientWritePlatformService = clientWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.clientWritePlatformService.unassignClientStaff(command.entityId(), command);
}
}
@@ -16,7 +16,7 @@ import org.mifosplatform.portfolio.group.service.SearchParameters;
public interface ClientReadPlatformService {
ClientData retrieveTemplate();
ClientData retrieveTemplate(Long officeId);
Page<ClientData> retrieveAll(SearchParameters searchParameters);
@@ -22,6 +22,8 @@ import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSourc
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData;
import org.mifosplatform.portfolio.client.data.ClientData;
@@ -40,6 +42,7 @@ import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Service
public class ClientReadPlatformServiceImpl implements ClientReadPlatformService {
@@ -47,6 +50,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
private final OfficeReadPlatformService officeReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
// data mappers
private final PaginationHelper<ClientData> paginationHelper = new PaginationHelper<ClientData>();
@@ -57,22 +61,28 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
@Autowired
public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource,
final OfficeReadPlatformService officeReadPlatformService) {
final OfficeReadPlatformService officeReadPlatformService, final StaffReadPlatformService staffReadPlatformService) {
this.context = context;
this.officeReadPlatformService = officeReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.staffReadPlatformService = staffReadPlatformService;
}
@Override
public ClientData retrieveTemplate() {
public ClientData retrieveTemplate(final Long officeId) {
final AppUser currentUser = context.authenticatedUser();
this.context.authenticatedUser();
final Long defaultOfficeId = defaultToUsersOfficeIfNull(officeId);
final Collection<OfficeData> offices = officeReadPlatformService.retrieveAllOfficesForDropdown();
final Long officeId = currentUser.getOffice().getId();
return ClientData.template(officeId, new LocalDate(), offices);
Collection<StaffData> staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(defaultOfficeId);
if (CollectionUtils.isEmpty(staffOptions)) {
staffOptions = null;
}
return ClientData.template(defaultOfficeId, new LocalDate(), offices, staffOptions);
}
@Override
@@ -226,10 +236,12 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
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.fullname as fullname, c.display_name as displayName, ");
sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId ");
sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName ");
sqlBuilder.append("from m_client c ");
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 ");
sqlBuilder.append("left join m_staff s on s.id = c.staff_id ");
this.schema = sqlBuilder.toString();
}
@@ -256,9 +268,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
final Long imageId = JdbcSupport.getLong(rs, "imageId");
final String officeName = rs.getString("officeName");
final Long staffId = JdbcSupport.getLong(rs, "staffId");
final String staffName = rs.getString("staffName");
return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
externalId, activationDate, imageId);
externalId, activationDate, imageId, staffId, staffName);
}
}
@@ -274,9 +288,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
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_id as imageId ");
builder.append("c.activation_date as activationDate, c.image_id as imageId, ");
builder.append("c.staff_id as staffId, s.display_name as staffName ");
builder.append("from m_client c ");
builder.append("join m_office o on o.id = c.office_id ");
builder.append("left join m_staff s on s.id = c.staff_id ");
this.schema = builder.toString();
}
@@ -304,9 +320,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
final String officeName = rs.getString("officeName");
final Long imageId = JdbcSupport.getLong(rs, "imageId");
final Long staffId = JdbcSupport.getLong(rs, "staffId");
final String staffName = rs.getString("staffName");
return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
externalId, activationDate, imageId);
externalId, activationDate, imageId, staffId, staffName);
}
}
@@ -560,4 +578,12 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
officeName);
}
}
private Long defaultToUsersOfficeIfNull(final Long officeId) {
Long defaultOfficeId = officeId;
if (defaultOfficeId == null) {
defaultOfficeId = this.context.authenticatedUser().getOffice().getId();
}
return defaultOfficeId;
}
}
@@ -18,4 +18,6 @@ public interface ClientWritePlatformService {
CommandProcessingResult deleteClient(Long clientId);
CommandProcessingResult unassignClientStaff(Long clientId, JsonCommand command);
}
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.client.service;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -16,22 +17,27 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.security.exception.PermissionDeniedException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper;
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.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.client.exception.ClientHasNoStaffException;
import org.mifosplatform.portfolio.client.exception.ClientMustBePendingToBeDeletedException;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepository;
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -51,12 +57,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private final GroupRepository groupRepository;
private final ClientDataValidator fromApiJsonDeserializer;
private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory;
private final StaffRepositoryWrapper staffRepository;
@Autowired
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository,
final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory,
final GroupRepository groupRepository) {
final GroupRepository groupRepository, final StaffRepositoryWrapper staffRepository) {
this.context = context;
this.clientRepository = clientRepository;
this.officeRepository = officeRepository;
@@ -64,6 +71,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory;
this.groupRepository = groupRepository;
this.staffRepository = staffRepository;
}
@Transactional
@@ -74,7 +82,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
if (client.isNotPending()) { throw new ClientMustBePendingToBeDeletedException(clientId); }
List<Note> relatedNotes = this.noteRepository.findByClientId(clientId);
final List<Note> relatedNotes = this.noteRepository.findByClientId(clientId);
this.noteRepository.deleteInBatch(relatedNotes);
this.clientRepository.delete(client);
@@ -92,7 +100,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
*/
private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
Throwable realCause = dve.getMostSpecificCause();
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("external_id")) {
final String externalId = command.stringValueOfParameterNamed("externalId");
@@ -114,7 +122,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
public CommandProcessingResult createClient(final JsonCommand command) {
try {
context.authenticatedUser();
this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreate(command.json());
@@ -131,7 +139,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
if (clientParentGroup == null) { throw new GroupNotFoundException(groupId); }
}
final Client newClient = Client.createNew(clientOffice, clientParentGroup, command);
Staff staff = null;
final Long staffId = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
if (staffId != null) {
staff = this.staffRepository.findByOfficeWithNotFoundDetection(staffId, officeId);
}
final Client newClient = Client.createNew(clientOffice, clientParentGroup, staff, command);
this.clientRepository.save(newClient);
@@ -149,7 +163,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
.withGroupId(groupId) //
.withEntityId(newClient.getId()) //
.build();
} catch (DataIntegrityViolationException dve) {
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
@@ -160,14 +174,35 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
public CommandProcessingResult updateClient(final Long clientId, final JsonCommand command) {
try {
context.authenticatedUser();
AppUser user = context.authenticatedUser();
this.fromApiJsonDeserializer.validateForUpdate(command.json());
final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId);
final String clientHierarchy = clientForUpdate.getOffice().getHierarchy();
// Don't allow other office user to update
final Office loginUserOffice = user.getOffice();
final String userHierarchy = loginUserOffice.getHierarchy();
if (!clientHierarchy.startsWith(userHierarchy)) {
final String code = "error.msg.not.enough.permissions.to.access.resource";
final String defaultMessage = "The User with id " + user.getId() + " don't have permission to update this client";
throw new PermissionDeniedException(code, defaultMessage, user.getId());
}
final Map<String, Object> changes = clientForUpdate.update(command);
if (changes.containsKey(ClientApiConstants.staffIdParamName)) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
Staff newStaff = null;
if (newValue != null) {
newStaff = this.staffRepository.findByOfficeWithNotFoundDetection(newValue, clientForUpdate.officeId());
}
clientForUpdate.updateStaff(newStaff);
}
if (!changes.isEmpty()) {
this.clientRepository.saveAndFlush(clientForUpdate);
}
@@ -179,7 +214,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
.withEntityId(clientId) //
.with(changes) //
.build();
} catch (DataIntegrityViolationException dve) {
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
@@ -207,7 +242,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
.withClientId(clientId) //
.withEntityId(clientId) //
.build();
} catch (DataIntegrityViolationException dve) {
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
@@ -216,4 +251,37 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve);
}
@Transactional
@Override
public CommandProcessingResult unassignClientStaff(final Long clientId, final JsonCommand command) {
this.context.authenticatedUser();
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(5);
this.fromApiJsonDeserializer.validateForUnassignStaff(command.json());
final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId);
final Staff presentStaff = clientForUpdate.getStaff();
Long presentStaffId = null;
if (presentStaff == null) { throw new ClientHasNoStaffException(clientId); }
presentStaffId = presentStaff.getId();
final String staffIdParamName = ClientApiConstants.staffIdParamName;
if (!command.isChangeInLongParameterNamed(staffIdParamName, presentStaffId)) {
clientForUpdate.unassignStaff();
}
this.clientRepository.saveAndFlush(clientForUpdate);
actualChanges.put(staffIdParamName, presentStaffId);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(clientForUpdate.officeId()) //
.withEntityId(clientForUpdate.getId()) //
.withClientId(clientId) //
.with(actualChanges) //
.build();
}
}
@@ -210,8 +210,8 @@ public class CentersApiResource {
final CommandWrapper commandRequest = builder.saveCenterCollectionSheet(centerId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
} else { //
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet" });
} else {
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", "createRole" });
}
}
@@ -16,6 +16,15 @@ public class GroupingTypesApiConstants {
public static final String CENTER_RESOURCE_NAME = "center";
public static final String GROUP_RESOURCE_NAME = "group";
public static final String COMMUNAL_BANK_RESOURCE_NAME = "communalbank";
//group roles
public static final String GROUP_ROLE_RESOURCE_NAME = "grouprole";
public static final String GROUP_ROLE_NAME = "GROUPROLE";
public static final String roleParamName = "role";
public static final String groupIdParamName = "groupId";
public static final String clientIdParamName = "clientId";
public static final String groupRolesParamName = "groupRoles";
// general
public static final String localeParamName = "locale";
@@ -51,6 +60,8 @@ public class GroupingTypesApiConstants {
public static final Set<String> GROUP_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName, dateFormatParamName,
idParamName, nameParamName, externalIdParamName, centerIdParamName, officeIdParamName, staffIdParamName, activeParamName,
activationDateParamName, clientMembersParamName));
public static final Set<String> GROUP_ROLES_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(roleParamName, clientIdParamName));
/**
* These parameters will match the class level parameters of
@@ -74,4 +85,5 @@ public class GroupingTypesApiConstants {
public static final Set<String> COLLECTIONSHEET_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("dueDate", "loanProducts", "groups"));
}
@@ -12,6 +12,7 @@ import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
@@ -43,8 +44,10 @@ import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData;
import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetReadPlatformService;
import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData;
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
import org.mifosplatform.portfolio.group.data.GroupRoleData;
import org.mifosplatform.portfolio.group.service.CenterReadPlatformService;
import org.mifosplatform.portfolio.group.service.GroupReadPlatformService;
import org.mifosplatform.portfolio.group.service.GroupRolesReadPlatformService;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
@@ -69,6 +72,7 @@ public class GroupsApiResource {
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final CollectionSheetReadPlatformService collectionSheetReadPlatformService;
private final FromJsonHelper fromJsonHelper;
private final GroupRolesReadPlatformService groupRolesReadPlatformService;
@Autowired
public GroupsApiResource(final PlatformSecurityContext context, final GroupReadPlatformService groupReadPlatformService,
@@ -78,7 +82,8 @@ public class GroupsApiResource {
final ToApiJsonSerializer<GroupAccountSummaryCollectionData> groupSummaryToApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper) {
final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper,
final GroupRolesReadPlatformService groupRolesReadPlatformService) {
this.context = context;
this.groupReadPlatformService = groupReadPlatformService;
this.centerReadPlatformService = centerReadPlatformService;
@@ -90,6 +95,7 @@ public class GroupsApiResource {
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.collectionSheetReadPlatformService = collectionSheetReadPlatformService;
this.fromJsonHelper = fromJsonHelper;
this.groupRolesReadPlatformService = groupRolesReadPlatformService;
}
@GET
@@ -97,8 +103,8 @@ public class GroupsApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId,
@QueryParam("center") final boolean isCenterGroup,
@QueryParam("centerId") final Long centerId) {
@QueryParam("center") final boolean isCenterGroup, @QueryParam("centerId") final Long centerId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
this.context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.GROUP_RESOURCE_NAME);
@@ -109,7 +115,8 @@ public class GroupsApiResource {
GroupingTypesApiConstants.CENTER_GROUP_RESPONSE_DATA_PARAMETERS);
}
final GroupGeneralData groupTemplate = this.groupReadPlatformService.retrieveTemplate(officeId, isCenterGroup);
final GroupGeneralData groupTemplate = this.groupReadPlatformService.retrieveTemplate(officeId, isCenterGroup,
staffInSelectedOfficeOnly);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.groupGeneralApiJsonSerializer.serialize(settings, groupTemplate,
GroupingTypesApiConstants.GROUP_RESPONSE_DATA_PARAMETERS);
@@ -130,7 +137,7 @@ public class GroupsApiResource {
limit, orderBy, sortOrder);
final Page<GroupGeneralData> groups = this.groupReadPlatformService.retrieveAll(searchParameters);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, groups, GroupingTypesApiConstants.GROUP_RESPONSE_DATA_PARAMETERS);
}
@@ -138,30 +145,50 @@ public class GroupsApiResource {
@Path("{groupId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveOne(@Context final UriInfo uriInfo, @PathParam("groupId") final Long groupId) {
public String retrieveOne(@Context final UriInfo uriInfo, @PathParam("groupId") final Long groupId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
@QueryParam("roleId") final Long roleId) {
this.context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.GROUP_RESOURCE_NAME);
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
GroupGeneralData group = this.groupReadPlatformService.retrieveOne(groupId);
// associations
Collection<ClientData> membersOfGroup = null;
Collection<GroupRoleData> groupRoles = null;
GroupRoleData selectedRole = null;
if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList("clientMembers", "groupRoles"));
}
if (associationParameters.contains("clientMembers")) {
membersOfGroup = this.clientReadPlatformService.retrieveClientMembersOfGroup(groupId);
if (CollectionUtils.isEmpty(membersOfGroup)) {
membersOfGroup = null;
}
}
if (associationParameters.contains("groupRoles")) {
groupRoles = this.groupRolesReadPlatformService.retrieveGroupRoles(groupId);
if (CollectionUtils.isEmpty(membersOfGroup)) {
groupRoles = null;
}
}
if (roleId != null) {
selectedRole = this.groupRolesReadPlatformService.retrieveGroupRole(groupId, roleId);
if (selectedRole != null) {
group = GroupGeneralData.updateSelectedRole(group, selectedRole);
}
}
}
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if (template) {
final GroupGeneralData templateGroup = this.groupReadPlatformService.retrieveTemplate(group.officeId(), false);
group = GroupGeneralData.withTemplateAndAssociations(templateGroup, group, membersOfGroup);
final GroupGeneralData templateGroup = this.groupReadPlatformService.retrieveTemplate(group.officeId(), false,
staffInSelectedOfficeOnly);
group = GroupGeneralData.withTemplateAndAssociations(templateGroup, group, membersOfGroup, groupRoles);
} else {
group = GroupGeneralData.withAssocations(group, membersOfGroup);
group = GroupGeneralData.withAssocations(group, membersOfGroup, groupRoles);
}
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -227,9 +254,9 @@ public class GroupsApiResource {
@Path("{groupId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String activateOrGenerateCollectionSheet(@PathParam("groupId") final Long groupId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson, @Context final UriInfo uriInfo) {
public String activateOrGenerateCollectionSheet(@PathParam("groupId") final Long groupId,
@QueryParam("command") final String commandParam, @QueryParam("roleId") final Long roleId, final String apiRequestBodyAsJson,
@Context final UriInfo uriInfo) {
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandProcessingResult result = null;
@@ -237,32 +264,46 @@ public class GroupsApiResource {
final CommandWrapper commandRequest = builder.activateGroup(groupId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}else if (is(commandParam, "associateClients")) {
} else if (is(commandParam, "associateClients")) {
final CommandWrapper commandRequest = builder.associateClientsToGroup(groupId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}else if (is(commandParam, "disassociateClients")) {
} else if (is(commandParam, "disassociateClients")) {
final CommandWrapper commandRequest = builder.disassociateClientsFromGroup(groupId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}else if (is(commandParam, "generateCollectionSheet")) {
} else if (is(commandParam, "generateCollectionSheet")) {
final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
final JLGCollectionSheetData collectionSheet = this.collectionSheetReadPlatformService.generateGroupCollectionSheet(groupId, query);
final JLGCollectionSheetData collectionSheet = this.collectionSheetReadPlatformService.generateGroupCollectionSheet(groupId,
query);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, collectionSheet, GroupingTypesApiConstants.COLLECTIONSHEET_DATA_PARAMETERS);
}else if (is(commandParam, "saveCollectionSheet")) {
} else if (is(commandParam, "saveCollectionSheet")) {
final CommandWrapper commandRequest = builder.saveGroupCollectionSheet(groupId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}else if (is(commandParam, "unassignStaff")) {
} else if (is(commandParam, "unassignStaff")) {
final CommandWrapper commandRequest = builder.unassignGroupStaff(groupId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
} else if (is(commandParam, "assignRole")) {
final CommandWrapper commandRequest = builder.assignRole(groupId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
} else if (is(commandParam, "unassignRole")) {
final CommandWrapper commandRequest = builder.unassignRole(groupId, roleId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
} else if (is(commandParam, "updateRole")) {
final CommandWrapper commandRequest = builder.updateRole(groupId, roleId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
} else {
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", "saveCollectionSheet", "unassignStaff" });
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet",
"saveCollectionSheet", "unassignStaff", "assignRole", "unassignRole", "updateassignRole" });
}
}
private boolean is(final String commandParam, final String commandValue) {
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.group.data;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.staff.data.StaffData;
@@ -38,50 +39,59 @@ public class GroupGeneralData {
// associations
private final Collection<ClientData> clientMembers;
private final Collection<GroupRoleData> groupRoles;
// template
private final Collection<CenterData> centerOptions;
private final Collection<OfficeData> officeOptions;
private final Collection<StaffData> staffOptions;
private final Collection<ClientData> clientOptions;
private final Collection<CodeValueData> availableRoles;
private final GroupRoleData selectedRole;
public static GroupGeneralData lookup(final Long groupId, final String groupName) {
final Collection<ClientData> clientMembers = null;
final Collection<GroupRoleData> groupRoles = null;
return new GroupGeneralData(groupId, groupName, null, null, null, null, null, null, null, null, null, null, clientMembers, null,
null, null, null);
null, null, null, groupRoles, null, null);
}
public static GroupGeneralData template(final Long officeId, final Long centerId, final String centerName, final Long staffId,
final String staffName, final Collection<CenterData> centerOptions, final Collection<OfficeData> officeOptions,
final Collection<StaffData> staffOptions, final Collection<ClientData> clientOptions) {
final Collection<StaffData> staffOptions, final Collection<ClientData> clientOptions,
final Collection<CodeValueData> availableRoles) {
final Collection<ClientData> clientMembers = null;
final Collection<GroupRoleData> groupRoles = null;
return new GroupGeneralData(null, null, null, null, null, officeId, null, centerId, centerName, staffId, staffName, null,
clientMembers, centerOptions, officeOptions, staffOptions, clientOptions);
clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, null);
}
public static GroupGeneralData withTemplate(final GroupGeneralData templatedGrouping, final GroupGeneralData grouping) {
return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate,
grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, grouping.clientMembers, templatedGrouping.centerOptions, templatedGrouping.officeOptions,
templatedGrouping.staffOptions, templatedGrouping.clientOptions);
templatedGrouping.staffOptions, templatedGrouping.clientOptions, grouping.groupRoles, templatedGrouping.availableRoles,
grouping.selectedRole);
}
public static GroupGeneralData withTemplateAndAssociations(final GroupGeneralData templatedGrouping, final GroupGeneralData grouping,
final Collection<ClientData> membersOfGroup) {
final Collection<ClientData> membersOfGroup, final Collection<GroupRoleData> groupRoles) {
return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate,
grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, membersOfGroup, templatedGrouping.centerOptions, templatedGrouping.officeOptions,
templatedGrouping.staffOptions, templatedGrouping.clientOptions);
templatedGrouping.staffOptions, templatedGrouping.clientOptions, groupRoles, templatedGrouping.availableRoles,
grouping.selectedRole);
}
public static GroupGeneralData withAssocations(final GroupGeneralData grouping, final Collection<ClientData> membersOfGroup) {
public static GroupGeneralData withAssocations(final GroupGeneralData grouping, final Collection<ClientData> membersOfGroup,
final Collection<GroupRoleData> groupRoles) {
return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate,
grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, membersOfGroup, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions,
grouping.clientOptions);
grouping.clientOptions, groupRoles, grouping.availableRoles, grouping.selectedRole);
}
public static GroupGeneralData instance(final Long id, final String name, final String externalId, final EnumOptionData status,
@@ -93,16 +103,21 @@ public class GroupGeneralData {
final Collection<OfficeData> officeOptions = null;
final Collection<StaffData> staffOptions = null;
final Collection<ClientData> clientOptions = null;
final Collection<GroupRoleData> groupRoles = null;
final Collection<CodeValueData> availableRoles = null;
final GroupRoleData role = null;
return new GroupGeneralData(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId,
staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions);
staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles,
role);
}
private GroupGeneralData(final Long id, final String name, final String externalId, final EnumOptionData status,
final LocalDate activationDate, final Long officeId, final String officeName, final Long centerId, final String centerName,
final Long staffId, final String staffName, final String hierarchy, final Collection<ClientData> clientMembers,
final Collection<CenterData> centerOptions, final Collection<OfficeData> officeOptions,
final Collection<StaffData> staffOptions, final Collection<ClientData> clientOptions) {
final Collection<StaffData> staffOptions, final Collection<ClientData> clientOptions,
final Collection<GroupRoleData> groupRoles, final Collection<CodeValueData> availableRoles, final GroupRoleData role) {
this.id = id;
this.name = name;
this.externalId = externalId;
@@ -134,6 +149,9 @@ public class GroupGeneralData {
clientOptions.removeAll(clientMembers);
}
this.clientOptions = clientOptions;
this.groupRoles = groupRoles;
this.availableRoles = availableRoles;
this.selectedRole = role;
}
public Long getId() {
@@ -147,9 +165,16 @@ public class GroupGeneralData {
public Long officeId() {
return this.officeId;
}
public String getHierarchy() {
return this.hierarchy;
}
public static GroupGeneralData updateSelectedRole(final GroupGeneralData grouping, final GroupRoleData selectedRole) {
return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate,
grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, grouping.clientMembers, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions,
grouping.clientOptions, grouping.groupRoles, grouping.availableRoles, selectedRole);
}
}
@@ -0,0 +1,27 @@
package org.mifosplatform.portfolio.group.data;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
public class GroupRoleData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final CodeValueData role;
@SuppressWarnings("unused")
private final Long clientId;
@SuppressWarnings("unused")
private final String clientName;
public static final GroupRoleData template() {
return new GroupRoleData(null, null, null, null);
}
public GroupRoleData(final Long id, final CodeValueData role, final Long clientId, final String clientName) {
this.id = id;
this.role = role;
this.clientId = clientId;
this.clientName = clientName;
}
}
@@ -107,11 +107,13 @@ public final class Group extends AbstractPersistable<Long> {
final Set<Client> clientMembers, final Set<Group> groupMembers) {
GroupingTypeStatus status = GroupingTypeStatus.PENDING;
LocalDate groupActivationDate = null;
if (active) {
status = GroupingTypeStatus.ACTIVE;
groupActivationDate = activationDate;//set activation date only if group is made active
}
return new Group(office, staff, parent, groupLevel, name, externalId, status, activationDate, clientMembers, groupMembers);
return new Group(office, staff, parent, groupLevel, name, externalId, status, groupActivationDate, clientMembers, groupMembers);
}
public Group(final Office office, final Staff staff, final Group parent, final GroupLevel groupLevel, final String name,
@@ -370,4 +372,8 @@ public final class Group extends AbstractPersistable<Long> {
public void setParent(final Group parent) {
this.parent = parent;
}
public Office getOffice() {
return this.office;
}
}
@@ -0,0 +1,74 @@
package org.mifosplatform.portfolio.group.domain;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_group_roles")
public class GroupRole extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id")
private Group group;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "client_id")
private Client client;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "role_cv_id")
private CodeValue role;
public GroupRole() {
// TODO Auto-generated constructor stub
}
public static final GroupRole createGroupRole(final Group group, final Client client, final CodeValue role) {
return new GroupRole(group, client, role);
}
public GroupRole(final Group group, final Client client, final CodeValue role) {
this.group = group;
this.client = client;
this.role = role;
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(2);
if (command.isChangeInLongParameterNamed(GroupingTypesApiConstants.clientIdParamName, this.client.getId())) {
final Long newValue = command.longValueOfParameterNamed(GroupingTypesApiConstants.clientIdParamName);
actualChanges.put(GroupingTypesApiConstants.clientIdParamName, newValue);
}
if (command.isChangeInLongParameterNamed(GroupingTypesApiConstants.roleParamName, this.role.getId())) {
final Long newValue = command.longValueOfParameterNamed(GroupingTypesApiConstants.roleParamName);
actualChanges.put(GroupingTypesApiConstants.roleParamName, newValue);
}
return actualChanges;
}
public void updateRole(final CodeValue role) {
this.role = role;
}
public void updateClient(final Client client) {
this.client = client;
}
}
@@ -0,0 +1,8 @@
package org.mifosplatform.portfolio.group.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface GroupRoleRepository extends JpaRepository<GroupRole, Long>, JpaSpecificationExecutor<GroupRole> {
}
@@ -0,0 +1,35 @@
package org.mifosplatform.portfolio.group.domain;
import org.mifosplatform.portfolio.group.exception.GroupRoleNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class GroupRoleRepositoryWrapper {
private final GroupRoleRepository repository;
@Autowired
public GroupRoleRepositoryWrapper(final GroupRoleRepository rRepository) {
this.repository = rRepository;
}
public GroupRole findOneWithNotFoundDetection(final Long id) {
final GroupRole entity = this.repository.findOne(id);
if (entity == null) { throw new GroupRoleNotFoundException(id); }
return entity;
}
public void save(final GroupRole entity) {
this.repository.save(entity);
}
public void saveAndFlush(final GroupRole entity) {
this.repository.saveAndFlush(entity);
}
public void delete(final GroupRole entity) {
this.repository.delete(entity);
}
}
@@ -0,0 +1,10 @@
package org.mifosplatform.portfolio.group.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class GroupRoleNotFoundException extends AbstractPlatformResourceNotFoundException {
public GroupRoleNotFoundException(final Long id) {
super("error.msg.group.role.id.invalid", "Group Role with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,27 @@
package org.mifosplatform.portfolio.group.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.group.service.GroupRolesWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AssignRoleCommandHandler implements NewCommandSourceHandler {
private final GroupRolesWritePlatformService groupRolesWritePlatformService;
@Autowired
public AssignRoleCommandHandler(final GroupRolesWritePlatformService groupRolesWritePlatformService) {
this.groupRolesWritePlatformService = groupRolesWritePlatformService;
}
@Override
@Transactional
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.groupRolesWritePlatformService.createRole(command);
}
}
@@ -0,0 +1,27 @@
package org.mifosplatform.portfolio.group.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.group.service.GroupRolesWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UnassignRoleCommandHandler implements NewCommandSourceHandler {
private final GroupRolesWritePlatformService groupRolesWritePlatformService;
@Autowired
public UnassignRoleCommandHandler(final GroupRolesWritePlatformService groupRolesWritePlatformService) {
this.groupRolesWritePlatformService = groupRolesWritePlatformService;
}
@Override
@Transactional
public CommandProcessingResult processCommand(JsonCommand command) {
return this.groupRolesWritePlatformService.deleteRole(command.entityId());
}
}
@@ -0,0 +1,27 @@
package org.mifosplatform.portfolio.group.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.group.service.GroupRolesWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UpdateGroupRoleCommandHandler implements NewCommandSourceHandler {
private final GroupRolesWritePlatformService groupRolesWritePlatformService;
@Autowired
public UpdateGroupRoleCommandHandler(final GroupRolesWritePlatformService groupRolesWritePlatformService) {
this.groupRolesWritePlatformService = groupRolesWritePlatformService;
}
@Override
@Transactional
public CommandProcessingResult processCommand(JsonCommand command) {
return this.groupRolesWritePlatformService.updateRole(command);
}
}
@@ -0,0 +1,89 @@
package org.mifosplatform.portfolio.group.serialization;
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.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.group.api.GroupingTypesApiConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public class GroupRolesDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public GroupRolesDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public void validateForCreateGroupRole(final JsonCommand command) {
final String json = command.json();
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper
.checkForUnsupportedParameters(typeOfMap, json, GroupingTypesApiConstants.GROUP_ROLES_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(GroupingTypesApiConstants.GROUP_ROLE_RESOURCE_NAME);
final JsonElement element = command.parsedJson();
final Long roleId = this.fromApiJsonHelper.extractLongNamed(GroupingTypesApiConstants.roleParamName, element);
baseDataValidator.reset().parameter(GroupingTypesApiConstants.roleParamName).value(roleId).notNull().longGreaterThanZero();
final Long clientId = this.fromApiJsonHelper.extractLongNamed(GroupingTypesApiConstants.clientIdParamName, element);
baseDataValidator.reset().parameter(GroupingTypesApiConstants.clientIdParamName).value(clientId).notNull().longGreaterThanZero();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdateRole(final JsonCommand command) {
final String json = command.json();
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper
.checkForUnsupportedParameters(typeOfMap, json, GroupingTypesApiConstants.GROUP_ROLES_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(GroupingTypesApiConstants.GROUP_ROLE_RESOURCE_NAME);
final JsonElement element = command.parsedJson();
final Long roleId = this.fromApiJsonHelper.extractLongNamed(GroupingTypesApiConstants.roleParamName, element);
baseDataValidator.reset().parameter(GroupingTypesApiConstants.roleParamName).value(roleId).ignoreIfNull().notBlank()
.longGreaterThanZero();
final Long clientId = this.fromApiJsonHelper.extractLongNamed(GroupingTypesApiConstants.clientIdParamName, element);
baseDataValidator.reset().parameter(GroupingTypesApiConstants.clientIdParamName).value(clientId).ignoreIfNull().notBlank()
.longGreaterThanZero();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
}
@@ -323,7 +323,7 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService
}
return GroupGeneralData.template(centerOfficeId, center.getId(), center.getName(), staffId, staffName, centerOptions,
officeOptions, staffOptions, clientOptions);
officeOptions, staffOptions, clientOptions, null);
}
@Override
@@ -14,7 +14,7 @@ import org.mifosplatform.portfolio.group.data.GroupGeneralData;
public interface GroupReadPlatformService {
GroupGeneralData retrieveTemplate(Long officeId, boolean isCenterGroup);
GroupGeneralData retrieveTemplate(Long officeId, boolean isCenterGroup, boolean staffInSelectedOfficeOnly);
Page<GroupGeneralData> retrieveAll(SearchParameters searchParameters);
@@ -12,6 +12,8 @@ import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.Page;
@@ -25,6 +27,7 @@ import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.client.service.LoanStatusMapper;
import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants;
import org.mifosplatform.portfolio.group.data.CenterData;
import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData;
import org.mifosplatform.portfolio.group.data.GroupAccountSummaryData;
@@ -48,6 +51,7 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
private final OfficeReadPlatformService officeReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final CenterReadPlatformService centerReadPlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
private final AllGroupTypesDataMapper allGroupTypesDataMapper = new AllGroupTypesDataMapper();
private final PaginationHelper<GroupGeneralData> paginationHelper = new PaginationHelper<GroupGeneralData>();
@@ -55,17 +59,19 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
@Autowired
public GroupReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource,
final CenterReadPlatformService centerReadPlatformService, final ClientReadPlatformService clientReadPlatformService,
final OfficeReadPlatformService officeReadPlatformService, final StaffReadPlatformService staffReadPlatformService) {
final OfficeReadPlatformService officeReadPlatformService, final StaffReadPlatformService staffReadPlatformService,
final CodeValueReadPlatformService codeValueReadPlatformService) {
this.context = context;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.centerReadPlatformService = centerReadPlatformService;
this.clientReadPlatformService = clientReadPlatformService;
this.officeReadPlatformService = officeReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
this.codeValueReadPlatformService = codeValueReadPlatformService;
}
@Override
public GroupGeneralData retrieveTemplate(final Long officeId, final boolean isCenterGroup) {
public GroupGeneralData retrieveTemplate(final Long officeId, final boolean isCenterGroup, final boolean staffInSelectedOfficeOnly) {
final Long defaultOfficeId = defaultToUsersOfficeIfNull(officeId);
@@ -75,7 +81,16 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
}
final Collection<OfficeData> officeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown();
Collection<StaffData> staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(defaultOfficeId);
final boolean loanOfficersOnly = false;
Collection<StaffData> staffOptions = null;
if (staffInSelectedOfficeOnly) {
staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(defaultOfficeId);
} else {
staffOptions = this.staffReadPlatformService.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(defaultOfficeId,
loanOfficersOnly);
}
if (CollectionUtils.isEmpty(staffOptions)) {
staffOptions = null;
}
@@ -85,13 +100,16 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
clientOptions = null;
}
final Collection<CodeValueData> availableRoles = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(GroupingTypesApiConstants.GROUP_ROLE_NAME);
final Long centerId = null;
final String centerName = null;
final Long staffId = null;
final String staffName = null;
return GroupGeneralData.template(defaultOfficeId, centerId, centerName, staffId, staffName, centerOptions, officeOptions,
staffOptions, clientOptions);
staffOptions, clientOptions, availableRoles);
}
private Long defaultToUsersOfficeIfNull(final Long officeId) {
@@ -105,11 +123,11 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
@Override
public Page<GroupGeneralData> retrieveAll(final SearchParameters searchParameters) {
final AppUser currentUser = context.authenticatedUser();
final AppUser currentUser = this.context.authenticatedUser();
final String hierarchy = currentUser.getOffice().getHierarchy();
final String hierarchySearchString = hierarchy + "%";
StringBuilder sqlBuilder = new StringBuilder(200);
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
sqlBuilder.append(this.allGroupTypesDataMapper.schema());
sqlBuilder.append(" where o.hierarchy like ?");
@@ -181,13 +199,13 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
public GroupGeneralData retrieveOne(final Long groupId) {
try {
final AppUser currentUser = context.authenticatedUser();
final AppUser currentUser = this.context.authenticatedUser();
final String hierarchy = currentUser.getOffice().getHierarchy();
final String hierarchySearchString = hierarchy + "%";
String sql = "select " + this.allGroupTypesDataMapper.schema() + " where g.id = ? and o.hierarchy like ?";
final String sql = "select " + this.allGroupTypesDataMapper.schema() + " where g.id = ? and o.hierarchy like ?";
return this.jdbcTemplate.queryForObject(sql, this.allGroupTypesDataMapper, new Object[] { groupId, hierarchySearchString });
} catch (EmptyResultDataAccessException e) {
} catch (final EmptyResultDataAccessException e) {
throw new GroupNotFoundException(groupId);
}
}
@@ -283,7 +301,8 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
final StringBuilder accountsSummary = new StringBuilder("l.id as id, l.external_id as externalId,");
accountsSummary.append("l.product_id as productId, lp.name as productName,").append("l.loan_status_id as statusId, ")
.append("l.account_no as accountNo ").append("from m_loan l ").append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id ");
.append("l.account_no as accountNo ").append("from m_loan l ")
.append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id ");
return accountsSummary.toString();
}
@@ -0,0 +1,12 @@
package org.mifosplatform.portfolio.group.service;
import java.util.Collection;
import org.mifosplatform.portfolio.group.data.GroupRoleData;
public interface GroupRolesReadPlatformService {
Collection<GroupRoleData> retrieveGroupRoles(Long groupId);
GroupRoleData retrieveGroupRole(Long groupId, Long roleId);
}
@@ -0,0 +1,71 @@
package org.mifosplatform.portfolio.group.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.group.data.GroupRoleData;
import org.mifosplatform.portfolio.group.exception.GroupRoleNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class GroupRolesReadPlatformServiceImpl implements GroupRolesReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
@Autowired
public GroupRolesReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) {
this.context = context;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Collection<GroupRoleData> retrieveGroupRoles(final Long groupId) {
this.context.authenticatedUser();
final GroupRolesDataMapper mapper = new GroupRolesDataMapper();
final String sql = "Select " + mapper.schema() + " where role.group_id=?";
return this.jdbcTemplate.query(sql, mapper, new Object[] { groupId });
}
@Override
public GroupRoleData retrieveGroupRole(final Long groupId, final Long roleId) {
try {
this.context.authenticatedUser();
final GroupRolesDataMapper mapper = new GroupRolesDataMapper();
final String sql = "Select " + mapper.schema() + " where role.group_id=? and role.id=?";
return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { groupId, roleId });
} catch (final EmptyResultDataAccessException e) {
throw new GroupRoleNotFoundException(roleId);
}
}
private static final class GroupRolesDataMapper implements RowMapper<GroupRoleData> {
public final String schema() {
return " role.id AS id, role.client_id AS clientId, c.display_name as clientName, role.role_cv_id AS roleId, cv.code_value AS roleName"
+ " from m_code_value cv join m_group_roles role on role.role_cv_id = cv.id left join m_client c on c.id = role.client_id ";
}
@Override
public GroupRoleData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = JdbcSupport.getLong(rs, "id");
final Long clientId = JdbcSupport.getLong(rs, "clientId");
final String clientName = rs.getString("clientName");
final Long roleId = JdbcSupport.getLong(rs, "roleId");
final String roleName = rs.getString("roleName");
final CodeValueData role = CodeValueData.instance(roleId, roleName);
return new GroupRoleData(id, role, clientId, clientName);
}
}
}
@@ -0,0 +1,15 @@
package org.mifosplatform.portfolio.group.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface GroupRolesWritePlatformService {
CommandProcessingResult createRole(JsonCommand command);
CommandProcessingResult updateRole(JsonCommand command);
CommandProcessingResult deleteRole(Long ruleId);
}
@@ -0,0 +1,147 @@
package org.mifosplatform.portfolio.group.service;
import java.util.Map;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
import org.mifosplatform.portfolio.group.domain.GroupRole;
import org.mifosplatform.portfolio.group.domain.GroupRoleRepositoryWrapper;
import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException;
import org.mifosplatform.portfolio.group.serialization.GroupRolesDataValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
@Service
public class GroupRolesWritePlatformServiceJpaRepositoryImpl implements GroupRolesWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(GroupRolesWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context;
private final GroupRepositoryWrapper groupRepository;
private final GroupRolesDataValidator fromApiJsonDeserializer;
private final CodeValueRepositoryWrapper codeValueRepository;
private final ClientRepositoryWrapper clientRepository;
private final GroupRoleRepositoryWrapper groupRoleRepository;
@Autowired
public GroupRolesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final GroupRepositoryWrapper groupRepository, final GroupRolesDataValidator fromApiJsonDeserializer,
final CodeValueRepositoryWrapper codeValueRepository, final ClientRepositoryWrapper clientRepository,
final GroupRoleRepositoryWrapper groupRoleRepository) {
this.context = context;
this.groupRepository = groupRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.codeValueRepository = codeValueRepository;
this.clientRepository = clientRepository;
this.groupRoleRepository = groupRoleRepository;
}
@Override
public CommandProcessingResult createRole(final JsonCommand command) {
try {
this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreateGroupRole(command);
final Long roleId = command.longValueOfParameterNamed(GroupingTypesApiConstants.roleParamName);
final CodeValue role = this.codeValueRepository.findOneWithNotFoundDetection(roleId);
final Long clientId = command.longValueOfParameterNamed(GroupingTypesApiConstants.clientIdParamName);
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
final Group group = this.groupRepository.findOneWithNotFoundDetection(command.getGroupId());
if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, command.getGroupId()); }
final GroupRole groupRole = GroupRole.createGroupRole(group, client, role);
this.groupRoleRepository.save(groupRole);
return new CommandProcessingResultBuilder().withClientId(client.getId()).withGroupId(group.getId())
.withEntityId(groupRole.getId()).build();
} catch (final DataIntegrityViolationException dve) {
handleGroupDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
private void handleGroupDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("UNIQUE_GROUP_ROLES")) {
final String clientId = command.stringValueOfParameterNamed(GroupingTypesApiConstants.clientIdParamName);
final String roleId = command.stringValueOfParameterNamed(GroupingTypesApiConstants.roleParamName);
final String errorMessageForUser = "Group Role with roleId `" + roleId + "`, clientId `" + clientId + "`, groupId `"
+ command.getGroupId() + "` already exists.";
final String errorMessageForMachine = "error.msg.group.role.already.exists";
throw new PlatformDataIntegrityException(errorMessageForMachine, errorMessageForUser,
GroupingTypesApiConstants.clientIdParamName, roleId, clientId, command.getGroupId());
}
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
@Override
public CommandProcessingResult updateRole(final JsonCommand command) {
try {
this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForUpdateRole(command);
final Group group = this.groupRepository.findOneWithNotFoundDetection(command.getGroupId());
final GroupRole groupRole = this.groupRoleRepository.findOneWithNotFoundDetection(command.entityId());
final Map<String, Object> actualChanges = groupRole.update(command);
if (actualChanges.containsKey(GroupingTypesApiConstants.roleParamName)) {
final Long newValue = command.longValueOfParameterNamed(GroupingTypesApiConstants.roleParamName);
CodeValue role = null;
if (newValue != null) {
role = this.codeValueRepository.findOneWithNotFoundDetection(newValue);
}
groupRole.updateRole(role);
}
if (actualChanges.containsKey(GroupingTypesApiConstants.clientIdParamName)) {
final Long newValue = command.longValueOfParameterNamed(GroupingTypesApiConstants.clientIdParamName);
Client client = null;
if (newValue != null) {
client = this.clientRepository.findOneWithNotFoundDetection(newValue);
if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(newValue, command.getGroupId()); }
}
groupRole.updateClient(client);
}
this.groupRoleRepository.saveAndFlush(groupRole);
return new CommandProcessingResultBuilder().with(actualChanges).withGroupId(group.getId()).withEntityId(groupRole.getId())
.build();
} catch (final DataIntegrityViolationException dve) {
handleGroupDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Override
public CommandProcessingResult deleteRole(final Long ruleId) {
this.context.authenticatedUser();
final GroupRole groupRole = this.groupRoleRepository.findOneWithNotFoundDetection(ruleId);
this.groupRoleRepository.delete(groupRole);
return new CommandProcessingResultBuilder().withEntityId(groupRole.getId()).build();
}
}
@@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.security.exception.PermissionDeniedException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
@@ -45,6 +46,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -213,8 +215,21 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
private CommandProcessingResult updateGroupingType(final Long groupId, final JsonCommand command, final GroupTypes groupingType) {
try {
AppUser user = this.context.authenticatedUser();
final Group groupForUpdate = this.groupRepository.findOneWithNotFoundDetection(groupId);
final Long officeId = groupForUpdate.officeId();
final Office groupOffice = groupForUpdate.getOffice();
final String groupHierarchy = groupOffice.getHierarchy();
//Don't allow other office user to update
final Office loginUserOffice = this.context.authenticatedUser().getOffice();
final String userHierarchy = loginUserOffice.getHierarchy();
if (!groupHierarchy.startsWith(userHierarchy)) {
final String code = "error.msg.not.enough.permissions.to.access.resource";
final String defaultMessage = "The User with id "+user.getId()+" don't have permission to update this "+ groupingType.getValue();
throw new PermissionDeniedException(code, defaultMessage, user.getId());
}
final Map<String, Object> actualChanges = groupForUpdate.update(command);
@@ -88,7 +88,7 @@ public class BulkLoansApiResource {
StaffAccountSummaryCollectionData staffAccountSummaryCollectionData = null;
if (officeId != null) {
loanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId);
loanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersInOfficeById(officeId);
}
if (loanOfficerId != null) {
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.loanaccount.api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -12,6 +13,7 @@ import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
@@ -162,6 +164,7 @@ public class LoansApiResource {
@Produces({ MediaType.APPLICATION_JSON })
public String template(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
@QueryParam("productId") final Long productId, @QueryParam("templateType") final String templateType,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
@@ -223,7 +226,29 @@ public class LoansApiResource {
throw new NotSupportedLoanTemplateTypeException(errorMsg, templateType);
}
if (officeId != null) allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId);
if (officeId != null) {
if (staffInSelectedOfficeOnly) {
// only bring back loan officers in selected branch/office
Collection<StaffData> loanOfficersInBranch = this.staffReadPlatformService
.retrieveAllLoanOfficersInOfficeById(officeId);
if (!CollectionUtils.isEmpty(loanOfficersInBranch)) {
allowedLoanOfficers = new ArrayList<StaffData>(loanOfficersInBranch);
}
} else {
// by default bring back all loan officers in selected
// branch/office as well as loan officers in officer above
// this office
final boolean restrictToLoanOfficersOnly = true;
Collection<StaffData> loanOfficersInHierarchy = this.staffReadPlatformService
.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, restrictToLoanOfficersOnly);
if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) {
allowedLoanOfficers = new ArrayList<StaffData>(loanOfficersInHierarchy);
}
}
}
// add product options, allowed loan officers and calendar options
// (calendar options will be null in individual loan)
newLoanAccount = LoanAccountData.associationsAndTemplate(newLoanAccount, productOptions, allowedLoanOfficers, calendarOptions);
@@ -336,9 +361,9 @@ public class LoansApiResource {
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
if (loanBasicDetails.officeId() != null) {
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.officeId());
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersInOfficeById(loanBasicDetails.officeId());
} else if (loanBasicDetails.groupOfficeId() != null) {
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.groupOfficeId());
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersInOfficeById(loanBasicDetails.groupOfficeId());
}
loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose");
@@ -492,8 +517,8 @@ public class LoansApiResource {
// for officeId and loanOfficerId in current way!
final LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveOne(loanId);
final Collection<StaffData> allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails
.officeId());
final Collection<StaffData> allowedLoanOfficers = this.staffReadPlatformService
.retrieveAllLoanOfficersInOfficeById(loanBasicDetails.officeId());
final Long fromLoanOfficerId = loanBasicDetails.loanOfficerId();
final BulkTransferLoanOfficerData loanReassignmentData = BulkTransferLoanOfficerData.template(fromLoanOfficerId,
@@ -20,6 +20,7 @@ import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
@@ -152,6 +153,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator, loanProduct);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
// validate dates against calendar recurring dates
// get calendar
@@ -279,7 +279,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
// get group associations
Collection<ClientData> membersOfGroup = this.clientReadPlatformService.retrieveClientMembersOfGroup(groupId);
if (!CollectionUtils.isEmpty(membersOfGroup)) {
groupAccount = GroupGeneralData.withAssocations(groupAccount, membersOfGroup);
groupAccount = GroupGeneralData.withAssocations(groupAccount, membersOfGroup, null);
}
final LocalDate expectedDisbursementDate = DateUtils.getLocalDateOfTenant();
@@ -938,7 +938,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
// get group associations
Collection<ClientData> membersOfGroup = this.clientReadPlatformService.retrieveClientMembersOfGroup(groupId);
if (!CollectionUtils.isEmpty(membersOfGroup)) {
groupAccount = GroupGeneralData.withAssocations(groupAccount, membersOfGroup);
groupAccount = GroupGeneralData.withAssocations(groupAccount, membersOfGroup, null);
}
return LoanAccountData.groupDefaults(groupAccount, expectedDisbursementDate);
@@ -450,7 +450,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
changes.put("note", noteText);
Note note = Note.loanTransactionNote(loan, newTransactionDetail, noteText);
Note note = null;
/**
* If a new transaction is not created, associate note with the
* transaction to be adjusted
**/
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
note = Note.loanTransactionNote(loan, newTransactionDetail, noteText);
} else {
note = Note.loanTransactionNote(loan, transactionToAdjust, noteText);
}
this.noteRepository.save(note);
}
@@ -183,7 +183,7 @@ public class SavingsAccountDataValidator {
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).notNull().positiveAmount();
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).positiveAmount();
if (annualFeeAmount != null) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
@@ -621,7 +621,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
break;
case ANNUAL:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
}
@@ -680,11 +680,11 @@ public class SavingsAccount extends AbstractPersistable<Long> {
break;
case ANNUAL:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
case NO_COMPOUNDING_SIMPLE_INTEREST:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
}