Merge remote-tracking branch 'vishwasbabu/mifosx-151'

This commit is contained in:
Keith Woodlock
2013-02-10 20:34:53 +00:00
20 changed files with 960 additions and 87 deletions
+772 -7
View File
@@ -293,6 +293,27 @@
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td></td>
<td>loans/{loanId}/guarantors</td>
<td><a href="#loans_guarantors_create">Create a Guarantor
</a></td>
<td><a href="#loans_guarantors_list">List Guarantors
</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>loans/{loanId}/guarantors/{guarantorId}</td>
<td></td>
<td><a href="#loans_guarantors_retrieve">Retrieve a Guarantor
</a></td>
<td><a href="#loans_guarantors_update">Update a Guarantor
</a></td>
<td><a href="#loans_guarantors_delete">Delete a Guarantor
</a></td>
</tr>
</table> </table>
</div> </div>
</div> </div>
@@ -1076,6 +1097,113 @@
</div> </div>
</div> </div>
</div> </div>
<div class="flybar-nav">
<h2 class="flybar-button">Accounting</h2>
<div id="toc-menu-group" 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="#glaccounts">General Ledger Account</a></td>
<td>glaccounts</td>
<td><a href="#glaccounts_create">Create a New Ledger Account</a></td>
<td><a href="#glaccounts_list">List Ledger Accounts</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>glaccounts/{glaccountId}</td>
<td></td>
<td><a href="#glaccounts_retrieve">Retrieve a Ledger Account</a></td>
<td><a href="#glaccounts_update">Update a Ledger Account</a></td>
<td><a href="#glaccounts_delete">Delete a Ledger Account</a></td>
</tr>
<tr class="alt">
<td><a href="#glclosures">Accounting Closure</a></td>
<td>glclosures</td>
<td><a href="#glclosures_create">Create an Accounting Closure</a></td>
<td><a href="#glclosures_list">List Accounting Closures</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>glclosures/{glclosureId}</td>
<td></td>
<td><a href="#glclosures_retrieve">Retrieve an Accounting Closure</a></td>
<td><a href="#glclosures_update">Update an Accounting Closure</a></td>
<td><a href="#glclosures_delete">Delete an Accounting Closure</a></td>
</tr>
<tr class="alt">
<td><a href="#glclosures">Journal Entries</a></td>
<td>journalentries</td>
<td><a href="#journalentries_create">Create Journal Entries</a></td>
<td><a href="#journalentries_list">List Journal Entries</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>journalentries/{entryId}</td>
<td></td>
<td><a href="#journalentries_retrieve">Retrieve a Journal Entry</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>journalentries/{transactionId}/reversal</td>
<td><a href="#journalentries_reverse">Reverse Journal Entries</a></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
</div>
</div>
</div> </div>
<div id="main-content-wrapper"> <div id="main-content-wrapper">
@@ -3976,7 +4104,7 @@ Request Body:
</tr> </tr>
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>accountingType</td> <td>accountingRule</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc><span>Example Values:</span>NONE(1), <td class=fielddesc><span>Example Values:</span>NONE(1),
@@ -4055,7 +4183,11 @@ GET https://DomainName/api/v1/loanproducts
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"accountingType": 1 "accountingRule": {
"id": 1,
"code": "accountingRuleType.none",
"value": "NONE"
}
} }
] ]
</code> </code>
@@ -4123,7 +4255,7 @@ GET https://DomainName/api/v1/loanproducts/template
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"accountingType": 1, "accountingRule": 1,
"currencyOptions": [ "currencyOptions": [
{ {
"code": "USD", "code": "USD",
@@ -4281,6 +4413,158 @@ GET https://DomainName/api/v1/loanproducts/template
} }
} }
], ],
"accountingOptions": {
"assetAccountOptions": [
{
"id": 1,
"name": "Fund Source For Loans",
"glCode": "100001",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 2,
"name": "Interest Receivable",
"glCode": "100002",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 3,
"name": "Fees Receivable",
"glCode": "100003",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 4,
"name": "Penalties Receivable",
"glCode": "100004",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "better duddette"
}
],
"expenseAccountOptions": [
{
"id": 5,
"name": "Written Off Losses",
"glCode": "500005",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 5,
"code": "accountType.expense",
"value": "EXPENSE"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
}
],
"incomeAccountOptions": [
{
"id": 6,
"name": "Income from Interest",
"glCode": "400001",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 7,
"name": "Income from Fees",
"glCode": "400002",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 8,
"name": "Income from Penalties",
"glCode": "400003",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
}
]
},
"accountingMappings": {} "accountingMappings": {}
} }
</code> </code>
@@ -4355,7 +4639,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"accountingType": 1, "accountingRule": 1,
"charges": [] "charges": []
} }
</code> </code>
@@ -4367,7 +4651,17 @@ GET https://DomainName/api/v1/loanproducts/{productId}
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h4>Create a Loan Product</h4> <h4>Create a Loan Product</h4>
<table class=matrixHeading> <p>Depending of the Accounting Rule (accountingRule) selected,
additional fields with details with the appropriate Ledger Account
identifiers would need to be passed in.
</p>
</p>Refer <a href="https://mifosforge.jira.com/wiki/display/MIFOSX/A+Possible+accounting+Spec">
MifosX Accounting Specs Draft</a> for more details regarding the
significance of the selected accounting Rule
</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG"> <tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Mandatory Fields</div></td> <td><div class="mifosXHeading2">Mandatory Fields</div></td>
</tr> </tr>
@@ -4376,7 +4670,24 @@ GET https://DomainName/api/v1/loanproducts/{productId}
inArrearsTolerance, repaymentFrequencyType, repaymentEvery, inArrearsTolerance, repaymentFrequencyType, repaymentEvery,
numberOfRepayments, interestRatePerPeriod, numberOfRepayments, interestRatePerPeriod,
interestRateFrequencyType, amortizationType, interestType, interestRateFrequencyType, amortizationType, interestType,
interestCalculationPeriodType, accountingType</td> interestCalculationPeriodType, accountingRule</td>
</tr>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Additional Mandatory Fields for Cash(2) based accounting</div></td>
</tr>
<tr class=alt>
<td>fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId,
incomeFromFeeAccountId, incomeFromPenaltyAccountId,
writeOffAccountId</td>
</tr>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Additional Mandatory Fields for Accrual(3) based accounting</div></td>
</tr>
<tr class=alt>
<td>fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId,
incomeFromFeeAccountId, incomeFromPenaltyAccountId,
writeOffAccountId, receivableInterestAccountId, receivableFeeAccountId,
receivablePenaltyAccountId</td>
</tr> </tr>
</table> </table>
</div> </div>
@@ -4403,7 +4714,7 @@ Request Body:
"amortizationType": 1, "amortizationType": 1,
"interestType": 0, "interestType": 0,
"interestCalculationPeriodType": 1, "interestCalculationPeriodType": 1,
"accountingType": 1 "accountingRule": 1
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -4411,6 +4722,76 @@ Request Body:
"resourceId": 1 "resourceId": 1
} }
</code> </code>
<code class="method-request">
POST loanproducts
Content-Type: application/json
Request Body:
{
"name": "A new loan product for me",
"currencyCode": "USD",
"locale": "en_GB",
"digitsAfterDecimal": "2",
"principal": "100,000.00",
"numberOfRepayments": "12",
"repaymentEvery": "1",
"repaymentFrequencyType": 2,
"transactionProcessingStrategyId": 1,
"interestRatePerPeriod": "1.75",
"interestRateFrequencyType": 2,
"amortizationType": 1,
"interestType": 0,
"interestCalculationPeriodType": 1,
"accountingRule":"3",
"fundSourceAccountId":"15",
"loanPortfolioAccountId":"16",
"interestOnLoanAccountId":"14",
"incomeFromFeeAccountId":"13",
"incomeFromPenaltyAccountId":"12",
"writeOffAccountId":"11"
}
</code>
<code class="method-response">
{
"resourceId": 2
}
</code>
<code class="method-request">
POST loanproducts
Content-Type: application/json
Request Body:
{
"name": "A new loan product for me",
"currencyCode": "USD",
"locale": "en_GB",
"digitsAfterDecimal": "2",
"principal": "100,000.00",
"numberOfRepayments": "12",
"repaymentEvery": "1",
"repaymentFrequencyType": 2,
"transactionProcessingStrategyId": 1,
"interestRatePerPeriod": "1.75",
"interestRateFrequencyType": 2,
"amortizationType": 1,
"interestType": 0,
"interestCalculationPeriodType": 1,
"accountingRule":"3",
"fundSourceAccountId":"15",
"loanPortfolioAccountId":"16",
"receivableInterestAccountId":"10",
"receivableFeeAccountId":"9",
"receivablePenaltyAccountId":"8",
"interestOnLoanAccountId":"14",
"incomeFromFeeAccountId":"13",
"incomeFromPenaltyAccountId":"12",
"writeOffAccountId":"11"
}
</code>
<code class="method-response">
{
"resourceId": 3
}
</code>
</div> </div>
</div> </div>
@@ -6985,6 +7366,390 @@ GET https://DomainName/api/v1/permissions
</div> </div>
</div> </div>
<!-- start of accounting api docs -->
<a id="glaccounts" name="glaccounts" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h3>General Ledger Account</h3>
<p>Ledger accounts represent an Individual account within an Organizations Chart
Of Accounts(COA) and are assigned a name and unique number by which they can
be identified. <br/>
All transactions relating to a company's assets, liabilities, owners' equity,
revenue and expenses are recorded against these accounts
</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td>
</tr>
<tr class=alt>
<td>name</td>
</tr>
<tr>
<td class=fielddesc>The name of the account</td>
</tr>
<tr class=alt>
<td>glCode</td>
</tr>
<tr>
<td class=fielddesc>The Ledger code associated with the
Account<br>These codes are mandatory and should be unique within
an organization</td>
</tr>
<tr class=alt>
<td>disabled</td>
</tr>
<tr>
<td class=fielddesc>A boolean flag that indicates whether an account
is currently in use</td>
</tr>
<tr class=alt>
<td>type</td>
</tr>
<tr>
<td class=fielddesc>Classifies the account into one of the following Types
<br/><b>Asset accounts:</b> represent the different types of economic resources
owned or controlled by business, common examples of Asset accounts are cash,
cash in bank, building, inventory, prepaid rent, goodwill, accounts receivable<br/>
<b>Liability accounts:</b> represent the different types of economic obligations
by a business, such as accounts payable, bank loan, bonds payable<br/>
<b>Income accounts:</b> represent the company's gross earnings and common examples
include Interest Income, Sales and Service revenue<br/>
<b>Expense accounts:</b> represent the company's expenditures to enable itself to
operate. Common examples are electricity and water, rentals, depreciation,
doubtful accounts, insurance.<br/>
<b>Equity accounts:</b> represent the residual equity of a business (after
deducting from Assets all the liabilities) including Retained Earnings
and Appropriations <br/><br/>
The options are fully listed in <a href="#glaccounts_template">Retrieve
General Ledger Accounts Template</a>.
</td>
</tr>
<tr class=alt>
<td>usage</td>
</tr>
<tr>
<td class=fielddesc>Determines how the account shall be used<br>
"Header" accounts specify the title of a group of accounts. Header accounts are used
only to group detail accounts that have a similar purpose; that is, detail accounts
are assigned to specific header accounts
<br/>
"Detail" accounts may have transactions logged against them<br/><br/>
The options are fully listed in <a href="#glaccounts_template">Retrieve
General Ledger Accounts Template</a>.
</td>
</tr>
<tr class=alt>
<td>manualEntriesAllowed</td>
</tr>
<tr>
<td class=fielddesc>Specifies if manual adjustments can be made
against this account using the <a href="#journalentries_create">
Create Journal Entries API</a> </td>
</tr>
<tr class=alt>
<td>description</td>
</tr>
<tr>
<td class=fielddesc>Human understandable description for the Ledger Account
</td>
</tr>
</table>
</div>
</div>
<a id="glaccounts_list" name="glaccounts_list" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>List General Ledger Accounts</h4>
<h5>Arguments</h5>
<dl class="argument-list">
<dt>type</dt>
<dd>
Integer <span>optional</span>
</dd>
<dt>manualAdjustmentsAllowed</dt>
<dd>
boolean <span>optional</span>
</dd>
<dt>usage</dt>
<dd>
Integer <span>optional</span>
</dd>
<dt>disabled</dt>
<dd>
boolean <span>optional</span>
</dd>
</dl>
<p>Example Requests:</p>
<div class=apiClick>glaccounts</div>
<br>
<br>
<div class=apiClick>glaccounts?type=1&manualAdjustmentsAllowed=true&usage=1&disabled=false</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/glaccounts
</code>
<code class="method-response">
[
{
"id": 16,
"name": "Cash",
"glCode": "100001",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 15,
"name": "Fund Source For Loan",
"glCode": "100002",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
}
]
</code>
</div>
</div>
<a id="glaccounts_template" name="glaccounts_template"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve GL Accounts 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>
<p>Example Request:</p>
<div class=apiClick>glaccounts/template</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/glaccounts/template
</code>
<code class="method-response">
{
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"accountTypeOptions": [
{
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
{
"id": 2,
"code": "accountType.liability",
"value": "LIABILITY"
},
{
"id": 3,
"code": "accountType.equity",
"value": "EQUITY"
},
{
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
{
"id": 5,
"code": "accountType.expense",
"value": "EXPENSE"
}
],
"usageOptions": [
{
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
{
"id": 2,
"code": "accountUsage.header",
"value": "HEADER"
}
]
}
</code>
</div>
</div>
<a id="glaccounts_retrieve" name="glaccounts_retrieve"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve a General Ledger Account</h4>
<p>Example Requests:</p>
<div class=apiClick>glaccounts/1</div>
<br>
<br>
<div class=apiClick>glaccounts/1?template=true</div>
<br>
<br>
<div class=apiClick>glaccounts/1?fields=name,glCode</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/glaccounts/{glaccountsId}
</code>
<code class="method-response">
{
"id": 1,
"name": "Cash",
"glCode": "100001",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset1",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
}
</code>
</div>
</div>
<a id="glaccounts_create" name="glaccounts_create" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Create a General Ledger Account</h4>
<p>
<b>Note:</b> You can optionally<br>
create a Hierarchical Chart of Account by using the "parentId"
property of an Account<br>
</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
</tr>
<tr class=alt>
<td>name, glCode, classification, usage and manualEntriesAllowed
</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">
POST https://DomainName/api/v1/glaccounts_update
</code>
<code class="method-request">
POST glaccounts
Content-Type: application/json
Request Body:
{
"name": "Cash at Bangalore",
"glCode": "100017",
"manualEntriesAllowed": true,
"classification": 1,
"usage": 1,
"description": "Cash at Bangalore Branch"
}
</code>
<code class="method-response">
{
"resourceId": 22
}
</code>
</div>
</div>
<a id="glaccounts_update" name="glaccounts_update" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Update a General Ledger Account</h4>
</div>
<div class="method-example">
<code class="method-declaration">
PUT https://DomainName/api/v1/glaccounts/{glaccountId}
</code>
<code class="method-request">
PUT glaccounts/1
Content-Type: application/json
Request Body:
{
"name": "Cash at Tumkur"
}
</code>
<code class="method-response">
{
"resourceId": 1,
}
</code>
</div>
</div>
<a id="glaccounts_delete" name="glaccounts_delete" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Delete a General Ledger Account</h4>
<p>
<b>Note:</b> Only Ledger Accounts against which no transactions have been logged
(either manually or by the loan or Savings portfolio) can be deleted.
</p>
</div>
<div class="method-example">
<code class="method-declaration">
DELETE https://DomainName/api/v1/glaccounts/{glaccountId}
</code>
<code class="method-request">
DELETE glaccounts/1
Content-Type: application/json
No Request Body:
</code>
<code class="method-response">
{
"resourceId": 1
}
</code>
</div>
</div>
</div> </div>
<!-- main-content-wrapper --> <!-- main-content-wrapper -->
@@ -70,7 +70,7 @@ public class GLAccountsApiResource {
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllAccounts(@Context final UriInfo uriInfo, @QueryParam("classification") final Integer classification, public String retrieveAllAccounts(@Context final UriInfo uriInfo, @QueryParam("type") final Integer classification,
@QueryParam("searchParam") final String searchParam, @QueryParam("usage") final Integer usage, @QueryParam("searchParam") final String searchParam, @QueryParam("usage") final Integer usage,
@QueryParam("manualAdjustmentsAllowed") final Boolean manualTransactionsAllowed, @QueryParam("disabled") final Boolean disabled) { @QueryParam("manualAdjustmentsAllowed") final Boolean manualTransactionsAllowed, @QueryParam("disabled") final Boolean disabled) {
@@ -15,6 +15,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand; import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand;
import org.mifosplatform.accounting.api.data.GLJournalEntryData; import org.mifosplatform.accounting.api.data.GLJournalEntryData;
import org.mifosplatform.accounting.api.data.JournalEntryIdentifier; import org.mifosplatform.accounting.api.data.JournalEntryIdentifier;
@@ -24,6 +25,7 @@ import org.mifosplatform.accounting.api.infrastructure.DateParam;
import org.mifosplatform.accounting.service.GLJournalEntryReadPlatformService; import org.mifosplatform.accounting.service.GLJournalEntryReadPlatformService;
import org.mifosplatform.accounting.service.GLJournalEntryWritePlatformService; import org.mifosplatform.accounting.service.GLJournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
@@ -58,9 +60,9 @@ public class GLJournalEntriesApiResource {
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllJournalEntries(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId, public String retrieveAllJournalEntries(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId,
@QueryParam("glAccountId") final Long glAccountId, @QueryParam("glAccountId") final Long glAccountId, @QueryParam("portfolioGenerated") final Boolean portfolioGenerated,
@QueryParam("portfolioGenerated") final Boolean portfolioGenerated, @QueryParam("fromDate") final DateParam fromDateParam, @QueryParam("fromDate") final DateParam fromDateParam, @QueryParam("toDate") final DateParam toDateParam,
@QueryParam("toDate") final DateParam toDateParam) { @QueryParam("transactionId") String transactionId) {
// TODO: Vishwas Add pagination, approach at // TODO: Vishwas Add pagination, approach at
// http://www.javaworld.com/community/node/8295 looks good // http://www.javaworld.com/community/node/8295 looks good
@@ -79,8 +81,13 @@ public class GLJournalEntriesApiResource {
if (toDateParam != null) { if (toDateParam != null) {
toDate = toDateParam.getDate(); toDate = toDateParam.getDate();
} }
glJournalEntryDatas = this.glJournalEntryReadPlatformService.retrieveAllGLJournalEntries(officeId, glAccountId, if (StringUtils.isBlank(transactionId)) {
portfolioGenerated, fromDate, toDate); glJournalEntryDatas = this.glJournalEntryReadPlatformService.retrieveAllGLJournalEntries(officeId, glAccountId,
portfolioGenerated, fromDate, toDate);
} else {
glJournalEntryDatas = this.glJournalEntryReadPlatformService.retrieveRelatedJournalEntries(transactionId);
}
return this.apiJsonSerializerService.serializeGLJournalEntryDataToJson(prettyPrint, responseParameters, glJournalEntryDatas); return this.apiJsonSerializerService.serializeGLJournalEntryDataToJson(prettyPrint, responseParameters, glJournalEntryDatas);
} }
@@ -88,7 +95,7 @@ public class GLJournalEntriesApiResource {
@Path("{glJournalEntryId}") @Path("{glJournalEntryId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retreiveAccount(@PathParam("glJournalEntryId") final Long glJournalEntryId, @Context final UriInfo uriInfo) { public String retreiveJournalEntryById(@PathParam("glJournalEntryId") final Long glJournalEntryId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(entityType); context.authenticatedUser().validateHasReadPermission(entityType);
@@ -111,13 +118,21 @@ public class GLJournalEntriesApiResource {
} }
@POST @POST
@Path("{transactionId}/reversal") @Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String createReversalJournalEntry(@PathParam("transactionId") final String transactionId) { public String createReversalJournalEntry(@PathParam("transactionId") final String transactionId,
@QueryParam("command") final String commandParam) {
this.glJournalEntryWritePlatformService.revertJournalEntry(transactionId); if (is(commandParam, "reverse")) {
this.glJournalEntryWritePlatformService.revertJournalEntry(transactionId);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
return this.apiJsonSerializerService.serializeJournalEntryIdentifier(new JournalEntryIdentifier(transactionId)); return this.apiJsonSerializerService.serializeJournalEntryIdentifier(new JournalEntryIdentifier(transactionId));
} }
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
} }
@@ -0,0 +1,18 @@
package org.mifosplatform.accounting.exceptions;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when journal entry resources are not found.
*/
public class GLJournalEntryNotFoundException extends AbstractPlatformResourceNotFoundException {
public GLJournalEntryNotFoundException(final String transactionId) {
super("error.msg.journalEntries.transactionId.invalid", "Journal Entries with transaction Identifier " + transactionId
+ " does not exist or are not system generated/reversible ", transactionId);
}
public GLJournalEntryNotFoundException(final Long entryId) {
super("error.msg.journalEntries.id.invalid", "Journal Entry with entry Id " + entryId + " does not exist ", entryId);
}
}
@@ -30,7 +30,7 @@ public class GLAccountCommandValidator {
baseDataValidator.reset().parameter("parentId").value(command.getParentId()).ignoreIfNull().integerGreaterThanZero(); baseDataValidator.reset().parameter("parentId").value(command.getParentId()).ignoreIfNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("classification").value(command.getClassification()) baseDataValidator.reset().parameter("classification").value(command.getClassification()).notNull()
.inMinMaxRange(GLAccountType.getMinValue(), GLAccountType.getMaxValue()); .inMinMaxRange(GLAccountType.getMinValue(), GLAccountType.getMaxValue());
baseDataValidator.reset().parameter("usage").value(command.getUsage()) baseDataValidator.reset().parameter("usage").value(command.getUsage())
@@ -10,6 +10,8 @@ public interface GLJournalEntryReadPlatformService {
List<GLJournalEntryData> retrieveAllGLJournalEntries(Long officeId, Long glAccountId, Boolean portfolioGenerated, Date fromDate, List<GLJournalEntryData> retrieveAllGLJournalEntries(Long officeId, Long glAccountId, Boolean portfolioGenerated, Date fromDate,
Date toDate); Date toDate);
List<GLJournalEntryData> retrieveRelatedJournalEntries(String transactionId);
GLJournalEntryData retrieveGLJournalEntryById(long glJournalEntryId); GLJournalEntryData retrieveGLJournalEntryById(long glJournalEntryId);
} }
@@ -45,7 +45,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
Long id = rs.getLong("id"); Long id = rs.getLong("id");
String name = rs.getString("name"); String name = rs.getString("name");
Long parentId = rs.getLong("parentId"); Long parentId = JdbcSupport.getLong(rs,"parentId");
String glCode = rs.getString("glCode"); String glCode = rs.getString("glCode");
boolean disabled = rs.getBoolean("disabled"); boolean disabled = rs.getBoolean("disabled");
boolean manualEntriesAllowed = rs.getBoolean("manualEntriesAllowed"); boolean manualEntriesAllowed = rs.getBoolean("manualEntriesAllowed");
@@ -73,7 +73,7 @@ public class GLClosureReadPlatformServiceImpl implements GLClosureReadPlatformSe
arrayPos = arrayPos + 1; arrayPos = arrayPos + 1;
} }
sql = sql + " order by glClosure.closing_date"; sql = sql + " order by glClosure.closing_date desc";
Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos);
return this.jdbcTemplate.query(sql, rm, finalObjectArray); return this.jdbcTemplate.query(sql, rm, finalObjectArray);
@@ -134,7 +134,7 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
} }
} }
sql += " order by journalEntry.entry_date,journalEntry.transaction_id"; sql += " order by journalEntry.entry_date desc,journalEntry.transaction_id";
Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos);
return this.jdbcTemplate.query(sql, rm, finalObjectArray); return this.jdbcTemplate.query(sql, rm, finalObjectArray);
@@ -147,12 +147,27 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
GLJournalEntryMapper rm = new GLJournalEntryMapper(); GLJournalEntryMapper rm = new GLJournalEntryMapper();
String sql = "select " + rm.schema() + " and journalEntry.id = ?"; String sql = "select " + rm.schema() + " and journalEntry.id = ?";
GLJournalEntryData glAccountData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { glJournalEntryId }); GLJournalEntryData glJournalEntryData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { glJournalEntryId });
return glAccountData; return glJournalEntryData;
} catch (EmptyResultDataAccessException e) { } catch (EmptyResultDataAccessException e) {
throw new GLJournalEntriesNotFoundException(glJournalEntryId); throw new GLJournalEntriesNotFoundException(glJournalEntryId);
} }
} }
@Override
public List<GLJournalEntryData> retrieveRelatedJournalEntries(String transactionId) {
try {
GLJournalEntryMapper rm = new GLJournalEntryMapper();
String sql = "select " + rm.schema() + " and journalEntry.transaction_id = ?";
List<GLJournalEntryData> journalEntryDatas = this.jdbcTemplate.query(sql, rm, new Object[] { transactionId });
return journalEntryDatas;
} catch (EmptyResultDataAccessException e) {
throw new GLJournalEntriesNotFoundException(transactionId);
}
}
} }
@@ -50,7 +50,7 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
@Transactional @Transactional
public void createLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command) { public void createLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command) {
final JsonElement element = this.fromApiJsonHelper.parse(command.json()); final JsonElement element = this.fromApiJsonHelper.parse(command.json());
final Integer accountingRuleTypeId = this.fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault()); final Integer accountingRuleTypeId = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId); final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
switch (accountingRuleType) { switch (accountingRuleType) {
@@ -152,27 +152,30 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder createGuarantor() { public CommandWrapperBuilder createGuarantor(final Long loanId) {
this.actionName = "CREATE"; this.actionName = "CREATE";
this.entityName = "GUARANTOR"; this.entityName = "GUARANTOR";
this.entityId = null; this.entityId = null;
this.href = "/guarantors/template"; this.loanId = loanId;
this.href = "/loans/" + loanId + "/guarantors/template";
return this; return this;
} }
public CommandWrapperBuilder updateGuarantor(final Long guarantorId) { public CommandWrapperBuilder updateGuarantor(final Long loanId, final Long guarantorId) {
this.actionName = "UPDATE"; this.actionName = "UPDATE";
this.entityName = "GUARANTOR"; this.entityName = "GUARANTOR";
this.entityId = guarantorId; this.entityId = guarantorId;
this.href = "/guarantors/" + guarantorId; this.loanId = loanId;
this.href = "/loans/" + loanId + "/guarantors/" + guarantorId;
return this; return this;
} }
public CommandWrapperBuilder deleteGuarantor(final Long guarantorId) { public CommandWrapperBuilder deleteGuarantor(final Long loanId, final Long guarantorId) {
this.actionName = "DELETE"; this.actionName = "DELETE";
this.entityName = "GUARANTOR"; this.entityName = "GUARANTOR";
this.entityId = guarantorId; this.entityId = guarantorId;
this.href = "/guarantors/" + guarantorId; this.loanId = loanId;
this.href = "/loans/" + loanId + "/guarantors/" + guarantorId;
return this; return this;
} }
@@ -34,12 +34,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/guarantors") @Path("/loans/{loanId}/guarantors")
@Component @Component
@Scope("singleton") @Scope("singleton")
public class GuarantorsApiResource { public class GuarantorsApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("externalGuarantor", "existingClientId", private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("guarantorTypeId", "entityId",
"firstname", "lastname", "addressLine1", "addressLine2", "city", "state", "zip", "country", "mobileNumber", "housePhoneNumber", "firstname", "lastname", "addressLine1", "addressLine2", "city", "state", "zip", "country", "mobileNumber", "housePhoneNumber",
"comment", "dob")); "comment", "dob"));
@@ -75,14 +75,28 @@ public class GuarantorsApiResource {
return this.apiJsonSerializerService.serialize(settings, guarantorData, RESPONSE_DATA_PARAMETERS); return this.apiJsonSerializerService.serialize(settings, guarantorData, RESPONSE_DATA_PARAMETERS);
} }
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveGuarantorDetails(@Context final UriInfo uriInfo, @PathParam("loanId") final Long loanId) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
List<GuarantorData> guarantorDatas = guarantorReadPlatformService.retrieveGuarantorsForValidLoan(loanId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, guarantorDatas, RESPONSE_DATA_PARAMETERS);
}
@GET @GET
@Path("{guarantorId}") @Path("{guarantorId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveGuarantorDetails(@Context final UriInfo uriInfo, @PathParam("guarantorId") final Long guarantorId) { public String retrieveGuarantorDetails(@Context final UriInfo uriInfo, @PathParam("loanId") final Long loanId,
@PathParam("guarantorId") final Long guarantorId) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission); context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
GuarantorData guarantorData = guarantorReadPlatformService.retrieveGuarantor(guarantorId); GuarantorData guarantorData = guarantorReadPlatformService.retrieveGuarantor(loanId, guarantorId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) { if (settings.isTemplate()) {
@@ -96,9 +110,9 @@ public class GuarantorsApiResource {
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String createGuarantor(final String apiRequestBodyAsJson) { public String createGuarantor(@PathParam("loanId") final Long loanId, final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createGuarantor().withJson(apiRequestBodyAsJson).build(); final CommandWrapper commandRequest = new CommandWrapperBuilder().createGuarantor(loanId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
@@ -109,8 +123,10 @@ public class GuarantorsApiResource {
@Path("{guarantorId}") @Path("{guarantorId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String updateGuarantor(@PathParam("guarantorId") final Long guarantorId, final String jsonRequestBody) { public String updateGuarantor(@PathParam("loanId") final Long loanId, @PathParam("guarantorId") final Long guarantorId,
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateGuarantor(guarantorId).withJson(jsonRequestBody).build(); final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateGuarantor(loanId, guarantorId).withJson(jsonRequestBody)
.build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
@@ -121,8 +137,8 @@ public class GuarantorsApiResource {
@Path("{guarantorId}") @Path("{guarantorId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String deleteGuarantor(@PathParam("guarantorId") final Long guarantorId) { public String deleteGuarantor(@PathParam("loanId") final Long loanId, @PathParam("guarantorId") final Long guarantorId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteGuarantor(guarantorId).build(); final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteGuarantor(loanId, guarantorId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
@@ -6,9 +6,24 @@ import org.mifosplatform.portfolio.loanaccount.guarantor.data.GuarantorData;
public interface GuarantorReadPlatformService { public interface GuarantorReadPlatformService {
/**
* Validates the passed in loanId before retrieving Loans for the same
*
* @param loanId
* @return
*/
List<GuarantorData> retrieveGuarantorsForValidLoan(Long loanId);
/**
* Methods Returns all Guarantors for a Given loan Id (if the loan Id is
* valid and Exists)
*
* @param loanId
* @return
*/
List<GuarantorData> retrieveGuarantorsForLoan(Long loanId); List<GuarantorData> retrieveGuarantorsForLoan(Long loanId);
GuarantorData retrieveGuarantor(Long guarantorId); GuarantorData retrieveGuarantor(Long loanId, Long guarantorId);
GuarantorData retrieveNewGuarantorDetails(); GuarantorData retrieveNewGuarantorDetails();
@@ -13,6 +13,9 @@ import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.guarantor.data.GuarantorData; import org.mifosplatform.portfolio.loanaccount.guarantor.data.GuarantorData;
import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorType; import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -26,13 +29,16 @@ public class GuarantorReadPlatformServiceImpl implements GuarantorReadPlatformSe
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final ClientReadPlatformService clientReadPlatformService; private final ClientReadPlatformService clientReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService; private final StaffReadPlatformService staffReadPlatformService;
private final LoanRepository loanRepository;
@Autowired @Autowired
public GuarantorReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource, public GuarantorReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource,
final ClientReadPlatformService clientReadPlatformService, final StaffReadPlatformService staffReadPlatformService) { final ClientReadPlatformService clientReadPlatformService, final StaffReadPlatformService staffReadPlatformService,
final LoanRepository loanRepository) {
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
this.clientReadPlatformService = clientReadPlatformService; this.clientReadPlatformService = clientReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService; this.staffReadPlatformService = staffReadPlatformService;
this.loanRepository = loanRepository;
} }
private static final class GuarantorMapper implements RowMapper<GuarantorData> { private static final class GuarantorMapper implements RowMapper<GuarantorData> {
@@ -68,6 +74,13 @@ public class GuarantorReadPlatformServiceImpl implements GuarantorReadPlatformSe
} }
} }
@Override
public List<GuarantorData> retrieveGuarantorsForValidLoan(Long loanId) {
Loan loan = this.loanRepository.findOne(loanId);
if (loan == null) { throw new LoanNotFoundException(loanId); }
return retrieveGuarantorsForLoan(loanId);
}
@Override @Override
public List<GuarantorData> retrieveGuarantorsForLoan(Long loanId) { public List<GuarantorData> retrieveGuarantorsForLoan(Long loanId) {
final GuarantorMapper rm = new GuarantorMapper(); final GuarantorMapper rm = new GuarantorMapper();
@@ -84,11 +97,11 @@ public class GuarantorReadPlatformServiceImpl implements GuarantorReadPlatformSe
} }
@Override @Override
public GuarantorData retrieveGuarantor(Long guarantorId) { public GuarantorData retrieveGuarantor(Long loanId, Long guarantorId) {
final GuarantorMapper rm = new GuarantorMapper(); final GuarantorMapper rm = new GuarantorMapper();
String sql = "select " + rm.schema(); String sql = "select " + rm.schema();
sql += " where loan_id = ?"; sql += " where g.loan_id = ? and g.id = ?";
GuarantorData guarantorData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { guarantorId }); GuarantorData guarantorData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId, guarantorId });
return mergeDetailsForClientOrStaffGuarantor(guarantorData); return mergeDetailsForClientOrStaffGuarantor(guarantorData);
} }
@@ -58,7 +58,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
final GuarantorCommand guarantorCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json()); final GuarantorCommand guarantorCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
guarantorCommand.validateForCreate(); guarantorCommand.validateForCreate();
final Long loanId = command.longValueOfParameterNamed(GUARANTOR_JSON_INPUT_PARAMS.LOAN_ID.getValue()); final Long loanId = command.getLoanId();
Loan loan = retrieveLoanById(loanId); Loan loan = retrieveLoanById(loanId);
Guarantor guarantor = null; Guarantor guarantor = null;
guarantor = Guarantor.fromJson(loan, command); guarantor = Guarantor.fromJson(loan, command);
@@ -83,6 +83,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
final GuarantorCommand guarantorCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json()); final GuarantorCommand guarantorCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
guarantorCommand.validateForUpdate(); guarantorCommand.validateForUpdate();
// TODO: Vishwas need to fetch by both loan and Guarantor Id
final Guarantor guarantorForUpdate = this.guarantorRepository.findOne(guarantorId); final Guarantor guarantorForUpdate = this.guarantorRepository.findOne(guarantorId);
if (guarantorForUpdate == null) { throw new GuarantorNotFoundException(guarantorId); } if (guarantorForUpdate == null) { throw new GuarantorNotFoundException(guarantorId); }
@@ -108,6 +109,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
@Override @Override
@Transactional @Transactional
public CommandProcessingResult removeGuarantor(final Long guarantorId) { public CommandProcessingResult removeGuarantor(final Long guarantorId) {
// TODO: Vishwas need to fetch by both loan and Guarantor Id
final Guarantor guarantorForDelete = this.guarantorRepository.findOne(guarantorId); final Guarantor guarantorForDelete = this.guarantorRepository.findOne(guarantorId);
if (guarantorForDelete == null) { throw new GuarantorNotFoundException(guarantorId); } if (guarantorForDelete == null) { throw new GuarantorNotFoundException(guarantorId); }
@@ -19,7 +19,6 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.api.data.GLAccountData; import org.mifosplatform.accounting.api.data.GLAccountData;
import org.mifosplatform.accounting.domain.GLAccountType; import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.service.GLAccountReadPlatformService; import org.mifosplatform.accounting.service.GLAccountReadPlatformService;
@@ -58,12 +57,7 @@ public class LoanProductsApiResource {
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "charges", "createdOn", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "charges", "createdOn",
"lastModifedOn", "currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "lastModifedOn", "currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions",
"chargeOptions", "assetAccountOptions", "incomeAccountOptions", "expenseAccountOptions", "accountingType", "chargeOptions", "accountingOptions", "accountingRule","accountingRuleOptions","accountingMappings","accountingMappingOptions"));
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue()));
private final String resourceNameForPermissions = "LOANPRODUCT"; private final String resourceNameForPermissions = "LOANPRODUCT";
@@ -205,22 +199,29 @@ public class LoanProductsApiResource {
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService
.retreiveTransactionProcessingStrategies(); .retreiveTransactionProcessingStrategies();
Map<String, List<GLAccountData>> accountOptions= new HashMap<String, List<GLAccountData>>();
List<GLAccountData> assetAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET); List<GLAccountData> assetAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET);
if (assetAccountOptions.isEmpty()) { if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null; assetAccountOptions = null;
} }
accountOptions.put("assetAccountOptions", assetAccountOptions);
List<GLAccountData> incomeAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME); List<GLAccountData> incomeAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME);
if (incomeAccountOptions.isEmpty()) { if (incomeAccountOptions.isEmpty()) {
incomeAccountOptions = null; incomeAccountOptions = null;
} }
accountOptions.put("incomeAccountOptions", incomeAccountOptions);
List<GLAccountData> expenseAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE); List<GLAccountData> expenseAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE);
if (expenseAccountOptions.isEmpty()) { if (expenseAccountOptions.isEmpty()) {
expenseAccountOptions = null; expenseAccountOptions = null;
} }
accountOptions.put("expenseAccountOptions", expenseAccountOptions);
List<EnumOptionData> accountingRuleTypeOptions = dropdownReadPlatformService.retrieveAccountingRuleTypeOptions();
return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions, return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, assetAccountOptions, interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions,accountingMappings);
incomeAccountOptions, expenseAccountOptions, accountingMappings);
} }
} }
@@ -45,7 +45,7 @@ public class LoanProductData {
private final EnumOptionData interestCalculationPeriodType; private final EnumOptionData interestCalculationPeriodType;
private final BigDecimal inArrearsTolerance; private final BigDecimal inArrearsTolerance;
private final EnumOptionData accountingType; private final EnumOptionData accountingRule;
private final Collection<ChargeData> charges; private final Collection<ChargeData> charges;
@@ -62,9 +62,8 @@ public class LoanProductData {
private final Collection<ChargeData> chargeOptions; private final Collection<ChargeData> chargeOptions;
// accounting related template fields // accounting related template fields
private final List<GLAccountData> assetAccountOptions; private final List<EnumOptionData> accountingRuleOptions;
private final List<GLAccountData> incomeAccountOptions; private final Map<String, List<GLAccountData>> accountingMappingOptions;
private final List<GLAccountData> expenseAccountOptions;
// accounting related mapping fields // accounting related mapping fields
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -140,7 +139,7 @@ public class LoanProductData {
productData.amortizationTypeOptions, productData.interestTypeOptions, productData.interestCalculationPeriodTypeOptions, productData.amortizationTypeOptions, productData.interestTypeOptions, productData.interestCalculationPeriodTypeOptions,
productData.loanTermFrequencyTypeOptions, productData.repaymentFrequencyTypeOptions, productData.loanTermFrequencyTypeOptions, productData.repaymentFrequencyTypeOptions,
productData.interestRateFrequencyTypeOptions, productData.fundOptions, productData.transactionProcessingStrategyOptions, productData.interestRateFrequencyTypeOptions, productData.fundOptions, productData.transactionProcessingStrategyOptions,
productData.assetAccountOptions, productData.incomeAccountOptions, productData.expenseAccountOptions, accountingMappings); productData.accountingMappingOptions, productData.accountingRuleOptions, accountingMappings);
} }
public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency, public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency,
@@ -173,7 +172,7 @@ public class LoanProductData {
this.transactionProcessingStrategyId = transactionProcessingStrategyId; this.transactionProcessingStrategyId = transactionProcessingStrategyId;
this.transactionProcessingStrategyName = transactionProcessingStrategyName; this.transactionProcessingStrategyName = transactionProcessingStrategyName;
this.charges = charges; this.charges = charges;
this.accountingType = accountingType; this.accountingRule = accountingType;
this.chargeOptions = null; this.chargeOptions = null;
this.currencyOptions = null; this.currencyOptions = null;
@@ -186,9 +185,8 @@ public class LoanProductData {
this.repaymentFrequencyTypeOptions = null; this.repaymentFrequencyTypeOptions = null;
this.interestRateFrequencyTypeOptions = null; this.interestRateFrequencyTypeOptions = null;
this.assetAccountOptions = null; this.accountingMappingOptions = null;
this.incomeAccountOptions = null; this.accountingRuleOptions = null;
this.expenseAccountOptions = null;
this.accountingMappings = null; this.accountingMappings = null;
} }
@@ -198,8 +196,8 @@ public class LoanProductData {
final List<EnumOptionData> loanTermFrequencyTypeOptions, final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> loanTermFrequencyTypeOptions, final List<EnumOptionData> repaymentFrequencyTypeOptions,
final List<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<FundData> fundOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions, final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions,
final List<GLAccountData> assetAccountOptions, final List<GLAccountData> incomeAccountOptions, final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
final List<GLAccountData> expenseAccountOptions, final Map<String, Object> accountingMappings) { final Map<String, Object> accountingMappings) {
this.id = productData.id; this.id = productData.id;
this.name = productData.name; this.name = productData.name;
this.description = productData.description; this.description = productData.description;
@@ -221,7 +219,7 @@ public class LoanProductData {
this.transactionProcessingStrategyId = productData.transactionProcessingStrategyId; this.transactionProcessingStrategyId = productData.transactionProcessingStrategyId;
this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName; this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName;
this.charges = nullIfEmpty(productData.charges()); this.charges = nullIfEmpty(productData.charges());
this.accountingType = productData.accountingType; this.accountingRule = productData.accountingRule;
this.chargeOptions = chargeOptions; this.chargeOptions = chargeOptions;
this.currencyOptions = currencyOptions; this.currencyOptions = currencyOptions;
@@ -239,9 +237,8 @@ public class LoanProductData {
this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions; this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions;
this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions; this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions;
this.assetAccountOptions = assetAccountOptions; this.accountingMappingOptions = accountingMappingOptions;
this.incomeAccountOptions = incomeAccountOptions; this.accountingRuleOptions = accountingRuleOptions;
this.expenseAccountOptions = expenseAccountOptions;
this.accountingMappings = accountingMappings; this.accountingMappings = accountingMappings;
} }
@@ -262,11 +259,11 @@ public class LoanProductData {
} }
public EnumOptionData accountingRuleType() { public EnumOptionData accountingRuleType() {
return this.accountingType; return this.accountingRule;
} }
public boolean hasAccountingEnabled() { public boolean hasAccountingEnabled() {
return this.accountingType.getId() > 1; return this.accountingRule.getId() > 1;
} }
public Long getId() { public Long getId() {
@@ -33,7 +33,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "description", "fundId", private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "description", "fundId",
"transactionProcessingStrategyId", "currencyCode", "digitsAfterDecimal", "principal", "inArrearsTolerance", "transactionProcessingStrategyId", "currencyCode", "digitsAfterDecimal", "principal", "inArrearsTolerance",
"interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", "interestRateFrequencyType", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", "interestRateFrequencyType",
"amortizationType", "interestType", "interestCalculationPeriodType", "charges", "locale", "accountingType", "amortizationType", "interestType", "interestCalculationPeriodType", "charges", "locale", "accountingRule",
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
@@ -114,8 +114,8 @@ public final class LoanProductCommandFromApiJsonDeserializer {
.inMinMaxRange(0, 1); .inMinMaxRange(0, 1);
// accounting related data validation // accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault()); final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingType").value(accountingRuleType).notNull().inMinMaxRange(1, 3); baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType)) { if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType)) {
@@ -266,8 +266,8 @@ public final class LoanProductCommandFromApiJsonDeserializer {
.inMinMaxRange(0, 1); .inMinMaxRange(0, 1);
} }
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault()); final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingType").value(accountingRuleType).ignoreIfNull().inMinMaxRange(1, 3); baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).ignoreIfNull().inMinMaxRange(1, 3);
final Long fundAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), element); final Long fundAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).value(fundAccountId).ignoreIfNull() baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).value(fundAccountId).ignoreIfNull()
@@ -21,4 +21,6 @@ public interface LoanDropdownReadPlatformService {
List<EnumOptionData> retrieveInterestRateFrequencyTypeOptions(); List<EnumOptionData> retrieveInterestRateFrequencyTypeOptions();
Collection<TransactionProcessingStrategyData> retreiveTransactionProcessingStrategies(); Collection<TransactionProcessingStrategyData> retreiveTransactionProcessingStrategies();
List<EnumOptionData> retrieveAccountingRuleTypeOptions();
} }
@@ -6,6 +6,7 @@ import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.i
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestType; import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.loanTermFrequencyType; import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.loanTermFrequencyType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType; import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.accountingRuleType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -15,6 +16,7 @@ import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData; import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod; import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
@@ -103,4 +105,11 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat
return strategyOptions; return strategyOptions;
} }
@Override
public List<EnumOptionData> retrieveAccountingRuleTypeOptions() {
List<EnumOptionData> accountingRuleTypeOptions = Arrays.asList(accountingRuleType(AccountingRuleType.NONE),
accountingRuleType(AccountingRuleType.CASH_BASED),accountingRuleType(AccountingRuleType.ACCRUAL_BASED));
return accountingRuleTypeOptions;
}
} }