Merge branch 'develop'
This commit is contained in:
+545
-37
@@ -11,7 +11,7 @@
|
|||||||
var result = {};
|
var result = {};
|
||||||
if (window.location.search) {
|
if (window.location.search) {
|
||||||
// split up the query string and store in an associative array
|
// split up the query string and store in an associative array
|
||||||
var params = window.location.search.slice(1).split("&"); x
|
var params = window.location.search.slice(1).split("&");
|
||||||
for ( var i = 0; i < params.length; i++) {
|
for ( var i = 0; i < params.length; i++) {
|
||||||
var tmp = params[i].split("=");
|
var tmp = params[i].split("=");
|
||||||
result[tmp[0]] = unescape(tmp[1]);
|
result[tmp[0]] = unescape(tmp[1]);
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="page-wrapper">
|
<div id="page-wrapper">
|
||||||
<div id="flybar">
|
<div id="flybar">
|
||||||
<div id="nav-logo">1.2.1.RELEASE API</div>
|
<div id="nav-logo">1.3.0.RELEASE API</div>
|
||||||
<div class="flybar-nav">
|
<div class="flybar-nav">
|
||||||
<h2 class="flybar-button">Overview</h2>
|
<h2 class="flybar-button">Overview</h2>
|
||||||
<div class="flybar-menu-overview">
|
<div class="flybar-menu-overview">
|
||||||
@@ -1063,11 +1063,19 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><a href="#datatables">Data Table</a></td>
|
<td><a href="#datatables">Data Table</a></td>
|
||||||
<td>datatables</td>
|
<td>datatables</td>
|
||||||
<td></td>
|
<td><a href="#datatables_createTable">Create Data Table</a></td>
|
||||||
<td><a href="#datatables_list">List Data Tables</a></td>
|
<td><a href="#datatables_list">List Data Tables</a></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>datatables/{datatable}</td>
|
||||||
|
<td></td>
|
||||||
|
<td><a href="#datatables_getTable">Retrieve Data Table Details</a></td>
|
||||||
|
<td><a href="#datatables_updateTable">Update Data Table</a></td>
|
||||||
|
<td><a href="#datatables_deleteTable">Delete Data Table</a></td>
|
||||||
|
</tr>
|
||||||
<tr class="alt">
|
<tr class="alt">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>datatables/register/{datatable}/{apptable}</td>
|
<td>datatables/register/{datatable}/{apptable}</td>
|
||||||
@@ -3813,6 +3821,34 @@ GET https://DomainName/mifosng-provider/api/v1/groups
|
|||||||
<span>Example Values:</span> 2=Per month, 3=Per year
|
<span>Example Values:</span> 2=Per month, 3=Per year
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>graceOnPrincipalPayment</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional: Integer - represents the number of repayment periods that grace should apply to the principal component of a repayment period.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>graceOnInterestPayment</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional: Integer - represents the number of repayment periods that grace should apply to the interest component of a repayment period. Interest is still calculated but offset to later repayment periods.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>graceOnInterestCharged</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional: Integer - represents the number of repayment periods that should be interest-free.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>interestChargedFromDate</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional: Date - The date from with interest is to start being charged.</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr class=alt>
|
<tr class=alt>
|
||||||
<td>expectedDisbursementDate</td>
|
<td>expectedDisbursementDate</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -4841,6 +4877,15 @@ Request Body:
|
|||||||
expectedDisbursementDate, submittedOnDate, loanType</td>
|
expectedDisbursementDate, submittedOnDate, loanType</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
<br/>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Optional Fields</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="method-example">
|
<div class="method-example">
|
||||||
<code class="method-declaration">
|
<code class="method-declaration">
|
||||||
@@ -6959,6 +7004,28 @@ Request Body:
|
|||||||
</ol>
|
</ol>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>graceOnPrincipalPayment</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional: Integer - represents the number of repayment periods that grace should apply to the principal component of a repayment period.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>graceOnInterestPayment</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional: Integer - represents the number of repayment periods that grace should apply to the interest component of a repayment period. Interest is still calculated but offset to later repayment periods.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>graceOnInterestCharged</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional: Integer - represents the number of repayment periods that should be interest-free.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr class=alt>
|
<tr class=alt>
|
||||||
<td>accountingRule</td>
|
<td>accountingRule</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -7516,13 +7583,14 @@ GET https://DomainName/api/v1/loanproducts/{productId}
|
|||||||
<td>name, currencyCode, digitsAfterDecimal, principal,
|
<td>name, currencyCode, digitsAfterDecimal, principal,
|
||||||
numberOfRepayments, repaymentEvery, repaymentFrequencyType,
|
numberOfRepayments, repaymentEvery, repaymentFrequencyType,
|
||||||
interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType,
|
interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType,
|
||||||
interestCalculationPeriodType, transactionProcessingStrategyId, accountingRule</td>
|
interestCalculationPeriodType, transactionProcessingStrategyId,
|
||||||
|
accountingRule</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="matrixHeadingBG">
|
<tr class="matrixHeadingBG">
|
||||||
<td><div class="mifosXHeading2">Optional Fields</div></td>
|
<td><div class="mifosXHeading2">Optional Fields</div></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class=alt>
|
<tr class=alt>
|
||||||
<td>inArrearsTolerance, charges,paymentChannelToFundSourceMappings</td>
|
<td>inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, paymentChannelToFundSourceMappings</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr class="matrixHeadingBG">
|
<tr class="matrixHeadingBG">
|
||||||
@@ -9426,6 +9494,30 @@ Content-Type: application/json
|
|||||||
<tr>
|
<tr>
|
||||||
<td class=fielddesc>Credit account for creating accounting rule</td>
|
<td class=fielddesc>Credit account for creating accounting rule</td>
|
||||||
</tr>
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -9537,6 +9629,64 @@ Request Body:
|
|||||||
{
|
{
|
||||||
"officeId":1,
|
"officeId":1,
|
||||||
"resourceId":1
|
"resourceId":1
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST accountingrules
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"name": "fordocs",
|
||||||
|
"officeId": "1",
|
||||||
|
"debitTags": ["10","11"],
|
||||||
|
"allowMultipleDebitEntries": "true",
|
||||||
|
"creditTags": ["12","13"],
|
||||||
|
"allowMultipleCreditEntries": "true",
|
||||||
|
"description": "for api docs"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"officeId":1,
|
||||||
|
"resourceId":2
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST accountingrules
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"name": "test123",
|
||||||
|
"officeId": "1",
|
||||||
|
"debitAccountHead": "21",
|
||||||
|
"creditTags": ["12", "13"],
|
||||||
|
"allowMultipleCreditEntries": "false",
|
||||||
|
"description": "Employee salary"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"officeId":1,
|
||||||
|
"resourceId":3
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST accountingrules
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"name": "fordocstest",
|
||||||
|
"officeId": "1",
|
||||||
|
"debitTags": ["10", "11"],
|
||||||
|
"allowMultipleDebitEntries": "false",
|
||||||
|
"creditAccountHead": "9",
|
||||||
|
"description": "for api docs"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"officeId":1,
|
||||||
|
"resourceId":4
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
@@ -9734,6 +9884,23 @@ Content-Type: application/json
|
|||||||
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>lockinPeriodFrequency</i> to indicate the length of time that the savings account is 'locked in' and withdrawals are not allowed. 0=Days, 1=Weeks, 2=Months, 3=Years
|
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>lockinPeriodFrequency</i> to indicate the length of time that the savings account is 'locked in' and withdrawals are not allowed. 0=Days, 1=Weeks, 2=Months, 3=Years
|
||||||
e.g. 6 <b>Months</b></td></tr>
|
e.g. 6 <b>Months</b></td></tr>
|
||||||
|
|
||||||
|
<tr class=alt><td>withdrawalFeeAmount</td></tr>
|
||||||
|
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>withdrawalFeeType</i> to indicate the amount to be charged on the account for withdrawing. e.g. <b>5</b> Flat or <b>5</b> % of Amount</td></tr>
|
||||||
|
|
||||||
|
<tr class=alt><td>withdrawalFeeType</td></tr>
|
||||||
|
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>withdrawalFeeAmount</i> to indicate the amount to be charged on the account for withdrawing. 1=Flat, 2=% of Amount, e.g. 5 <b>Flat</b> or 5 <b>% of Amount</b></td></tr>
|
||||||
|
|
||||||
|
<tr class=alt><td>annualFeeAmount</td></tr>
|
||||||
|
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>annualFeeOnMonthDay</i> to indicate the amount to be charged on the account annually on a given day of month. e.g. <b>10</b> on 10 May. <i>monthDayFormat</i> indicates day and month formatting used e.g. "dd MMM" for 10 May
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class=alt><td>annualFeeOnMonthDay</td></tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc><b>Optional</b>: If provided, used along with <i>annualFeeAmount</i> and <i>monthDayFormat</i> to indicate the amount to be charged on the account annually on a given day of month. e.g. 10 on <b>10 May</b>. <i>monthDayFormat</i> indicates day and month formatting used e.g. "dd MMM" for 10 May
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr class=alt><td>accountingRule</td></tr>
|
<tr class=alt><td>accountingRule</td></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=fielddesc>Specifies if accounting is enabled for the particular
|
<td class=fielddesc>Specifies if accounting is enabled for the particular
|
||||||
@@ -10252,7 +10419,7 @@ Content-Type: application/json
|
|||||||
<br/>
|
<br/>
|
||||||
<table class=matrixHeading>
|
<table class=matrixHeading>
|
||||||
<tr class="matrixHeadingBG"><td><div class="mifosXHeading2">Optional Fields</div></td></tr>
|
<tr class="matrixHeadingBG"><td><div class="mifosXHeading2">Optional Fields</div></td></tr>
|
||||||
<tr class=alt><td>minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, paymentChannelToFundSourceMappings</tr>
|
<tr class=alt><td>minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay, monthDayFormat, paymentChannelToFundSourceMappings</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="method-example">
|
<div class="method-example">
|
||||||
@@ -10558,6 +10725,23 @@ Content-Type: application/json
|
|||||||
<tr class=alt><td>lockinPeriodFrequencyType</td></tr>
|
<tr class=alt><td>lockinPeriodFrequencyType</td></tr>
|
||||||
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>lockinPeriodFrequency</i> to indicate the length of time that the savings account is 'locked in' and withdrawals are not allowed. 0=Days, 1=Weeks, 2=Months, 3=Years
|
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>lockinPeriodFrequency</i> to indicate the length of time that the savings account is 'locked in' and withdrawals are not allowed. 0=Days, 1=Weeks, 2=Months, 3=Years
|
||||||
e.g. 6 <b>Months</b></td></tr>
|
e.g. 6 <b>Months</b></td></tr>
|
||||||
|
|
||||||
|
<tr class=alt><td>withdrawalFeeAmount</td></tr>
|
||||||
|
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>withdrawalFeeType</i> to indicate the amount to be charged on the account for withdrawing. e.g. <b>5</b> Flat or <b>5</b> % of Amount</td></tr>
|
||||||
|
|
||||||
|
<tr class=alt><td>withdrawalFeeType</td></tr>
|
||||||
|
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>withdrawalFeeAmount</i> to indicate the amount to be charged on the account for withdrawing. 1=Flat, 2=% of Amount, e.g. 5 <b>Flat</b> or 5 <b>% of Amount</b></td></tr>
|
||||||
|
|
||||||
|
<tr class=alt><td>annualFeeAmount</td></tr>
|
||||||
|
<tr><td class=fielddesc><b>Optional</b>: If provided, used along with <i>annualFeeOnMonthDay</i> to indicate the amount to be charged on the account annually on a given day of month. e.g. <b>10</b> on 10 May. <i>monthDayFormat</i> indicates day and month formatting used e.g. "dd MMM" for 10 May
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class=alt><td>annualFeeOnMonthDay</td></tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc><b>Optional</b>: If provided, used along with <i>annualFeeAmount</i> and <i>monthDayFormat</i> to indicate the amount to be charged on the account annually on a given day of month. e.g. 10 on <b>10 May</b>. <i>monthDayFormat</i> indicates day and month formatting used e.g. "dd MMM" for 10 May
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -10758,7 +10942,9 @@ Content-Type: application/json
|
|||||||
<br/>
|
<br/>
|
||||||
<table class=matrixHeading>
|
<table class=matrixHeading>
|
||||||
<tr class="matrixHeadingBG"><td><div class="mifosXHeading2">Inherited from Product (if not provided)</div></td></tr>
|
<tr class="matrixHeadingBG"><td><div class="mifosXHeading2">Inherited from Product (if not provided)</div></td></tr>
|
||||||
<tr class=alt><td>nominalAnnualInterestRate, interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType</td></tr>
|
<tr class=alt><td>nominalAnnualInterestRate, interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||||
|
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="method-example">
|
<div class="method-example">
|
||||||
@@ -11240,6 +11426,57 @@ No Request Body:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a id="datatables_createTable" name="datatables_createTable"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Create Data Table</h4>
|
||||||
|
<p>Create a new data table and registers it with the Mifos X Core application
|
||||||
|
table.</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/datatables
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST https://DomainName/api/v1/datatables
|
||||||
|
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"datatableName": "extra_client_details",
|
||||||
|
"apptableName": "m_client",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "Gender",
|
||||||
|
"type": "Dropdown",
|
||||||
|
"code": "Gender"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Some Decimal",
|
||||||
|
"type": "Decimal",
|
||||||
|
"mandatory": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Birth Date",
|
||||||
|
"type": "Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Question 2",
|
||||||
|
"type": "String",
|
||||||
|
"length": 100,
|
||||||
|
"mandatory": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceIdentifier": "extra_client_details"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a id="datatables_list" name="datatables_list"
|
<a id="datatables_list" name="datatables_list"
|
||||||
class="old-syle-anchor"> </a>
|
class="old-syle-anchor"> </a>
|
||||||
@@ -11267,25 +11504,191 @@ No Request Body:
|
|||||||
<code class="method-declaration">
|
<code class="method-declaration">
|
||||||
GET https://DomainName/api/v1/datatables
|
GET https://DomainName/api/v1/datatables
|
||||||
</code>
|
</code>
|
||||||
<code class="method-response">
|
<code class="method-response">
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"applicationTableName": "m_client",
|
"applicationTableName": "m_client",
|
||||||
"registeredTableName": "Additional Client Fields Data"
|
"registeredTableName": "extra_client_details",
|
||||||
},
|
"columnHeaderData": [
|
||||||
{
|
{
|
||||||
"applicationTableName": "m_client",
|
"columnName": "client_id",
|
||||||
"registeredTableName": "Extra Family Details Data"
|
"columnType": "bigint",
|
||||||
},
|
"columnLength": 0,
|
||||||
{
|
"columnDisplayType": "INTEGER",
|
||||||
"applicationTableName": "m_loan",
|
"isColumnNullable": false,
|
||||||
"registeredTableName": "Additional Loan Fields Data"
|
"isColumnPrimaryKey": true,
|
||||||
}
|
"columnValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnName": "Gender_cd_Question",
|
||||||
|
"columnType": "int",
|
||||||
|
"columnLength": 0,
|
||||||
|
"columnDisplayType": "INTEGER",
|
||||||
|
"isColumnNullable": true,
|
||||||
|
"isColumnPrimaryKey": false,
|
||||||
|
"columnValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnName": "Some Decimal",
|
||||||
|
"columnType": "decimal",
|
||||||
|
"columnLength": 0,
|
||||||
|
"columnDisplayType": "DECIMAL",
|
||||||
|
"isColumnNullable": true,
|
||||||
|
"isColumnPrimaryKey": false,
|
||||||
|
"columnValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnName": "Birth Date",
|
||||||
|
"columnType": "date",
|
||||||
|
"columnLength": 0,
|
||||||
|
"columnDisplayType": "DATE",
|
||||||
|
"isColumnNullable": true,
|
||||||
|
"isColumnPrimaryKey": false,
|
||||||
|
"columnValues": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a id="datatables_getTable" name="datatables_getTable"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Retrieve Data Table Details</h4>
|
||||||
|
<p>Lists a registered data table details and the Mifos X Core
|
||||||
|
application table they are registered to.</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
GET https://DomainName/api/v1/datatables/{datatable}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"applicationTableName": "m_client",
|
||||||
|
"registeredTableName": "extra_client_details",
|
||||||
|
"columnHeaderData": [
|
||||||
|
{
|
||||||
|
"columnName": "client_id",
|
||||||
|
"columnType": "bigint",
|
||||||
|
"columnLength": 0,
|
||||||
|
"columnDisplayType": "INTEGER",
|
||||||
|
"isColumnNullable": false,
|
||||||
|
"isColumnPrimaryKey": true,
|
||||||
|
"columnValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnName": "Gender_cd_Question",
|
||||||
|
"columnType": "int",
|
||||||
|
"columnLength": 0,
|
||||||
|
"columnDisplayType": "INTEGER",
|
||||||
|
"isColumnNullable": true,
|
||||||
|
"isColumnPrimaryKey": false,
|
||||||
|
"columnValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnName": "Some Decimal",
|
||||||
|
"columnType": "decimal",
|
||||||
|
"columnLength": 0,
|
||||||
|
"columnDisplayType": "DECIMAL",
|
||||||
|
"isColumnNullable": true,
|
||||||
|
"isColumnPrimaryKey": false,
|
||||||
|
"columnValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnName": "Birth Date",
|
||||||
|
"columnType": "date",
|
||||||
|
"columnLength": 0,
|
||||||
|
"columnDisplayType": "DATE",
|
||||||
|
"isColumnNullable": true,
|
||||||
|
"isColumnPrimaryKey": false,
|
||||||
|
"columnValues": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="datatables_updateTable" name="datatables_updateTable"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
PUT https://DomainName/api/v1/datatables/{datatables}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
PUT https://DomainName/api/v1/datatables/extra_client_details
|
||||||
|
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"apptableName": "m_client",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "Gender",
|
||||||
|
"type": "Dropdown",
|
||||||
|
"code": "Gender"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Some Decimal",
|
||||||
|
"type": "Decimal",
|
||||||
|
"mandatory": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Birth Date",
|
||||||
|
"type": "Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Question 2",
|
||||||
|
"type": "String",
|
||||||
|
"length": 100,
|
||||||
|
"mandatory": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceIdentifier": "extra_client_details"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="datatables_deleteTable" name="datatables_deleteTable"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<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
|
||||||
|
application table.</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
DELETE https://DomainName/api/v1/datatables/{datatables}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
DELETE https://DomainName/api/v1/datatables/extra_client_details
|
||||||
|
|
||||||
|
Content-Type: application/json
|
||||||
|
No Request Body
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceIdentifier": "extra_client_details"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<a id="datatables_register" name="datatables_register"
|
<a id="datatables_register" name="datatables_register"
|
||||||
class="old-syle-anchor"> </a>
|
class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
@@ -12720,8 +13123,7 @@ GET https://DomainName/api/v1/runreports/{reportName}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a id="authenticate_request" name="authenticate_request"
|
<a id="authenticate_request" name="authenticate_request" class="old-syle-anchor"> </a>
|
||||||
class="old-syle-anchor"> </a>
|
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
<h4>Verify authentication</h4>
|
<h4>Verify authentication</h4>
|
||||||
@@ -12736,24 +13138,60 @@ POST authentication?username=mifos&password=password
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
No Request Body
|
No Request Body
|
||||||
</code>
|
</code>
|
||||||
|
<p>Example response of autentication for user that is not linked with any staff.</p>
|
||||||
<code class="method-response">
|
<code class="method-response">
|
||||||
{
|
{
|
||||||
"username": "mifos",
|
"username": "mifos",
|
||||||
"userId": 1,
|
"userId": 1,
|
||||||
"base64EncodedAuthenticationKey": "bWlmb3M6cGFzc3dvcmQ=",
|
"base64EncodedAuthenticationKey": "bWlmb3M6cGFzc3dvcmQ=",
|
||||||
"authenticated": true,
|
"authenticated": true,
|
||||||
"roles": [
|
"officeId": 1,
|
||||||
{
|
"officeName": "Head Office",
|
||||||
"id": 1,
|
"roles": [
|
||||||
"name": "Super user",
|
{
|
||||||
"description": "This role provides all application permissions."
|
"id": 1,
|
||||||
}
|
"name": "Super user",
|
||||||
],
|
"description": "This role provides all application permissions."
|
||||||
"permissions": [
|
}
|
||||||
"ALL_FUNCTIONS"
|
],
|
||||||
]
|
"permissions": [
|
||||||
|
"ALL_FUNCTIONS"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
<p>Example response of autentication for user that is linked with a staff member and role.</p>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"username": "mifos",
|
||||||
|
"userId": 1,
|
||||||
|
"base64EncodedAuthenticationKey": "bWlmb3M6cGFzc3dvcmQ=",
|
||||||
|
"authenticated": true,
|
||||||
|
"officeId": 1,
|
||||||
|
"officeName": "Head Office",
|
||||||
|
"staffId": 1,
|
||||||
|
"staffDisplayName": "Director, Program",
|
||||||
|
"organisationalRole": {
|
||||||
|
"id": 100,
|
||||||
|
"code": "staffOrganisationalRoleType.programDirector",
|
||||||
|
"value": "Program Director"
|
||||||
|
},
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Super user",
|
||||||
|
"description": "This role provides all application permissions."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"ALL_FUNCTIONS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<code class="method-request">
|
<code class="method-request">
|
||||||
POST authentication?username=mifos&password=fail
|
POST authentication?username=mifos&password=fail
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
@@ -14534,6 +14972,68 @@ Request Body:
|
|||||||
{
|
{
|
||||||
"officeId":1,
|
"officeId":1,
|
||||||
"transactionId":"RS9MCISID4WK1ZM"
|
"transactionId":"RS9MCISID4WK1ZM"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/journalentries
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST journalentries
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"locale": "en",
|
||||||
|
"dateFormat": "dd MMMM yyyy",
|
||||||
|
"officeId": "1",
|
||||||
|
"transactionDate": "23 May 2013",
|
||||||
|
"referenceNumber": "547",
|
||||||
|
"comments": "asdf",
|
||||||
|
"accountingRule": "2",
|
||||||
|
"credits":[{"glAccountId":1,
|
||||||
|
"amount":5000},
|
||||||
|
{"glAccountId":2,
|
||||||
|
"amount":5000}
|
||||||
|
],
|
||||||
|
"debits":[{"glAccountId":3,
|
||||||
|
"amount":5000},
|
||||||
|
{"glAccountId":4,
|
||||||
|
"amount":5000}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"officeId":1,
|
||||||
|
"transactionId":"RS9MCISID4WK1ZM"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/journalentries
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST journalentries
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"locale": "en",
|
||||||
|
"dateFormat": "dd MMMM yyyy",
|
||||||
|
"officeId": "1",
|
||||||
|
"transactionDate": "23 May 2013",
|
||||||
|
"referenceNumber": "547",
|
||||||
|
"comments": "asdf",
|
||||||
|
"accountingRule": "3",
|
||||||
|
"credits":[{"glAccountId":1,
|
||||||
|
"amount":5000},
|
||||||
|
{"glAccountId":2,
|
||||||
|
"amount":5000}
|
||||||
|
],
|
||||||
|
"amount": "10000"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"officeId":1,
|
||||||
|
"transactionId":"RS9MCISID4WK1ZM"
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
@@ -15286,11 +15786,19 @@ GET https://DomainName/api/v1/search?query=000000111
|
|||||||
<tr class="alt">
|
<tr class="alt">
|
||||||
<td><a href="#datatables">Data Table</a></td>
|
<td><a href="#datatables">Data Table</a></td>
|
||||||
<td>datatables</td>
|
<td>datatables</td>
|
||||||
<td></td>
|
<td><a href="#datatables_createTable">Create Data Table</a></td>
|
||||||
<td><a href="#datatables_list">List Data Tables</a></td>
|
<td><a href="#datatables_list">List Data Tables</a></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>datatables/{datatable}</td>
|
||||||
|
<td></td>
|
||||||
|
<td><a href="#datatables_getTable">Retrieve Data Table Details</a></td>
|
||||||
|
<td><a href="#datatables_updateTable">Update Data Table</a></td>
|
||||||
|
<td><a href="#datatables_deleteTable">Delete Data Table</a></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>datatables/register/{datatable}/{apptable}</td>
|
<td>datatables/register/{datatable}/{apptable}</td>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
create table `acc_rule_tags` (
|
||||||
|
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`acc_rule_id` BIGINT(20) NOT NULL,
|
||||||
|
`tag_id` INT(11) NOT NULL,
|
||||||
|
`acc_type_enum` SMALLINT(5) NOT NULL,
|
||||||
|
primary key(`id`),
|
||||||
|
INDEX `FK_acc_accounting_rule_id` (`acc_rule_id`),
|
||||||
|
INDEX `FK_m_code_value_id` (`tag_id`),
|
||||||
|
CONSTRAINT `FK_acc_accounting_rule_id` FOREIGN KEY (`acc_rule_id`) REFERENCES `acc_accounting_rule` (`id`),
|
||||||
|
CONSTRAINT `FK_m_code_value_id` FOREIGN KEY (`tag_id`) REFERENCES `m_code_value` (`id`)
|
||||||
|
);
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||||
|
VALUES
|
||||||
|
('configuration', 'CREATE_DATATABLE', 'DATATABLE', 'CREATE', 0),
|
||||||
|
('configuration', 'CREATE_DATATABLE_CHECKER', 'DATATABLE', 'CREATE', 0),
|
||||||
|
('configuration', 'UPDATE_DATATABLE', 'DATATABLE', 'UPDATE', 0),
|
||||||
|
('configuration', 'UPDATE_DATATABLE_CHECKER', 'DATATABLE', 'UPDATE', 0),
|
||||||
|
('configuration', 'DELETE_DATATABLE', 'DATATABLE', 'DELETE', 0),
|
||||||
|
('configuration', 'DELETE_DATATABLE_CHECKER', 'DATATABLE', 'DELETE', 0);
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
ALTER TABLE `m_appuser`
|
||||||
|
ADD COLUMN `staff_id` BIGINT(20) NULL DEFAULT NULL AFTER `office_id` ;
|
||||||
|
|
||||||
|
ALTER TABLE m_appuser
|
||||||
|
ADD CONSTRAINT `fk_m_appuser_002`
|
||||||
|
FOREIGN KEY (`staff_id`)
|
||||||
|
REFERENCES m_staff (`id`)
|
||||||
|
ON DELETE NO ACTION
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
,ADD INDEX `fk_m_appuser_002x` (`staff_id` ASC);
|
||||||
|
|
||||||
|
ALTER TABLE `m_staff`
|
||||||
|
ADD COLUMN `organisational_role_enum` SMALLINT NULL DEFAULT NULL AFTER `external_id`,
|
||||||
|
ADD COLUMN `organisational_role_parent_staff_id` BIGINT(20) NULL DEFAULT NULL AFTER `organisational_role_enum`;
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
INSERT INTO c_configuration (name,enabled) VALUES ('amazon-S3',0);
|
||||||
|
|
||||||
|
/*New table for storing details of external services used*/
|
||||||
|
CREATE TABLE `c_external_service` (
|
||||||
|
`name` VARCHAR(150) NOT NULL,
|
||||||
|
`value` VARCHAR(250) NULL DEFAULT NULL,
|
||||||
|
UNIQUE INDEX `name` (`name`)
|
||||||
|
)
|
||||||
|
COLLATE='utf8_general_ci'
|
||||||
|
ENGINE=InnoDB;
|
||||||
|
|
||||||
|
INSERT INTO c_external_service (name) VALUES ('s3_bucket_name');
|
||||||
|
INSERT INTO c_external_service (name) VALUES ('s3_access_key');
|
||||||
|
INSERT INTO c_external_service (name) VALUES ('s3_secret_key');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*Image tables stores details of all images*/
|
||||||
|
CREATE TABLE IF NOT EXISTS `m_image`(
|
||||||
|
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`location` varchar(500),
|
||||||
|
`storage_type_enum` SMALLINT(5),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
)
|
||||||
|
COLLATE='utf8_general_ci'
|
||||||
|
ENGINE=InnoDB;
|
||||||
|
|
||||||
|
/*Client points to image table*/
|
||||||
|
ALTER TABLE `m_client`
|
||||||
|
ADD COLUMN `image_id` BIGINT(20) NULL DEFAULT NULL AFTER `display_name`,
|
||||||
|
ADD CONSTRAINT `FK_m_client_m_image` FOREIGN KEY (`image_id`) REFERENCES `m_image` (`id`);
|
||||||
|
|
||||||
|
/*Move existing image locations to new table*/
|
||||||
|
drop procedure if exists migrate_customer_image_data;
|
||||||
|
|
||||||
|
delimiter #
|
||||||
|
create procedure migrate_customer_image_data()
|
||||||
|
begin
|
||||||
|
DECLARE v_counter INT DEFAULT 0;
|
||||||
|
DECLARE num_of_clients INT DEFAULT 0;
|
||||||
|
DECLARE curr_image INT DEFAULT 0;
|
||||||
|
DECLARE prev_image INT DEFAULT 0;
|
||||||
|
SELECT COUNT(*) FROM m_client INTO num_of_clients;
|
||||||
|
SET @curr_client := 0;
|
||||||
|
while v_counter < num_of_clients do
|
||||||
|
SET @s = CONCAT('INSERT INTO m_image(`location`,`storage_type_enum`) select image_key, 1 FROM m_client where image_key is not null LIMIT ', v_counter , ', ', 1);
|
||||||
|
PREPARE stmt1 FROM @s;
|
||||||
|
EXECUTE stmt1;
|
||||||
|
DEALLOCATE PREPARE stmt1;
|
||||||
|
|
||||||
|
select ifnull(max(`id`),0) from m_image INTO curr_image;
|
||||||
|
SET @z = CONCAT('select id INTO @curr_client FROM m_client where image_key is not null limit ', v_counter , ', ', 1);
|
||||||
|
PREPARE stmt2 FROM @z;
|
||||||
|
EXECUTE stmt2;
|
||||||
|
|
||||||
|
IF (prev_image != curr_image) THEN UPDATE m_client set image_id = curr_image where id=@curr_client;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
set prev_image=curr_image;
|
||||||
|
set v_counter=v_counter+1;
|
||||||
|
end while;
|
||||||
|
delete from m_image where `location` is null;
|
||||||
|
end#
|
||||||
|
|
||||||
|
delimiter ;
|
||||||
|
call migrate_customer_image_data();
|
||||||
|
|
||||||
|
/*Now drop the procedure*/
|
||||||
|
drop procedure if exists migrate_customer_image_data;
|
||||||
|
|
||||||
|
/*Delete image key from client*/
|
||||||
|
ALTER TABLE `m_client` DROP COLUMN `image_key`;
|
||||||
|
|
||||||
|
|
||||||
|
/*Add storage type for m_document table and update existing documents to file storage*/
|
||||||
|
ALTER TABLE m_document ADD COLUMN storage_type_enum SMALLINT(5);
|
||||||
|
UPDATE m_document set storage_type_enum=1;
|
||||||
|
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE `m_loan_charge_paid_by` (
|
||||||
|
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`loan_transaction_id` BIGINT(20) NOT NULL,
|
||||||
|
`loan_charge_id` BIGINT(20) NOT NULL,
|
||||||
|
`amount` DECIMAL(19,6) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `FK__m_loan_transaction` (`loan_transaction_id`),
|
||||||
|
INDEX `FK__m_loan_charge` (`loan_charge_id`),
|
||||||
|
CONSTRAINT `FK__m_loan_charge` FOREIGN KEY (`loan_charge_id`) REFERENCES `m_loan_charge` (`id`),
|
||||||
|
CONSTRAINT `FK__m_loan_transaction` FOREIGN KEY (`loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`)
|
||||||
|
)
|
||||||
|
COLLATE='utf8_general_ci'
|
||||||
|
ENGINE=InnoDB;
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ALTER TABLE `m_product_loan`
|
||||||
|
ADD COLUMN `grace_on_principal_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `max_number_of_repayments`,
|
||||||
|
ADD COLUMN `grace_on_interest_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_principal_periods`,
|
||||||
|
ADD COLUMN `grace_interest_free_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_interest_periods`;
|
||||||
|
|
||||||
|
ALTER TABLE `m_loan`
|
||||||
|
ADD COLUMN `grace_on_principal_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `number_of_repayments`,
|
||||||
|
ADD COLUMN `grace_on_interest_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_principal_periods`,
|
||||||
|
ADD COLUMN `grace_interest_free_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_interest_periods`;
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE `m_loan_repayment_schedule`
|
||||||
|
ADD COLUMN `obligations_met_on_date` DATE NULL DEFAULT NULL AFTER `completed_derived`;
|
||||||
|
|
||||||
|
ALTER TABLE `m_loan_repayment_schedule`
|
||||||
|
CHANGE COLUMN `interest_waived_derived` `interest_waived_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `interest_writtenoff_derived` ;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
ALTER TABLE `acc_accounting_rule`
|
||||||
|
ADD COLUMN `allow_multiple_debits` TINYINT(1) NOT NULL DEFAULT '0' AFTER `debit_account_id`,
|
||||||
|
ADD COLUMN `allow_multiple_credits` TINYINT(1) NOT NULL DEFAULT '0' AFTER `credit_account_id`;
|
||||||
|
|
||||||
|
ALTER TABLE acc_rule_tags
|
||||||
|
ADD UNIQUE KEY `UNIQUE_ACCOUNT_RULE_TAGS` (`acc_rule_id`,`tag_id`,`acc_type_enum`);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE `m_loan_repayment_schedule`
|
||||||
|
ADD COLUMN `total_paid_in_advance_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `penalty_charges_waived_derived`,
|
||||||
|
ADD COLUMN `total_paid_late_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `total_paid_in_advance_derived`;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE `acc_product_mapping`
|
||||||
|
ADD COLUMN `charge_id` BIGINT(20) NULL DEFAULT NULL AFTER `payment_type`,
|
||||||
|
ADD CONSTRAINT `FK_acc_product_mapping_m_charge` FOREIGN KEY (`charge_id`) REFERENCES `m_charge` (`id`);
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
ALTER TABLE `ref_loan_transaction_processing_strategy`
|
||||||
|
DROP COLUMN `lastmodified_date` ,
|
||||||
|
DROP COLUMN `lastmodifiedby_id` ,
|
||||||
|
DROP COLUMN `created_date` ,
|
||||||
|
DROP COLUMN `createdby_id` ;
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `ref_loan_transaction_processing_strategy` (`id`, `code`, `name`) VALUES
|
||||||
|
(5,'principal-interest-penalties-fees-order-strategy', 'Principal Interest Penalties Fees Order');
|
||||||
|
|
||||||
|
INSERT INTO `ref_loan_transaction_processing_strategy` (`id`,`code`, `name`)
|
||||||
|
VALUES (6,'interest-principal-penalties-fees-order-strategy', 'Interest Principal Penalties Fees Order');
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `m_loan`
|
||||||
|
ADD COLUMN `total_overpaid_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `total_outstanding_derived`;
|
||||||
@@ -160,7 +160,8 @@ dependencies {
|
|||||||
[group: 'com.lowagie', name: 'itext-rtf', version: '2.1.7'],
|
[group: 'com.lowagie', name: 'itext-rtf', version: '2.1.7'],
|
||||||
[group: 'org.mnode.ical4j', name: 'ical4j', version: '1.0.4'],
|
[group: 'org.mnode.ical4j', name: 'ical4j', version: '1.0.4'],
|
||||||
[group: 'com.googlecode.flyway', name: 'flyway-maven-plugin', version: '2.1.1'],
|
[group: 'com.googlecode.flyway', name: 'flyway-maven-plugin', version: '2.1.1'],
|
||||||
[group: 'org.quartz-scheduler', name: 'quartz', version: '2.1.7']
|
[group: 'org.quartz-scheduler', name: 'quartz', version: '2.1.7'],
|
||||||
|
[group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.2.1']
|
||||||
)
|
)
|
||||||
|
|
||||||
testCompile 'junit:junit:4.11',
|
testCompile 'junit:junit:4.11',
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
releaseVersion=1.2.1.RELEASE
|
releaseVersion=1.3.0.RELEASE
|
||||||
+30
-15
@@ -9,10 +9,10 @@ import java.util.HashMap;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mifosplatform.integrationtests.common.ClientHelper;
|
import org.mifosplatform.integrationtests.common.ClientHelper;
|
||||||
|
import org.mifosplatform.integrationtests.common.Utils;
|
||||||
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
|
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
|
||||||
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
|
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
|
||||||
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
|
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
|
||||||
import org.mifosplatform.integrationtests.common.Utils;
|
|
||||||
|
|
||||||
import com.jayway.restassured.builder.RequestSpecBuilder;
|
import com.jayway.restassured.builder.RequestSpecBuilder;
|
||||||
import com.jayway.restassured.builder.ResponseSpecBuilder;
|
import com.jayway.restassured.builder.ResponseSpecBuilder;
|
||||||
@@ -30,6 +30,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
private ResponseSpecification responseSpec;
|
private ResponseSpecification responseSpec;
|
||||||
private RequestSpecification requestSpec;
|
private RequestSpecification requestSpec;
|
||||||
private LoanTransactionHelper loanTransactionHelper;
|
private LoanTransactionHelper loanTransactionHelper;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
Utils.initializeRESTAssured();
|
Utils.initializeRESTAssured();
|
||||||
@@ -40,7 +41,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkClientLoanCreateAndDisburseFlow() {
|
public void checkClientLoanCreateAndDisburseFlow() {
|
||||||
loanTransactionHelper = new LoanTransactionHelper(requestSpec,responseSpec);
|
loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
||||||
|
|
||||||
Integer clientID = ClientHelper.createClient(requestSpec, responseSpec);
|
Integer clientID = ClientHelper.createClient(requestSpec, responseSpec);
|
||||||
ClientHelper.verifyClientCreatedOnServer(requestSpec, responseSpec, clientID);
|
ClientHelper.verifyClientCreatedOnServer(requestSpec, responseSpec, clientID);
|
||||||
@@ -53,21 +54,35 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
private Integer createLoanProduct() {
|
private Integer createLoanProduct() {
|
||||||
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||||
String loanProductJSON = new LoanProductTestBuilder().withPrincipal("12,000.00").withNumberOfRepayments("4")
|
String loanProductJSON = new LoanProductTestBuilder() //
|
||||||
.withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("2")
|
.withPrincipal("12,000.00") //
|
||||||
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
|
.withNumberOfRepayments("4") //
|
||||||
.withinterestRatePerPeriod("1").build();
|
.withRepaymentAfterEvery("1") //
|
||||||
|
.withRepaymentTypeAsMonth() //
|
||||||
|
.withinterestRatePerPeriod("1") //
|
||||||
|
.withInterestRateFrequencyTypeAsMonths() //
|
||||||
|
.withAmortizationTypeAsEqualInstallments() //
|
||||||
|
.withInterestTypeAsDecliningBalance() //
|
||||||
|
.build();
|
||||||
return loanTransactionHelper.getLoanProductId(loanProductJSON);
|
return loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID) {
|
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID) {
|
||||||
System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||||
String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("12,000.00").withLoanTermFrequency("4")
|
String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
||||||
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("4").withRepaymentEveryAfter("1")
|
.withPrincipal("12,000.00") //
|
||||||
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2")
|
.withLoanTermFrequency("4") //
|
||||||
.withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
|
.withLoanTermFrequencyAsMonths() //
|
||||||
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate("20 September 2011")
|
.withNumberOfRepayments("4") //
|
||||||
.withSubmittedOnDate("20 September 2011").build(clientID.toString(), loanProductID.toString());
|
.withRepaymentEveryAfter("1") //
|
||||||
|
.withRepaymentFrequencyTypeAsMonths() //
|
||||||
|
.withInterestRatePerPeriod("2") //
|
||||||
|
.withAmortizationTypeAsEqualInstallments() //
|
||||||
|
.withInterestTypeAsDecliningBalance() //
|
||||||
|
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
|
||||||
|
.withExpectedDisbursementDate("20 September 2011") //
|
||||||
|
.withSubmittedOnDate("20 September 2011") //
|
||||||
|
.build(clientID.toString(), loanProductID.toString());
|
||||||
return loanTransactionHelper.getLoanId(loanApplicationJSON);
|
return loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,17 +96,17 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
assertEquals("Checking for Due Date for 2nd Month", new ArrayList<Integer>(Arrays.asList(2011, 11, 20)),
|
assertEquals("Checking for Due Date for 2nd Month", new ArrayList<Integer>(Arrays.asList(2011, 11, 20)),
|
||||||
loanSchedule.get(2).get("dueDate"));
|
loanSchedule.get(2).get("dueDate"));
|
||||||
assertEquals("Checking for Principal Due for 2nd Month", new Float("2969.72"), loanSchedule.get(2).get("principalDue"));
|
assertEquals("Checking for Principal Due for 2nd Month", new Float("2969.71"), loanSchedule.get(2).get("principalDue"));
|
||||||
assertEquals("Checking for Interest Due for 2nd Month", new Float("181.77"), loanSchedule.get(2).get("interestOriginalDue"));
|
assertEquals("Checking for Interest Due for 2nd Month", new Float("181.77"), loanSchedule.get(2).get("interestOriginalDue"));
|
||||||
|
|
||||||
assertEquals("Checking for Due Date for 3rd Month", new ArrayList<Integer>(Arrays.asList(2011, 12, 20)),
|
assertEquals("Checking for Due Date for 3rd Month", new ArrayList<Integer>(Arrays.asList(2011, 12, 20)),
|
||||||
loanSchedule.get(3).get("dueDate"));
|
loanSchedule.get(3).get("dueDate"));
|
||||||
assertEquals("Checking for Principal Due for 3rd Month", new Float("3029.11"), loanSchedule.get(3).get("principalDue"));
|
assertEquals("Checking for Principal Due for 3rd Month", new Float("3029.1"), loanSchedule.get(3).get("principalDue"));
|
||||||
assertEquals("Checking for Interest Due for 3rd Month", new Float("122.38"), loanSchedule.get(3).get("interestOriginalDue"));
|
assertEquals("Checking for Interest Due for 3rd Month", new Float("122.38"), loanSchedule.get(3).get("interestOriginalDue"));
|
||||||
|
|
||||||
assertEquals("Checking for Due Date for 4th Month", new ArrayList<Integer>(Arrays.asList(2012, 1, 20)),
|
assertEquals("Checking for Due Date for 4th Month", new ArrayList<Integer>(Arrays.asList(2012, 1, 20)),
|
||||||
loanSchedule.get(4).get("dueDate"));
|
loanSchedule.get(4).get("dueDate"));
|
||||||
assertEquals("Checking for Principal Due for 4th Month", new Float("3089.68"), loanSchedule.get(4).get("principalDue"));
|
assertEquals("Checking for Principal Due for 4th Month", new Float("3089.7"), loanSchedule.get(4).get("principalDue"));
|
||||||
assertEquals("Checking for Interest Due for 4th Month", new Float("61.79"), loanSchedule.get(4).get("interestOriginalDue"));
|
assertEquals("Checking for Interest Due for 4th Month", new Float("61.79"), loanSchedule.get(4).get("interestOriginalDue"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -31,7 +31,7 @@ public class LoanApplicationTestBuilder {
|
|||||||
private final String transactionProcessingID = MIFOS_STANDARD_STRATEGY;
|
private final String transactionProcessingID = MIFOS_STANDARD_STRATEGY;
|
||||||
private String expectedDisbursmentDate = "";
|
private String expectedDisbursmentDate = "";
|
||||||
private String submittedOnDate = "";
|
private String submittedOnDate = "";
|
||||||
private String loanType = "individual"; //default it to individual
|
private String loanType = "individual";
|
||||||
|
|
||||||
public String build(final String clientID, final String loanProductId) {
|
public String build(final String clientID, final String loanProductId) {
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ public class LoanApplicationTestBuilder {
|
|||||||
map.put("productId", loanProductId);
|
map.put("productId", loanProductId);
|
||||||
map.put("principal", principal);
|
map.put("principal", principal);
|
||||||
map.put("loanTermFrequency", loanTermFrequency);
|
map.put("loanTermFrequency", loanTermFrequency);
|
||||||
map.put("loanTermFrequencyType", loanTermFrequencyType); // FIXME - Should this be using
|
map.put("loanTermFrequencyType", loanTermFrequencyType);
|
||||||
map.put("numberOfRepayments", numberOfRepayment);
|
map.put("numberOfRepayments", numberOfRepayment);
|
||||||
map.put("repaymentEvery", repaymentPeriod);
|
map.put("repaymentEvery", repaymentPeriod);
|
||||||
map.put("repaymentFrequencyType", repaymentFrequencyType);
|
map.put("repaymentFrequencyType", repaymentFrequencyType);
|
||||||
@@ -53,7 +53,7 @@ public class LoanApplicationTestBuilder {
|
|||||||
map.put("transactionProcessingStrategyId", transactionProcessingID);
|
map.put("transactionProcessingStrategyId", transactionProcessingID);
|
||||||
map.put("expectedDisbursementDate", expectedDisbursmentDate);
|
map.put("expectedDisbursementDate", expectedDisbursmentDate);
|
||||||
map.put("submittedOnDate", submittedOnDate);
|
map.put("submittedOnDate", submittedOnDate);
|
||||||
map.put("loanType", "individual");
|
map.put("loanType", loanType);
|
||||||
return new Gson().toJson(map);
|
return new Gson().toJson(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -85,7 +85,8 @@ public class AccountingConstants {
|
|||||||
"incomeFromFeeAccountId"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccountId"), LOSSES_WRITTEN_OFF("writeOffAccountId"), INTEREST_RECEIVABLE(
|
"incomeFromFeeAccountId"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccountId"), LOSSES_WRITTEN_OFF("writeOffAccountId"), INTEREST_RECEIVABLE(
|
||||||
"receivableInterestAccountId"), FEES_RECEIVABLE("receivableFeeAccountId"), PENALTIES_RECEIVABLE(
|
"receivableInterestAccountId"), FEES_RECEIVABLE("receivableFeeAccountId"), PENALTIES_RECEIVABLE(
|
||||||
"receivablePenaltyAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelToFundSourceMappings"), PAYMENT_TYPE(
|
"receivablePenaltyAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelToFundSourceMappings"), PAYMENT_TYPE(
|
||||||
"paymentTypeId");
|
"paymentTypeId"), FEE_INCOME_ACCOUNT_MAPPING("feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING(
|
||||||
|
"penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID("incomeAccountId");
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
|
|||||||
+27
-15
@@ -50,9 +50,10 @@ import org.springframework.stereotype.Component;
|
|||||||
public class GLAccountsApiResource {
|
public class GLAccountsApiResource {
|
||||||
|
|
||||||
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "parentId", "glCode",
|
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "parentId", "glCode",
|
||||||
"disabled", "manualEntriesAllowed", "type", "usage", "description", "assetHeaderAccountOptions", "liabilityHeaderAccountOptions",
|
"disabled", "manualEntriesAllowed", "type", "usage", "description", "assetHeaderAccountOptions",
|
||||||
"equityHeaderAccountOptions", "incomeHeaderAccountOptions", "expenseHeaderAccountOptions", "nameDecorated", "tagId",
|
"liabilityHeaderAccountOptions", "equityHeaderAccountOptions", "incomeHeaderAccountOptions", "expenseHeaderAccountOptions",
|
||||||
"allowedAssetsTagOptions", "allowedLiabilitiesTagOptions", "allowedEquityTagOptions", "allowedIncomeTagOptions", "allowedExpensesTagOptions"));
|
"nameDecorated", "tagId", "allowedAssetsTagOptions", "allowedLiabilitiesTagOptions", "allowedEquityTagOptions",
|
||||||
|
"allowedIncomeTagOptions", "allowedExpensesTagOptions", "creditAccounts", "debitAccounts"));
|
||||||
|
|
||||||
private final String resourceNameForPermission = "GLACCOUNT";
|
private final String resourceNameForPermission = "GLACCOUNT";
|
||||||
|
|
||||||
@@ -68,7 +69,8 @@ public class GLAccountsApiResource {
|
|||||||
public GLAccountsApiResource(final PlatformSecurityContext context, final GLAccountReadPlatformService glAccountReadPlatformService,
|
public GLAccountsApiResource(final PlatformSecurityContext context, final GLAccountReadPlatformService glAccountReadPlatformService,
|
||||||
final DefaultToApiJsonSerializer<GLAccountData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
|
final DefaultToApiJsonSerializer<GLAccountData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||||
final AccountingDropdownReadPlatformService dropdownReadPlatformService, final CodeValueReadPlatformService codeValueReadPlatformService) {
|
final AccountingDropdownReadPlatformService dropdownReadPlatformService,
|
||||||
|
final CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||||
@@ -82,7 +84,7 @@ public class GLAccountsApiResource {
|
|||||||
@Path("template")
|
@Path("template")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String retrieveNewAccountDetails(@Context final UriInfo uriInfo, @QueryParam("type") final Integer type) {
|
public String retrieveNewAccountDetails(@Context final UriInfo uriInfo, @QueryParam("type") final Integer type) {
|
||||||
|
|
||||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
|
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
|
||||||
|
|
||||||
@@ -167,16 +169,26 @@ public class GLAccountsApiResource {
|
|||||||
private GLAccountData handleTemplate(final GLAccountData glAccountData) {
|
private GLAccountData handleTemplate(final GLAccountData glAccountData) {
|
||||||
final List<EnumOptionData> accountTypeOptions = this.dropdownReadPlatformService.retrieveGLAccountTypeOptions();
|
final List<EnumOptionData> accountTypeOptions = this.dropdownReadPlatformService.retrieveGLAccountTypeOptions();
|
||||||
final List<EnumOptionData> usageOptions = this.dropdownReadPlatformService.retrieveGLAccountUsageOptions();
|
final List<EnumOptionData> usageOptions = this.dropdownReadPlatformService.retrieveGLAccountUsageOptions();
|
||||||
final List<GLAccountData> assetHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService.retrieveAllEnabledHeaderGLAccounts(GLAccountType.ASSET));
|
final List<GLAccountData> assetHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
|
||||||
final List<GLAccountData> liabilityHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService.retrieveAllEnabledHeaderGLAccounts(GLAccountType.LIABILITY));
|
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.ASSET));
|
||||||
final List<GLAccountData> equityHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService.retrieveAllEnabledHeaderGLAccounts(GLAccountType.EQUITY));
|
final List<GLAccountData> liabilityHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
|
||||||
final List<GLAccountData> incomeHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService.retrieveAllEnabledHeaderGLAccounts(GLAccountType.INCOME));
|
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.LIABILITY));
|
||||||
final List<GLAccountData> expenseHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService.retrieveAllEnabledHeaderGLAccounts(GLAccountType.EXPENSE));
|
final List<GLAccountData> equityHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
|
||||||
final Collection<CodeValueData> allowedAssetsTagOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME);
|
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.EQUITY));
|
||||||
final Collection<CodeValueData> allowedLiabilitiesTagOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME);
|
final List<GLAccountData> incomeHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
|
||||||
final Collection<CodeValueData> allowedEquityTagOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME);
|
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.INCOME));
|
||||||
final Collection<CodeValueData> allowedIncomeTagOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.INCOME_TAG_OPTION_CODE_NAME);
|
final List<GLAccountData> expenseHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
|
||||||
final Collection<CodeValueData> allowedExpensesTagOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME);
|
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.EXPENSE));
|
||||||
|
final Collection<CodeValueData> allowedAssetsTagOptions = this.codeValueReadPlatformService
|
||||||
|
.retrieveCodeValuesByCode(AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME);
|
||||||
|
final Collection<CodeValueData> allowedLiabilitiesTagOptions = this.codeValueReadPlatformService
|
||||||
|
.retrieveCodeValuesByCode(AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME);
|
||||||
|
final Collection<CodeValueData> allowedEquityTagOptions = this.codeValueReadPlatformService
|
||||||
|
.retrieveCodeValuesByCode(AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME);
|
||||||
|
final Collection<CodeValueData> allowedIncomeTagOptions = this.codeValueReadPlatformService
|
||||||
|
.retrieveCodeValuesByCode(AccountingConstants.INCOME_TAG_OPTION_CODE_NAME);
|
||||||
|
final Collection<CodeValueData> allowedExpensesTagOptions = this.codeValueReadPlatformService
|
||||||
|
.retrieveCodeValuesByCode(AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME);
|
||||||
|
|
||||||
return new GLAccountData(glAccountData, accountTypeOptions, usageOptions, assetHeaderAccountOptions, liabilityHeaderAccountOptions,
|
return new GLAccountData(glAccountData, accountTypeOptions, usageOptions, assetHeaderAccountOptions, liabilityHeaderAccountOptions,
|
||||||
equityHeaderAccountOptions, incomeHeaderAccountOptions, expenseHeaderAccountOptions, allowedAssetsTagOptions,
|
equityHeaderAccountOptions, incomeHeaderAccountOptions, expenseHeaderAccountOptions, allowedAssetsTagOptions,
|
||||||
|
|||||||
+20
-21
@@ -71,9 +71,9 @@ public class GLAccountData {
|
|||||||
this.expenseHeaderAccountOptions = null;
|
this.expenseHeaderAccountOptions = null;
|
||||||
this.allowedAssetsTagOptions = null;
|
this.allowedAssetsTagOptions = null;
|
||||||
this.allowedLiabilitiesTagOptions = null;
|
this.allowedLiabilitiesTagOptions = null;
|
||||||
this.allowedEquityTagOptions=null;
|
this.allowedEquityTagOptions = null;
|
||||||
this.allowedIncomeTagOptions=null;
|
this.allowedIncomeTagOptions = null;
|
||||||
this.allowedExpensesTagOptions=null;
|
this.allowedExpensesTagOptions = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GLAccountData(final GLAccountData accountData, final List<EnumOptionData> accountTypeOptions,
|
public GLAccountData(final GLAccountData accountData, final List<EnumOptionData> accountTypeOptions,
|
||||||
@@ -116,10 +116,11 @@ public class GLAccountData {
|
|||||||
final boolean disabled = false;
|
final boolean disabled = false;
|
||||||
final boolean manualEntriesAllowed = true;
|
final boolean manualEntriesAllowed = true;
|
||||||
final EnumOptionData type;
|
final EnumOptionData type;
|
||||||
if(glAccType != null && glAccType >= GLAccountType.getMinValue() && glAccType <= GLAccountType.getMaxValue())
|
if (glAccType != null && glAccType >= GLAccountType.getMinValue() && glAccType <= GLAccountType.getMaxValue()) {
|
||||||
type = AccountingEnumerations.gLAccountType(glAccType);
|
type = AccountingEnumerations.gLAccountType(glAccType);
|
||||||
else
|
} else {
|
||||||
type = AccountingEnumerations.gLAccountType(GLAccountType.ASSET);
|
type = AccountingEnumerations.gLAccountType(GLAccountType.ASSET);
|
||||||
|
}
|
||||||
final EnumOptionData usage = AccountingEnumerations.gLAccountUsage(GLAccountUsage.DETAIL);
|
final EnumOptionData usage = AccountingEnumerations.gLAccountUsage(GLAccountUsage.DETAIL);
|
||||||
final String description = null;
|
final String description = null;
|
||||||
final String nameDecorated = null;
|
final String nameDecorated = null;
|
||||||
@@ -128,7 +129,7 @@ public class GLAccountData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GLAccountData(final Long id, final String name, final String glCode) {
|
public GLAccountData(final Long id, final String name, final String glCode) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.parentId = null;
|
this.parentId = null;
|
||||||
this.glCode = glCode;
|
this.glCode = glCode;
|
||||||
@@ -148,23 +149,21 @@ public class GLAccountData {
|
|||||||
this.expenseHeaderAccountOptions = null;
|
this.expenseHeaderAccountOptions = null;
|
||||||
this.allowedAssetsTagOptions = null;
|
this.allowedAssetsTagOptions = null;
|
||||||
this.allowedLiabilitiesTagOptions = null;
|
this.allowedLiabilitiesTagOptions = null;
|
||||||
this.allowedEquityTagOptions=null;
|
this.allowedEquityTagOptions = null;
|
||||||
this.allowedIncomeTagOptions=null;
|
this.allowedIncomeTagOptions = null;
|
||||||
this.allowedExpensesTagOptions=null;
|
this.allowedExpensesTagOptions = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGlCode() {
|
|
||||||
return this.glCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGlCode() {
|
||||||
|
return this.glCode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.accounting.glaccount.data;
|
||||||
|
|
||||||
|
public class GLAccountDataForLookup {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final String name;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final String glCode;
|
||||||
|
|
||||||
|
public GLAccountDataForLookup(final Long id, final String name, final String glCode) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.glCode = glCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+9
-3
@@ -1,11 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.glaccount.exception;
|
package org.mifosplatform.accounting.glaccount.exception;
|
||||||
|
|
||||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
public class InvalidParentGLAccountHeadException extends AbstractPlatformDomainRuleException {
|
public class InvalidParentGLAccountHeadException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
public InvalidParentGLAccountHeadException(Long glAccountId, Long parentId) {
|
public InvalidParentGLAccountHeadException(final Long glAccountId, final Long parentId) {
|
||||||
super("error.msg.glaccount.id.and.parentid.must.not.same", "parentId:"+parentId+", id"+glAccountId+" should not be same", glAccountId, parentId);
|
super("error.msg.glaccount.id.and.parentid.must.not.same", "parentId:" + parentId + ", id" + glAccountId + " should not be same",
|
||||||
}
|
glAccountId, parentId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -8,6 +8,7 @@ package org.mifosplatform.accounting.glaccount.service;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
||||||
|
import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
|
||||||
|
|
||||||
public interface GLAccountReadPlatformService {
|
public interface GLAccountReadPlatformService {
|
||||||
@@ -24,4 +25,6 @@ public interface GLAccountReadPlatformService {
|
|||||||
List<GLAccountData> retrieveAllEnabledHeaderGLAccounts(GLAccountType accountType);
|
List<GLAccountData> retrieveAllEnabledHeaderGLAccounts(GLAccountType accountType);
|
||||||
|
|
||||||
GLAccountData retrieveNewGLAccountDetails(final Integer type);
|
GLAccountData retrieveNewGLAccountDetails(final Integer type);
|
||||||
|
|
||||||
|
List<GLAccountDataForLookup> retrieveAccountsByTagId(final Long ruleId, final Integer transactionType);
|
||||||
}
|
}
|
||||||
+25
-1
@@ -13,6 +13,7 @@ import java.util.List;
|
|||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.accounting.common.AccountingEnumerations;
|
import org.mifosplatform.accounting.common.AccountingEnumerations;
|
||||||
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
||||||
|
import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
|
||||||
import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidClassificationException;
|
import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidClassificationException;
|
||||||
@@ -65,7 +66,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
|
|||||||
final String nameDecorated = rs.getString("nameDecorated");
|
final String nameDecorated = rs.getString("nameDecorated");
|
||||||
final Long codeId = rs.wasNull() ? null : rs.getLong("codeId");
|
final Long codeId = rs.wasNull() ? null : rs.getLong("codeId");
|
||||||
final String codeValue = rs.getString("codeValue");
|
final String codeValue = rs.getString("codeValue");
|
||||||
CodeValueData tagId = CodeValueData.instance(codeId, codeValue);
|
final CodeValueData tagId = CodeValueData.instance(codeId, codeValue);
|
||||||
return new GLAccountData(id, name, parentId, glCode, disabled, manualEntriesAllowed, accountType, usage, description,
|
return new GLAccountData(id, name, parentId, glCode, disabled, manualEntriesAllowed, accountType, usage, description,
|
||||||
nameDecorated, tagId);
|
nameDecorated, tagId);
|
||||||
}
|
}
|
||||||
@@ -202,4 +203,27 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
|
|||||||
public List<GLAccountData> retrieveAllEnabledHeaderGLAccounts(final GLAccountType accountType) {
|
public List<GLAccountData> retrieveAllEnabledHeaderGLAccounts(final GLAccountType accountType) {
|
||||||
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.HEADER.getValue(), null, false);
|
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.HEADER.getValue(), null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GLAccountDataForLookup> retrieveAccountsByTagId(final Long ruleId, final Integer transactionType) {
|
||||||
|
final GLAccountDataLookUpMapper mapper = new GLAccountDataLookUpMapper();
|
||||||
|
final String sql = "Select " + mapper.schema() + " where rule.id=? and tags.acc_type_enum=?";
|
||||||
|
return this.jdbcTemplate.query(sql, mapper, new Object[] { ruleId, transactionType });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class GLAccountDataLookUpMapper implements RowMapper<GLAccountDataForLookup> {
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
return " gl.id as id, gl.name as name, gl.gl_code as glCode from acc_accounting_rule rule join acc_rule_tags tags on tags.acc_rule_id = rule.id join acc_gl_account gl on gl.tag_id=tags.tag_id";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GLAccountDataForLookup mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
|
final Long id = JdbcSupport.getLong(rs, "id");
|
||||||
|
final String name = rs.getString("name");
|
||||||
|
final String glCode = rs.getString("glCode");
|
||||||
|
return new GLAccountDataForLookup(id, name, glCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+30
-34
@@ -30,8 +30,8 @@ public class JournalEntryCommand {
|
|||||||
private final SingleDebitOrCreditEntryCommand[] debits;
|
private final SingleDebitOrCreditEntryCommand[] debits;
|
||||||
|
|
||||||
public JournalEntryCommand(final Long officeId, final LocalDate transactionDate, final String comments,
|
public JournalEntryCommand(final Long officeId, final LocalDate transactionDate, final String comments,
|
||||||
final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits,
|
final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits, final String referenceNumber,
|
||||||
final String referenceNumber, final Long accountingRuleId, final BigDecimal amount) {
|
final Long accountingRuleId, final BigDecimal amount) {
|
||||||
this.officeId = officeId;
|
this.officeId = officeId;
|
||||||
this.transactionDate = transactionDate;
|
this.transactionDate = transactionDate;
|
||||||
this.comments = comments;
|
this.comments = comments;
|
||||||
@@ -58,41 +58,37 @@ public class JournalEntryCommand {
|
|||||||
|
|
||||||
baseDataValidator.reset().parameter("accountingRule").value(this.accountingRuleId).ignoreIfNull().longGreaterThanZero();
|
baseDataValidator.reset().parameter("accountingRule").value(this.accountingRuleId).ignoreIfNull().longGreaterThanZero();
|
||||||
|
|
||||||
if (this.accountingRuleId == null) {
|
baseDataValidator.reset().parameter("credits").value(this.credits).ignoreIfNull();
|
||||||
|
|
||||||
baseDataValidator.reset().parameter("credits").value(this.credits).notNull();
|
baseDataValidator.reset().parameter("debits").value(this.debits).ignoreIfNull();
|
||||||
|
|
||||||
baseDataValidator.reset().parameter("debits").value(this.debits).notNull();
|
// validation for credit array elements
|
||||||
|
if (this.credits != null) {
|
||||||
// validation for credit array elements
|
if (this.credits.length == 0) {
|
||||||
if (this.credits != null) {
|
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
|
||||||
if (this.credits.length == 0) {
|
} else {
|
||||||
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
|
int i = 0;
|
||||||
} else {
|
for (final SingleDebitOrCreditEntryCommand credit : this.credits) {
|
||||||
int i = 0;
|
validateSingleDebitOrCredit(baseDataValidator, "credits", i, credit);
|
||||||
for (final SingleDebitOrCreditEntryCommand credit : this.credits) {
|
i++;
|
||||||
validateSingleDebitOrCredit(baseDataValidator, "credits", i, credit);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validation for debit array elements
|
|
||||||
if (this.debits != null) {
|
|
||||||
if (this.debits.length == 0) {
|
|
||||||
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
|
|
||||||
} else {
|
|
||||||
int i = 0;
|
|
||||||
for (final SingleDebitOrCreditEntryCommand debit : this.debits) {
|
|
||||||
validateSingleDebitOrCredit(baseDataValidator, "debits", i, debit);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
baseDataValidator.reset().parameter("amount").value(this.amount).notNull().zeroOrPositiveAmount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validation for debit array elements
|
||||||
|
if (this.debits != null) {
|
||||||
|
if (this.debits.length == 0) {
|
||||||
|
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
|
||||||
|
} else {
|
||||||
|
int i = 0;
|
||||||
|
for (final SingleDebitOrCreditEntryCommand debit : this.debits) {
|
||||||
|
validateSingleDebitOrCredit(baseDataValidator, "debits", i, debit);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
baseDataValidator.reset().parameter("amount").value(this.amount).ignoreIfNull().zeroOrPositiveAmount();
|
||||||
|
|
||||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||||
"Validation errors exist.", dataValidationErrors); }
|
"Validation errors exist.", dataValidationErrors); }
|
||||||
}
|
}
|
||||||
@@ -134,8 +130,8 @@ public class JournalEntryCommand {
|
|||||||
return this.referenceNumber;
|
return this.referenceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getAccountingRuleId() {
|
public Long getAccountingRuleId() {
|
||||||
return this.accountingRuleId;
|
return this.accountingRuleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package org.mifosplatform.accounting.journalentry.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public class ChargePaymentDTO {
|
||||||
|
|
||||||
|
private final Long chargeId;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
private final Long loanChargeId;
|
||||||
|
|
||||||
|
public ChargePaymentDTO(Long chargeId, Long loanChargeId, BigDecimal amount) {
|
||||||
|
this.chargeId = chargeId;
|
||||||
|
this.amount = amount;
|
||||||
|
this.loanChargeId = loanChargeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getChargeId() {
|
||||||
|
return this.chargeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getAmount() {
|
||||||
|
return this.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getLoanChargeId() {
|
||||||
|
return this.loanChargeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+17
-1
@@ -7,6 +7,7 @@ package org.mifosplatform.accounting.journalentry.data;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||||
|
|
||||||
@@ -28,9 +29,14 @@ public class LoanTransactionDTO {
|
|||||||
/*** Boolean values determines if the transaction is reversed ***/
|
/*** Boolean values determines if the transaction is reversed ***/
|
||||||
private final boolean reversed;
|
private final boolean reversed;
|
||||||
|
|
||||||
|
/** Breakdowns of fees and penalties this Transaction pays **/
|
||||||
|
private final List<ChargePaymentDTO> penaltyPayments;
|
||||||
|
private final List<ChargePaymentDTO> feePayments;
|
||||||
|
|
||||||
public LoanTransactionDTO(final Long paymentTypeId, final String transactionId, final Date transactionDate,
|
public LoanTransactionDTO(final Long paymentTypeId, final String transactionId, final Date transactionDate,
|
||||||
final LoanTransactionEnumData transactionType, final BigDecimal amount, final BigDecimal principal, final BigDecimal interest,
|
final LoanTransactionEnumData transactionType, final BigDecimal amount, final BigDecimal principal, final BigDecimal interest,
|
||||||
final BigDecimal fees, final BigDecimal penalties, final boolean reversed) {
|
final BigDecimal fees, final BigDecimal penalties, final boolean reversed, final List<ChargePaymentDTO> feePayments,
|
||||||
|
final List<ChargePaymentDTO> penaltyPayments) {
|
||||||
this.paymentTypeId = paymentTypeId;
|
this.paymentTypeId = paymentTypeId;
|
||||||
this.transactionId = transactionId;
|
this.transactionId = transactionId;
|
||||||
this.transactionDate = transactionDate;
|
this.transactionDate = transactionDate;
|
||||||
@@ -41,6 +47,8 @@ public class LoanTransactionDTO {
|
|||||||
this.penalties = penalties;
|
this.penalties = penalties;
|
||||||
this.reversed = reversed;
|
this.reversed = reversed;
|
||||||
this.transactionType = transactionType;
|
this.transactionType = transactionType;
|
||||||
|
this.feePayments = feePayments;
|
||||||
|
this.penaltyPayments = penaltyPayments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTransactionId() {
|
public String getTransactionId() {
|
||||||
@@ -83,4 +91,12 @@ public class LoanTransactionDTO {
|
|||||||
return this.paymentTypeId;
|
return this.paymentTypeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ChargePaymentDTO> getPenaltyPayments() {
|
||||||
|
return this.penaltyPayments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ChargePaymentDTO> getFeePayments() {
|
||||||
|
return this.feePayments;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -139,8 +139,8 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
this.reversed = reversed;
|
this.reversed = reversed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getReferenceNumber() {
|
public String getReferenceNumber() {
|
||||||
return this.referenceNumber;
|
return this.referenceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+8
@@ -45,4 +45,12 @@ public enum JournalEntryType {
|
|||||||
return name().toString();
|
return name().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDebitType() {
|
||||||
|
return this.value.equals(JournalEntryType.DEBIT.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCreditType() {
|
||||||
|
return this.value.equals(JournalEntryType.CREDIT.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,7 @@ public class JournalEntryInvalidException extends AbstractPlatformDomainRuleExce
|
|||||||
|
|
||||||
/*** enum of reasons for invalid Journal Entry **/
|
/*** enum of reasons for invalid Journal Entry **/
|
||||||
public static enum GL_JOURNAL_ENTRY_INVALID_REASON {
|
public static enum GL_JOURNAL_ENTRY_INVALID_REASON {
|
||||||
FUTURE_DATE, ACCOUNTING_CLOSED, NO_DEBITS_OR_CREDITS, DEBIT_CREDIT_SUM_MISMATCH, DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, GL_ACCOUNT_DISABLED, GL_ACCOUNT_MANUAL_ENTRIES_NOT_PERMITTED;
|
FUTURE_DATE, ACCOUNTING_CLOSED, NO_DEBITS_OR_CREDITS, DEBIT_CREDIT_SUM_MISMATCH_WITH_AMOUNT, DEBIT_CREDIT_SUM_MISMATCH, DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, GL_ACCOUNT_DISABLED, GL_ACCOUNT_MANUAL_ENTRIES_NOT_PERMITTED;
|
||||||
|
|
||||||
public String errorMessage() {
|
public String errorMessage() {
|
||||||
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) {
|
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) {
|
||||||
@@ -25,6 +25,8 @@ public class JournalEntryInvalidException extends AbstractPlatformDomainRuleExce
|
|||||||
return "Journal entry cannot be made prior to last account closing date for the branch";
|
return "Journal entry cannot be made prior to last account closing date for the branch";
|
||||||
} else if (name().toString().equalsIgnoreCase("NO_DEBITS_OR_CREDITS")) {
|
} else if (name().toString().equalsIgnoreCase("NO_DEBITS_OR_CREDITS")) {
|
||||||
return "Journal Entry must have atleast one Debit and one Credit";
|
return "Journal Entry must have atleast one Debit and one Credit";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("DEBIT_CREDIT_SUM_MISMATCH_WITH_AMOUNT")) {
|
||||||
|
return "Sum of All Debits OR Credits must equal the Amount for a Journal Entry";
|
||||||
} else if (name().toString().equalsIgnoreCase("DEBIT_CREDIT_SUM_MISMATCH")) {
|
} else if (name().toString().equalsIgnoreCase("DEBIT_CREDIT_SUM_MISMATCH")) {
|
||||||
return "Sum of All Debits must equal the sum of all Credits for a Journal Entry";
|
return "Sum of All Debits must equal the sum of all Credits for a Journal Entry";
|
||||||
} else if (name().toString().equalsIgnoreCase("DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY")) {
|
} else if (name().toString().equalsIgnoreCase("DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY")) {
|
||||||
|
|||||||
+90
-1
@@ -8,6 +8,7 @@ package org.mifosplatform.accounting.journalentry.service;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_
|
|||||||
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
|
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
|
||||||
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SAVINGS;
|
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SAVINGS;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.mifosplatform.accounting.journalentry.data.ChargePaymentDTO;
|
||||||
import org.mifosplatform.accounting.journalentry.data.LoanDTO;
|
import org.mifosplatform.accounting.journalentry.data.LoanDTO;
|
||||||
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
|
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
|
||||||
import org.mifosplatform.accounting.journalentry.data.SavingsDTO;
|
import org.mifosplatform.accounting.journalentry.data.SavingsDTO;
|
||||||
@@ -30,6 +32,7 @@ import org.mifosplatform.accounting.journalentry.exception.JournalEntryInvalidEx
|
|||||||
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
|
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
|
||||||
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
|
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
|
||||||
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
|
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
import org.mifosplatform.organisation.office.domain.Office;
|
import org.mifosplatform.organisation.office.domain.Office;
|
||||||
import org.mifosplatform.organisation.office.domain.OfficeRepository;
|
import org.mifosplatform.organisation.office.domain.OfficeRepository;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||||
@@ -77,8 +80,28 @@ public class AccountingProcessorHelper {
|
|||||||
final boolean reversed = (Boolean) map.get("reversed");
|
final boolean reversed = (Boolean) map.get("reversed");
|
||||||
final Long paymentTypeId = (Long) map.get("paymentTypeId");
|
final Long paymentTypeId = (Long) map.get("paymentTypeId");
|
||||||
|
|
||||||
|
List<ChargePaymentDTO> feePayments = new ArrayList<ChargePaymentDTO>();
|
||||||
|
List<ChargePaymentDTO> penaltyPayments = new ArrayList<ChargePaymentDTO>();
|
||||||
|
// extract charge payment details (if exists)
|
||||||
|
if (map.containsKey("loanChargesPaid")) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<Map<String, Object>> loanChargesPaidData = (List<Map<String, Object>>) map.get("loanChargesPaid");
|
||||||
|
for (final Map<String, Object> loanChargePaid : loanChargesPaidData) {
|
||||||
|
final Long chargeId = (Long) loanChargePaid.get("chargeId");
|
||||||
|
final Long loanChargeId = (Long) loanChargePaid.get("chargeId");
|
||||||
|
final boolean isPenalty = (Boolean) loanChargePaid.get("isPenalty");
|
||||||
|
final BigDecimal chargeAmountPaid = (BigDecimal) loanChargePaid.get("amount");
|
||||||
|
ChargePaymentDTO chargePaymentDTO = new ChargePaymentDTO(chargeId, loanChargeId, chargeAmountPaid);
|
||||||
|
if (isPenalty) {
|
||||||
|
penaltyPayments.add(chargePaymentDTO);
|
||||||
|
} else {
|
||||||
|
feePayments.add(chargePaymentDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final LoanTransactionDTO transaction = new LoanTransactionDTO(paymentTypeId, transactionId, transactionDate, transactionType,
|
final LoanTransactionDTO transaction = new LoanTransactionDTO(paymentTypeId, transactionId, transactionDate, transactionType,
|
||||||
amount, principal, interest, fees, penalties, reversed);
|
amount, principal, interest, fees, penalties, reversed, feePayments, penaltyPayments);
|
||||||
|
|
||||||
newLoanTransactions.add(transaction);
|
newLoanTransactions.add(transaction);
|
||||||
|
|
||||||
@@ -304,6 +327,48 @@ public class AccountingProcessorHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void createCreditJournalEntryOrReversalForLoanCharges(final Office office, final int accountMappingTypeId,
|
||||||
|
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate,
|
||||||
|
final BigDecimal totalAmount, final Boolean isReversal, final List<ChargePaymentDTO> chargePaymentDTOs) {
|
||||||
|
/***
|
||||||
|
* Map to track each account and the net credit to be made for a
|
||||||
|
* particular account
|
||||||
|
***/
|
||||||
|
Map<GLAccount, BigDecimal> creditDetailsMap = new LinkedHashMap<GLAccount, BigDecimal>();
|
||||||
|
for (ChargePaymentDTO chargePaymentDTO : chargePaymentDTOs) {
|
||||||
|
Long chargeId = chargePaymentDTO.getChargeId();
|
||||||
|
GLAccount chargeSpecificAccount = getLinkedGLAccountForLoanCharges(loanProductId, accountMappingTypeId, chargeId);
|
||||||
|
BigDecimal chargeSpecificAmount = chargePaymentDTO.getAmount();
|
||||||
|
|
||||||
|
// adjust net credit amount if the account is already present in the
|
||||||
|
// map
|
||||||
|
if (creditDetailsMap.containsKey(chargeSpecificAccount)) {
|
||||||
|
BigDecimal existingAmount = creditDetailsMap.get(chargeSpecificAccount);
|
||||||
|
chargeSpecificAmount = chargeSpecificAmount.add(existingAmount);
|
||||||
|
}
|
||||||
|
creditDetailsMap.put(chargeSpecificAccount, chargeSpecificAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal totalCreditedAmount = BigDecimal.ZERO;
|
||||||
|
for (Map.Entry<GLAccount, BigDecimal> entry : creditDetailsMap.entrySet()) {
|
||||||
|
GLAccount account = entry.getKey();
|
||||||
|
BigDecimal amount = entry.getValue();
|
||||||
|
totalCreditedAmount = totalCreditedAmount.add(amount);
|
||||||
|
if (isReversal) {
|
||||||
|
createDebitJournalEntryForLoan(office, account, loanId, transactionId, transactionDate, amount);
|
||||||
|
} else {
|
||||||
|
createCreditJournalEntryForLoan(office, account, loanId, transactionId, transactionDate, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Vishwas Temporary validation to be removed before moving to
|
||||||
|
// release branch
|
||||||
|
if (totalAmount.compareTo(totalCreditedAmount) != 0) { throw new PlatformDataIntegrityException(
|
||||||
|
"Meltdown in advanced accounting...sum of all charges is not equal to the fee charge for a transaction",
|
||||||
|
"Meltdown in advanced accounting...sum of all charges is not equal to the fee charge for a transaction",
|
||||||
|
totalCreditedAmount, totalAmount); }
|
||||||
|
}
|
||||||
|
|
||||||
private void createCreditJournalEntryOrReversalForLoan(final Office office, final int accountMappingTypeId, final Long loanProductId,
|
private void createCreditJournalEntryOrReversalForLoan(final Office office, final int accountMappingTypeId, final Long loanProductId,
|
||||||
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
|
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
|
||||||
final Boolean isReversal) {
|
final Boolean isReversal) {
|
||||||
@@ -363,6 +428,30 @@ public class AccountingProcessorHelper {
|
|||||||
accountMapping = paymentChannelSpecificAccountMapping;
|
accountMapping = paymentChannelSpecificAccountMapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return accountMapping.getGlAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private GLAccount getLinkedGLAccountForLoanCharges(final Long loanProductId, final int accountMappingTypeId, final Long chargeId) {
|
||||||
|
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(loanProductId,
|
||||||
|
PortfolioProductType.LOAN.getValue(), accountMappingTypeId);
|
||||||
|
/*****
|
||||||
|
* Get more specific mappings for Charges and penalties (based on the
|
||||||
|
* actual charge /penalty coupled with the loan product). Note the
|
||||||
|
* income from fees and income from penalties placeholder ID would be
|
||||||
|
* the same for both cash and accrual based accounts
|
||||||
|
*****/
|
||||||
|
|
||||||
|
// Vishwas TODO: remove this condition as it should always be true
|
||||||
|
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue()
|
||||||
|
|| accountMappingTypeId == CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue()) {
|
||||||
|
ProductToGLAccountMapping chargeSpecificIncomeAccountMapping = this.accountMappingRepository
|
||||||
|
.findByProductIdAndProductTypeAndFinancialAccountTypeAndChargeId(loanProductId, PortfolioProductType.LOAN.getValue(),
|
||||||
|
accountMappingTypeId, chargeId);
|
||||||
|
if (chargeSpecificIncomeAccountMapping != null) {
|
||||||
|
accountMapping = chargeSpecificIncomeAccountMapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
return accountMapping.getGlAccount();
|
return accountMapping.getGlAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -134,14 +134,15 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
|||||||
|
|
||||||
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(feesAmount);
|
totalDebitAmount = totalDebitAmount.add(feesAmount);
|
||||||
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES, loanProductId, paymentTypeId,
|
helper.createCreditJournalEntryOrReversalForLoanCharges(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue(),
|
||||||
loanId, transactionId, transactionDate, feesAmount, isReversal);
|
loanProductId, loanId, transactionId, transactionDate, feesAmount, isReversal, loanTransactionDTO.getFeePayments());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
|
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
|
||||||
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId,
|
helper.createCreditJournalEntryOrReversalForLoanCharges(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
|
||||||
paymentTypeId, loanId, transactionId, transactionDate, penaltiesAmount, isReversal);
|
loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, isReversal,
|
||||||
|
loanTransactionDTO.getPenaltyPayments());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** create a single debit entry (or reversal) for the entire amount **/
|
/*** create a single debit entry (or reversal) for the entire amount **/
|
||||||
|
|||||||
+130
-36
@@ -6,6 +6,7 @@
|
|||||||
package org.mifosplatform.accounting.journalentry.service;
|
package org.mifosplatform.accounting.journalentry.service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -15,9 +16,11 @@ import org.apache.commons.lang.StringUtils;
|
|||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.accounting.closure.domain.GLClosure;
|
import org.mifosplatform.accounting.closure.domain.GLClosure;
|
||||||
import org.mifosplatform.accounting.closure.domain.GLClosureRepository;
|
import org.mifosplatform.accounting.closure.domain.GLClosureRepository;
|
||||||
|
import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
|
||||||
import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException;
|
import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException;
|
||||||
|
import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService;
|
||||||
import org.mifosplatform.accounting.journalentry.api.JournalEntryJsonInputParams;
|
import org.mifosplatform.accounting.journalentry.api.JournalEntryJsonInputParams;
|
||||||
import org.mifosplatform.accounting.journalentry.command.JournalEntryCommand;
|
import org.mifosplatform.accounting.journalentry.command.JournalEntryCommand;
|
||||||
import org.mifosplatform.accounting.journalentry.command.SingleDebitOrCreditEntryCommand;
|
import org.mifosplatform.accounting.journalentry.command.SingleDebitOrCreditEntryCommand;
|
||||||
@@ -61,6 +64,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
private final AccountingProcessorHelper helper;
|
private final AccountingProcessorHelper helper;
|
||||||
private final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
private final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||||
private final AccountingRuleRepository accountingRuleRepository;
|
private final AccountingRuleRepository accountingRuleRepository;
|
||||||
|
private final GLAccountReadPlatformService glAccountReadPlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public JournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
|
public JournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
|
||||||
@@ -68,7 +72,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
final GLAccountRepository glAccountRepository, final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer,
|
final GLAccountRepository glAccountRepository, final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer,
|
||||||
final AccountingProcessorHelper accountingProcessorHelper, final AccountingRuleRepository accountingRuleRepository,
|
final AccountingProcessorHelper accountingProcessorHelper, final AccountingRuleRepository accountingRuleRepository,
|
||||||
final AccountingProcessorForLoanFactory accountingProcessorForLoanFactory,
|
final AccountingProcessorForLoanFactory accountingProcessorForLoanFactory,
|
||||||
final AccountingProcessorForSavingsFactory accountingProcessorForSavingsFactory) {
|
final AccountingProcessorForSavingsFactory accountingProcessorForSavingsFactory,
|
||||||
|
final GLAccountReadPlatformService glAccountReadPlatformService) {
|
||||||
this.glClosureRepository = glClosureRepository;
|
this.glClosureRepository = glClosureRepository;
|
||||||
this.officeRepository = officeRepository;
|
this.officeRepository = officeRepository;
|
||||||
this.glJournalEntryRepository = glJournalEntryRepository;
|
this.glJournalEntryRepository = glJournalEntryRepository;
|
||||||
@@ -78,6 +83,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
this.accountingProcessorForSavingsFactory = accountingProcessorForSavingsFactory;
|
this.accountingProcessorForSavingsFactory = accountingProcessorForSavingsFactory;
|
||||||
this.helper = accountingProcessorHelper;
|
this.helper = accountingProcessorHelper;
|
||||||
this.accountingRuleRepository = accountingRuleRepository;
|
this.accountingRuleRepository = accountingRuleRepository;
|
||||||
|
this.glAccountReadPlatformService = glAccountReadPlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -99,32 +105,65 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
/** Set a transaction Id and save these Journal entries **/
|
/** Set a transaction Id and save these Journal entries **/
|
||||||
final Date transactionDate = command.DateValueOfParameterNamed(JournalEntryJsonInputParams.TRANSACTION_DATE.getValue());
|
final Date transactionDate = command.DateValueOfParameterNamed(JournalEntryJsonInputParams.TRANSACTION_DATE.getValue());
|
||||||
final String transactionId = generateTransactionId();
|
final String transactionId = generateTransactionId();
|
||||||
final String referenceNumber = journalEntryCommand.getReferenceNumber();
|
final String referenceNumber = command.stringValueOfParameterNamed(JournalEntryJsonInputParams.REFERENCE_NUMBER.getValue());
|
||||||
final boolean manualEntry = true;
|
final boolean manualEntry = true;
|
||||||
|
|
||||||
if (accountRuleId != null) {
|
if (accountRuleId != null) {
|
||||||
|
|
||||||
final Long accountingRuleId = command.longValueOfParameterNamed(JournalEntryJsonInputParams.ACCOUNTING_RULE.getValue());
|
|
||||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed(JournalEntryJsonInputParams.AMOUNT.getValue());
|
final BigDecimal amount = command.bigDecimalValueOfParameterNamed(JournalEntryJsonInputParams.AMOUNT.getValue());
|
||||||
final AccountingRule accountingRule = this.accountingRuleRepository.findOne(accountingRuleId);
|
|
||||||
if (accountingRule == null) { throw new AccountingRuleNotFoundException(accountingRuleId); }
|
String description = null;
|
||||||
final GLAccount creditAccountHead = accountingRule.getAccountToCredit();
|
|
||||||
validateGLAccountForTransaction(creditAccountHead);
|
|
||||||
final GLAccount debitAccountHead = accountingRule.getAccountToDebit();
|
|
||||||
validateGLAccountForTransaction(debitAccountHead);
|
|
||||||
String description = accountingRule.getDescription();
|
|
||||||
if (StringUtils.isNotBlank(journalEntryCommand.getComments())) {
|
if (StringUtils.isNotBlank(journalEntryCommand.getComments())) {
|
||||||
description = journalEntryCommand.getComments();
|
description = journalEntryCommand.getComments();
|
||||||
}
|
}
|
||||||
|
|
||||||
JournalEntry creditEntry = JournalEntry.createNew(office, creditAccountHead, transactionId, manualEntry, transactionDate,
|
final AccountingRule accountingRule = this.accountingRuleRepository.findOne(accountRuleId);
|
||||||
JournalEntryType.CREDIT, amount, description, null, null, referenceNumber);
|
if (accountingRule == null) { throw new AccountingRuleNotFoundException(accountRuleId); }
|
||||||
this.glJournalEntryRepository.saveAndFlush(creditEntry);
|
|
||||||
|
|
||||||
JournalEntry debitEntry = JournalEntry.createNew(office, debitAccountHead, transactionId, manualEntry, transactionDate,
|
if (accountingRule.getAccountToCredit() == null) {
|
||||||
JournalEntryType.DEBIT, amount, description, null, null, referenceNumber);
|
if (journalEntryCommand.getCredits() == null) { throw new RuntimeException(
|
||||||
this.glJournalEntryRepository.saveAndFlush(debitEntry);
|
"You have to pass atleast one value for credits"); }
|
||||||
|
if (journalEntryCommand.getDebits() != null) {
|
||||||
|
checkDebitOrCreditAccountsAreValid(accountingRule, journalEntryCommand.getCredits(),
|
||||||
|
journalEntryCommand.getDebits());
|
||||||
|
checkDebitAndCreditAmounts(journalEntryCommand.getCredits(), journalEntryCommand.getDebits());
|
||||||
|
} else {
|
||||||
|
checkDebitAndCreditAmounts(journalEntryCommand.getCredits(), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getCredits(),
|
||||||
|
transactionId, JournalEntryType.CREDIT, referenceNumber);
|
||||||
|
} else {
|
||||||
|
if (journalEntryCommand.getCredits() != null) { throw new RuntimeException(
|
||||||
|
"You can not pass credits for this rule please verify your rule"); }
|
||||||
|
final GLAccount creditAccountHead = accountingRule.getAccountToCredit();
|
||||||
|
validateGLAccountForTransaction(creditAccountHead);
|
||||||
|
final JournalEntry creditEntry = JournalEntry.createNew(office, creditAccountHead, transactionId, manualEntry,
|
||||||
|
transactionDate, JournalEntryType.CREDIT, amount, description, null, null, referenceNumber);
|
||||||
|
this.glJournalEntryRepository.saveAndFlush(creditEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountingRule.getAccountToDebit() == null) {
|
||||||
|
if (journalEntryCommand.getDebits() == null) { throw new RuntimeException(
|
||||||
|
"You have to pass atleast one parameter for debits"); }
|
||||||
|
if (journalEntryCommand.getCredits() != null) {
|
||||||
|
checkDebitOrCreditAccountsAreValid(accountingRule, journalEntryCommand.getCredits(),
|
||||||
|
journalEntryCommand.getDebits());
|
||||||
|
checkDebitAndCreditAmounts(journalEntryCommand.getCredits(), journalEntryCommand.getDebits());
|
||||||
|
} else {
|
||||||
|
checkDebitAndCreditAmounts(journalEntryCommand.getDebits(), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getDebits(),
|
||||||
|
transactionId, JournalEntryType.DEBIT, referenceNumber);
|
||||||
|
} else {
|
||||||
|
if (journalEntryCommand.getDebits() != null) { throw new RuntimeException(
|
||||||
|
"You can not pass debits for this rule please verify your rule"); }
|
||||||
|
final GLAccount debitAccountHead = accountingRule.getAccountToDebit();
|
||||||
|
validateGLAccountForTransaction(debitAccountHead);
|
||||||
|
final JournalEntry debitEntry = JournalEntry.createNew(office, debitAccountHead, transactionId, manualEntry,
|
||||||
|
transactionDate, JournalEntryType.DEBIT, amount, description, null, null, referenceNumber);
|
||||||
|
this.glJournalEntryRepository.saveAndFlush(debitEntry);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getDebits(), transactionId,
|
saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getDebits(), transactionId,
|
||||||
@@ -143,7 +182,76 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateGLAccountForTransaction(GLAccount creditOrDebitAccountHead) {
|
@SuppressWarnings("null")
|
||||||
|
private void checkDebitOrCreditAccountsAreValid(final AccountingRule accountingRule, final SingleDebitOrCreditEntryCommand[] credits,
|
||||||
|
final SingleDebitOrCreditEntryCommand[] debits) {
|
||||||
|
// Validate the debit and credit arrays are appropriate accounts
|
||||||
|
List<GLAccountDataForLookup> allowedCreditGLAccounts = new ArrayList<GLAccountDataForLookup>();
|
||||||
|
List<GLAccountDataForLookup> allowedDebitGLAccounts = new ArrayList<GLAccountDataForLookup>();
|
||||||
|
final SingleDebitOrCreditEntryCommand[] validCredits = new SingleDebitOrCreditEntryCommand[credits.length];
|
||||||
|
final SingleDebitOrCreditEntryCommand[] validDebits = new SingleDebitOrCreditEntryCommand[debits.length];
|
||||||
|
|
||||||
|
if (credits != null && credits.length > 0) {
|
||||||
|
allowedCreditGLAccounts = this.glAccountReadPlatformService.retrieveAccountsByTagId(accountingRule.getId(),
|
||||||
|
JournalEntryType.CREDIT.getValue());
|
||||||
|
for (final GLAccountDataForLookup accountDataForLookup : allowedCreditGLAccounts) {
|
||||||
|
for (int i = 0; i < credits.length; i++) {
|
||||||
|
final SingleDebitOrCreditEntryCommand credit = credits[i];
|
||||||
|
if (credit.getGlAccountId().equals(accountDataForLookup.getId())) {
|
||||||
|
validCredits[i] = credit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (credits.length != validCredits.length) { throw new RuntimeException("Invalid credits"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debits != null && debits.length > 0) {
|
||||||
|
allowedDebitGLAccounts = this.glAccountReadPlatformService.retrieveAccountsByTagId(accountingRule.getId(),
|
||||||
|
JournalEntryType.DEBIT.getValue());
|
||||||
|
for (final GLAccountDataForLookup accountDataForLookup : allowedDebitGLAccounts) {
|
||||||
|
for (int i = 0; i < debits.length; i++) {
|
||||||
|
final SingleDebitOrCreditEntryCommand debit = debits[i];
|
||||||
|
if (debit.getGlAccountId().equals(accountDataForLookup.getId())) {
|
||||||
|
validDebits[i] = debit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (debits.length != validDebits.length) { throw new RuntimeException("Invalid debits"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDebitAndCreditAmounts(final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits) {
|
||||||
|
// sum of all debits must be = sum of all credits
|
||||||
|
BigDecimal creditsSum = BigDecimal.ZERO;
|
||||||
|
BigDecimal debitsSum = BigDecimal.ZERO;
|
||||||
|
for (final SingleDebitOrCreditEntryCommand creditEntryCommand : credits) {
|
||||||
|
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
|
||||||
|
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
|
||||||
|
creditsSum = creditsSum.add(creditEntryCommand.getAmount());
|
||||||
|
}
|
||||||
|
for (final SingleDebitOrCreditEntryCommand debitEntryCommand : debits) {
|
||||||
|
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
|
||||||
|
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
|
||||||
|
debitsSum = debitsSum.add(debitEntryCommand.getAmount());
|
||||||
|
}
|
||||||
|
if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException(
|
||||||
|
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH, null, null, null); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDebitAndCreditAmounts(final SingleDebitOrCreditEntryCommand[] creditOrDebits, final BigDecimal amount) {
|
||||||
|
if (amount == null) { throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH_WITH_AMOUNT,
|
||||||
|
null, null, null); }
|
||||||
|
BigDecimal creditOrDebitSum = BigDecimal.ZERO;
|
||||||
|
for (final SingleDebitOrCreditEntryCommand creditEntryCommand : creditOrDebits) {
|
||||||
|
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
|
||||||
|
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
|
||||||
|
creditOrDebitSum = creditOrDebitSum.add(creditEntryCommand.getAmount());
|
||||||
|
}
|
||||||
|
if (creditOrDebitSum.compareTo(amount) != 0) { throw new JournalEntryInvalidException(
|
||||||
|
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH, null, null, null); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateGLAccountForTransaction(final GLAccount creditOrDebitAccountHead) {
|
||||||
/***
|
/***
|
||||||
* validate that the account allows manual adjustments and is not
|
* validate that the account allows manual adjustments and is not
|
||||||
* disabled
|
* disabled
|
||||||
@@ -199,7 +307,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
final boolean accrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("accrualBasedAccountingEnabled");
|
final boolean accrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("accrualBasedAccountingEnabled");
|
||||||
|
|
||||||
if (cashBasedAccountingEnabled || accrualBasedAccountingEnabled) {
|
if (cashBasedAccountingEnabled || accrualBasedAccountingEnabled) {
|
||||||
final LoanDTO loanDTO = helper.populateLoanDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled,
|
final LoanDTO loanDTO = this.helper.populateLoanDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled,
|
||||||
accrualBasedAccountingEnabled);
|
accrualBasedAccountingEnabled);
|
||||||
final AccountingProcessorForLoan accountingProcessorForLoan = this.accountingProcessorForLoanFactory
|
final AccountingProcessorForLoan accountingProcessorForLoan = this.accountingProcessorForLoanFactory
|
||||||
.determineProcessor(loanDTO);
|
.determineProcessor(loanDTO);
|
||||||
@@ -209,13 +317,13 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public void createJournalEntriesForSavings(Map<String, Object> accountingBridgeData) {
|
public void createJournalEntriesForSavings(final Map<String, Object> accountingBridgeData) {
|
||||||
|
|
||||||
final boolean cashBasedAccountingEnabled = (Boolean) accountingBridgeData.get("cashBasedAccountingEnabled");
|
final boolean cashBasedAccountingEnabled = (Boolean) accountingBridgeData.get("cashBasedAccountingEnabled");
|
||||||
final boolean accrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("accrualBasedAccountingEnabled");
|
final boolean accrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("accrualBasedAccountingEnabled");
|
||||||
|
|
||||||
if (cashBasedAccountingEnabled || accrualBasedAccountingEnabled) {
|
if (cashBasedAccountingEnabled || accrualBasedAccountingEnabled) {
|
||||||
final SavingsDTO savingsDTO = helper.populateSavingsDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled,
|
final SavingsDTO savingsDTO = this.helper.populateSavingsDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled,
|
||||||
accrualBasedAccountingEnabled);
|
accrualBasedAccountingEnabled);
|
||||||
final AccountingProcessorForSavings accountingProcessorForSavings = this.accountingProcessorForSavingsFactory
|
final AccountingProcessorForSavings accountingProcessorForSavings = this.accountingProcessorForSavingsFactory
|
||||||
.determineProcessor(savingsDTO);
|
.determineProcessor(savingsDTO);
|
||||||
@@ -247,21 +355,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
if ((credits == null || credits.length <= 0) || (debits == null || debits.length <= 0)) { throw new JournalEntryInvalidException(
|
if ((credits == null || credits.length <= 0) || (debits == null || debits.length <= 0)) { throw new JournalEntryInvalidException(
|
||||||
GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null, null, null); }
|
GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null, null, null); }
|
||||||
|
|
||||||
// sum of all debits must be = sum of all credits
|
checkDebitAndCreditAmounts(credits, debits);
|
||||||
BigDecimal creditsSum = BigDecimal.ZERO;
|
|
||||||
BigDecimal debitsSum = BigDecimal.ZERO;
|
|
||||||
for (final SingleDebitOrCreditEntryCommand creditEntryCommand : credits) {
|
|
||||||
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
|
|
||||||
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
|
|
||||||
creditsSum = creditsSum.add(creditEntryCommand.getAmount());
|
|
||||||
}
|
|
||||||
for (final SingleDebitOrCreditEntryCommand debitEntryCommand : debits) {
|
|
||||||
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
|
|
||||||
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
|
|
||||||
debitsSum = debitsSum.add(debitEntryCommand.getAmount());
|
|
||||||
}
|
|
||||||
if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException(
|
|
||||||
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH, null, null, null); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.producttoaccountmapping.data;
|
||||||
|
|
||||||
|
public class ChargeToGLAccountMapper {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final long chargeId;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final long incomeAccountId;
|
||||||
|
|
||||||
|
public ChargeToGLAccountMapper(long chargeId, long incomeAccountId) {
|
||||||
|
this.chargeId = chargeId;
|
||||||
|
this.incomeAccountId = incomeAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+5
@@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.producttoaccountmapping.data;
|
package org.mifosplatform.accounting.producttoaccountmapping.data;
|
||||||
|
|
||||||
public class PaymentTypeToGLAccountMapper {
|
public class PaymentTypeToGLAccountMapper {
|
||||||
|
|||||||
+25
-1
@@ -15,6 +15,7 @@ import javax.persistence.UniqueConstraint;
|
|||||||
|
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
||||||
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
|
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
|
||||||
|
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@@ -33,6 +34,10 @@ public class ProductToGLAccountMapping extends AbstractPersistable<Long> {
|
|||||||
@JoinColumn(name = "payment_type", nullable = true)
|
@JoinColumn(name = "payment_type", nullable = true)
|
||||||
private CodeValue paymentType;
|
private CodeValue paymentType;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "charge_id", nullable = true)
|
||||||
|
private Charge charge;
|
||||||
|
|
||||||
@Column(name = "product_type", nullable = false)
|
@Column(name = "product_type", nullable = false)
|
||||||
private int productType;
|
private int productType;
|
||||||
|
|
||||||
@@ -49,16 +54,27 @@ public class ProductToGLAccountMapping extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ProductToGLAccountMapping(final GLAccount glAccount, final Long productId, final int productType, final int financialAccountType) {
|
public ProductToGLAccountMapping(final GLAccount glAccount, final Long productId, final int productType, final int financialAccountType) {
|
||||||
this(glAccount, productId, productType, financialAccountType, null);
|
this(glAccount, productId, productType, financialAccountType, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProductToGLAccountMapping(final GLAccount glAccount, final Long productId, final int productType,
|
||||||
|
final int financialAccountType, final Charge charge) {
|
||||||
|
this(glAccount, productId, productType, financialAccountType, null, charge);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProductToGLAccountMapping(final GLAccount glAccount, final Long productId, final int productType,
|
public ProductToGLAccountMapping(final GLAccount glAccount, final Long productId, final int productType,
|
||||||
final int financialAccountType, final CodeValue paymentType) {
|
final int financialAccountType, final CodeValue paymentType) {
|
||||||
|
this(glAccount, productId, productType, financialAccountType, paymentType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProductToGLAccountMapping(final GLAccount glAccount, final Long productId, final int productType,
|
||||||
|
final int financialAccountType, final CodeValue paymentType, final Charge charge) {
|
||||||
this.glAccount = glAccount;
|
this.glAccount = glAccount;
|
||||||
this.productId = productId;
|
this.productId = productId;
|
||||||
this.productType = productType;
|
this.productType = productType;
|
||||||
this.financialAccountType = financialAccountType;
|
this.financialAccountType = financialAccountType;
|
||||||
this.paymentType = paymentType;
|
this.paymentType = paymentType;
|
||||||
|
this.charge = charge;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GLAccount getGlAccount() {
|
public GLAccount getGlAccount() {
|
||||||
@@ -101,4 +117,12 @@ public class ProductToGLAccountMapping extends AbstractPersistable<Long> {
|
|||||||
this.paymentType = paymentType;
|
this.paymentType = paymentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Charge getCharge() {
|
||||||
|
return this.charge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCharge(Charge charge) {
|
||||||
|
this.charge = charge;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+14
-1
@@ -18,7 +18,10 @@ public interface ProductToGLAccountMappingRepository extends JpaRepository<Produ
|
|||||||
ProductToGLAccountMapping findByProductIdAndProductTypeAndFinancialAccountTypeAndPaymentTypeId(Long productId, int productType,
|
ProductToGLAccountMapping findByProductIdAndProductTypeAndFinancialAccountTypeAndPaymentTypeId(Long productId, int productType,
|
||||||
int financialAccountType, Long paymentType);
|
int financialAccountType, Long paymentType);
|
||||||
|
|
||||||
@Query("from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=:financialAccountType and mapping.paymentType is NULL")
|
ProductToGLAccountMapping findByProductIdAndProductTypeAndFinancialAccountTypeAndChargeId(Long productId, int productType,
|
||||||
|
int financialAccountType, Long chargeId);
|
||||||
|
|
||||||
|
@Query("from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=:financialAccountType and mapping.paymentType is NULL and mapping.charge is NULL")
|
||||||
ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType,
|
ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType,
|
||||||
@Param("financialAccountType") int financialAccountType);
|
@Param("financialAccountType") int financialAccountType);
|
||||||
|
|
||||||
@@ -27,5 +30,15 @@ public interface ProductToGLAccountMappingRepository extends JpaRepository<Produ
|
|||||||
List<ProductToGLAccountMapping> findAllPaymentTypeToFundSourceMappings(@Param("productId") Long productId,
|
List<ProductToGLAccountMapping> findAllPaymentTypeToFundSourceMappings(@Param("productId") Long productId,
|
||||||
@Param("productType") int productType);
|
@Param("productType") int productType);
|
||||||
|
|
||||||
|
/*** The financial Account Type for income from interest will always be 4 ***/
|
||||||
|
@Query("from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=4 and mapping.charge is not NULL")
|
||||||
|
List<ProductToGLAccountMapping> findAllFeeToIncomeAccountMappings(@Param("productId") Long productId,
|
||||||
|
@Param("productType") int productType);
|
||||||
|
|
||||||
|
/*** The financial Account Type for income from interest will always be 5 ***/
|
||||||
|
@Query("from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=5 and mapping.charge is not NULL")
|
||||||
|
List<ProductToGLAccountMapping> findAllPenaltyToIncomeAccountMappings(@Param("productId") Long productId,
|
||||||
|
@Param("productType") int productType);
|
||||||
|
|
||||||
List<ProductToGLAccountMapping> findByProductIdAndProductType(Long productId, int productType);
|
List<ProductToGLAccountMapping> findByProductIdAndProductType(Long productId, int productType);
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-7
@@ -1,12 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.producttoaccountmapping.service;
|
package org.mifosplatform.accounting.producttoaccountmapping.service;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.mifosplatform.accounting.common.AccountingRuleType;
|
|
||||||
import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
|
import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
|
||||||
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
|
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
|
||||||
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
|
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
|
||||||
|
import org.mifosplatform.accounting.common.AccountingRuleType;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
|
||||||
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
|
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
|
||||||
@@ -14,6 +19,7 @@ import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAc
|
|||||||
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -23,10 +29,10 @@ import com.google.gson.JsonElement;
|
|||||||
public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappingHelper {
|
public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappingHelper {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LoanProductToGLAccountMappingHelper(GLAccountRepository glAccountRepository,
|
public LoanProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
|
||||||
ProductToGLAccountMappingRepository glAccountMappingRepository, FromJsonHelper fromApiJsonHelper,
|
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
|
||||||
CodeValueRepositoryWrapper codeValueRepositoryWrapper) {
|
final CodeValueRepositoryWrapper codeValueRepositoryWrapper, final ChargeRepositoryWrapper chargeRepositoryWrapper) {
|
||||||
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, codeValueRepositoryWrapper);
|
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, codeValueRepositoryWrapper, chargeRepositoryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Set of abstractions for saving Loan Products to GL Account Mappings ***/
|
/*** Set of abstractions for saving Loan Products to GL Account Mappings ***/
|
||||||
@@ -68,15 +74,29 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
/*** Abstractions for payments channel related to loan products ***/
|
/*** Abstractions for payments channel related to loan products ***/
|
||||||
|
|
||||||
public void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
public void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
Map<String, Object> changes) {
|
final Map<String, Object> changes) {
|
||||||
savePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.LOAN);
|
savePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.LOAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
public void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
Map<String, Object> changes) {
|
final Map<String, Object> changes) {
|
||||||
updatePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.LOAN);
|
updatePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.LOAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
|
final Map<String, Object> changes) {
|
||||||
|
// save both fee and penalty charges
|
||||||
|
saveChargesToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN, true);
|
||||||
|
saveChargesToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
|
final Map<String, Object> changes) {
|
||||||
|
// update both fee and penalty charges
|
||||||
|
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN, true);
|
||||||
|
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN, false);
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> populateChangesForNewLoanProductToGLAccountMappingCreation(final JsonElement element,
|
public Map<String, Object> populateChangesForNewLoanProductToGLAccountMappingCreation(final JsonElement element,
|
||||||
final AccountingRuleType accountingRuleType) {
|
final AccountingRuleType accountingRuleType) {
|
||||||
final Map<String, Object> changes = new HashMap<String, Object>();
|
final Map<String, Object> changes = new HashMap<String, Object>();
|
||||||
|
|||||||
+160
-1
@@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.producttoaccountmapping.service;
|
package org.mifosplatform.accounting.producttoaccountmapping.service;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -21,6 +26,8 @@ import org.mifosplatform.infrastructure.codes.domain.CodeValue;
|
|||||||
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||||
|
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||||
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
|
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -36,15 +43,17 @@ public class ProductToGLAccountMappingHelper {
|
|||||||
protected final ProductToGLAccountMappingRepository accountMappingRepository;
|
protected final ProductToGLAccountMappingRepository accountMappingRepository;
|
||||||
protected final FromJsonHelper fromApiJsonHelper;
|
protected final FromJsonHelper fromApiJsonHelper;
|
||||||
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
||||||
|
private final ChargeRepositoryWrapper chargeRepositoryWrapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
|
public ProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
|
||||||
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
|
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
|
||||||
final CodeValueRepositoryWrapper codeValueRepositoryWrapper) {
|
final CodeValueRepositoryWrapper codeValueRepositoryWrapper, final ChargeRepositoryWrapper chargeRepositoryWrapper) {
|
||||||
this.accountRepository = glAccountRepository;
|
this.accountRepository = glAccountRepository;
|
||||||
this.accountMappingRepository = glAccountMappingRepository;
|
this.accountMappingRepository = glAccountMappingRepository;
|
||||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
|
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
|
||||||
|
this.chargeRepositoryWrapper = chargeRepositoryWrapper;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +114,128 @@ public class ProductToGLAccountMappingHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the Charge to Income account mappings for a particular
|
||||||
|
* product/product type (also populates the changes array if passed in)
|
||||||
|
*
|
||||||
|
* @param command
|
||||||
|
* @param element
|
||||||
|
* @param productId
|
||||||
|
* @param changes
|
||||||
|
*/
|
||||||
|
public void saveChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
|
Map<String, Object> changes, final PortfolioProductType portfolioProductType, final boolean isPenalty) {
|
||||||
|
String arrayName;
|
||||||
|
if (isPenalty) {
|
||||||
|
arrayName = LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue();
|
||||||
|
} else {
|
||||||
|
arrayName = LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray chargeToIncomeAccountMappingArray = fromApiJsonHelper.extractJsonArrayNamed(arrayName, element);
|
||||||
|
if (chargeToIncomeAccountMappingArray != null) {
|
||||||
|
if (changes != null) {
|
||||||
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
|
||||||
|
command.jsonFragment(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue()));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < chargeToIncomeAccountMappingArray.size(); i++) {
|
||||||
|
final JsonObject jsonObject = chargeToIncomeAccountMappingArray.get(i).getAsJsonObject();
|
||||||
|
final Long chargeId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.CHARGE_ID.getValue()).getAsLong();
|
||||||
|
final Long incomeAccountId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_ACCOUNT_ID.getValue()).getAsLong();
|
||||||
|
saveChargeToFundSourceMapping(productId, chargeId, incomeAccountId, portfolioProductType, isPenalty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param command
|
||||||
|
* @param element
|
||||||
|
* @param productId
|
||||||
|
* @param changes
|
||||||
|
*/
|
||||||
|
public void updateChargeToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
|
Map<String, Object> changes, final PortfolioProductType portfolioProductType, boolean isPenalty) {
|
||||||
|
// find all existing payment Channel to Fund source Mappings
|
||||||
|
List<ProductToGLAccountMapping> existingChargeToIncomeAccountMappings;
|
||||||
|
String arrayFragmentName;
|
||||||
|
|
||||||
|
if (isPenalty) {
|
||||||
|
existingChargeToIncomeAccountMappings = accountMappingRepository.findAllPenaltyToIncomeAccountMappings(productId,
|
||||||
|
portfolioProductType.getValue());
|
||||||
|
arrayFragmentName = LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue();
|
||||||
|
} else {
|
||||||
|
existingChargeToIncomeAccountMappings = accountMappingRepository.findAllFeeToIncomeAccountMappings(productId,
|
||||||
|
portfolioProductType.getValue());
|
||||||
|
arrayFragmentName = LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray chargeToIncomeAccountMappingArray = fromApiJsonHelper.extractJsonArrayNamed(arrayFragmentName, element);
|
||||||
|
/**
|
||||||
|
* Variable stores a map representation of charges (key) and their
|
||||||
|
* associated income Id's (value) extracted from the passed in
|
||||||
|
* Jsoncommand
|
||||||
|
**/
|
||||||
|
Map<Long, Long> inputChargeToIncomeAccountMap = new HashMap<Long, Long>();
|
||||||
|
/***
|
||||||
|
* Variable stores all charges which have already been mapped to Income
|
||||||
|
* Accounts in the system
|
||||||
|
**/
|
||||||
|
Set<Long> existingCharges = new HashSet<Long>();
|
||||||
|
if (chargeToIncomeAccountMappingArray != null) {
|
||||||
|
if (changes != null) {
|
||||||
|
changes.put(arrayFragmentName, command.jsonFragment(arrayFragmentName));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < chargeToIncomeAccountMappingArray.size(); i++) {
|
||||||
|
final JsonObject jsonObject = chargeToIncomeAccountMappingArray.get(i).getAsJsonObject();
|
||||||
|
final Long chargeId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.CHARGE_ID.getValue()).getAsLong();
|
||||||
|
final Long incomeAccountId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_ACCOUNT_ID.getValue()).getAsLong();
|
||||||
|
inputChargeToIncomeAccountMap.put(chargeId, incomeAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If input map is empty, delete all existing mappings
|
||||||
|
if (inputChargeToIncomeAccountMap.size() == 0) {
|
||||||
|
accountMappingRepository.deleteInBatch(existingChargeToIncomeAccountMappings);
|
||||||
|
}/**
|
||||||
|
* Else, <br/>
|
||||||
|
* update existing mappings OR <br/>
|
||||||
|
* delete old mappings (which are already present, but not passed in
|
||||||
|
* as a part of Jsoncommand)<br/>
|
||||||
|
* Create new mappings for charges that are passed in as a part of
|
||||||
|
* the Jsoncommand but not already present
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
else {
|
||||||
|
for (ProductToGLAccountMapping chargeToIncomeAccountMapping : existingChargeToIncomeAccountMappings) {
|
||||||
|
Long currentCharge = chargeToIncomeAccountMapping.getCharge().getId();
|
||||||
|
existingCharges.add(currentCharge);
|
||||||
|
// update existing mappings (if required)
|
||||||
|
if (inputChargeToIncomeAccountMap.containsKey(currentCharge)) {
|
||||||
|
Long newGLAccountId = inputChargeToIncomeAccountMap.get(currentCharge);
|
||||||
|
if (newGLAccountId != chargeToIncomeAccountMapping.getGlAccount().getId()) {
|
||||||
|
final GLAccount glAccount = getAccountByIdAndType(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_ACCOUNT_ID.getValue(),
|
||||||
|
GLAccountType.INCOME, newGLAccountId);
|
||||||
|
chargeToIncomeAccountMapping.setGlAccount(glAccount);
|
||||||
|
accountMappingRepository.save(chargeToIncomeAccountMapping);
|
||||||
|
}
|
||||||
|
}// deleted payment type
|
||||||
|
else {
|
||||||
|
accountMappingRepository.delete(chargeToIncomeAccountMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create new mappings
|
||||||
|
Set<Long> incomingCharges = inputChargeToIncomeAccountMap.keySet();
|
||||||
|
incomingCharges.removeAll(existingCharges);
|
||||||
|
// incomingPaymentTypes now only contains the newly added
|
||||||
|
// payment Type mappings
|
||||||
|
for (Long newCharge : incomingCharges) {
|
||||||
|
Long newGLAccountId = inputChargeToIncomeAccountMap.get(newCharge);
|
||||||
|
saveChargeToFundSourceMapping(productId, newCharge, newGLAccountId, portfolioProductType, isPenalty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param command
|
* @param command
|
||||||
* @param element
|
* @param element
|
||||||
@@ -199,6 +330,34 @@ public class ProductToGLAccountMappingHelper {
|
|||||||
this.accountMappingRepository.save(accountMapping);
|
this.accountMappingRepository.save(accountMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param productId
|
||||||
|
* @param jsonObject
|
||||||
|
*/
|
||||||
|
private void saveChargeToFundSourceMapping(final Long productId, final Long chargeId, final Long incomeAccountId,
|
||||||
|
final PortfolioProductType portfolioProductType, final boolean isPenalty) {
|
||||||
|
Charge charge = chargeRepositoryWrapper.findOneWithNotFoundDetection(chargeId);
|
||||||
|
|
||||||
|
// TODO Vishwas: Need to validate if given charge is fee or Penalty
|
||||||
|
// based on input condition
|
||||||
|
|
||||||
|
final GLAccount glAccount = getAccountByIdAndType(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_ACCOUNT_ID.getValue(),
|
||||||
|
GLAccountType.INCOME, incomeAccountId);
|
||||||
|
/**
|
||||||
|
* Both CASH and Accrual placeholders have the same value for income
|
||||||
|
* from Interest and penalties
|
||||||
|
**/
|
||||||
|
CASH_ACCOUNTS_FOR_LOAN placeHolderAccountType;
|
||||||
|
if (isPenalty) {
|
||||||
|
placeHolderAccountType = CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES;
|
||||||
|
} else {
|
||||||
|
placeHolderAccountType = CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES;
|
||||||
|
}
|
||||||
|
final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping(glAccount, productId,
|
||||||
|
portfolioProductType.getValue(), placeHolderAccountType.getValue(), charge);
|
||||||
|
this.accountMappingRepository.save(accountMapping);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches account with a particular Id and throws and Exception it is not
|
* Fetches account with a particular Id and throws and Exception it is not
|
||||||
* of the expected Account Category ('ASSET','liability' etc)
|
* of the expected Account Category ('ASSET','liability' etc)
|
||||||
|
|||||||
+6
@@ -8,6 +8,7 @@ package org.mifosplatform.accounting.producttoaccountmapping.service;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.mifosplatform.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
||||||
import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
||||||
|
|
||||||
public interface ProductToGLAccountMappingReadPlatformService {
|
public interface ProductToGLAccountMappingReadPlatformService {
|
||||||
@@ -16,7 +17,12 @@ public interface ProductToGLAccountMappingReadPlatformService {
|
|||||||
|
|
||||||
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForLoanProduct(final Long loanProductId);
|
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForLoanProduct(final Long loanProductId);
|
||||||
|
|
||||||
|
public List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForLoanProduct(final Long loanProductId);
|
||||||
|
|
||||||
|
public List<ChargeToGLAccountMapper> fetchPenaltyToIncomeAccountMappingsForLoanProduct(final Long loanProductId);
|
||||||
|
|
||||||
public Map<String, Object> fetchAccountMappingDetailsForSavingsProduct(final Long savingsProductId, final Integer accountingType);
|
public Map<String, Object> fetchAccountMappingDetailsForSavingsProduct(final Long savingsProductId, final Integer accountingType);
|
||||||
|
|
||||||
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForSavingsProduct(final Long savingsProductId);
|
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForSavingsProduct(final Long savingsProductId);
|
||||||
|
|
||||||
}
|
}
|
||||||
+50
-10
@@ -18,8 +18,10 @@ import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR
|
|||||||
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
|
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
|
||||||
import org.mifosplatform.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS;
|
import org.mifosplatform.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS;
|
||||||
import org.mifosplatform.accounting.common.AccountingRuleType;
|
import org.mifosplatform.accounting.common.AccountingRuleType;
|
||||||
|
import org.mifosplatform.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
||||||
import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
||||||
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
|
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
|
||||||
|
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
@@ -39,8 +41,11 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
private static final class ProductToGLAccountMappingMapper implements RowMapper<Map<String, Object>> {
|
private static final class ProductToGLAccountMappingMapper implements RowMapper<Map<String, Object>> {
|
||||||
|
|
||||||
public String schema() {
|
public String schema() {
|
||||||
return " id as id, gl_account_id as glAccountId,product_id as productId,product_type as productType,financial_account_type as financialAccountType,"
|
return " mapping.id as id, mapping.gl_account_id as glAccountId, mapping.product_id as productId, "
|
||||||
+ " payment_type as paymentTypeId from acc_product_mapping " + " where product_type= ? ";
|
+ " mapping.product_type as productType,mapping.financial_account_type as financialAccountType, "
|
||||||
|
+ " mapping.payment_type as paymentTypeId, mapping.charge_id as chargeId, charge.is_penalty as penalty "
|
||||||
|
+ " from acc_product_mapping mapping left join m_charge charge on mapping.charge_id=charge.id "
|
||||||
|
+ " where mapping.product_type= ? ";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -49,7 +54,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
final Long id = rs.getLong("id");
|
final Long id = rs.getLong("id");
|
||||||
final Long glAccountId = rs.getLong("glAccountId");
|
final Long glAccountId = rs.getLong("glAccountId");
|
||||||
final Long productId = rs.getLong("productId");
|
final Long productId = rs.getLong("productId");
|
||||||
final Long paymentTypeId = rs.getLong("paymentTypeId");
|
final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentTypeId");
|
||||||
|
final Long chargeId = rs.getLong("chargeId");
|
||||||
final Integer productType = rs.getInt("productType");
|
final Integer productType = rs.getInt("productType");
|
||||||
final Integer financialAccountType = rs.getInt("financialAccountType");
|
final Integer financialAccountType = rs.getInt("financialAccountType");
|
||||||
|
|
||||||
@@ -60,6 +66,7 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
loanProductToGLAccountMap.put("productType", productType);
|
loanProductToGLAccountMap.put("productType", productType);
|
||||||
loanProductToGLAccountMap.put("financialAccountType", financialAccountType);
|
loanProductToGLAccountMap.put("financialAccountType", financialAccountType);
|
||||||
loanProductToGLAccountMap.put("paymentTypeId", paymentTypeId);
|
loanProductToGLAccountMap.put("paymentTypeId", paymentTypeId);
|
||||||
|
loanProductToGLAccountMap.put("chargeId", chargeId);
|
||||||
|
|
||||||
return loanProductToGLAccountMap;
|
return loanProductToGLAccountMap;
|
||||||
}
|
}
|
||||||
@@ -71,7 +78,7 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
final Map<String, Object> accountMappingDetails = new LinkedHashMap<String, Object>(8);
|
final Map<String, Object> accountMappingDetails = new LinkedHashMap<String, Object>(8);
|
||||||
|
|
||||||
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
|
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
|
||||||
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null ";
|
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null";
|
||||||
|
|
||||||
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, new Object[] {
|
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, new Object[] {
|
||||||
PortfolioProductType.LOAN.getValue(), loanProductId });
|
PortfolioProductType.LOAN.getValue(), loanProductId });
|
||||||
@@ -138,7 +145,7 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
final Map<String, Object> accountMappingDetails = new LinkedHashMap<String, Object>(8);
|
final Map<String, Object> accountMappingDetails = new LinkedHashMap<String, Object>(8);
|
||||||
|
|
||||||
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
|
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
|
||||||
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null ";
|
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null ";
|
||||||
|
|
||||||
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, new Object[] {
|
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, new Object[] {
|
||||||
PortfolioProductType.SAVING.getValue(), savingsProductId });
|
PortfolioProductType.SAVING.getValue(), savingsProductId });
|
||||||
@@ -168,14 +175,12 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForLoanProduct(Long loanProductId) {
|
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForLoanProduct(Long loanProductId) {
|
||||||
List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers = null;
|
return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.LOAN, loanProductId);
|
||||||
return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.LOAN, loanProductId, paymentTypeToGLAccountMappers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForSavingsProduct(Long savingsProductId) {
|
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForSavingsProduct(Long savingsProductId) {
|
||||||
List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers = null;
|
return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.SAVING, savingsProductId);
|
||||||
return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.SAVING, savingsProductId, paymentTypeToGLAccountMappers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,13 +189,14 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappings(PortfolioProductType portfolioProductType,
|
private List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappings(PortfolioProductType portfolioProductType,
|
||||||
Long loanProductId, List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers) {
|
Long loanProductId) {
|
||||||
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
|
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
|
||||||
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is not null";
|
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is not null";
|
||||||
|
|
||||||
final List<Map<String, Object>> paymentTypeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, new Object[] {
|
final List<Map<String, Object>> paymentTypeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, new Object[] {
|
||||||
portfolioProductType.getValue(), loanProductId });
|
portfolioProductType.getValue(), loanProductId });
|
||||||
|
|
||||||
|
List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers = null;
|
||||||
for (final Map<String, Object> productToGLAccountMap : paymentTypeToFundSourceMappingsList) {
|
for (final Map<String, Object> productToGLAccountMap : paymentTypeToFundSourceMappingsList) {
|
||||||
if (paymentTypeToGLAccountMappers == null) {
|
if (paymentTypeToGLAccountMappers == null) {
|
||||||
paymentTypeToGLAccountMappers = new ArrayList<PaymentTypeToGLAccountMapper>();
|
paymentTypeToGLAccountMappers = new ArrayList<PaymentTypeToGLAccountMapper>();
|
||||||
@@ -203,4 +209,38 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
return paymentTypeToGLAccountMappers;
|
return paymentTypeToGLAccountMappers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForLoanProduct(Long loanProductId) {
|
||||||
|
return fetchChargeToIncomeAccountMappings(PortfolioProductType.LOAN, loanProductId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ChargeToGLAccountMapper> fetchPenaltyToIncomeAccountMappingsForLoanProduct(Long loanProductId) {
|
||||||
|
return fetchChargeToIncomeAccountMappings(PortfolioProductType.LOAN, loanProductId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ChargeToGLAccountMapper> fetchChargeToIncomeAccountMappings(PortfolioProductType portfolioProductType, Long loanProductId,
|
||||||
|
boolean penalty) {
|
||||||
|
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
|
||||||
|
String sql = "select " + rm.schema() + " and product_id = ? and mapping.charge_id is not null and charge.is_penalty=";
|
||||||
|
if (penalty) {
|
||||||
|
sql = sql + " 1";
|
||||||
|
} else {
|
||||||
|
sql = sql + " 0";
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Map<String, Object>> chargeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, new Object[] {
|
||||||
|
portfolioProductType.getValue(), loanProductId });
|
||||||
|
List<ChargeToGLAccountMapper> chargeToGLAccountMappers = null;
|
||||||
|
for (final Map<String, Object> chargeToIncomeAccountMap : chargeToFundSourceMappingsList) {
|
||||||
|
if (chargeToGLAccountMappers == null) {
|
||||||
|
chargeToGLAccountMappers = new ArrayList<ChargeToGLAccountMapper>();
|
||||||
|
}
|
||||||
|
final Long glAccountId = (Long) chargeToIncomeAccountMap.get("glAccountId");
|
||||||
|
final Long chargeId = (Long) chargeToIncomeAccountMap.get("chargeId");
|
||||||
|
ChargeToGLAccountMapper chargeToGLAccountMapper = new ChargeToGLAccountMapper(chargeId, glAccountId);
|
||||||
|
chargeToGLAccountMappers.add(chargeToGLAccountMapper);
|
||||||
|
}
|
||||||
|
return chargeToGLAccountMappers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+3
-2
@@ -81,6 +81,7 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
|
|
||||||
// advanced accounting mappings
|
// advanced accounting mappings
|
||||||
loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
|
loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
|
||||||
|
loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null);
|
||||||
break;
|
break;
|
||||||
case ACCRUAL_BASED:
|
case ACCRUAL_BASED:
|
||||||
|
|
||||||
@@ -119,6 +120,7 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
|
|
||||||
// advanced accounting mappings
|
// advanced accounting mappings
|
||||||
loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
|
loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
|
||||||
|
loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,12 +189,12 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
createLoanProductToGLAccountMapping(loanProductId, command);
|
createLoanProductToGLAccountMapping(loanProductId, command);
|
||||||
changes = loanProductToGLAccountMappingHelper.populateChangesForNewLoanProductToGLAccountMappingCreation(element,
|
changes = loanProductToGLAccountMappingHelper.populateChangesForNewLoanProductToGLAccountMappingCreation(element,
|
||||||
accountingRuleType);
|
accountingRuleType);
|
||||||
loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, changes);
|
|
||||||
}/*** else examine and update individual changes ***/
|
}/*** else examine and update individual changes ***/
|
||||||
else {
|
else {
|
||||||
loanProductToGLAccountMappingHelper.handleChangesToLoanProductToGLAccountMappings(loanProductId, changes, element,
|
loanProductToGLAccountMappingHelper.handleChangesToLoanProductToGLAccountMappings(loanProductId, changes, element,
|
||||||
accountingRuleType);
|
accountingRuleType);
|
||||||
loanProductToGLAccountMappingHelper.updatePaymentChannelToFundSourceMappings(command, element, loanProductId, changes);
|
loanProductToGLAccountMappingHelper.updatePaymentChannelToFundSourceMappings(command, element, loanProductId, changes);
|
||||||
|
loanProductToGLAccountMappingHelper.updateChargesToIncomeAccountMappings(command, element, loanProductId, changes);
|
||||||
}
|
}
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
@@ -218,7 +220,6 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
createSavingProductToGLAccountMapping(savingsProductId, command);
|
createSavingProductToGLAccountMapping(savingsProductId, command);
|
||||||
changes = savingsProductToGLAccountMappingHelper.populateChangesForNewSavingsProductToGLAccountMappingCreation(element,
|
changes = savingsProductToGLAccountMappingHelper.populateChangesForNewSavingsProductToGLAccountMappingCreation(element,
|
||||||
accountingRuleType);
|
accountingRuleType);
|
||||||
savingsProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, savingsProductId, changes);
|
|
||||||
}/*** else examine and update individual changes ***/
|
}/*** else examine and update individual changes ***/
|
||||||
else {
|
else {
|
||||||
savingsProductToGLAccountMappingHelper.handleChangesToSavingsProductToGLAccountMappings(savingsProductId, changes, element,
|
savingsProductToGLAccountMappingHelper.handleChangesToSavingsProductToGLAccountMappings(savingsProductId, changes, element,
|
||||||
|
|||||||
+8
-2
@@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.producttoaccountmapping.service;
|
package org.mifosplatform.accounting.producttoaccountmapping.service;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -13,6 +18,7 @@ import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAc
|
|||||||
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -24,8 +30,8 @@ public class SavingsProductToGLAccountMappingHelper extends ProductToGLAccountMa
|
|||||||
@Autowired
|
@Autowired
|
||||||
public SavingsProductToGLAccountMappingHelper(GLAccountRepository glAccountRepository,
|
public SavingsProductToGLAccountMappingHelper(GLAccountRepository glAccountRepository,
|
||||||
ProductToGLAccountMappingRepository glAccountMappingRepository, FromJsonHelper fromApiJsonHelper,
|
ProductToGLAccountMappingRepository glAccountMappingRepository, FromJsonHelper fromApiJsonHelper,
|
||||||
CodeValueRepositoryWrapper codeValueRepositoryWrapper) {
|
CodeValueRepositoryWrapper codeValueRepositoryWrapper, ChargeRepositoryWrapper chargeRepositoryWrapper) {
|
||||||
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, codeValueRepositoryWrapper);
|
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, codeValueRepositoryWrapper, chargeRepositoryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Set of abstractions for saving Saving Products to GL Account Mappings ***/
|
/*** Set of abstractions for saving Saving Products to GL Account Mappings ***/
|
||||||
|
|||||||
+92
-25
@@ -5,9 +5,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.accounting.rule.api;
|
package org.mifosplatform.accounting.rule.api;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
@@ -18,18 +22,22 @@ import javax.ws.rs.PUT;
|
|||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
|
||||||
import javax.ws.rs.core.Context;
|
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.common.AccountingConstants;
|
||||||
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
||||||
import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService;
|
import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService;
|
||||||
import org.mifosplatform.accounting.rule.data.AccountingRuleData;
|
import org.mifosplatform.accounting.rule.data.AccountingRuleData;
|
||||||
|
import org.mifosplatform.accounting.rule.data.AccountingTagRuleData;
|
||||||
import org.mifosplatform.accounting.rule.service.AccountingRuleReadPlatformService;
|
import org.mifosplatform.accounting.rule.service.AccountingRuleReadPlatformService;
|
||||||
import org.mifosplatform.commands.domain.CommandWrapper;
|
import org.mifosplatform.commands.domain.CommandWrapper;
|
||||||
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
||||||
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||||
|
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
|
||||||
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||||
@@ -47,7 +55,9 @@ import org.springframework.stereotype.Component;
|
|||||||
public class AccountingRuleApiResource {
|
public class AccountingRuleApiResource {
|
||||||
|
|
||||||
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "officeId", "officeName",
|
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "officeId", "officeName",
|
||||||
"accountToDebitId", "accountToCreditId", "name", "description", "systemDefined"));
|
"accountToDebitId", "accountToCreditId", "name", "description", "systemDefined", "allowedCreditTagOptions",
|
||||||
|
"allowedDebitTagOptions", "debitTags", "creditTags", "creditAccounts", "debitAccounts", "allowMultipleCreditEntries",
|
||||||
|
"allowMultipleDebitEntries", "tag"));
|
||||||
|
|
||||||
private final String resourceNameForPermission = "ACCOUNTINGRULE";
|
private final String resourceNameForPermission = "ACCOUNTINGRULE";
|
||||||
|
|
||||||
@@ -58,6 +68,7 @@ public class AccountingRuleApiResource {
|
|||||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AccountingRuleApiResource(final PlatformSecurityContext context,
|
public AccountingRuleApiResource(final PlatformSecurityContext context,
|
||||||
@@ -65,7 +76,7 @@ public class AccountingRuleApiResource {
|
|||||||
final DefaultToApiJsonSerializer<AccountingRuleData> toApiJsonSerializer,
|
final DefaultToApiJsonSerializer<AccountingRuleData> toApiJsonSerializer,
|
||||||
final ApiRequestParameterHelper apiRequestParameterHelper, final GLAccountReadPlatformService accountReadPlatformService,
|
final ApiRequestParameterHelper apiRequestParameterHelper, final GLAccountReadPlatformService accountReadPlatformService,
|
||||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||||
final OfficeReadPlatformService officeReadPlatformService) {
|
final OfficeReadPlatformService officeReadPlatformService, final CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||||
@@ -73,30 +84,40 @@ public class AccountingRuleApiResource {
|
|||||||
this.accountingRuleReadPlatformService = accountingRuleReadPlatformService;
|
this.accountingRuleReadPlatformService = accountingRuleReadPlatformService;
|
||||||
this.officeReadPlatformService = officeReadPlatformService;
|
this.officeReadPlatformService = officeReadPlatformService;
|
||||||
this.accountReadPlatformService = accountReadPlatformService;
|
this.accountReadPlatformService = accountReadPlatformService;
|
||||||
|
this.codeValueReadPlatformService = codeValueReadPlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("template")
|
@Path("template")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String retrieveTemplate(@Context final UriInfo uriInfo) {
|
public String retrieveTemplate(@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
|
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
|
||||||
|
|
||||||
AccountingRuleData accountingRuleData = null;
|
AccountingRuleData accountingRuleData = null;
|
||||||
accountingRuleData = handleTemplate(accountingRuleData);
|
accountingRuleData = handleTemplate(accountingRuleData);
|
||||||
|
|
||||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS);
|
return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String retrieveAllAccountingRules(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) {
|
public String retrieveAllAccountingRules(@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
|
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
|
||||||
final List<AccountingRuleData> accountingRuleDatas = this.accountingRuleReadPlatformService.retrieveAllAccountingRules(officeId);
|
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||||
|
final Long officeId = this.context.authenticatedUser().getOffice().getId();
|
||||||
|
boolean isAssociationParametersExists = false;
|
||||||
|
if (!associationParameters.isEmpty()) {
|
||||||
|
if (associationParameters.contains("all")) {
|
||||||
|
isAssociationParametersExists = true; // If true, retrieve additional fields for journal entry form.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final List<AccountingRuleData> accountingRuleDatas = this.accountingRuleReadPlatformService.retrieveAllAccountingRules(officeId,
|
||||||
|
isAssociationParametersExists);
|
||||||
|
|
||||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
return this.apiJsonSerializerService.serialize(settings, accountingRuleDatas, RESPONSE_DATA_PARAMETERS);
|
return this.apiJsonSerializerService.serialize(settings, accountingRuleDatas, RESPONSE_DATA_PARAMETERS);
|
||||||
@@ -109,12 +130,11 @@ public class AccountingRuleApiResource {
|
|||||||
public String retreiveAccountingRule(@PathParam("accountingRuleId") final Long accountingRuleId, @Context final UriInfo uriInfo) {
|
public String retreiveAccountingRule(@PathParam("accountingRuleId") final Long accountingRuleId, @Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
|
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
|
||||||
|
|
||||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
|
||||||
AccountingRuleData accountingRuleData = this.accountingRuleReadPlatformService.retrieveAccountingRuleById(accountingRuleId);
|
AccountingRuleData accountingRuleData = this.accountingRuleReadPlatformService.retrieveAccountingRuleById(accountingRuleId);
|
||||||
if (settings.isTemplate()) {
|
if (settings.isTemplate()) {
|
||||||
accountingRuleData = handleTemplate(accountingRuleData);
|
accountingRuleData = handleTemplate(accountingRuleData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS);
|
return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS);
|
||||||
@@ -158,12 +178,59 @@ public class AccountingRuleApiResource {
|
|||||||
return this.apiJsonSerializerService.serialize(result);
|
return this.apiJsonSerializerService.serialize(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountingRuleData handleTemplate(AccountingRuleData accountingRuleData) {
|
private AccountingRuleData handleTemplate(AccountingRuleData accountingRuleData) {
|
||||||
List<GLAccountData> allowedAccounts = this.accountReadPlatformService.retrieveAllEnabledDetailGLAccounts();
|
final List<GLAccountData> allowedAccounts = this.accountReadPlatformService.retrieveAllEnabledDetailGLAccounts();
|
||||||
List<OfficeData> allowedOffices = (List<OfficeData>) this.officeReadPlatformService.retrieveAllOfficesForDropdown();
|
final List<OfficeData> allowedOffices = (List<OfficeData>) this.officeReadPlatformService.retrieveAllOfficesForDropdown();
|
||||||
if (accountingRuleData == null) {
|
final Collection<CodeValueData> allowedTagOptions = this.codeValueReadPlatformService
|
||||||
return new AccountingRuleData(allowedAccounts, allowedOffices);
|
.retrieveCodeValuesByCode(AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME);
|
||||||
}
|
|
||||||
return new AccountingRuleData(accountingRuleData, allowedAccounts, allowedOffices);
|
allowedTagOptions.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME));
|
||||||
}
|
allowedTagOptions.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME));
|
||||||
|
allowedTagOptions.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.INCOME_TAG_OPTION_CODE_NAME));
|
||||||
|
allowedTagOptions.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME));
|
||||||
|
|
||||||
|
if (accountingRuleData == null) {
|
||||||
|
|
||||||
|
final Collection<CodeValueData> allowedCreditTagOptions = allowedTagOptions;
|
||||||
|
final Collection<CodeValueData> allowedDebitTagOptions = allowedTagOptions;
|
||||||
|
|
||||||
|
accountingRuleData = new AccountingRuleData(allowedAccounts, allowedOffices, allowedCreditTagOptions, allowedDebitTagOptions);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
final Collection<CodeValueData> allowedCreditTagOptions;
|
||||||
|
final Collection<CodeValueData> allowedDebitTagOptions;
|
||||||
|
|
||||||
|
if (accountingRuleData.getCreditTags() != null) {
|
||||||
|
allowedCreditTagOptions = retrieveSelectedTags(allowedTagOptions, accountingRuleData.getCreditTags());
|
||||||
|
} else {
|
||||||
|
allowedCreditTagOptions = allowedTagOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountingRuleData.getDebitTags() != null) {
|
||||||
|
allowedDebitTagOptions = retrieveSelectedTags(allowedTagOptions, accountingRuleData.getDebitTags());
|
||||||
|
} else {
|
||||||
|
allowedDebitTagOptions = allowedTagOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
accountingRuleData = new AccountingRuleData(accountingRuleData, allowedAccounts, allowedOffices, allowedCreditTagOptions,
|
||||||
|
allowedDebitTagOptions);
|
||||||
|
}
|
||||||
|
return accountingRuleData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<CodeValueData> retrieveSelectedTags(final Collection<CodeValueData> allowedTagOptions,
|
||||||
|
final List<AccountingTagRuleData> existedTags) {
|
||||||
|
final Collection<CodeValueData> tempOptions = new ArrayList<CodeValueData>(allowedTagOptions);
|
||||||
|
final Map<Long, CodeValueData> selectedTags = new HashMap<Long, CodeValueData>();
|
||||||
|
for (final AccountingTagRuleData accountingTagRuleData : existedTags) {
|
||||||
|
for (final CodeValueData codeValueData : tempOptions) {
|
||||||
|
if (codeValueData.getId().equals(accountingTagRuleData.getTag().getId())) {
|
||||||
|
selectedTags.put(codeValueData.getId(), codeValueData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tempOptions.removeAll(selectedTags.values());
|
||||||
|
return tempOptions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -13,7 +13,8 @@ import java.util.Set;
|
|||||||
***/
|
***/
|
||||||
public enum AccountingRuleJsonInputParams {
|
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("debitAccountHead"), ACCOUNT_TO_CREDIT("creditAccountHead"), NAME("name"), DESCRIPTION(
|
||||||
"description"), SYSTEM_DEFINED("systemDefined");
|
"description"), SYSTEM_DEFINED("systemDefined"), DEBIT_ACCOUNT_TAGS("debitTags"), CREDIT_ACCOUNT_TAGS("creditTags"), ALLOW_MULTIPLE_CREDIT_ENTRIES(
|
||||||
|
"allowMultipleCreditEntries"), ALLOW_MULTIPLE_DEBIT_ENTRIES("allowMultipleDebitEntries");
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
|
|||||||
-79
@@ -1,79 +0,0 @@
|
|||||||
/**
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
package org.mifosplatform.accounting.rule.command;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams;
|
|
||||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
|
||||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Immutable command for adding an accounting closure
|
|
||||||
*/
|
|
||||||
public class AccountingRuleCommand {
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final Long id;
|
|
||||||
private final Long officeId;
|
|
||||||
private final Long accountToDebitId;
|
|
||||||
private final Long accountToCreditId;
|
|
||||||
private final String name;
|
|
||||||
private final String description;
|
|
||||||
|
|
||||||
public AccountingRuleCommand(Long id, Long officeId, Long accountToDebitId, Long accountToCreditId, String name, String description) {
|
|
||||||
this.id = id;
|
|
||||||
this.officeId = officeId;
|
|
||||||
this.accountToDebitId = accountToDebitId;
|
|
||||||
this.accountToCreditId = accountToCreditId;
|
|
||||||
this.name = name;
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validateForCreate() {
|
|
||||||
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
|
||||||
|
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("AccountingRule");
|
|
||||||
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()).value(this.accountToDebitId)
|
|
||||||
.notNull().integerGreaterThanZero();
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()).value(this.accountToCreditId)
|
|
||||||
.notNull().integerGreaterThanZero();
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.OFFICE_ID.getValue()).value(this.officeId).ignoreIfNull()
|
|
||||||
.integerGreaterThanZero();
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.NAME.getValue()).value(this.name).notBlank()
|
|
||||||
.notExceedingLengthOf(100);
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DESCRIPTION.getValue()).value(this.description).ignoreIfNull()
|
|
||||||
.notExceedingLengthOf(500);
|
|
||||||
|
|
||||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
|
||||||
"Validation errors exist.", dataValidationErrors); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validateForUpdate() {
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
|
||||||
|
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("AccountingRule");
|
|
||||||
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()).value(this.accountToDebitId)
|
|
||||||
.ignoreIfNull().notBlank().integerGreaterThanZero();
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()).value(this.accountToCreditId)
|
|
||||||
.ignoreIfNull().notBlank().integerGreaterThanZero();
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.OFFICE_ID.getValue()).value(this.officeId).ignoreIfNull()
|
|
||||||
.integerGreaterThanZero();
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.NAME.getValue()).value(this.name).ignoreIfNull().notBlank()
|
|
||||||
.notExceedingLengthOf(100);
|
|
||||||
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DESCRIPTION.getValue()).value(this.description).ignoreIfNull()
|
|
||||||
.notExceedingLengthOf(500);
|
|
||||||
|
|
||||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
|
||||||
"Validation errors exist.", dataValidationErrors); }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+131
-74
@@ -6,9 +6,12 @@
|
|||||||
package org.mifosplatform.accounting.rule.data;
|
package org.mifosplatform.accounting.rule.data;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
||||||
|
import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup;
|
||||||
|
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||||
import org.mifosplatform.organisation.office.data.OfficeData;
|
import org.mifosplatform.organisation.office.data.OfficeData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,85 +22,139 @@ import org.mifosplatform.organisation.office.data.OfficeData;
|
|||||||
*/
|
*/
|
||||||
public class AccountingRuleData {
|
public class AccountingRuleData {
|
||||||
|
|
||||||
private final Long id;
|
private final Long id;
|
||||||
private final Long officeId;
|
private final Long officeId;
|
||||||
private final String officeName;
|
private final String officeName;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String description;
|
private final String description;
|
||||||
private final boolean systemDefined;
|
private final boolean systemDefined;
|
||||||
private final GLAccountData debitAccountHead;
|
private final boolean allowMultipleDebitEntries;
|
||||||
private final GLAccountData creditAccountHead;
|
private final boolean allowMultipleCreditEntries;
|
||||||
|
private final GLAccountData debitAccountHead;
|
||||||
|
private final GLAccountData creditAccountHead;
|
||||||
|
private final List<AccountingTagRuleData> creditTags;
|
||||||
|
private final List<AccountingTagRuleData> debitTags;
|
||||||
|
|
||||||
// template
|
// template
|
||||||
@SuppressWarnings("unused")
|
private List<OfficeData> allowedOffices = new ArrayList<OfficeData>();
|
||||||
private List<OfficeData> allowedOffices = new ArrayList<OfficeData>();
|
private List<GLAccountData> allowedAccounts = new ArrayList<GLAccountData>();
|
||||||
@SuppressWarnings("unused")
|
private final Collection<CodeValueData> allowedCreditTagOptions;
|
||||||
private List<GLAccountData> allowedAccounts = new ArrayList<GLAccountData>();
|
private final Collection<CodeValueData> allowedDebitTagOptions;
|
||||||
|
private final List<GLAccountDataForLookup> creditAccounts;
|
||||||
|
private final List<GLAccountDataForLookup> debitAccounts;
|
||||||
|
|
||||||
/*public AccountingRuleData(final Long id, final Long accountToDebitId, final Long accountToCreditId) {
|
public AccountingRuleData(final AccountingRuleData accountingRuleData, final List<GLAccountData> allowedAccounts,
|
||||||
this(id, null, null, accountToDebitId, accountToCreditId, null, null, true, null, null, null, null, null, null);
|
final List<OfficeData> allowedOffices, final Collection<CodeValueData> allowedCreditTagOptions,
|
||||||
}*/
|
final Collection<CodeValueData> allowedDebitTagOptions) {
|
||||||
|
this.id = accountingRuleData.id;
|
||||||
|
this.officeId = accountingRuleData.officeId;
|
||||||
|
this.officeName = accountingRuleData.officeName;
|
||||||
|
this.name = accountingRuleData.name;
|
||||||
|
this.description = accountingRuleData.description;
|
||||||
|
this.systemDefined = accountingRuleData.systemDefined;
|
||||||
|
this.allowMultipleDebitEntries = accountingRuleData.allowMultipleDebitEntries;
|
||||||
|
this.allowMultipleCreditEntries = accountingRuleData.allowMultipleCreditEntries;
|
||||||
|
this.allowedOffices = allowedOffices;
|
||||||
|
this.allowedAccounts = allowedAccounts;
|
||||||
|
this.debitAccountHead = accountingRuleData.debitAccountHead;
|
||||||
|
this.creditAccountHead = accountingRuleData.creditAccountHead;
|
||||||
|
this.allowedCreditTagOptions = allowedCreditTagOptions;
|
||||||
|
this.allowedDebitTagOptions = allowedDebitTagOptions;
|
||||||
|
this.creditTags = accountingRuleData.creditTags;
|
||||||
|
this.debitTags = accountingRuleData.debitTags;
|
||||||
|
this.creditAccounts = accountingRuleData.creditAccounts;
|
||||||
|
this.debitAccounts = accountingRuleData.debitAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
/*public AccountingRuleData(final Long id, final Long officeId, final String officeName, final Long accountToDebitId,
|
public AccountingRuleData(final List<GLAccountData> allowedAccounts, final List<OfficeData> allowedOffices,
|
||||||
final Long accountToCreditId, final String name, final String description, final boolean systemDefined,
|
final Collection<CodeValueData> allowedCreditTagOptions, final Collection<CodeValueData> allowedDebitTagOptions) {
|
||||||
final String debitAccountName, final String creditAccountName, final String debitAccountGLCode,
|
this.id = null;
|
||||||
final String creditAccountGLCode) {
|
this.officeId = null;
|
||||||
this(id, officeId, officeName, accountToDebitId, accountToCreditId, name, description, systemDefined, debitAccountName,
|
this.officeName = null;
|
||||||
creditAccountName, debitAccountGLCode, creditAccountGLCode, null, null);
|
this.name = null;
|
||||||
}*/
|
this.description = null;
|
||||||
|
this.systemDefined = false;
|
||||||
|
this.allowMultipleDebitEntries = false;
|
||||||
|
this.allowMultipleCreditEntries = false;
|
||||||
|
this.allowedOffices = allowedOffices;
|
||||||
|
this.allowedAccounts = allowedAccounts;
|
||||||
|
this.debitAccountHead = null;
|
||||||
|
this.creditAccountHead = null;
|
||||||
|
this.allowedCreditTagOptions = allowedCreditTagOptions;
|
||||||
|
this.allowedDebitTagOptions = allowedDebitTagOptions;
|
||||||
|
this.creditTags = null;
|
||||||
|
this.debitTags = null;
|
||||||
|
this.creditAccounts = null;
|
||||||
|
this.debitAccounts = null;
|
||||||
|
}
|
||||||
|
|
||||||
public AccountingRuleData(final Long id, final Long officeId, final String officeName, final String name, final String description,
|
public AccountingRuleData(final Long id, final Long officeId, final String officeName, final String name, final String description,
|
||||||
final boolean systemDefined, final List<OfficeData> allowedOffices, final List<GLAccountData> allowedAccounts) {
|
final boolean systemDefined, final boolean allowMultipleDebitEntries, final boolean allowMultipleCreditEntries,
|
||||||
this.id = id;
|
final GLAccountData debitAccountData, final GLAccountData creditAccountData) {
|
||||||
this.officeId = officeId;
|
this.id = id;
|
||||||
this.officeName = officeName;
|
this.officeId = officeId;
|
||||||
this.name = name;
|
this.officeName = officeName;
|
||||||
this.description = description;
|
this.name = name;
|
||||||
this.systemDefined = systemDefined;
|
this.description = description;
|
||||||
this.allowedOffices = allowedOffices;
|
this.systemDefined = systemDefined;
|
||||||
this.allowedAccounts = allowedAccounts;
|
this.allowMultipleDebitEntries = allowMultipleDebitEntries;
|
||||||
this.debitAccountHead = null;
|
this.allowMultipleCreditEntries = allowMultipleCreditEntries;
|
||||||
this.creditAccountHead = null;
|
this.allowedOffices = null;
|
||||||
}
|
this.allowedAccounts = null;
|
||||||
|
this.debitAccountHead = debitAccountData;
|
||||||
|
this.creditAccountHead = creditAccountData;
|
||||||
|
this.allowedCreditTagOptions = null;
|
||||||
|
this.allowedDebitTagOptions = null;
|
||||||
|
this.creditTags = null;
|
||||||
|
this.debitTags = null;
|
||||||
|
this.creditAccounts = new ArrayList<GLAccountDataForLookup>();
|
||||||
|
this.debitAccounts = new ArrayList<GLAccountDataForLookup>();
|
||||||
|
}
|
||||||
|
|
||||||
public AccountingRuleData(final AccountingRuleData accountingRuleData,
|
public AccountingRuleData(final AccountingRuleData accountingRuleData, final List<AccountingTagRuleData> creditTags,
|
||||||
final List<GLAccountData> allowedAccounts, final List<OfficeData> allowedOffices) {
|
final List<GLAccountDataForLookup> creditAccounts, final List<AccountingTagRuleData> debitTags,
|
||||||
this.id = accountingRuleData.id;
|
final List<GLAccountDataForLookup> debitAccounts) {
|
||||||
this.officeId = accountingRuleData.officeId;
|
this.id = accountingRuleData.id;
|
||||||
this.officeName = accountingRuleData.officeName;
|
this.officeId = accountingRuleData.officeId;
|
||||||
this.name = accountingRuleData.name;
|
this.officeName = accountingRuleData.officeName;
|
||||||
this.description = accountingRuleData.description;
|
this.name = accountingRuleData.name;
|
||||||
this.systemDefined = accountingRuleData.systemDefined;
|
this.description = accountingRuleData.description;
|
||||||
this.allowedOffices = allowedOffices;
|
this.systemDefined = accountingRuleData.systemDefined;
|
||||||
this.allowedAccounts = allowedAccounts;
|
this.allowMultipleDebitEntries = accountingRuleData.allowMultipleDebitEntries;
|
||||||
this.debitAccountHead = accountingRuleData.debitAccountHead;
|
this.allowMultipleCreditEntries = accountingRuleData.allowMultipleCreditEntries;
|
||||||
this.creditAccountHead = accountingRuleData.creditAccountHead;
|
this.allowedOffices = accountingRuleData.allowedOffices;
|
||||||
}
|
this.allowedAccounts = accountingRuleData.allowedAccounts;
|
||||||
|
this.debitAccountHead = accountingRuleData.debitAccountHead;
|
||||||
|
this.creditAccountHead = accountingRuleData.creditAccountHead;
|
||||||
|
this.allowedCreditTagOptions = accountingRuleData.allowedCreditTagOptions;
|
||||||
|
this.allowedDebitTagOptions = accountingRuleData.allowedDebitTagOptions;
|
||||||
|
this.creditTags = creditTags;
|
||||||
|
this.creditAccounts = creditAccounts;
|
||||||
|
this.debitTags = debitTags;
|
||||||
|
this.debitAccounts = debitAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
public AccountingRuleData(final List<GLAccountData> allowedAccounts, final List<OfficeData> allowedOffices) {
|
public GLAccountData getDebitAccountHead() {
|
||||||
this.id = null;
|
return this.debitAccountHead;
|
||||||
this.officeId = null;
|
}
|
||||||
this.officeName = null;
|
|
||||||
this.name = null;
|
|
||||||
this.description = null;
|
|
||||||
this.systemDefined = false;
|
|
||||||
this.allowedOffices = allowedOffices;
|
|
||||||
this.allowedAccounts = allowedAccounts;
|
|
||||||
this.debitAccountHead = null;
|
|
||||||
this.creditAccountHead = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AccountingRuleData(Long id, Long officeId, String officeName, String name, String description,
|
public GLAccountData getCreditAccountHead() {
|
||||||
boolean systemDefined, GLAccountData debitAccountData, GLAccountData creditAccountData) {
|
return this.creditAccountHead;
|
||||||
this.id = id;
|
}
|
||||||
this.officeId = officeId;
|
|
||||||
this.officeName = officeName;
|
public List<AccountingTagRuleData> getCreditTags() {
|
||||||
this.name = name;
|
return this.creditTags;
|
||||||
this.description = description;
|
}
|
||||||
this.systemDefined = systemDefined;
|
|
||||||
this.allowedOffices = null;
|
public List<AccountingTagRuleData> getDebitTags() {
|
||||||
this.allowedAccounts = null;
|
return this.debitTags;
|
||||||
this.debitAccountHead = debitAccountData;
|
}
|
||||||
this.creditAccountHead = creditAccountData;
|
|
||||||
}
|
public List<GLAccountDataForLookup> getCreditAccounts() {
|
||||||
|
return this.creditAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GLAccountDataForLookup> getDebitAccounts() {
|
||||||
|
return this.debitAccounts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.rule.data;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||||
|
|
||||||
|
public class AccountingTagRuleData {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Long id;
|
||||||
|
private final CodeValueData tag;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final EnumOptionData transactionType;
|
||||||
|
|
||||||
|
public AccountingTagRuleData(final Long id, final CodeValueData tag, final EnumOptionData transactionType) {
|
||||||
|
this.id = id;
|
||||||
|
this.tag = tag;
|
||||||
|
this.transactionType = transactionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeValueData getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+113
-13
@@ -5,18 +5,27 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.accounting.rule.domain;
|
package org.mifosplatform.accounting.rule.domain;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.UniqueConstraint;
|
import javax.persistence.UniqueConstraint;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.hibernate.annotations.LazyCollection;
|
||||||
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.mifosplatform.accounting.journalentry.domain.JournalEntryType;
|
||||||
import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams;
|
import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.organisation.office.domain.Office;
|
import org.mifosplatform.organisation.office.domain.Office;
|
||||||
@@ -34,11 +43,11 @@ public class AccountingRule extends AbstractPersistable<Long> {
|
|||||||
private Office office;
|
private Office office;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "debit_account_id", nullable = false)
|
@JoinColumn(name = "debit_account_id", nullable = true)
|
||||||
private GLAccount accountToDebit;
|
private GLAccount accountToDebit;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "credit_account_id", nullable = false)
|
@JoinColumn(name = "credit_account_id", nullable = true)
|
||||||
private GLAccount accountToCredit;
|
private GLAccount accountToCredit;
|
||||||
|
|
||||||
@Column(name = "description", nullable = true, length = 500)
|
@Column(name = "description", nullable = true, length = 500)
|
||||||
@@ -47,10 +56,21 @@ public class AccountingRule extends AbstractPersistable<Long> {
|
|||||||
@Column(name = "system_defined", nullable = false)
|
@Column(name = "system_defined", nullable = false)
|
||||||
private Boolean systemDefined;
|
private Boolean systemDefined;
|
||||||
|
|
||||||
|
@LazyCollection(LazyCollectionOption.FALSE)
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountingRule", orphanRemoval = true)
|
||||||
|
private final List<AccountingTagRule> accountingTagRules = new ArrayList<AccountingTagRule>();
|
||||||
|
|
||||||
|
@Column(name = "allow_multiple_credits", nullable = false)
|
||||||
|
private boolean allowMultipleCreditEntries;
|
||||||
|
|
||||||
|
@Column(name = "allow_multiple_debits", nullable = false)
|
||||||
|
private boolean allowMultipleDebitEntries;
|
||||||
|
|
||||||
protected AccountingRule() {}
|
protected AccountingRule() {}
|
||||||
|
|
||||||
private AccountingRule(Office office, GLAccount accountToDebit, GLAccount accountToCredit, String name, String description,
|
private AccountingRule(final Office office, final GLAccount accountToDebit, final GLAccount accountToCredit, final String name,
|
||||||
boolean systemDefined) {
|
final String description, final boolean systemDefined, final boolean allowMultipleCreditEntries,
|
||||||
|
final boolean allowMultipleDebitEntries) {
|
||||||
this.accountToDebit = accountToDebit;
|
this.accountToDebit = accountToDebit;
|
||||||
this.accountToCredit = accountToCredit;
|
this.accountToCredit = accountToCredit;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@@ -60,24 +80,34 @@ public class AccountingRule extends AbstractPersistable<Long> {
|
|||||||
this.description = this.description.trim();
|
this.description = this.description.trim();
|
||||||
}
|
}
|
||||||
this.systemDefined = systemDefined;
|
this.systemDefined = systemDefined;
|
||||||
|
this.allowMultipleCreditEntries = allowMultipleCreditEntries;
|
||||||
|
this.allowMultipleDebitEntries = allowMultipleDebitEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AccountingRule fromJson(final Office office, final GLAccount accountToDebit, final GLAccount accountToCredit,
|
public static AccountingRule fromJson(final Office office, final GLAccount accountToDebit, final GLAccount accountToCredit,
|
||||||
JsonCommand command) {
|
final JsonCommand command, final boolean allowMultipleCreditEntries, final boolean allowMultipleDebitEntries) {
|
||||||
final String name = command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue());
|
final String name = command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue());
|
||||||
final String description = command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue());
|
final String description = command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue());
|
||||||
final boolean systemDefined = false;
|
final boolean systemDefined = false;
|
||||||
return new AccountingRule(office, accountToDebit, accountToCredit, name, description, systemDefined);
|
return new AccountingRule(office, accountToDebit, accountToCredit, name, description, systemDefined, allowMultipleCreditEntries,
|
||||||
|
allowMultipleDebitEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> update(final JsonCommand command) {
|
public Map<String, Object> update(final JsonCommand command) {
|
||||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(5);
|
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(10);
|
||||||
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.OFFICE_ID.getValue(), 0L);
|
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.OFFICE_ID.getValue(), this.office == null ? 0L
|
||||||
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(), 0L);
|
: this.office.getId());
|
||||||
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(), 0L);
|
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
|
||||||
|
this.accountToDebit == null ? 0L : this.accountToDebit.getId());
|
||||||
|
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
|
||||||
|
this.accountToCredit == null ? 0L : this.accountToCredit.getId());
|
||||||
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.NAME.getValue(), this.name);
|
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.NAME.getValue(), this.name);
|
||||||
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.DESCRIPTION.getValue(), this.description);
|
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.DESCRIPTION.getValue(), this.description);
|
||||||
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue(), this.systemDefined);
|
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue(), this.systemDefined);
|
||||||
|
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(),
|
||||||
|
this.allowMultipleCreditEntries);
|
||||||
|
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(),
|
||||||
|
this.allowMultipleDebitEntries);
|
||||||
return actualChanges;
|
return actualChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +133,14 @@ public class AccountingRule extends AbstractPersistable<Long> {
|
|||||||
// now update actual property
|
// now update actual property
|
||||||
if (paramName.equals(AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue())) {
|
if (paramName.equals(AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue())) {
|
||||||
this.systemDefined = newValue;
|
this.systemDefined = newValue;
|
||||||
|
} else if (paramName.equals(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())) {
|
||||||
|
if (this.accountToCredit == null) {
|
||||||
|
this.allowMultipleCreditEntries = newValue;
|
||||||
|
}
|
||||||
|
} else if (paramName.equals(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())) {
|
||||||
|
if (this.accountToDebit == null) {
|
||||||
|
this.allowMultipleDebitEntries = newValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +161,7 @@ public class AccountingRule extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOffice(Office office) {
|
public void setOffice(final Office office) {
|
||||||
this.office = office;
|
this.office = office;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,11 +177,11 @@ public class AccountingRule extends AbstractPersistable<Long> {
|
|||||||
return this.accountToCredit;
|
return this.accountToCredit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAccountToDebit(GLAccount accountToDebit) {
|
public void setAccountToDebit(final GLAccount accountToDebit) {
|
||||||
this.accountToDebit = accountToDebit;
|
this.accountToDebit = accountToDebit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAccountToCredit(GLAccount accountToCredit) {
|
public void setAccountToCredit(final GLAccount accountToCredit) {
|
||||||
this.accountToCredit = accountToCredit;
|
this.accountToCredit = accountToCredit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,4 +189,66 @@ public class AccountingRule extends AbstractPersistable<Long> {
|
|||||||
return this.description;
|
return this.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<AccountingTagRule> getAccountingTagRules() {
|
||||||
|
return this.accountingTagRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateAccountingRuleForTags(final List<AccountingTagRule> debitAccountingTagRules) {
|
||||||
|
for (final AccountingTagRule accountingTagRule : debitAccountingTagRules) {
|
||||||
|
accountingTagRule.updateAccountingTagRule(this);
|
||||||
|
this.accountingTagRules.add(accountingTagRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateDebitAccount(final GLAccount accountToDebit) {
|
||||||
|
this.accountToDebit = accountToDebit;
|
||||||
|
this.allowMultipleDebitEntries = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateCreditAccount(final GLAccount accountToCredit) {
|
||||||
|
this.accountToCredit = accountToCredit;
|
||||||
|
this.allowMultipleCreditEntries = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateAllowMultipleCreditEntries(final boolean allowMultipleCreditEntries) {
|
||||||
|
this.allowMultipleCreditEntries = allowMultipleCreditEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateAllowMultipleDebitEntries(final boolean allowMultipleDebitEntries) {
|
||||||
|
this.allowMultipleDebitEntries = allowMultipleDebitEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateTags(final JournalEntryType type) {
|
||||||
|
final Set<AccountingTagRule> existedCreditTags = new HashSet<AccountingTagRule>();
|
||||||
|
final Set<AccountingTagRule> existedDebitTags = new HashSet<AccountingTagRule>();
|
||||||
|
for (final AccountingTagRule accountingTagRule : this.accountingTagRules) {
|
||||||
|
if (accountingTagRule.isCreditAccount()) {
|
||||||
|
existedCreditTags.add(accountingTagRule);
|
||||||
|
} else if (accountingTagRule.isDebitAccount()) {
|
||||||
|
existedDebitTags.add(accountingTagRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.isCreditType()) {
|
||||||
|
this.accountingTagRules.retainAll(existedCreditTags);
|
||||||
|
} else if (type.isDebitType()) {
|
||||||
|
this.accountingTagRules.retainAll(existedDebitTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<AccountingTagRule> getAccountingTagRulesByType(final JournalEntryType type) {
|
||||||
|
final Set<AccountingTagRule> existedTags = new HashSet<AccountingTagRule>();
|
||||||
|
for (final AccountingTagRule accountingTagRule : this.accountingTagRules) {
|
||||||
|
if (accountingTagRule.getAccountType().equals(type.getValue())) {
|
||||||
|
existedTags.add(accountingTagRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return existedTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOldTags(final List<AccountingTagRule> accountsToRemove) {
|
||||||
|
this.accountingTagRules.removeAll(accountsToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.rule.domain;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.UniqueConstraint;
|
||||||
|
|
||||||
|
import org.mifosplatform.accounting.journalentry.domain.JournalEntryType;
|
||||||
|
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "acc_rule_tags", uniqueConstraints = { @UniqueConstraint(columnNames = { "acc_rule_id", "tag_id", "acc_type_enum" }, name = "UNIQUE_ACCOUNT_RULE_TAGS") })
|
||||||
|
public class AccountingTagRule extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "acc_rule_id", nullable = false)
|
||||||
|
private AccountingRule accountingRule;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "tag_id", nullable = false)
|
||||||
|
private CodeValue tagId;
|
||||||
|
|
||||||
|
@Column(name = "acc_type_enum", nullable = false)
|
||||||
|
private Integer accountType;
|
||||||
|
|
||||||
|
public static AccountingTagRule create(final CodeValue tagId, final Integer accountType) {
|
||||||
|
return new AccountingTagRule(tagId, accountType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountingTagRule(final CodeValue tagId, final Integer accountType) {
|
||||||
|
this.tagId = tagId;
|
||||||
|
this.accountType = accountType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateAccountingTagRule(final AccountingRule accountingRule) {
|
||||||
|
this.accountingRule = accountingRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountingTagRule() {
|
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getAccountType() {
|
||||||
|
return this.accountType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDebitAccount() {
|
||||||
|
return JournalEntryType.fromInt(this.accountType).isDebitType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCreditAccount() {
|
||||||
|
return JournalEntryType.fromInt(this.accountType).isCreditType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTagId() {
|
||||||
|
return this.tagId.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package org.mifosplatform.accounting.rule.exception;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class AccountingRuleDataException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public AccountingRuleDataException(final String debitOrCreditAccount, final String debitOrCreditTags) {
|
||||||
|
super("error.msg.accounting.rule." + debitOrCreditAccount + ".or." + debitOrCreditTags + ".required", debitOrCreditAccount + " or "
|
||||||
|
+ debitOrCreditTags + " required", debitOrCreditAccount, debitOrCreditTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+4
@@ -17,4 +17,8 @@ public class AccountingRuleDuplicateException extends AbstractPlatformDomainRule
|
|||||||
super("error.msg.accounting.rule.duplicate", "An accounting rule with the name " + name + " already exists" + name);
|
super("error.msg.accounting.rule.duplicate", "An accounting rule with the name " + name + " already exists" + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccountingRuleDuplicateException() {
|
||||||
|
super("error.msg.accounting.rule.tag.duplicate", "The accounting rule already have the tags which you defined");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+168
-18
@@ -6,56 +6,206 @@
|
|||||||
package org.mifosplatform.accounting.rule.serialization;
|
package org.mifosplatform.accounting.rule.serialization;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams;
|
import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams;
|
||||||
import org.mifosplatform.accounting.rule.command.AccountingRuleCommand;
|
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.InvalidJsonException;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
|
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.mifosplatform.portfolio.loanaccount.guarantor.command.GuarantorCommand;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of {@link FromApiJsonDeserializer} for
|
|
||||||
* {@link GuarantorCommand}'s.
|
|
||||||
*/
|
|
||||||
@Component
|
@Component
|
||||||
public final class AccountingRuleCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<AccountingRuleCommand> {
|
public class AccountingRuleCommandFromApiJsonDeserializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parameters supported for this command.
|
||||||
|
*/
|
||||||
|
private final Set<String> supportedParameters = AccountingRuleJsonInputParams.getAllValues();
|
||||||
|
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AccountingRuleCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonfromApiJsonHelper) {
|
public AccountingRuleCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
|
||||||
this.fromApiJsonHelper = fromApiJsonfromApiJsonHelper;
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void validateForCreate(final String json) {
|
||||||
public AccountingRuleCommand commandFromApiJson(final String json) {
|
|
||||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
|
|
||||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
final Set<String> supportedParameters = AccountingRuleJsonInputParams.getAllValues();
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
|
||||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("AccountingRule");
|
||||||
|
|
||||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
|
||||||
final Long id = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ID.getValue(), element);
|
|
||||||
final Long officeId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue(), element);
|
|
||||||
final Long accountToDebitId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
|
final Long accountToDebitId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
|
||||||
element);
|
element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()).value(accountToDebitId)
|
||||||
|
.ignoreIfNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Long accountToCreditId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
|
final Long accountToCreditId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
|
||||||
element);
|
element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()).value(accountToCreditId)
|
||||||
|
.ignoreIfNull().integerGreaterThanZero();
|
||||||
|
|
||||||
|
final Long officeId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.OFFICE_ID.getValue()).value(officeId).notNull()
|
||||||
|
.integerGreaterThanZero();
|
||||||
|
|
||||||
final String name = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.NAME.getValue(), element);
|
final String name = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.NAME.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.NAME.getValue()).value(name).notBlank().notExceedingLengthOf(100);
|
||||||
|
|
||||||
|
final String description = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DESCRIPTION.getValue()).value(description).ignoreIfNull()
|
||||||
|
.notExceedingLengthOf(500);
|
||||||
|
|
||||||
|
final String[] creditTags = this.fromApiJsonHelper.extractArrayNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue(),
|
||||||
|
element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()).value(creditTags).ignoreIfNull()
|
||||||
|
.arrayNotEmpty();
|
||||||
|
validateCreditOrDebitTagArray(creditTags, baseDataValidator, AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
|
||||||
|
|
||||||
|
final String[] debitTags = this.fromApiJsonHelper.extractArrayNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue(),
|
||||||
|
element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()).value(debitTags).ignoreIfNull()
|
||||||
|
.arrayNotEmpty();
|
||||||
|
validateCreditOrDebitTagArray(debitTags, baseDataValidator, AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
|
||||||
|
|
||||||
|
if (creditTags == null && accountToCreditId == null) {
|
||||||
|
final String creditTag = AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue();
|
||||||
|
final String creditAccount = AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue();
|
||||||
|
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(creditAccount).append(".or.")
|
||||||
|
.append(creditTag).append(".required");
|
||||||
|
final StringBuilder defaultUserMessage = new StringBuilder("The parameter ").append(creditAccount).append(" or")
|
||||||
|
.append(creditTag).append(" required");
|
||||||
|
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultUserMessage.toString(),
|
||||||
|
creditAccount + "," + creditTag, new Object[] { creditAccount, creditTag });
|
||||||
|
dataValidationErrors.add(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debitTags == null && accountToDebitId == null) {
|
||||||
|
final String debitTag = AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue();
|
||||||
|
final String debitAccount = AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue();
|
||||||
|
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(debitAccount).append(".or.")
|
||||||
|
.append(debitTag).append(".required");
|
||||||
|
final StringBuilder defaultUserMessage = new StringBuilder("The parameter ").append(debitAccount).append(" or")
|
||||||
|
.append(debitTag).append(" required");
|
||||||
|
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultUserMessage.toString(),
|
||||||
|
debitAccount + "," + debitTag, new Object[] { debitAccount, debitTag });
|
||||||
|
dataValidationErrors.add(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String allowMultipleCredits = this.fromApiJsonHelper.extractStringNamed(
|
||||||
|
AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())
|
||||||
|
.value(allowMultipleCredits).ignoreIfNull().notBlank();
|
||||||
|
final Boolean allowMultipleCreditEntries = this.fromApiJsonHelper.extractBooleanNamed(
|
||||||
|
AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())
|
||||||
|
.value(allowMultipleCreditEntries).ignoreIfNull();
|
||||||
|
final String allowMultipleDebits = this.fromApiJsonHelper.extractStringNamed(
|
||||||
|
AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())
|
||||||
|
.value(allowMultipleDebits).ignoreIfNull().notBlank();
|
||||||
|
final Boolean allowMultipleDebitEntries = this.fromApiJsonHelper.extractBooleanNamed(
|
||||||
|
AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())
|
||||||
|
.value(allowMultipleDebitEntries).ignoreIfNull();
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||||
|
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateCreditOrDebitTagArray(final String[] creditOrDebitTagArray, final DataValidatorBuilder baseDataValidator,
|
||||||
|
final String parameter) {
|
||||||
|
if (creditOrDebitTagArray != null && !ObjectUtils.isEmpty(creditOrDebitTagArray)) {
|
||||||
|
for (final String tag : creditOrDebitTagArray) {
|
||||||
|
baseDataValidator.reset().parameter(parameter).value(tag).ignoreIfNull().notBlank().longGreaterThanZero();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateForUpdate(final String json) {
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
|
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("AccountingRule");
|
||||||
|
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
|
||||||
|
final Long accountToDebitId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
|
||||||
|
element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()).value(accountToDebitId)
|
||||||
|
.ignoreIfNull().notBlank().integerGreaterThanZero();
|
||||||
|
|
||||||
|
final Long accountToCreditId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
|
||||||
|
element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()).value(accountToCreditId)
|
||||||
|
.ignoreIfNull().notBlank().integerGreaterThanZero();
|
||||||
|
|
||||||
|
final Long officeId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.OFFICE_ID.getValue()).value(officeId).ignoreIfNull()
|
||||||
|
.integerGreaterThanZero();
|
||||||
|
|
||||||
|
final String name = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.NAME.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.NAME.getValue()).value(name).ignoreIfNull().notBlank()
|
||||||
|
.notExceedingLengthOf(100);
|
||||||
|
|
||||||
final String description = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue(), element);
|
final String description = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue(), element);
|
||||||
|
|
||||||
return new AccountingRuleCommand(id, officeId, accountToDebitId, accountToCreditId, name, description);
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DESCRIPTION.getValue()).value(description).ignoreIfNull()
|
||||||
|
.notExceedingLengthOf(500);
|
||||||
|
|
||||||
|
final String[] creditTags = this.fromApiJsonHelper.extractArrayNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue(),
|
||||||
|
element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()).value(creditTags).ignoreIfNull()
|
||||||
|
.arrayNotEmpty();
|
||||||
|
validateCreditOrDebitTagArray(creditTags, baseDataValidator, AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
|
||||||
|
|
||||||
|
final String[] debitTags = this.fromApiJsonHelper.extractArrayNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue(),
|
||||||
|
element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()).value(debitTags).ignoreIfNull()
|
||||||
|
.arrayNotEmpty();
|
||||||
|
validateCreditOrDebitTagArray(debitTags, baseDataValidator, AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
|
||||||
|
|
||||||
|
final String allowMultipleCredits = this.fromApiJsonHelper.extractStringNamed(
|
||||||
|
AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())
|
||||||
|
.value(allowMultipleCredits).ignoreIfNull().notBlank();
|
||||||
|
final Boolean allowMultipleCreditEntries = this.fromApiJsonHelper.extractBooleanNamed(
|
||||||
|
AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())
|
||||||
|
.value(allowMultipleCreditEntries).ignoreIfNull();
|
||||||
|
final String allowMultipleDebits = this.fromApiJsonHelper.extractStringNamed(
|
||||||
|
AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())
|
||||||
|
.value(allowMultipleDebits).ignoreIfNull().notBlank();
|
||||||
|
final Boolean allowMultipleDebitEntries = this.fromApiJsonHelper.extractBooleanNamed(
|
||||||
|
AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())
|
||||||
|
.value(allowMultipleDebitEntries).ignoreIfNull();
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -11,8 +11,8 @@ import org.mifosplatform.accounting.rule.data.AccountingRuleData;
|
|||||||
|
|
||||||
public interface AccountingRuleReadPlatformService {
|
public interface AccountingRuleReadPlatformService {
|
||||||
|
|
||||||
List<AccountingRuleData> retrieveAllAccountingRules(Long OfficeId);
|
List<AccountingRuleData> retrieveAllAccountingRules(Long OfficeId, boolean isAssociationParametersExists);
|
||||||
|
|
||||||
AccountingRuleData retrieveAccountingRuleById(long accountingRuleId);
|
AccountingRuleData retrieveAccountingRuleById(Long accountingRuleId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+125
-45
@@ -7,16 +7,28 @@ package org.mifosplatform.accounting.rule.service;
|
|||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.mifosplatform.accounting.common.AccountingEnumerations;
|
||||||
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
|
||||||
|
import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup;
|
||||||
|
import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService;
|
||||||
|
import org.mifosplatform.accounting.journalentry.domain.JournalEntryType;
|
||||||
import org.mifosplatform.accounting.rule.data.AccountingRuleData;
|
import org.mifosplatform.accounting.rule.data.AccountingRuleData;
|
||||||
|
import org.mifosplatform.accounting.rule.data.AccountingTagRuleData;
|
||||||
import org.mifosplatform.accounting.rule.exception.AccountingRuleNotFoundException;
|
import org.mifosplatform.accounting.rule.exception.AccountingRuleNotFoundException;
|
||||||
|
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -24,29 +36,37 @@ import org.springframework.stereotype.Service;
|
|||||||
public class AccountingRuleReadPlatformServiceImpl implements AccountingRuleReadPlatformService {
|
public class AccountingRuleReadPlatformServiceImpl implements AccountingRuleReadPlatformService {
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
private final GLAccountReadPlatformService glAccountReadPlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AccountingRuleReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) {
|
public AccountingRuleReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource,
|
||||||
|
final GLAccountReadPlatformService glAccountReadPlatformService) {
|
||||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||||
|
this.glAccountReadPlatformService = glAccountReadPlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class AccountingRuleMapper implements RowMapper<AccountingRuleData> {
|
private static final class AccountingRuleDataExtractor implements ResultSetExtractor<Map<Long, AccountingRuleData>> {
|
||||||
|
|
||||||
private final String schemaSql;
|
private final String schemaSql;
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
private final GLAccountReadPlatformService glAccountReadPlatformService;
|
||||||
|
private final boolean isAssociationParametersExists;
|
||||||
|
|
||||||
public AccountingRuleMapper() {
|
public AccountingRuleDataExtractor(final JdbcTemplate jdbcTemplate,
|
||||||
|
final GLAccountReadPlatformService glAccountReadPlatformService, final boolean isAssociationParametersExists) {
|
||||||
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
|
this.glAccountReadPlatformService = glAccountReadPlatformService;
|
||||||
|
this.isAssociationParametersExists = isAssociationParametersExists;
|
||||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
sqlBuilder.append("distinct rule.id as id,rule.name as name, rule.office_id as officeId,office.name as officeName,")
|
sqlBuilder
|
||||||
.append(" rule.debit_account_id as debitAccountId,rule.credit_account_id as creditAccountId,")
|
.append(" rule.id as id,rule.name as name, rule.office_id as officeId,office.name as officeName,")
|
||||||
.append(" rule.description as description, rule.system_defined as systemDefined, ")
|
.append(" rule.description as description, rule.system_defined as systemDefined, rule.allow_multiple_debits as allowMultipleDebitEntries, rule.allow_multiple_credits as allowMultipleCreditEntries, ")
|
||||||
.append("debitAccount.glName as debitAccountName, debitAccount.glCode as debitAccountGLCode,")
|
.append("debitAccount.id AS debitAccountId, debitAccount.name as debitAccountName, debitAccount.gl_code as debitAccountGLCode, ")
|
||||||
.append("creditAccount.glName as creditAccountName, creditAccount.glCode as creditAccountGLCode ")
|
.append("creditAccount.id AS creditAccountId, creditAccount.name as creditAccountName, creditAccount.gl_code as creditAccountGLCode")
|
||||||
.append("from (Select gl.id as id,gl.name as glName, gl.gl_code as glCode from acc_gl_account gl,acc_accounting_rule rule ")
|
.append(" from m_office AS office, acc_accounting_rule AS rule ")
|
||||||
.append("where gl.id=rule.debit_account_id ) as debitAccount,")
|
.append(" LEFT JOIN acc_gl_account AS creditAccount ON rule.credit_account_id = creditAccount.id ")
|
||||||
.append("(Select gl.id as id,gl.name as glName, gl.gl_code as glCode from acc_gl_account gl,acc_accounting_rule rule ")
|
.append(" LEFT JOIN acc_gl_account AS debitAccount ON rule.debit_account_id = debitAccount.id ")
|
||||||
.append("where gl.id=rule.credit_account_id ) as creditAccount,")
|
.append("WHERE office.id=rule.office_id ");
|
||||||
.append("acc_accounting_rule rule left join m_office office on rule.office_id=office.id ")
|
|
||||||
.append("where debitAccount.id=rule.debit_account_id and creditAccount.id = rule.credit_account_id");
|
|
||||||
this.schemaSql = sqlBuilder.toString();
|
this.schemaSql = sqlBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,52 +75,112 @@ public class AccountingRuleReadPlatformServiceImpl implements AccountingRuleRead
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccountingRuleData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
public Map<Long, AccountingRuleData> extractData(final ResultSet rs) throws SQLException, DataAccessException {
|
||||||
|
final Map<Long, AccountingRuleData> extractedData = new HashMap<Long, AccountingRuleData>();
|
||||||
|
|
||||||
final Long id = rs.getLong("id");
|
while (rs.next()) {
|
||||||
final Long officeId = JdbcSupport.getLong(rs, "officeId");
|
final Long id = rs.getLong("id");
|
||||||
final String officeName = rs.getString("officeName");
|
AccountingRuleData accountingRuleData = extractedData.get(id);
|
||||||
final String name = rs.getString("name");
|
if (accountingRuleData == null) {
|
||||||
final String description = rs.getString("description");
|
final Long officeId = JdbcSupport.getLong(rs, "officeId");
|
||||||
final Long accountToDebitId = rs.getLong("debitAccountId");
|
final String officeName = rs.getString("officeName");
|
||||||
final Long accountToCreditId = rs.getLong("creditAccountId");
|
final String name = rs.getString("name");
|
||||||
final boolean systemDefined = rs.getBoolean("systemDefined");
|
final String description = rs.getString("description");
|
||||||
final String debitAccountName = rs.getString("debitAccountName");
|
final Long accountToDebitId = JdbcSupport.getLong(rs, "debitAccountId");
|
||||||
final String creditAccountName = rs.getString("creditAccountName");
|
final Long accountToCreditId = JdbcSupport.getLong(rs, "creditAccountId");
|
||||||
final String debitAccountGLCode = rs.getString("debitAccountGLCode");
|
final boolean systemDefined = rs.getBoolean("systemDefined");
|
||||||
final String creditAccountGLCode = rs.getString("creditAccountGLCode");
|
final boolean allowMultipleDebitEntries = rs.getBoolean("allowMultipleDebitEntries");
|
||||||
|
final boolean allowMultipleCreditEntries = rs.getBoolean("allowMultipleCreditEntries");
|
||||||
|
final String debitAccountName = rs.getString("debitAccountName");
|
||||||
|
final String creditAccountName = rs.getString("creditAccountName");
|
||||||
|
final String debitAccountGLCode = rs.getString("debitAccountGLCode");
|
||||||
|
final String creditAccountGLCode = rs.getString("creditAccountGLCode");
|
||||||
|
|
||||||
final GLAccountData debitAccountData = new GLAccountData(accountToDebitId, debitAccountName, debitAccountGLCode);
|
final GLAccountData debitAccountData = accountToDebitId == null ? null : new GLAccountData(accountToDebitId,
|
||||||
final GLAccountData creditAccountData = new GLAccountData(accountToCreditId, creditAccountName, creditAccountGLCode);
|
debitAccountName, debitAccountGLCode);
|
||||||
|
final GLAccountData creditAccountData = accountToCreditId == null ? null : new GLAccountData(accountToCreditId,
|
||||||
return new AccountingRuleData(id, officeId, officeName, name, description, systemDefined, debitAccountData, creditAccountData);
|
creditAccountName, creditAccountGLCode);
|
||||||
|
accountingRuleData = new AccountingRuleData(id, officeId, officeName, name, description, systemDefined,
|
||||||
|
allowMultipleDebitEntries, allowMultipleCreditEntries, debitAccountData, creditAccountData);
|
||||||
|
}
|
||||||
|
if (accountingRuleData.getCreditAccountHead() == null) {
|
||||||
|
final List<AccountingTagRuleData> creditTags = !this.isAssociationParametersExists ? getCreditOrDebitTags(id,
|
||||||
|
JournalEntryType.CREDIT.getValue()) : null;
|
||||||
|
final List<GLAccountDataForLookup> creditAccounts = this.isAssociationParametersExists ? this.glAccountReadPlatformService
|
||||||
|
.retrieveAccountsByTagId(id, JournalEntryType.CREDIT.getValue()) : null;
|
||||||
|
accountingRuleData = new AccountingRuleData(accountingRuleData, creditTags, creditAccounts,
|
||||||
|
accountingRuleData.getDebitTags(), accountingRuleData.getDebitAccounts());
|
||||||
|
}
|
||||||
|
if (accountingRuleData.getDebitAccountHead() == null) {
|
||||||
|
final List<AccountingTagRuleData> debitTags = !this.isAssociationParametersExists ? getCreditOrDebitTags(id,
|
||||||
|
JournalEntryType.DEBIT.getValue()) : null;
|
||||||
|
final List<GLAccountDataForLookup> debitAccounts = this.isAssociationParametersExists ? this.glAccountReadPlatformService
|
||||||
|
.retrieveAccountsByTagId(id, JournalEntryType.DEBIT.getValue()) : null;
|
||||||
|
accountingRuleData = new AccountingRuleData(accountingRuleData, accountingRuleData.getCreditTags(),
|
||||||
|
accountingRuleData.getCreditAccounts(), debitTags, debitAccounts);
|
||||||
|
}
|
||||||
|
extractedData.put(id, accountingRuleData);
|
||||||
|
}
|
||||||
|
return extractedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<AccountingTagRuleData> getCreditOrDebitTags(final Long creditOrDebitAccount, final Integer transactionType) {
|
||||||
|
final AccountingTagRuleDataMapper mapper = new AccountingTagRuleDataMapper();
|
||||||
|
final String taggedAccountsSchema = "Select " + mapper.taggedAccountSchema() + " where rule.id = ? and tag.acc_type_enum=?";
|
||||||
|
return this.jdbcTemplate.query(taggedAccountsSchema, mapper, new Object[] { creditOrDebitAccount, transactionType });
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AccountingRuleData> retrieveAllAccountingRules(Long officeId) {
|
public List<AccountingRuleData> retrieveAllAccountingRules(final Long officeId, final boolean isAssociationParametersExists) {
|
||||||
final AccountingRuleMapper rm = new AccountingRuleMapper();
|
final AccountingRuleDataExtractor resultSetExtractor = new AccountingRuleDataExtractor(this.jdbcTemplate,
|
||||||
Object[] arguments = new Object[]{};
|
this.glAccountReadPlatformService, isAssociationParametersExists);
|
||||||
String sql = "select " + rm.schema() +" and system_defined=0 ";
|
Object[] arguments = new Object[] {};
|
||||||
if(officeId != null) {
|
String sql = "select " + resultSetExtractor.schema() + " and system_defined=0 ";
|
||||||
sql = sql +" and office.id = ?";
|
if (officeId != null) {
|
||||||
arguments = new Object[] { officeId };
|
sql = sql + " and office.id = ?";
|
||||||
|
arguments = new Object[] { officeId };
|
||||||
}
|
}
|
||||||
sql = sql + " order by rule.id asc";
|
sql = sql + " order by rule.id asc";
|
||||||
return this.jdbcTemplate.query(sql, rm, arguments);
|
final Map<Long, AccountingRuleData> extractedData = this.jdbcTemplate.query(sql, resultSetExtractor, arguments);
|
||||||
|
return new ArrayList<AccountingRuleData>(extractedData.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccountingRuleData retrieveAccountingRuleById(long accountingRuleId) {
|
public AccountingRuleData retrieveAccountingRuleById(final Long accountingRuleId) {
|
||||||
try {
|
try {
|
||||||
|
final AccountingRuleDataExtractor resultSetExtractor = new AccountingRuleDataExtractor(this.jdbcTemplate,
|
||||||
|
this.glAccountReadPlatformService, false);
|
||||||
|
final String sql = "select " + resultSetExtractor.schema() + " and rule.id = ?";
|
||||||
|
|
||||||
final AccountingRuleMapper rm = new AccountingRuleMapper();
|
final Map<Long, AccountingRuleData> extractedData = this.jdbcTemplate.query(sql, resultSetExtractor,
|
||||||
final String sql = "select " + rm.schema() + " and rule.id = ?";
|
new Object[] { accountingRuleId });
|
||||||
|
final AccountingRuleData accountingRuleData = extractedData.get(accountingRuleId);
|
||||||
final AccountingRuleData accountingRuleData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { accountingRuleId });
|
if (accountingRuleData == null) { throw new AccountingRuleNotFoundException(accountingRuleId); }
|
||||||
return accountingRuleData;
|
return accountingRuleData;
|
||||||
} catch (final EmptyResultDataAccessException e) {
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
throw new AccountingRuleNotFoundException(accountingRuleId);
|
throw new AccountingRuleNotFoundException(accountingRuleId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class AccountingTagRuleDataMapper implements RowMapper<AccountingTagRuleData> {
|
||||||
|
|
||||||
|
public String taggedAccountSchema() {
|
||||||
|
return " tag.id as id,tag.tag_id as tagId, tag.acc_type_enum as transactionType, cv.code_value as tagName from m_code_value cv join acc_rule_tags tag on tag.tag_id=cv.id "
|
||||||
|
+ "join acc_accounting_rule rule on tag.acc_rule_id=rule.id ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccountingTagRuleData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final Long tagId = rs.getLong("tagId");
|
||||||
|
final Integer transactionType = JdbcSupport.getInteger(rs, "transactionType");
|
||||||
|
final String tagName = rs.getString("tagName");
|
||||||
|
final CodeValueData tag = CodeValueData.instance(tagId, tagName);
|
||||||
|
final EnumOptionData transactionTypeEnum = AccountingEnumerations.journalEntryType(transactionType);
|
||||||
|
return new AccountingTagRuleData(id, tag, transactionTypeEnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+236
-57
@@ -5,18 +5,30 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.accounting.rule.service;
|
package org.mifosplatform.accounting.rule.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams;
|
import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
|
||||||
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
||||||
|
import org.mifosplatform.accounting.journalentry.domain.JournalEntryType;
|
||||||
import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams;
|
import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams;
|
||||||
import org.mifosplatform.accounting.rule.command.AccountingRuleCommand;
|
|
||||||
import org.mifosplatform.accounting.rule.domain.AccountingRule;
|
import org.mifosplatform.accounting.rule.domain.AccountingRule;
|
||||||
import org.mifosplatform.accounting.rule.domain.AccountingRuleRepository;
|
import org.mifosplatform.accounting.rule.domain.AccountingRuleRepository;
|
||||||
import org.mifosplatform.accounting.rule.domain.AccountingRuleRepositoryWrapper;
|
import org.mifosplatform.accounting.rule.domain.AccountingRuleRepositoryWrapper;
|
||||||
|
import org.mifosplatform.accounting.rule.domain.AccountingTagRule;
|
||||||
|
import org.mifosplatform.accounting.rule.exception.AccountingRuleDataException;
|
||||||
import org.mifosplatform.accounting.rule.exception.AccountingRuleDuplicateException;
|
import org.mifosplatform.accounting.rule.exception.AccountingRuleDuplicateException;
|
||||||
import org.mifosplatform.accounting.rule.serialization.AccountingRuleCommandFromApiJsonDeserializer;
|
import org.mifosplatform.accounting.rule.serialization.AccountingRuleCommandFromApiJsonDeserializer;
|
||||||
|
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
|
||||||
|
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepository;
|
||||||
|
import org.mifosplatform.infrastructure.codes.exception.CodeValueNotFoundException;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
@@ -41,16 +53,19 @@ public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements Acco
|
|||||||
private final GLAccountRepositoryWrapper accountRepositoryWrapper;
|
private final GLAccountRepositoryWrapper accountRepositoryWrapper;
|
||||||
private final OfficeRepository officeRepository;
|
private final OfficeRepository officeRepository;
|
||||||
private final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
private final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||||
|
private final CodeValueRepository codeValueRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AccountingRuleWritePlatformServiceJpaRepositoryImpl(final AccountingRuleRepositoryWrapper accountingRuleRepositoryWrapper,
|
public AccountingRuleWritePlatformServiceJpaRepositoryImpl(final AccountingRuleRepositoryWrapper accountingRuleRepositoryWrapper,
|
||||||
final GLAccountRepositoryWrapper accountRepositoryWrapper, final OfficeRepository officeRepository,
|
final GLAccountRepositoryWrapper accountRepositoryWrapper, final OfficeRepository officeRepository,
|
||||||
final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer, final AccountingRuleRepository ruleRepository) {
|
final AccountingRuleRepository ruleRepository, final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer,
|
||||||
|
final CodeValueRepository codeValueRepository) {
|
||||||
this.accountRepositoryWrapper = accountRepositoryWrapper;
|
this.accountRepositoryWrapper = accountRepositoryWrapper;
|
||||||
this.officeRepository = officeRepository;
|
this.officeRepository = officeRepository;
|
||||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
|
||||||
this.accountingRuleRepository = ruleRepository;
|
this.accountingRuleRepository = ruleRepository;
|
||||||
this.accountingRuleRepositoryWrapper = accountingRuleRepositoryWrapper;
|
this.accountingRuleRepositoryWrapper = accountingRuleRepositoryWrapper;
|
||||||
|
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||||
|
this.codeValueRepository = codeValueRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,8 +74,9 @@ public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements Acco
|
|||||||
*/
|
*/
|
||||||
private void handleAccountingRuleIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
|
private void handleAccountingRuleIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
|
||||||
final Throwable realCause = dve.getMostSpecificCause();
|
final Throwable realCause = dve.getMostSpecificCause();
|
||||||
if (realCause.getMessage().contains("accounting_rule_name_unique")) { throw new AccountingRuleDuplicateException(
|
if (realCause.getMessage().contains("accounting_rule_name_unique")) {
|
||||||
command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue())); }
|
throw new AccountingRuleDuplicateException(command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue()));
|
||||||
|
} else if (realCause.getMessage().contains("UNIQUE_ACCOUNT_RULE_TAGS")) { throw new AccountingRuleDuplicateException(); }
|
||||||
|
|
||||||
logger.error(dve.getMessage(), dve);
|
logger.error(dve.getMessage(), dve);
|
||||||
throw new PlatformDataIntegrityException("error.msg.accounting.rule.unknown.data.integrity.issue",
|
throw new PlatformDataIntegrityException("error.msg.accounting.rule.unknown.data.integrity.issue",
|
||||||
@@ -69,10 +85,10 @@ public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements Acco
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult createAccountingRule(JsonCommand command) {
|
public CommandProcessingResult createAccountingRule(final JsonCommand command) {
|
||||||
try {
|
try {
|
||||||
final AccountingRuleCommand accountingRuleCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
|
|
||||||
accountingRuleCommand.validateForCreate();
|
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
||||||
|
|
||||||
// check office is valid
|
// check office is valid
|
||||||
final Long officeId = command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue());
|
final Long officeId = command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue());
|
||||||
@@ -82,15 +98,8 @@ public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements Acco
|
|||||||
if (office == null) { throw new OfficeNotFoundException(officeId); }
|
if (office == null) { throw new OfficeNotFoundException(officeId); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the GL Accounts to Debit and Credit
|
final AccountingRule accountingRule = assembleAccountingRuleAndTags(office, command);
|
||||||
final Long accountToDebitId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue());
|
|
||||||
final Long accountToCreditId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue());
|
|
||||||
final GLAccount accountToDebit = accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId);
|
|
||||||
final GLAccount accountToCredit = accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId);
|
|
||||||
|
|
||||||
final AccountingRule accountingRule = AccountingRule.fromJson(office, accountToDebit, accountToCredit, command);
|
|
||||||
this.accountingRuleRepository.saveAndFlush(accountingRule);
|
this.accountingRuleRepository.saveAndFlush(accountingRule);
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId)
|
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId)
|
||||||
.withEntityId(accountingRule.getId()).build();
|
.withEntityId(accountingRule.getId()).build();
|
||||||
} catch (final DataIntegrityViolationException dve) {
|
} catch (final DataIntegrityViolationException dve) {
|
||||||
@@ -99,59 +108,229 @@ public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements Acco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
private AccountingRule assembleAccountingRuleAndTags(final Office office, final JsonCommand command) {
|
||||||
@Override
|
// get the GL Accounts or tags to Debit and Credit
|
||||||
public CommandProcessingResult updateAccountingRule(Long accountingRuleId, JsonCommand command) {
|
final String[] debitTags = command.arrayValueOfParameterNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
|
||||||
final AccountingRuleCommand accountingRuleCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
|
final String[] creditTags = command.arrayValueOfParameterNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
|
||||||
accountingRuleCommand.validateForUpdate();
|
final Set<String> incomingDebitTags = debitTags == null ? new HashSet<String>(): new HashSet<String>(Arrays.asList(debitTags));
|
||||||
|
final Set<String> incomingCreditTags = creditTags == null ? new HashSet<String>() : new HashSet<String>(Arrays.asList(creditTags));
|
||||||
|
final Long accountToDebitId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue());
|
||||||
|
final Long accountToCreditId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue());
|
||||||
|
|
||||||
Long officeId = null;
|
boolean allowMultipleCreditEntries = false;
|
||||||
if (command.parameterExists(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) {
|
boolean allowMultipleDebitEntries = false;
|
||||||
officeId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue());
|
GLAccount debitAccount = null;
|
||||||
|
GLAccount creditAccount = null;
|
||||||
|
List<AccountingTagRule> accountingTagRules = new ArrayList<AccountingTagRule>();
|
||||||
|
|
||||||
|
if ((accountToDebitId != null && debitTags != null) || (accountToDebitId == null && debitTags == null)) {
|
||||||
|
throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
|
||||||
|
AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
|
||||||
|
} else if (accountToDebitId != null) {
|
||||||
|
debitAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId);
|
||||||
|
} else if (debitTags != null) {
|
||||||
|
accountingTagRules = saveDebitOrCreditTags(incomingDebitTags, JournalEntryType.DEBIT, accountingTagRules);
|
||||||
|
allowMultipleDebitEntries = command
|
||||||
|
.booleanPrimitiveValueOfParameterNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
Long accountToDebitId = null;
|
if ((accountToCreditId != null && creditTags != null) || (accountToCreditId == null && creditTags == null)) {
|
||||||
if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) {
|
throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
|
||||||
accountToDebitId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue());
|
AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
|
||||||
|
} else if (accountToCreditId != null) {
|
||||||
|
creditAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId);
|
||||||
|
} else if (creditTags != null) {
|
||||||
|
accountingTagRules = saveDebitOrCreditTags(incomingCreditTags, JournalEntryType.CREDIT, accountingTagRules);
|
||||||
|
allowMultipleCreditEntries = command
|
||||||
|
.booleanPrimitiveValueOfParameterNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
Long accountToCreditId = null;
|
final AccountingRule accountingRule = AccountingRule.fromJson(office, debitAccount, creditAccount, command,
|
||||||
if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) {
|
allowMultipleCreditEntries, allowMultipleDebitEntries);
|
||||||
accountToCreditId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue());
|
accountingRule.updateAccountingRuleForTags(accountingTagRules);
|
||||||
}
|
|
||||||
|
|
||||||
final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper.findOneWithNotFoundDetection(accountingRuleId);
|
return accountingRule;
|
||||||
final Map<String, Object> changesOnly = accountingRule.update(command);
|
|
||||||
|
|
||||||
if (accountToDebitId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) {
|
|
||||||
final GLAccount accountToDebit = accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId);
|
|
||||||
accountingRule.setAccountToDebit(accountToDebit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountToCreditId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) {
|
|
||||||
final GLAccount accountToCredit = accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId);
|
|
||||||
accountingRule.setAccountToCredit(accountToCredit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (officeId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) {
|
|
||||||
final Office userOffice = this.officeRepository.findOne(officeId);
|
|
||||||
if (userOffice == null) { throw new OfficeNotFoundException(officeId); }
|
|
||||||
accountingRule.setOffice(userOffice);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!changesOnly.isEmpty()) {
|
|
||||||
this.accountingRuleRepository.saveAndFlush(accountingRule);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(accountingRule.getId())
|
|
||||||
.with(changesOnly).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult deleteAccountingRule(Long accountingRuleId) {
|
public CommandProcessingResult updateAccountingRule(final Long accountingRuleId, final JsonCommand command) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
this.fromApiJsonDeserializer.validateForUpdate(command.json());
|
||||||
|
|
||||||
|
Long officeId = null;
|
||||||
|
if (command.parameterExists(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) {
|
||||||
|
officeId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
Long accountToDebitId = null;
|
||||||
|
if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) {
|
||||||
|
accountToDebitId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
Long accountToCreditId = null;
|
||||||
|
if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) {
|
||||||
|
accountToCreditId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] debitTags = null;
|
||||||
|
if (command.parameterExists(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue())) {
|
||||||
|
debitTags = command.arrayValueOfParameterNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] creditTags = null;
|
||||||
|
if (command.parameterExists(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue())) {
|
||||||
|
creditTags = command.arrayValueOfParameterNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountToDebitId != null && debitTags != null) { throw new AccountingRuleDataException(
|
||||||
|
AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(), AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()); }
|
||||||
|
|
||||||
|
if (accountToCreditId != null && creditTags != null) { throw new AccountingRuleDataException(
|
||||||
|
AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
|
||||||
|
AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()); }
|
||||||
|
|
||||||
|
boolean allowMultipleCreditEntries = false;
|
||||||
|
if (command.parameterExists(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())) {
|
||||||
|
allowMultipleCreditEntries = command
|
||||||
|
.booleanPrimitiveValueOfParameterNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allowMultipleDebitEntries = false;
|
||||||
|
if (command.parameterExists(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())) {
|
||||||
|
allowMultipleDebitEntries = command
|
||||||
|
.booleanPrimitiveValueOfParameterNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper.findOneWithNotFoundDetection(accountingRuleId);
|
||||||
|
final Map<String, Object> changesOnly = accountingRule.update(command);
|
||||||
|
|
||||||
|
if (accountToDebitId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) {
|
||||||
|
final GLAccount accountToDebit = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId);
|
||||||
|
accountingRule.updateDebitAccount(accountToDebit);
|
||||||
|
accountingRule.updateTags(JournalEntryType.CREDIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountToCreditId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) {
|
||||||
|
final GLAccount accountToCredit = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId);
|
||||||
|
accountingRule.updateCreditAccount(accountToCredit);
|
||||||
|
accountingRule.updateTags(JournalEntryType.DEBIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creditTags != null && creditTags.length > 0
|
||||||
|
&& command.parameterExists(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue())) {
|
||||||
|
|
||||||
|
final Set<String> creditTagsToAdd = determineCreditTagToAddAndRemoveOldTags(creditTags, JournalEntryType.CREDIT,
|
||||||
|
accountingRule);
|
||||||
|
|
||||||
|
if (!creditTagsToAdd.isEmpty()) {
|
||||||
|
List<AccountingTagRule> accountingTagRules = new ArrayList<AccountingTagRule>();
|
||||||
|
accountingTagRules = saveDebitOrCreditTags(creditTagsToAdd, JournalEntryType.CREDIT, accountingTagRules);
|
||||||
|
accountingRule.updateAccountingRuleForTags(accountingTagRules);
|
||||||
|
accountingRule.updateCreditAccount(null);
|
||||||
|
if (allowMultipleCreditEntries) {
|
||||||
|
accountingRule.updateAllowMultipleCreditEntries(allowMultipleCreditEntries);
|
||||||
|
}
|
||||||
|
changesOnly.put(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue(), creditTagsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debitTags != null && debitTags.length > 0
|
||||||
|
&& command.parameterExists(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue())) {
|
||||||
|
final Set<String> debitTagsToAdd = determineCreditTagToAddAndRemoveOldTags(debitTags, JournalEntryType.DEBIT,
|
||||||
|
accountingRule);
|
||||||
|
if (!debitTagsToAdd.isEmpty()) {
|
||||||
|
List<AccountingTagRule> accountingTagRules = new ArrayList<AccountingTagRule>();
|
||||||
|
accountingTagRules = saveDebitOrCreditTags(debitTagsToAdd, JournalEntryType.DEBIT, accountingTagRules);
|
||||||
|
accountingRule.updateAccountingRuleForTags(accountingTagRules);
|
||||||
|
accountingRule.updateDebitAccount(null);
|
||||||
|
if (allowMultipleDebitEntries) {
|
||||||
|
accountingRule.updateAllowMultipleDebitEntries(allowMultipleDebitEntries);
|
||||||
|
}
|
||||||
|
changesOnly.put(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue(), debitTagsToAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (officeId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) {
|
||||||
|
final Office userOffice = this.officeRepository.findOne(officeId);
|
||||||
|
if (userOffice == null) { throw new OfficeNotFoundException(officeId); }
|
||||||
|
accountingRule.setOffice(userOffice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changesOnly.isEmpty()) {
|
||||||
|
this.accountingRuleRepository.saveAndFlush(accountingRule);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(accountingRule.getId())
|
||||||
|
.with(changesOnly).build();
|
||||||
|
} catch (final DataIntegrityViolationException dve) {
|
||||||
|
handleAccountingRuleIntegrityIssues(command, dve);
|
||||||
|
return CommandProcessingResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> determineCreditTagToAddAndRemoveOldTags(final String[] creditOrDebitTags, final JournalEntryType type,
|
||||||
|
final AccountingRule accountingRule) {
|
||||||
|
|
||||||
|
final Set<String> incomingTags = new HashSet<String>(Arrays.asList(creditOrDebitTags));
|
||||||
|
final Set<AccountingTagRule> existingTags = accountingRule.getAccountingTagRulesByType(type);
|
||||||
|
final Set<String> existingTagIds = retrieveExistingTagIds(existingTags);
|
||||||
|
final Set<String> tagsToAdd = new HashSet<String>();
|
||||||
|
final Set<String> tagsToRemove = existingTagIds;
|
||||||
|
final Map<Long, AccountingTagRule> accountsToRemove = new HashMap<Long, AccountingTagRule>();
|
||||||
|
|
||||||
|
for (final String tagId : incomingTags) {
|
||||||
|
if (existingTagIds.contains(tagId)) {
|
||||||
|
tagsToRemove.remove(tagId);
|
||||||
|
} else {
|
||||||
|
tagsToAdd.add(tagId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tagsToRemove.isEmpty()) {
|
||||||
|
for (final String tagId : tagsToRemove) {
|
||||||
|
for (final AccountingTagRule accountingTagRule : existingTags) {
|
||||||
|
if (tagId.equals(accountingTagRule.getTagId().toString())) {
|
||||||
|
accountsToRemove.put(accountingTagRule.getId(), accountingTagRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accountingRule.removeOldTags(new ArrayList<AccountingTagRule>(accountsToRemove.values()));
|
||||||
|
}
|
||||||
|
return tagsToAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> retrieveExistingTagIds(final Set<AccountingTagRule> existingCreditTags) {
|
||||||
|
final Set<String> existingCreditTagIds = new HashSet<String>();
|
||||||
|
for (final AccountingTagRule accountingTagRule : existingCreditTags) {
|
||||||
|
existingCreditTagIds.add(accountingTagRule.getTagId().toString());
|
||||||
|
}
|
||||||
|
return existingCreditTagIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult deleteAccountingRule(final Long accountingRuleId) {
|
||||||
final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper.findOneWithNotFoundDetection(accountingRuleId);
|
final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper.findOneWithNotFoundDetection(accountingRuleId);
|
||||||
this.accountingRuleRepository.delete(accountingRule);
|
this.accountingRuleRepository.delete(accountingRule);
|
||||||
return new CommandProcessingResultBuilder().withEntityId(accountingRule.getId()).build();
|
return new CommandProcessingResultBuilder().withEntityId(accountingRule.getId()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<AccountingTagRule> saveDebitOrCreditTags(final Set<String> creditOrDebitTagArray, final JournalEntryType transactionType,
|
||||||
|
final List<AccountingTagRule> accountingTagRules) {
|
||||||
|
for (final String creditOrDebitTag : creditOrDebitTagArray) {
|
||||||
|
if (creditOrDebitTag != null && StringUtils.isNotBlank(creditOrDebitTag)) {
|
||||||
|
final Long creditOrDebitTagIdLongValue = Long.valueOf(creditOrDebitTag);
|
||||||
|
final CodeValue creditOrDebitAccount = this.codeValueRepository.findOne(creditOrDebitTagIdLongValue);
|
||||||
|
if (creditOrDebitAccount == null) { throw new CodeValueNotFoundException(creditOrDebitTagIdLongValue); }
|
||||||
|
final AccountingTagRule accountingTagRule = AccountingTagRule.create(creditOrDebitAccount, transactionType.getValue());
|
||||||
|
accountingTagRules.add(accountingTagRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accountingTagRules;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,21 @@ public class CommandWrapper {
|
|||||||
return this.actionName.equalsIgnoreCase("CREATE");
|
return this.actionName.equalsIgnoreCase("CREATE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCreateDatatable() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUpdateDatatable() {
|
||||||
|
return (this.actionName.equalsIgnoreCase("UPDATE") && this.href.startsWith("/datatables/")
|
||||||
|
&& this.entityId == null);
|
||||||
|
}
|
||||||
|
|
||||||
public String getTaskPermissionName() {
|
public String getTaskPermissionName() {
|
||||||
return this.taskPermissionName;
|
return this.taskPermissionName;
|
||||||
}
|
}
|
||||||
|
|||||||
+27
@@ -356,6 +356,33 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createDBDatatable(final String json) {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "DATATABLE";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/datatables/";
|
||||||
|
this.json = json;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder updateDBDatatable(final String datatable, final String json) {
|
||||||
|
this.actionName = "UPDATE";
|
||||||
|
this.entityName = "DATATABLE";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/datatables/" + datatable;
|
||||||
|
this.json = json;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder deleteDBDatatable(final String datatable, final String json) {
|
||||||
|
this.actionName = "DELETE";
|
||||||
|
this.entityName = "DATATABLE";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/datatables/" + datatable;
|
||||||
|
this.json = json;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder createDatatable(final String datatable, final Long apptableId, final Long datatableId) {
|
public CommandWrapperBuilder createDatatable(final String datatable, final Long apptableId, final Long datatableId) {
|
||||||
this.actionName = "CREATE";
|
this.actionName = "CREATE";
|
||||||
commonDatatableSettings(datatable, apptableId, datatableId);
|
commonDatatableSettings(datatable, apptableId, datatableId);
|
||||||
|
|||||||
+2
-2
@@ -63,7 +63,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
|
|||||||
final JsonCommand command = JsonCommand.from(json, parsedCommand, this.fromApiJsonHelper, wrapper.getEntityName(),
|
final JsonCommand command = JsonCommand.from(json, parsedCommand, this.fromApiJsonHelper, wrapper.getEntityName(),
|
||||||
wrapper.getEntityId(), wrapper.getSubentityId(), wrapper.getGroupId(), wrapper.getClientId(), wrapper.getLoanId(),
|
wrapper.getEntityId(), wrapper.getSubentityId(), wrapper.getGroupId(), wrapper.getClientId(), wrapper.getLoanId(),
|
||||||
wrapper.getSavingsId(), wrapper.getCodeId(), wrapper.getSupportedEntityType(), wrapper.getSupportedEntityId(),
|
wrapper.getSavingsId(), wrapper.getCodeId(), wrapper.getSupportedEntityType(), wrapper.getSupportedEntityId(),
|
||||||
wrapper.getTransactionId());
|
wrapper.getTransactionId(), wrapper.getHref());
|
||||||
|
|
||||||
result = this.processAndLogCommandService.processAndLogCommand(wrapper, command, isApprovedByChecker);
|
result = this.processAndLogCommandService.processAndLogCommand(wrapper, command, isApprovedByChecker);
|
||||||
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
|
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
|
||||||
@@ -86,7 +86,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
|
|||||||
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(commandSourceInput.json());
|
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(commandSourceInput.json());
|
||||||
final JsonCommand command = JsonCommand.fromExistingCommand(makerCheckerId, commandSourceInput.json(), parsedCommand,
|
final JsonCommand command = JsonCommand.fromExistingCommand(makerCheckerId, commandSourceInput.json(), parsedCommand,
|
||||||
this.fromApiJsonHelper, commandSourceInput.getEntityName(), commandSourceInput.resourceId(),
|
this.fromApiJsonHelper, commandSourceInput.getEntityName(), commandSourceInput.resourceId(),
|
||||||
commandSourceInput.subresourceId());
|
commandSourceInput.subresourceId(), commandSourceInput.getResourceGetUrl());
|
||||||
|
|
||||||
final boolean makerCheckerApproval = true;
|
final boolean makerCheckerApproval = true;
|
||||||
return this.processAndLogCommandService.processAndLogCommand(wrapper, command, makerCheckerApproval);
|
return this.processAndLogCommandService.processAndLogCommand(wrapper, command, makerCheckerApproval);
|
||||||
|
|||||||
+8
-2
@@ -78,7 +78,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
commandSourceResult.updateJsonTo(changesOnlyJson);
|
commandSourceResult.updateJsonTo(changesOnlyJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.hasChanges() && wrapper.isUpdateOperation()) {
|
if (!result.hasChanges() && wrapper.isUpdateOperation() && !wrapper.isUpdateDatatable()) {
|
||||||
commandSourceResult.updateJsonTo(null);
|
commandSourceResult.updateJsonTo(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +108,13 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
if (wrapper.isConfigurationResource()) {
|
if (wrapper.isConfigurationResource()) {
|
||||||
handler = applicationContext.getBean("updateGlobalConfigurationCommandHandler", NewCommandSourceHandler.class);
|
handler = applicationContext.getBean("updateGlobalConfigurationCommandHandler", NewCommandSourceHandler.class);
|
||||||
} else if (wrapper.isDatatableResource()) {
|
} else if (wrapper.isDatatableResource()) {
|
||||||
if (wrapper.isCreate()) {
|
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);
|
handler = applicationContext.getBean("createDatatableEntryCommandHandler", NewCommandSourceHandler.class);
|
||||||
} else if (wrapper.isUpdateMultiple()) {
|
} else if (wrapper.isUpdateMultiple()) {
|
||||||
handler = applicationContext.getBean("updateOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class);
|
handler = applicationContext.getBean("updateOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
|||||||
+4
-1
@@ -10,7 +10,6 @@ package org.mifosplatform.infrastructure.codes.data;
|
|||||||
*/
|
*/
|
||||||
public class CodeValueData {
|
public class CodeValueData {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final Long id;
|
private final Long id;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -32,4 +31,8 @@ public class CodeValueData {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package org.mifosplatform.infrastructure.configuration.data;
|
||||||
|
|
||||||
|
public class S3CredentialsData {
|
||||||
|
|
||||||
|
private final String bucketName;
|
||||||
|
private final String accessKey;
|
||||||
|
private final String secretKey;
|
||||||
|
|
||||||
|
public S3CredentialsData(String bucketName, String accessKey, String secretKey) {
|
||||||
|
this.bucketName = bucketName;
|
||||||
|
this.accessKey = accessKey;
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBucketName() {
|
||||||
|
return this.bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessKey() {
|
||||||
|
return this.accessKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecretKey() {
|
||||||
|
return this.secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
@@ -8,4 +8,5 @@ package org.mifosplatform.infrastructure.configuration.domain;
|
|||||||
public interface ConfigurationDomainService {
|
public interface ConfigurationDomainService {
|
||||||
|
|
||||||
boolean isMakerCheckerEnabledForTask(String taskPermissionCode);
|
boolean isMakerCheckerEnabledForTask(String taskPermissionCode);
|
||||||
|
boolean isAmazonS3Enabled();
|
||||||
}
|
}
|
||||||
+5
@@ -39,4 +39,9 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
|||||||
|
|
||||||
return thisTask.hasMakerCheckerEnabled() && property.isEnabled();
|
return thisTask.hasMakerCheckerEnabled() && property.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAmazonS3Enabled(){
|
||||||
|
return this.globalConfigurationRepository.findOneByName("amazon-S3").isEnabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package org.mifosplatform.infrastructure.configuration.domain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.UniqueConstraint;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "c_external_service", uniqueConstraints = { @UniqueConstraint(columnNames = ("name")) })
|
||||||
|
public class ExternalService extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Column(name = "name", nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "value", nullable = false)
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public ExternalService(final String name, final String value) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExternalService() {}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package org.mifosplatform.infrastructure.configuration.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface ExternalServiceRepository extends JpaRepository<ExternalService, Long>, JpaSpecificationExecutor<ExternalService> {
|
||||||
|
|
||||||
|
ExternalService findOneByName(String name);
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package org.mifosplatform.infrastructure.configuration.service;
|
||||||
|
|
||||||
|
public class ExternalServicesConstants {
|
||||||
|
|
||||||
|
public static final String S3_BUCKET_NAME = "s3_bucket_name";
|
||||||
|
public static final String S3_ACCESS_KEY = "s3_access_key";
|
||||||
|
public static final String S3_SECRET_KEY = "s3_secret_key";
|
||||||
|
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package org.mifosplatform.infrastructure.configuration.service;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData;
|
||||||
|
|
||||||
|
public interface ExternalServicesReadPlatformService {
|
||||||
|
|
||||||
|
S3CredentialsData getS3Credentials();
|
||||||
|
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
package org.mifosplatform.infrastructure.configuration.service;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ExternalServicesReadPlatformServiceImpl implements ExternalServicesReadPlatformService {
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ExternalServicesReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) {
|
||||||
|
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class S3CredentialsDataExtractor implements ResultSetExtractor<S3CredentialsData> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public S3CredentialsData extractData(ResultSet rs) throws SQLException, DataAccessException {
|
||||||
|
String accessKey = null;
|
||||||
|
String bucketName = null;
|
||||||
|
String secretKey = null;
|
||||||
|
while (rs.next()) {
|
||||||
|
if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_ACCESS_KEY)) {
|
||||||
|
accessKey = rs.getString("value");
|
||||||
|
} else if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_BUCKET_NAME)) {
|
||||||
|
bucketName = rs.getString("value");
|
||||||
|
} else if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_SECRET_KEY)) {
|
||||||
|
secretKey = rs.getString("value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new S3CredentialsData(bucketName, accessKey, secretKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public S3CredentialsData getS3Credentials() {
|
||||||
|
final ResultSetExtractor<S3CredentialsData> resultSetExtractor = new S3CredentialsDataExtractor();
|
||||||
|
final String sql = "SELECT es.name, es.value FROM c_external_service es where es.name in('s3_bucket_name','s3_access_key','s3_secret_key')";
|
||||||
|
final S3CredentialsData s3CredentialsData = this.jdbcTemplate.query(sql, resultSetExtractor, new Object[] {});
|
||||||
|
return s3CredentialsData;
|
||||||
|
}
|
||||||
|
}
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
package org.mifosplatform.infrastructure.core.api;
|
|
||||||
|
|
||||||
public class ApiConstants {
|
|
||||||
|
|
||||||
// TODO:Vishwas Need to move these settings to the Database
|
|
||||||
public static final Integer MAX_FILE_UPLOAD_SIZE_IN_MB = 5;
|
|
||||||
|
|
||||||
// TODO:Vishwas Need to move these settings to the Database
|
|
||||||
public static final Integer MAX_IMAGE_UPLOAD_SIZE_IN_MB = 1;
|
|
||||||
}
|
|
||||||
+13
-5
@@ -45,24 +45,27 @@ public final class JsonCommand {
|
|||||||
private final String supportedEntityType;
|
private final String supportedEntityType;
|
||||||
private final Long supportedEntityId;
|
private final Long supportedEntityId;
|
||||||
private final String transactionId;
|
private final String transactionId;
|
||||||
|
private final String url;
|
||||||
public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper,
|
public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper,
|
||||||
final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId,
|
final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId,
|
||||||
final Long loanId, final Long savingsId, final Long codeId, final String supportedEntityType, final Long supportedEntityId,
|
final Long loanId, final Long savingsId, final Long codeId, final String supportedEntityType, final Long supportedEntityId,
|
||||||
final String transactionId) {
|
final String transactionId, final String url) {
|
||||||
return new JsonCommand(null, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, groupId,
|
return new JsonCommand(null, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, groupId,
|
||||||
clientId, loanId, savingsId, codeId, supportedEntityType, supportedEntityId, transactionId);
|
clientId, loanId, savingsId, codeId, supportedEntityType, supportedEntityId, transactionId, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JsonCommand fromExistingCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
|
public static JsonCommand fromExistingCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
|
||||||
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId) {
|
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId,
|
||||||
|
final String url) {
|
||||||
return new JsonCommand(commandId, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, null, null,
|
return new JsonCommand(commandId, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, null, null,
|
||||||
null, null, null, null, null, null);
|
null, null, null, null, null, null, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
|
public JsonCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
|
||||||
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId,
|
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId,
|
||||||
final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final Long codeId,
|
final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final Long codeId,
|
||||||
final String supportedEntityType, final Long supportedEntityId, final String transactionId) {
|
final String supportedEntityType, final Long supportedEntityId, final String transactionId,
|
||||||
|
final String url) {
|
||||||
this.commandId = commandId;
|
this.commandId = commandId;
|
||||||
this.jsonCommand = jsonCommand;
|
this.jsonCommand = jsonCommand;
|
||||||
this.parsedCommand = parsedCommand;
|
this.parsedCommand = parsedCommand;
|
||||||
@@ -78,6 +81,7 @@ public final class JsonCommand {
|
|||||||
this.supportedEntityType = supportedEntityType;
|
this.supportedEntityType = supportedEntityType;
|
||||||
this.supportedEntityId = supportedEntityId;
|
this.supportedEntityId = supportedEntityId;
|
||||||
this.transactionId = transactionId;
|
this.transactionId = transactionId;
|
||||||
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String json() {
|
public String json() {
|
||||||
@@ -145,6 +149,10 @@ public final class JsonCommand {
|
|||||||
return this.transactionId;
|
return this.transactionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean differenceExists(final LocalDate baseValue, final LocalDate workingCopyValue) {
|
private boolean differenceExists(final LocalDate baseValue, final LocalDate workingCopyValue) {
|
||||||
boolean differenceExists = false;
|
boolean differenceExists = false;
|
||||||
|
|
||||||
|
|||||||
+62
-2
@@ -16,6 +16,8 @@ import net.fortuna.ical4j.model.property.RRule;
|
|||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
|
||||||
public class DataValidatorBuilder {
|
public class DataValidatorBuilder {
|
||||||
|
|
||||||
private final List<ApiParameterError> dataValidationErrors;
|
private final List<ApiParameterError> dataValidationErrors;
|
||||||
@@ -206,12 +208,35 @@ public class DataValidatorBuilder {
|
|||||||
|
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
List<Object> valuesList = Arrays.asList(values);
|
List<Object> valuesList = Arrays.asList(values);
|
||||||
|
String valuesListStr = StringUtils.join(valuesList, ", ");
|
||||||
|
|
||||||
if (!valuesList.contains(this.value)) {
|
if (!valuesList.contains(this.value)) {
|
||||||
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
||||||
.append(".is.not.one.of.expected.enumerations");
|
.append(".is.not.one.of.expected.enumerations");
|
||||||
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must be one of [ ")
|
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must be one of [ ")
|
||||||
.append(values).append(" ] ").append(".");
|
.append(valuesListStr).append(" ] ").append(".");
|
||||||
|
|
||||||
|
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
|
||||||
|
defaultEnglishMessage.toString(), parameter, this.value, values);
|
||||||
|
|
||||||
|
dataValidationErrors.add(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataValidatorBuilder isNotOneOfTheseValues(final Object... values) {
|
||||||
|
if (value == null && ignoreNullValue) { return this; }
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
List<Object> valuesList = Arrays.asList(values);
|
||||||
|
String valuesListStr = StringUtils.join(valuesList, ", ");
|
||||||
|
|
||||||
|
if (valuesList.contains(this.value)) {
|
||||||
|
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
||||||
|
.append(".is.one.of.unwanted.enumerations");
|
||||||
|
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must not be any of [ ")
|
||||||
|
.append(valuesListStr).append(" ] ").append(".");
|
||||||
|
|
||||||
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
|
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
|
||||||
defaultEnglishMessage.toString(), parameter, this.value, values);
|
defaultEnglishMessage.toString(), parameter, this.value, values);
|
||||||
@@ -310,6 +335,22 @@ public class DataValidatorBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataValidatorBuilder jsonArrayNotEmpty() {
|
||||||
|
if (value == null && ignoreNullValue) { return this; }
|
||||||
|
|
||||||
|
JsonArray array = (JsonArray)value;
|
||||||
|
if (value != null && !array.iterator().hasNext()) {
|
||||||
|
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
||||||
|
.append(".cannot.be.empty");
|
||||||
|
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(
|
||||||
|
" cannot be empty. You must select at least one.");
|
||||||
|
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
|
||||||
|
parameter);
|
||||||
|
dataValidationErrors.add(error);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void expectedArrayButIsNot() {
|
public void expectedArrayButIsNot() {
|
||||||
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
||||||
.append(".is.not.an.array");
|
.append(".is.not.an.array");
|
||||||
@@ -385,7 +426,7 @@ public class DataValidatorBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DataValidatorBuilder cantBeBlankWhenParameterProvidedIs(final String parameterName, final Object parameterValue) {
|
public DataValidatorBuilder cantBeBlankWhenParameterProvidedIs(final String parameterName, final Object parameterValue) {
|
||||||
if (StringUtils.isNotBlank(value.toString())) { return this; }
|
if (value != null && StringUtils.isNotBlank(value.toString())) { return this; }
|
||||||
|
|
||||||
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
||||||
.append(".must.be.provided.when.").append(parameterName).append(".is.").append(parameterValue);
|
.append(".must.be.provided.when.").append(parameterName).append(".is.").append(parameterValue);
|
||||||
@@ -533,4 +574,23 @@ public class DataValidatorBuilder {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataValidatorBuilder matchesRegularExpression(final String expression) {
|
||||||
|
if (value == null && ignoreNullValue) { return this; }
|
||||||
|
|
||||||
|
if (value != null && !value.toString().matches(expression)) {
|
||||||
|
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
||||||
|
.append(".does.not.match.regexp");
|
||||||
|
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter)
|
||||||
|
.append(" must match the provided regular expression [ ")
|
||||||
|
.append(expression).append(" ] ").append(".");
|
||||||
|
|
||||||
|
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
|
||||||
|
defaultEnglishMessage.toString(), parameter, this.value, expression);
|
||||||
|
|
||||||
|
dataValidationErrors.add(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -13,12 +13,13 @@ import org.springframework.security.authentication.dao.SaltSource;
|
|||||||
import org.springframework.security.authentication.encoding.PasswordEncoder;
|
import org.springframework.security.authentication.encoding.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Service(value = "applicationPasswordEncoder")
|
@Service(value = "applicationPasswordEncoder")
|
||||||
@Scope("singleton")
|
@Scope("singleton")
|
||||||
public class DefaultPlatformPasswordEncoder implements PlatformPasswordEncoder {
|
public class DefaultPlatformPasswordEncoder implements PlatformPasswordEncoder {
|
||||||
|
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final SaltSource saltSource;
|
private final SaltSource saltSource;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public DefaultPlatformPasswordEncoder(final PasswordEncoder passwordEncoder, final SaltSource saltSource) {
|
public DefaultPlatformPasswordEncoder(final PasswordEncoder passwordEncoder, final SaltSource saltSource) {
|
||||||
|
|||||||
+13
@@ -47,6 +47,19 @@ public class JdbcSupport {
|
|||||||
return (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class);
|
return (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Integer getIntegerDefaultToNullIfZero(final ResultSet rs, final String columnName) throws SQLException {
|
||||||
|
Integer value = (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class);
|
||||||
|
return defaultToNullIfZero(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer defaultToNullIfZero(final Integer value) {
|
||||||
|
Integer result = value;
|
||||||
|
if (result != null && Integer.valueOf(0).equals(value)) {
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public static BigDecimal getBigDecimalDefaultToZeroIfNull(final ResultSet rs, final String columnName) throws SQLException {
|
public static BigDecimal getBigDecimalDefaultToZeroIfNull(final ResultSet rs, final String columnName) throws SQLException {
|
||||||
final BigDecimal value = rs.getBigDecimal(columnName);
|
final BigDecimal value = rs.getBigDecimal(columnName);
|
||||||
return defaultToZeroIfNull(value);
|
return defaultToZeroIfNull(value);
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
* 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/.
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.savings.domain;
|
package org.mifosplatform.infrastructure.core.domain;
|
||||||
|
|
||||||
import org.joda.time.Days;
|
import org.joda.time.Days;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
+239
@@ -0,0 +1,239 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.core.serialization;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.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.exception.PlatformDataIntegrityException;
|
||||||
|
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.reflect.TypeToken;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class DatatableCommandFromApiJsonDeserializer {
|
||||||
|
|
||||||
|
private final static String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$";
|
||||||
|
private final static String DATATABLE_COLUMN_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,}[a-zA-Z0-9]$";
|
||||||
|
/**
|
||||||
|
* The parameters supported for this command.
|
||||||
|
*/
|
||||||
|
private final Set<String> supportedParametersForCreate = new HashSet<String>(Arrays.asList(
|
||||||
|
"datatableName", "apptableName", "multiRow", "columns"));
|
||||||
|
private final Set<String> supportedParametersForCreateColumns = new HashSet<String>(Arrays.asList(
|
||||||
|
"name", "type", "length", "mandatory", "code"));
|
||||||
|
private final Set<String> supportedParametersForUpdate = new HashSet<String>(Arrays.asList(
|
||||||
|
"apptableName", "changeColumns", "addColumns", "dropColumns"));
|
||||||
|
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"));
|
||||||
|
private final Set<String> supportedParametersForDropColumns = new HashSet<String>(Arrays.asList(
|
||||||
|
"name"));
|
||||||
|
private final Object[] supportedColumnTypes = { "String", "Number", "Decimal", "Date", "Text", "Dropdown" };
|
||||||
|
private final Object[] supportedApptableNames = { "m_loan", "m_savings_account", "m_client", "m_group", "m_office" };
|
||||||
|
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DatatableCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
|
||||||
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateType(final DataValidatorBuilder baseDataValidator, final JsonElement column) {
|
||||||
|
final String type = fromApiJsonHelper.extractStringNamed("type", column);
|
||||||
|
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();
|
||||||
|
} else {
|
||||||
|
baseDataValidator.reset().parameter("length").value(length)
|
||||||
|
.cantBeBlankWhenParameterProvidedIs("type", type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
baseDataValidator.reset().parameter("length").value(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);
|
||||||
|
} else {
|
||||||
|
baseDataValidator.reset().parameter("code").value(code).mustBeBlankWhenParameterProvided("type", type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, supportedParametersForCreate);
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
|
||||||
|
|
||||||
|
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||||
|
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);
|
||||||
|
|
||||||
|
final JsonArray columns = fromApiJsonHelper.extractJsonArrayNamed("columns", element);
|
||||||
|
baseDataValidator.reset().parameter("columns").value(columns).notNull().jsonArrayNotEmpty();
|
||||||
|
|
||||||
|
if (columns != null) {
|
||||||
|
for (JsonElement column : columns) {
|
||||||
|
fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForCreateColumns);
|
||||||
|
|
||||||
|
final String name = fromApiJsonHelper.extractStringNamed("name", column);
|
||||||
|
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);
|
||||||
|
baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().notBlank()
|
||||||
|
.isOneOfTheseValues(true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateForUpdate(final String json) {
|
||||||
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
|
// Because all parameters are optional, a check to see if at least one parameter
|
||||||
|
// has been specified is necessary in order to avoid JSON requests with no parameters
|
||||||
|
if (!json.matches("(?s)\\A\\{.*?(\\\".*?\\\"\\s*?:\\s*?)+.*?\\}\\z")) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.invalid.request.body.no.parameters",
|
||||||
|
"Provided JSON request body does not have any parameters.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersForUpdate);
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
|
||||||
|
|
||||||
|
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||||
|
final String apptableName = fromApiJsonHelper.extractStringNamed("apptableName", element);
|
||||||
|
baseDataValidator.reset().parameter("apptableName").value(apptableName).ignoreIfNull().notBlank()
|
||||||
|
.isOneOfTheseValues(supportedApptableNames);
|
||||||
|
final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : "";
|
||||||
|
|
||||||
|
final JsonArray changeColumns = fromApiJsonHelper.extractJsonArrayNamed("changeColumns", element);
|
||||||
|
baseDataValidator.reset().parameter("changeColumns").value(changeColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||||
|
|
||||||
|
if (changeColumns != null) {
|
||||||
|
for (JsonElement column : changeColumns) {
|
||||||
|
fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForChangeColumns);
|
||||||
|
|
||||||
|
final String name = fromApiJsonHelper.extractStringNamed("name", column);
|
||||||
|
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
||||||
|
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||||
|
|
||||||
|
final String newName = fromApiJsonHelper.extractStringNamed("newName", column);
|
||||||
|
baseDataValidator.reset().parameter("newName").value(newName).ignoreIfNull().notBlank()
|
||||||
|
.notExceedingLengthOf(50).isNotOneOfTheseValues("id", fkColumnName)
|
||||||
|
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
|
||||||
|
|
||||||
|
final String code = fromApiJsonHelper.extractStringNamed("code", column);
|
||||||
|
baseDataValidator.reset().parameter("code").value(code).ignoreIfNull().notBlank()
|
||||||
|
.notExceedingLengthOf(100).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||||
|
|
||||||
|
final String newCode = fromApiJsonHelper.extractStringNamed("newCode", column);
|
||||||
|
baseDataValidator.reset().parameter("newCode").value(newCode).ignoreIfNull().notBlank()
|
||||||
|
.notExceedingLengthOf(100).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(code) && StringUtils.isNotBlank(newCode)) {
|
||||||
|
baseDataValidator.reset().parameter("code").value(code)
|
||||||
|
.cantBeBlankWhenParameterProvidedIs("newCode", newCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column);
|
||||||
|
baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().notBlank()
|
||||||
|
.isOneOfTheseValues(true, false);
|
||||||
|
|
||||||
|
final Boolean after = fromApiJsonHelper.extractBooleanNamed("after", column);
|
||||||
|
baseDataValidator.reset().parameter("after").value(after).ignoreIfNull().notBlank()
|
||||||
|
.isOneOfTheseValues(true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonArray addColumns = fromApiJsonHelper.extractJsonArrayNamed("addColumns", element);
|
||||||
|
baseDataValidator.reset().parameter("addColumns").value(addColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||||
|
|
||||||
|
if (addColumns != null) {
|
||||||
|
for (JsonElement column : addColumns) {
|
||||||
|
fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForAddColumns);
|
||||||
|
|
||||||
|
final String name = fromApiJsonHelper.extractStringNamed("name", column);
|
||||||
|
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
||||||
|
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||||
|
|
||||||
|
validateType(baseDataValidator, column);
|
||||||
|
|
||||||
|
final Boolean mandatory = fromApiJsonHelper.extractBooleanNamed("mandatory", column);
|
||||||
|
baseDataValidator.reset().parameter("mandatory").value(mandatory).ignoreIfNull().notBlank()
|
||||||
|
.isOneOfTheseValues(true, false);
|
||||||
|
|
||||||
|
final Boolean after = fromApiJsonHelper.extractBooleanNamed("after", column);
|
||||||
|
baseDataValidator.reset().parameter("after").value(after).ignoreIfNull().notBlank()
|
||||||
|
.isOneOfTheseValues(true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonArray dropColumns = fromApiJsonHelper.extractJsonArrayNamed("dropColumns", element);
|
||||||
|
baseDataValidator.reset().parameter("dropColumns").value(dropColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||||
|
|
||||||
|
if (dropColumns != null) {
|
||||||
|
for (JsonElement column : dropColumns) {
|
||||||
|
fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), supportedParametersForDropColumns);
|
||||||
|
|
||||||
|
final String name = fromApiJsonHelper.extractStringNamed("name", column);
|
||||||
|
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
||||||
|
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||||
|
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||||
|
"Validation errors exist.", dataValidationErrors); }
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -7,11 +7,13 @@ package org.mifosplatform.infrastructure.core.serialization;
|
|||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
@@ -79,6 +81,21 @@ public class FromJsonHelper {
|
|||||||
if (!unsupportedParameterList.isEmpty()) { throw new UnsupportedParameterException(unsupportedParameterList); }
|
if (!unsupportedParameterList.isEmpty()) { throw new UnsupportedParameterException(unsupportedParameterList); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void checkForUnsupportedParameters(final JsonObject object, final Set<String> supportedParams) {
|
||||||
|
if (object == null) { throw new InvalidParameterException(); }
|
||||||
|
|
||||||
|
Set<Entry<String, JsonElement>> entries = object.entrySet();
|
||||||
|
List<String> unsupportedParameterList = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (Entry<String, JsonElement> providedParameter : entries) {
|
||||||
|
if (!supportedParams.contains(providedParameter.getKey())) {
|
||||||
|
unsupportedParameterList.add(providedParameter.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unsupportedParameterList.isEmpty()) { throw new UnsupportedParameterException(unsupportedParameterList); }
|
||||||
|
}
|
||||||
|
|
||||||
public JsonElement parse(final String json) {
|
public JsonElement parse(final String json) {
|
||||||
return parser.parse(json);
|
return parser.parse(json);
|
||||||
}
|
}
|
||||||
|
|||||||
+52
@@ -70,6 +70,46 @@ public class DatatablesApiResource {
|
|||||||
return this.toApiJsonSerializer.serializePretty(prettyPrint, result);
|
return this.toApiJsonSerializer.serializePretty(prettyPrint, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String createDatatable(final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||||
|
.createDBDatatable(apiRequestBodyAsJson).build();
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("{datatableName}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String updateDatatable(@PathParam("datatableName") final String datatableName,
|
||||||
|
final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||||
|
.updateDBDatatable(datatableName, apiRequestBodyAsJson).build();
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{datatableName}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String deleteDatatable(@PathParam("datatableName") final String datatableName,
|
||||||
|
final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||||
|
.deleteDBDatatable(datatableName, apiRequestBodyAsJson).build();
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("register/{datatable}/{apptable}")
|
@Path("register/{datatable}/{apptable}")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@@ -95,6 +135,18 @@ public class DatatablesApiResource {
|
|||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{datatable}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String getDatatable(@PathParam("datatable") final String datatable, @Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
final DatatableData result = this.readWriteNonCoreDataService.retrieveDatatable(datatable);
|
||||||
|
|
||||||
|
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
|
||||||
|
return this.toApiJsonSerializer.serializePretty(prettyPrint, result);
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{datatable}/{apptableId}")
|
@Path("{datatable}/{apptableId}")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
|||||||
+10
-3
@@ -5,6 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.dataqueries.data;
|
package org.mifosplatform.infrastructure.dataqueries.data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable data object representing datatable data.
|
* Immutable data object representing datatable data.
|
||||||
*/
|
*/
|
||||||
@@ -14,13 +16,18 @@ public class DatatableData {
|
|||||||
private final String applicationTableName;
|
private final String applicationTableName;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private final String registeredTableName;
|
private final String registeredTableName;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final List<ResultsetColumnHeaderData> columnHeaderData;
|
||||||
|
|
||||||
public static DatatableData create(final String applicationTableName, final String registeredTableName) {
|
public static DatatableData create(final String applicationTableName, final String registeredTableName,
|
||||||
return new DatatableData(applicationTableName, registeredTableName);
|
final List<ResultsetColumnHeaderData> columnHeaderData) {
|
||||||
|
return new DatatableData(applicationTableName, registeredTableName, columnHeaderData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DatatableData(final String applicationTableName, final String registeredTableName) {
|
private DatatableData(final String applicationTableName, final String registeredTableName,
|
||||||
|
final List<ResultsetColumnHeaderData> columnHeaderData) {
|
||||||
this.applicationTableName = applicationTableName;
|
this.applicationTableName = applicationTableName;
|
||||||
this.registeredTableName = registeredTableName;
|
this.registeredTableName = registeredTableName;
|
||||||
|
this.columnHeaderData = columnHeaderData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-1
@@ -17,7 +17,6 @@ public final class ResultsetColumnHeaderData {
|
|||||||
|
|
||||||
private final String columnName;
|
private final String columnName;
|
||||||
private final String columnType;
|
private final String columnType;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final Long columnLength;
|
private final Long columnLength;
|
||||||
private final String columnDisplayType;
|
private final String columnDisplayType;
|
||||||
private final boolean isColumnNullable;
|
private final boolean isColumnNullable;
|
||||||
@@ -162,6 +161,10 @@ public final class ResultsetColumnHeaderData {
|
|||||||
return this.columnType;
|
return this.columnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getColumnLength() {
|
||||||
|
return this.columnLength;
|
||||||
|
}
|
||||||
|
|
||||||
public String getColumnDisplayType() {
|
public String getColumnDisplayType() {
|
||||||
return this.columnDisplayType;
|
return this.columnDisplayType;
|
||||||
}
|
}
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.dataqueries.handler;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.infrastructure.dataqueries.service.ReadWriteNonCoreDataService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CreateDatatableCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final ReadWriteNonCoreDataService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public CreateDatatableCommandHandler(final ReadWriteNonCoreDataService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
return this.writePlatformService.createDatatable(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.dataqueries.handler;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||||
|
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.dataqueries.service.ReadWriteNonCoreDataService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DeleteDatatableCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final ReadWriteNonCoreDataService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DeleteDatatableCommandHandler(final ReadWriteNonCoreDataService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
final String datatableName = command.getUrl().replaceAll("/datatables/", "");
|
||||||
|
|
||||||
|
this.writePlatformService.deleteDatatable(datatableName);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withCommandId(command.commandId())
|
||||||
|
.withResourceIdAsString(datatableName)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.dataqueries.handler;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||||
|
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.dataqueries.service.ReadWriteNonCoreDataService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UpdateDatatableCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final ReadWriteNonCoreDataService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UpdateDatatableCommandHandler(final ReadWriteNonCoreDataService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
final String datatableName = command.getUrl().replaceAll("/datatables/", "");
|
||||||
|
|
||||||
|
this.writePlatformService.updateDatatable(datatableName, command);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withCommandId(command.commandId())
|
||||||
|
.withResourceIdAsString(datatableName)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -29,7 +29,6 @@ import javax.ws.rs.core.StreamingOutput;
|
|||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
|
||||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||||
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
|
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
|
||||||
import org.mifosplatform.infrastructure.dataqueries.data.ReportData;
|
import org.mifosplatform.infrastructure.dataqueries.data.ReportData;
|
||||||
@@ -38,6 +37,7 @@ import org.mifosplatform.infrastructure.dataqueries.data.ReportParameterJoinData
|
|||||||
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||||
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData;
|
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData;
|
||||||
import org.mifosplatform.infrastructure.dataqueries.exception.ReportNotFoundException;
|
import org.mifosplatform.infrastructure.dataqueries.exception.ReportNotFoundException;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
|
||||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.mifosplatform.useradministration.domain.AppUser;
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
|
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
|
||||||
@@ -243,7 +243,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
|||||||
if (noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled",
|
if (noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled",
|
||||||
"Pentaho is not enabled"); }
|
"Pentaho is not enabled"); }
|
||||||
|
|
||||||
final String reportPath = FileUtils.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt";
|
final String reportPath = FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt";
|
||||||
logger.info("Report path: " + reportPath);
|
logger.info("Report path: " + reportPath);
|
||||||
|
|
||||||
// load report definition
|
// load report definition
|
||||||
@@ -357,7 +357,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
|||||||
@Override
|
@Override
|
||||||
public String retrieveReportPDF(final String reportName, final String type, final Map<String, String> queryParams) {
|
public String retrieveReportPDF(final String reportName, final String type, final Map<String, String> queryParams) {
|
||||||
|
|
||||||
String fileLocation = FileUtils.MIFOSX_BASE_DIR + File.separator + "";
|
String fileLocation = FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + "";
|
||||||
if (!new File(fileLocation).isDirectory()) {
|
if (!new File(fileLocation).isDirectory()) {
|
||||||
new File(fileLocation).mkdirs();
|
new File(fileLocation).mkdirs();
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -17,6 +17,8 @@ public interface ReadWriteNonCoreDataService {
|
|||||||
|
|
||||||
List<DatatableData> retrieveDatatableNames(String appTable);
|
List<DatatableData> retrieveDatatableNames(String appTable);
|
||||||
|
|
||||||
|
DatatableData retrieveDatatable(String datatable);
|
||||||
|
|
||||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'REGISTER_DATATABLE')")
|
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'REGISTER_DATATABLE')")
|
||||||
void registerDatatable(String datatable, String appTable);
|
void registerDatatable(String datatable, String appTable);
|
||||||
|
|
||||||
@@ -25,6 +27,12 @@ public interface ReadWriteNonCoreDataService {
|
|||||||
|
|
||||||
GenericResultsetData retrieveDataTableGenericResultSet(String datatable, Long appTableId, String order, Long id);
|
GenericResultsetData retrieveDataTableGenericResultSet(String datatable, Long appTableId, String order, Long id);
|
||||||
|
|
||||||
|
CommandProcessingResult createDatatable(JsonCommand command);
|
||||||
|
|
||||||
|
void updateDatatable(String datatableName, JsonCommand command);
|
||||||
|
|
||||||
|
void deleteDatatable(String datatableName);
|
||||||
|
|
||||||
CommandProcessingResult createNewDatatableEntry(String datatable, Long appTableId, JsonCommand command);
|
CommandProcessingResult createNewDatatableEntry(String datatable, Long appTableId, JsonCommand command);
|
||||||
|
|
||||||
CommandProcessingResult updateDatatableEntryOneToOne(String datatable, Long appTableId, JsonCommand command);
|
CommandProcessingResult updateDatatableEntryOneToOne(String datatable, Long appTableId, JsonCommand command);
|
||||||
|
|||||||
+467
-87
@@ -18,6 +18,7 @@ import javax.sql.DataSource;
|
|||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.hibernate.exception.ConstraintViolationException;
|
import org.hibernate.exception.ConstraintViolationException;
|
||||||
|
import org.hibernate.exception.SQLGrammarException;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||||
@@ -25,6 +26,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
|||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.DatatableCommandFromApiJsonDeserializer;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper;
|
import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper;
|
||||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||||
@@ -47,12 +49,21 @@ import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataService {
|
public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataService {
|
||||||
|
|
||||||
|
private final static String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$";
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class);
|
private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class);
|
||||||
|
private final static HashMap<String, String> apiTypeToMySQL = new HashMap<String, String>() {{
|
||||||
|
put("String", "VARCHAR"); put("Number", "INT"); put("Decimal", "DECIMAL"); put("Date", "DATE");
|
||||||
|
put("Text", "TEXT"); put ("Dropdown", "INT");
|
||||||
|
}};
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
@@ -60,16 +71,19 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
private final FromJsonHelper fromJsonHelper;
|
private final FromJsonHelper fromJsonHelper;
|
||||||
private final JsonParserHelper helper;
|
private final JsonParserHelper helper;
|
||||||
private final GenericDataService genericDataService;
|
private final GenericDataService genericDataService;
|
||||||
|
private final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ReadWriteNonCoreDataServiceImpl(final TenantAwareRoutingDataSource dataSource, final PlatformSecurityContext context,
|
public ReadWriteNonCoreDataServiceImpl(final TenantAwareRoutingDataSource dataSource, final PlatformSecurityContext context,
|
||||||
final FromJsonHelper fromJsonHelper, final GenericDataService genericDataService) {
|
final FromJsonHelper fromJsonHelper, final GenericDataService genericDataService,
|
||||||
|
final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
|
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.fromJsonHelper = fromJsonHelper;
|
this.fromJsonHelper = fromJsonHelper;
|
||||||
this.helper = new JsonParserHelper();
|
this.helper = new JsonParserHelper();
|
||||||
this.genericDataService = genericDataService;
|
this.genericDataService = genericDataService;
|
||||||
|
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -96,13 +110,41 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
final String appTableName = rs.getString("application_table_name");
|
final String appTableName = rs.getString("application_table_name");
|
||||||
final String registeredDatatableName = rs.getString("registered_table_name");
|
final String registeredDatatableName = rs.getString("registered_table_name");
|
||||||
|
final List<ResultsetColumnHeaderData> columnHeaderData = this.genericDataService
|
||||||
|
.fillResultsetColumnHeaders(registeredDatatableName);
|
||||||
|
|
||||||
datatables.add(DatatableData.create(appTableName, registeredDatatableName));
|
datatables.add(DatatableData.create(appTableName, registeredDatatableName, columnHeaderData));
|
||||||
}
|
}
|
||||||
|
|
||||||
return datatables;
|
return datatables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DatatableData retrieveDatatable(final String datatable) {
|
||||||
|
|
||||||
|
// PERMITTED datatables
|
||||||
|
final String sql = "select application_table_name, registered_table_name" + " from x_registered_table " + " where exists"
|
||||||
|
+ " (select 'f'" + " from m_appuser_role ur " + " join m_role r on r.id = ur.role_id"
|
||||||
|
+ " left join m_role_permission rp on rp.role_id = r.id" + " left join m_permission p on p.id = rp.permission_id"
|
||||||
|
+ " where ur.appuser_id = " + context.authenticatedUser().getId() + " and registered_table_name='" + datatable + "'"
|
||||||
|
+ " and (p.code in ('ALL_FUNCTIONS', 'ALL_FUNCTIONS_READ') or p.code = concat('READ_', registered_table_name))) "
|
||||||
|
+ " order by application_table_name, registered_table_name";
|
||||||
|
|
||||||
|
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
|
||||||
|
|
||||||
|
DatatableData datatableData = null;
|
||||||
|
while (rs.next()) {
|
||||||
|
final String appTableName = rs.getString("application_table_name");
|
||||||
|
final String registeredDatatableName = rs.getString("registered_table_name");
|
||||||
|
final List<ResultsetColumnHeaderData> columnHeaderData = this.genericDataService
|
||||||
|
.fillResultsetColumnHeaders(registeredDatatableName);
|
||||||
|
|
||||||
|
datatableData = DatatableData.create(appTableName, registeredDatatableName, columnHeaderData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return datatableData;
|
||||||
|
}
|
||||||
|
|
||||||
private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) {
|
private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) {
|
||||||
logger.error(dve.getMessage(), dve);
|
logger.error(dve.getMessage(), dve);
|
||||||
}
|
}
|
||||||
@@ -160,15 +202,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
+ ", 'DELETE', '"
|
+ ", 'DELETE', '"
|
||||||
+ dataTableName + "', false)";
|
+ dataTableName + "', false)";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String[] sqlArray = {registerDatatableSql, permissionsSql};
|
String[] sqlArray = { registerDatatableSql, permissionsSql };
|
||||||
this.jdbcTemplate.batchUpdate(sqlArray);
|
this.jdbcTemplate.batchUpdate(sqlArray);
|
||||||
|
|
||||||
} catch (DataIntegrityViolationException dve) {
|
} catch (DataIntegrityViolationException dve) {
|
||||||
Throwable realCause = dve.getMostSpecificCause();
|
Throwable realCause = dve.getMostSpecificCause();
|
||||||
//even if duplicate is only due to permission duplicate, okay to show duplicate datatable error msg
|
// even if duplicate is only due to permission duplicate, okay to
|
||||||
|
// show duplicate datatable error msg
|
||||||
if (realCause.getMessage().contains("Duplicate entry")) { throw new PlatformDataIntegrityException(
|
if (realCause.getMessage().contains("Duplicate entry")) { throw new PlatformDataIntegrityException(
|
||||||
"error.msg.datatable.registered", "Datatable `" + dataTableName
|
"error.msg.datatable.registered", "Datatable `" + dataTableName
|
||||||
+ "` is already registered against an application table.", "dataTableName", dataTableName); }
|
+ "` is already registered against an application table.", "dataTableName", dataTableName); }
|
||||||
@@ -193,7 +234,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
|
|
||||||
final String deleteRegisteredDatatableSql = "delete from x_registered_table where registered_table_name = '" + datatable + "'";
|
final String deleteRegisteredDatatableSql = "delete from x_registered_table where registered_table_name = '" + datatable + "'";
|
||||||
|
|
||||||
String[] sqlArray = {deleteRolePermissionsSql, deletePermissionsSql, deleteRegisteredDatatableSql};
|
String[] sqlArray = { deleteRolePermissionsSql, deletePermissionsSql, deleteRegisteredDatatableSql };
|
||||||
this.jdbcTemplate.batchUpdate(sqlArray);
|
this.jdbcTemplate.batchUpdate(sqlArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,11 +281,356 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateDatatableName(final String name) {
|
||||||
|
|
||||||
|
if (name == null || name.isEmpty()) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.datatables.datatable.null.name",
|
||||||
|
"Data table name must not be blank.");
|
||||||
|
} else if (!name.matches(DATATABLE_NAME_REGEX_PATTERN)) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.datatables.datatable.invalid.name.regex",
|
||||||
|
"Invalid data table name.", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String datatableColumnNameToCodeValueName(final String columnName, final String code) {
|
||||||
|
|
||||||
|
return (code + "_cd_" + columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseDatatableColumnObjectForCreate(final JsonObject column, StringBuilder sqlBuilder) {
|
||||||
|
|
||||||
|
String name = (column.has("name")) ? column.get("name").getAsString() : null;
|
||||||
|
String type = (column.has("type")) ? column.get("type").getAsString() : null;
|
||||||
|
Integer length = (column.has("length")) ? column.get("length").getAsInt() : null;
|
||||||
|
Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false;
|
||||||
|
String code = (column.has("code")) ? column.get("code").getAsString() : null;
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(code)) {
|
||||||
|
name = datatableColumnNameToCodeValueName(name, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
String mysqlType = apiTypeToMySQL.get(type);
|
||||||
|
sqlBuilder = sqlBuilder.append("`" + name + "` " + mysqlType);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
if (type.equals("String")) {
|
||||||
|
sqlBuilder = sqlBuilder.append("(" + length + ")");
|
||||||
|
} else if (type.equals("Decimal")) {
|
||||||
|
sqlBuilder = sqlBuilder.append("(19,6)");
|
||||||
|
} else if (type.equals("Dropdown")) {
|
||||||
|
sqlBuilder = sqlBuilder.append("(11)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mandatory != null) {
|
||||||
|
if (mandatory) {
|
||||||
|
sqlBuilder = sqlBuilder.append(" NOT NULL");
|
||||||
|
} else {
|
||||||
|
sqlBuilder = sqlBuilder.append(" DEFAULT NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlBuilder = sqlBuilder.append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult createDatatable(final JsonCommand command) {
|
||||||
|
|
||||||
|
String datatableName = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
context.authenticatedUser();
|
||||||
|
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
||||||
|
|
||||||
|
JsonElement element = this.fromJsonHelper.parse(command.json());
|
||||||
|
JsonArray columns = this.fromJsonHelper.extractJsonArrayNamed("columns", element);
|
||||||
|
datatableName = this.fromJsonHelper.extractStringNamed("datatableName", element);
|
||||||
|
String apptableName = this.fromJsonHelper.extractStringNamed("apptableName", element);
|
||||||
|
Boolean multiRow = this.fromJsonHelper.extractBooleanNamed("multiRow", element);
|
||||||
|
|
||||||
|
if (multiRow == null) {
|
||||||
|
multiRow = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
validateDatatableName(datatableName);
|
||||||
|
validateAppTable(apptableName);
|
||||||
|
|
||||||
|
String fkColumnName = apptableName.substring(2) + "_id";
|
||||||
|
String fkName = datatableName.toLowerCase().replaceAll("\\s", "_");
|
||||||
|
StringBuilder sqlBuilder = new StringBuilder();
|
||||||
|
sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` (");
|
||||||
|
|
||||||
|
if (multiRow) {
|
||||||
|
sqlBuilder = sqlBuilder
|
||||||
|
.append("`id` BIGINT(20) NOT NULL AUTO_INCREMENT, ")
|
||||||
|
.append("`" + fkColumnName + "` BIGINT(20) NOT NULL, ");
|
||||||
|
} else {
|
||||||
|
sqlBuilder = sqlBuilder
|
||||||
|
.append("`" + fkColumnName + "` BIGINT(20) NOT NULL, ");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JsonElement column : columns) {
|
||||||
|
parseDatatableColumnObjectForCreate(column.getAsJsonObject(), sqlBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove trailing comma and space
|
||||||
|
sqlBuilder = sqlBuilder.delete(sqlBuilder.length() - 2, sqlBuilder.length());
|
||||||
|
|
||||||
|
if (multiRow) {
|
||||||
|
sqlBuilder = sqlBuilder
|
||||||
|
.append(", PRIMARY KEY (`id`)")
|
||||||
|
.append(", KEY `fk_" + apptableName + "` (`" + fkColumnName + "`)")
|
||||||
|
.append(", CONSTRAINT `fk_" + fkName + "` ")
|
||||||
|
.append("FOREIGN KEY (`" + fkColumnName + "`) ")
|
||||||
|
.append("REFERENCES `" + apptableName + "` (`id`)");
|
||||||
|
} else {
|
||||||
|
sqlBuilder = sqlBuilder
|
||||||
|
.append(", PRIMARY KEY (`" + fkColumnName + "`)")
|
||||||
|
.append(", CONSTRAINT `fk_" + fkName + "` ")
|
||||||
|
.append("FOREIGN KEY (`" + fkColumnName + "`) ")
|
||||||
|
.append("REFERENCES `" + apptableName + "` (`id`)");
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlBuilder = sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;");
|
||||||
|
this.jdbcTemplate.execute(sqlBuilder.toString());
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withCommandId(command.commandId())
|
||||||
|
.withResourceIdAsString(datatableName)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseDatatableColumnForUpdate(final JsonObject column,
|
||||||
|
final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition, StringBuilder sqlBuilder) {
|
||||||
|
|
||||||
|
String name = (column.has("name")) ? column.get("name").getAsString() : 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;
|
||||||
|
String code = (column.has("code")) ? column.get("code").getAsString() : null;
|
||||||
|
String newCode = (column.has("newCode")) ? column.get("newCode").getAsString() : null;
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(code)) {
|
||||||
|
name = datatableColumnNameToCodeValueName(name, code);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(newCode)) {
|
||||||
|
newName = datatableColumnNameToCodeValueName(newName, newCode);
|
||||||
|
} else {
|
||||||
|
newName = datatableColumnNameToCodeValueName(newName, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mapColumnNameDefinition.containsKey(name)) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.datatable.column.missing.update.parse",
|
||||||
|
"Column " + name + " does not exist.", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String type = mapColumnNameDefinition.get(name).getColumnType();
|
||||||
|
Long length = mapColumnNameDefinition.get(name).getColumnLength();
|
||||||
|
|
||||||
|
sqlBuilder = sqlBuilder.append(", CHANGE `" + name + "` `" + newName + "` " + type);
|
||||||
|
if (length != null && length > 0) {
|
||||||
|
if (type.equals("DECIMAL")) {
|
||||||
|
sqlBuilder.append("(19,6)");
|
||||||
|
} else if (type.equals("VARCHAR")) {
|
||||||
|
sqlBuilder.append("(" + length + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mandatory != null) {
|
||||||
|
if (mandatory) {
|
||||||
|
sqlBuilder = sqlBuilder.append(" NOT NULL");
|
||||||
|
} else {
|
||||||
|
sqlBuilder = sqlBuilder.append(" DEFAULT NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (after != null) {
|
||||||
|
sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseDatatableColumnForAdd(final JsonObject column, StringBuilder sqlBuilder) {
|
||||||
|
|
||||||
|
String name = (column.has("name")) ? column.get("name").getAsString() : null;
|
||||||
|
String type = (column.has("type")) ? column.get("type").getAsString() : null;
|
||||||
|
Integer length = (column.has("length")) ? column.get("length").getAsInt() : null;
|
||||||
|
Boolean mandatory = (column.has("mandatory")) ? column.get("mandatory").getAsBoolean() : false;
|
||||||
|
String after = (column.has("after")) ? column.get("after").getAsString() : null;
|
||||||
|
String code = (column.has("code")) ? column.get("code").getAsString() : null;
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(code)) {
|
||||||
|
name = datatableColumnNameToCodeValueName(name, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
String mysqlType = apiTypeToMySQL.get(type);
|
||||||
|
sqlBuilder = sqlBuilder.append(", ADD `" + name + "` " + mysqlType);
|
||||||
|
|
||||||
|
if (type != null) {
|
||||||
|
if (type.equals("String") && length != null) {
|
||||||
|
sqlBuilder = sqlBuilder.append("(" + length + ")");
|
||||||
|
} else if (type.equals("Decimal")) {
|
||||||
|
sqlBuilder = sqlBuilder.append("(19,6)");
|
||||||
|
} else if (type.equals("Dropdown")) {
|
||||||
|
sqlBuilder = sqlBuilder.append("(11)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mandatory != null) {
|
||||||
|
if (mandatory) {
|
||||||
|
sqlBuilder = sqlBuilder.append(" NOT NULL");
|
||||||
|
} else {
|
||||||
|
sqlBuilder = sqlBuilder.append(" DEFAULT NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (after != null) {
|
||||||
|
sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseDatatableColumnForDrop(final JsonObject column, StringBuilder sqlBuilder) {
|
||||||
|
|
||||||
|
String name = (column.has("name")) ? column.get("name").getAsString() : null;
|
||||||
|
|
||||||
|
sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public void updateDatatable(final String datatableName, final JsonCommand command) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
context.authenticatedUser();
|
||||||
|
this.fromApiJsonDeserializer.validateForUpdate(command.json());
|
||||||
|
|
||||||
|
JsonElement element = this.fromJsonHelper.parse(command.json());
|
||||||
|
JsonArray changeColumns = this.fromJsonHelper.extractJsonArrayNamed("changeColumns", element);
|
||||||
|
JsonArray addColumns = this.fromJsonHelper.extractJsonArrayNamed("addColumns", element);
|
||||||
|
JsonArray dropColumns = this.fromJsonHelper.extractJsonArrayNamed("dropColumns", element);
|
||||||
|
String apptableName = this.fromJsonHelper.extractStringNamed("apptableName", element);
|
||||||
|
|
||||||
|
validateDatatableName(datatableName);
|
||||||
|
|
||||||
|
List<ResultsetColumnHeaderData> columnHeaderData = this.genericDataService.fillResultsetColumnHeaders(datatableName);
|
||||||
|
Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition = new HashMap<String, ResultsetColumnHeaderData>();
|
||||||
|
for (ResultsetColumnHeaderData columnHeader : columnHeaderData) {
|
||||||
|
mapColumnNameDefinition.put(columnHeader.getColumnName(), columnHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.isBlank(apptableName)) {
|
||||||
|
validateAppTable(apptableName);
|
||||||
|
|
||||||
|
String oldApptableName = this.queryForApplicationTableName(datatableName);
|
||||||
|
if (!StringUtils.equals(oldApptableName, apptableName)) {
|
||||||
|
deregisterDatatable(datatableName);
|
||||||
|
registerDatatable(datatableName, apptableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeColumns == null && addColumns == null && dropColumns == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropColumns != null) {
|
||||||
|
|
||||||
|
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
|
||||||
|
|
||||||
|
for (JsonElement column : dropColumns) {
|
||||||
|
parseDatatableColumnForDrop(column.getAsJsonObject(), sqlBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the first comma, right after ALTER TABLE `datatable`
|
||||||
|
int indexOfFirstComma = sqlBuilder.indexOf(",");
|
||||||
|
if (indexOfFirstComma != -1) {
|
||||||
|
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.jdbcTemplate.execute(sqlBuilder.toString());
|
||||||
|
}
|
||||||
|
if (addColumns != null) {
|
||||||
|
|
||||||
|
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
|
||||||
|
|
||||||
|
for (JsonElement column : addColumns) {
|
||||||
|
parseDatatableColumnForAdd(column.getAsJsonObject(), sqlBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the first comma, right after ALTER TABLE `datatable`
|
||||||
|
int indexOfFirstComma = sqlBuilder.indexOf(",");
|
||||||
|
if (indexOfFirstComma != -1) {
|
||||||
|
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.jdbcTemplate.execute(sqlBuilder.toString());
|
||||||
|
}
|
||||||
|
if (changeColumns != null) {
|
||||||
|
|
||||||
|
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`");
|
||||||
|
|
||||||
|
for (JsonElement column : changeColumns) {
|
||||||
|
parseDatatableColumnForUpdate(column.getAsJsonObject(), mapColumnNameDefinition, sqlBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the first comma, right after ALTER TABLE `datatable`
|
||||||
|
int indexOfFirstComma = sqlBuilder.indexOf(",");
|
||||||
|
if (indexOfFirstComma != -1) {
|
||||||
|
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.jdbcTemplate.execute(sqlBuilder.toString());
|
||||||
|
}
|
||||||
|
} catch (SQLGrammarException e) {
|
||||||
|
Throwable realCause = e.getCause();
|
||||||
|
|
||||||
|
if (realCause.getMessage().toLowerCase().contains("unknown column")) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.datatable.column.missing.update", realCause.getMessage());
|
||||||
|
} else if (realCause.getMessage().toLowerCase().contains("can't drop")) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.datatable.column.missing.drop", realCause.getMessage());
|
||||||
|
} else if (realCause.getMessage().toLowerCase().contains("duplicate column")) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.datatable.duplicate.column", realCause.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public void deleteDatatable(final String datatableName) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
context.authenticatedUser();
|
||||||
|
|
||||||
|
validateDatatableName(datatableName);
|
||||||
|
deregisterDatatable(datatableName);
|
||||||
|
|
||||||
|
String sql = "DROP TABLE `" + datatableName + "`";
|
||||||
|
this.jdbcTemplate.execute(sql);
|
||||||
|
} catch (SQLGrammarException e) {
|
||||||
|
Throwable realCause = e.getCause();
|
||||||
|
|
||||||
|
if (realCause.getMessage().contains("Unknown table")) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.datatable.missing", realCause.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult updateDatatableEntryOneToOne(final String dataTableName, final Long appTableId, final JsonCommand command) {
|
public CommandProcessingResult updateDatatableEntryOneToOne(final String dataTableName, final Long appTableId, final JsonCommand command) {
|
||||||
|
|
||||||
return updateDatatableEntry(dataTableName, appTableId, null, command);
|
return updateDatatableEntry(dataTableName, appTableId, null, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -252,10 +638,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
public CommandProcessingResult updateDatatableEntryOneToMany(final String dataTableName, final Long appTableId, final Long datatableId,
|
public CommandProcessingResult updateDatatableEntryOneToMany(final String dataTableName, final Long appTableId, final Long datatableId,
|
||||||
final JsonCommand command) {
|
final JsonCommand command) {
|
||||||
|
|
||||||
return updateDatatableEntry(dataTableName, appTableId, datatableId, command);
|
return updateDatatableEntry(dataTableName, appTableId, datatableId, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private CommandProcessingResult updateDatatableEntry(final String dataTableName, final Long appTableId, final Long datatableId,
|
private CommandProcessingResult updateDatatableEntry(final String dataTableName, final Long appTableId, final Long datatableId,
|
||||||
final JsonCommand command) {
|
final JsonCommand command) {
|
||||||
|
|
||||||
@@ -272,14 +657,18 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
|
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
|
||||||
Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
|
Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
|
||||||
|
|
||||||
String pkName = "id"; //1:M datatable
|
String pkName = "id"; // 1:M datatable
|
||||||
if (datatableId == null) { pkName = getFKField(appTable);} //1:1 datatable
|
if (datatableId == null) {
|
||||||
|
pkName = getFKField(appTable);
|
||||||
|
} // 1:1 datatable
|
||||||
|
|
||||||
final Map<String, Object> changes = getAffectedAndChangedColumns(grs, dataParams, pkName);
|
final Map<String, Object> changes = getAffectedAndChangedColumns(grs, dataParams, pkName);
|
||||||
|
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
Long pkValue = appTableId;
|
Long pkValue = appTableId;
|
||||||
if (datatableId != null) { pkValue = datatableId;}
|
if (datatableId != null) {
|
||||||
|
pkValue = datatableId;
|
||||||
|
}
|
||||||
final String sql = getUpdateSql(dataTableName, pkName, pkValue, changes);
|
final String sql = getUpdateSql(dataTableName, pkName, pkValue, changes);
|
||||||
logger.info("Update sql: " + sql);
|
logger.info("Update sql: " + sql);
|
||||||
if (StringUtils.isNotBlank(sql)) {
|
if (StringUtils.isNotBlank(sql)) {
|
||||||
@@ -287,21 +676,20 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
changes.put("locale", dataParams.get("locale"));
|
changes.put("locale", dataParams.get("locale"));
|
||||||
changes.put("dateFormat", "yyyy-MM-dd");
|
changes.put("dateFormat", "yyyy-MM-dd");
|
||||||
} else {
|
} else {
|
||||||
logger.info("No Changes");
|
logger.info("No Changes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder() //
|
return new CommandProcessingResultBuilder() //
|
||||||
.withOfficeId(commandProcessingResult.getOfficeId()) //
|
.withOfficeId(commandProcessingResult.getOfficeId()) //
|
||||||
.withGroupId(commandProcessingResult.getGroupId()) //
|
.withGroupId(commandProcessingResult.getGroupId()) //
|
||||||
.withClientId(commandProcessingResult.getClientId()) //
|
.withClientId(commandProcessingResult.getClientId()) //
|
||||||
.withSavingsId(commandProcessingResult.getSavingsId()) //
|
.withSavingsId(commandProcessingResult.getSavingsId()) //
|
||||||
.withLoanId(commandProcessingResult.getLoanId()) //
|
.withLoanId(commandProcessingResult.getLoanId()) //
|
||||||
.with(changes) //
|
.with(changes) //
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult deleteDatatableEntries(final String dataTableName, final Long appTableId) {
|
public CommandProcessingResult deleteDatatableEntries(final String dataTableName, final Long appTableId) {
|
||||||
@@ -309,7 +697,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
final String appTable = queryForApplicationTableName(dataTableName);
|
final String appTable = queryForApplicationTableName(dataTableName);
|
||||||
CommandProcessingResult commandProcessingResult = checkMainResourceExistsWithinScope(appTable, appTableId);
|
CommandProcessingResult commandProcessingResult = checkMainResourceExistsWithinScope(appTable, appTableId);
|
||||||
|
|
||||||
|
|
||||||
final String deleteOneToOneEntrySql = getDeleteEntriesSql(dataTableName, getFKField(appTable), appTableId);
|
final String deleteOneToOneEntrySql = getDeleteEntriesSql(dataTableName, getFKField(appTable), appTableId);
|
||||||
|
|
||||||
int rowsDeleted = this.jdbcTemplate.update(deleteOneToOneEntrySql);
|
int rowsDeleted = this.jdbcTemplate.update(deleteOneToOneEntrySql);
|
||||||
@@ -359,9 +746,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
return new GenericResultsetData(columnHeaders, result);
|
return new GenericResultsetData(columnHeaders, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private GenericResultsetData retrieveDataTableGenericResultSetForUpdate(final String appTable, final String dataTableName,
|
||||||
private GenericResultsetData retrieveDataTableGenericResultSetForUpdate(final String appTable, String dataTableName, final Long appTableId,
|
final Long appTableId, final Long id) {
|
||||||
final Long id) {
|
|
||||||
|
|
||||||
final List<ResultsetColumnHeaderData> columnHeaders = this.genericDataService.fillResultsetColumnHeaders(dataTableName);
|
final List<ResultsetColumnHeaderData> columnHeaders = this.genericDataService.fillResultsetColumnHeaders(dataTableName);
|
||||||
|
|
||||||
@@ -383,7 +769,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
private CommandProcessingResult checkMainResourceExistsWithinScope(final String appTable, final Long appTableId) {
|
private CommandProcessingResult checkMainResourceExistsWithinScope(final String appTable, final Long appTableId) {
|
||||||
|
|
||||||
final String sql = dataScopedSQL(appTable, appTableId);
|
final String sql = dataScopedSQL(appTable, appTableId);
|
||||||
logger.info("data scoped sql: " + sql);
|
logger.info("data scoped sql: " + sql);
|
||||||
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
|
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
|
||||||
|
|
||||||
if (!rs.next()) { throw new DatatableNotFoundException(appTable, appTableId); }
|
if (!rs.next()) { throw new DatatableNotFoundException(appTable, appTableId); }
|
||||||
@@ -397,86 +783,82 @@ logger.info("data scoped sql: " + sql);
|
|||||||
if (rs.next()) { throw new DatatableSystemErrorException("System Error: More than one row returned from data scoping query"); }
|
if (rs.next()) { throw new DatatableSystemErrorException("System Error: More than one row returned from data scoping query"); }
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder() //
|
return new CommandProcessingResultBuilder() //
|
||||||
.withOfficeId(officeId) //
|
.withOfficeId(officeId) //
|
||||||
.withGroupId(groupId) //
|
.withGroupId(groupId) //
|
||||||
.withClientId(clientId) //
|
.withClientId(clientId) //
|
||||||
.withSavingsId(savingsId) //
|
.withSavingsId(savingsId) //
|
||||||
.withLoanId(LoanId) //
|
.withLoanId(LoanId) //
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long getLongSqlRowSet(SqlRowSet rs, String column) {
|
private Long getLongSqlRowSet(final SqlRowSet rs, final String column) {
|
||||||
Long val = rs.getLong(column);
|
Long val = rs.getLong(column);
|
||||||
if (val == 0) val = null;
|
if (val == 0) val = null;
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String dataScopedSQL(final String appTable, final Long appTableId) {
|
private String dataScopedSQL(final String appTable, final Long appTableId) {
|
||||||
/*
|
/*
|
||||||
* unfortunately have to, one way or another, be able to restrict data
|
* unfortunately have to, one way or another, be able to restrict data
|
||||||
* to the users office hierarchy. Here, a few key tables are done.
|
* to the users office hierarchy. Here, a few key tables are done. But
|
||||||
* But if additional fields are needed on other tables the
|
* if additional fields are needed on other tables the same pattern
|
||||||
* same pattern applies
|
* applies
|
||||||
*/
|
*/
|
||||||
|
|
||||||
AppUser currentUser = context.authenticatedUser();
|
AppUser currentUser = context.authenticatedUser();
|
||||||
String scopedSQL = null;
|
String scopedSQL = null;
|
||||||
/*
|
/*
|
||||||
* m_loan and m_savings_account are connected to an m_office thru either an m_client or an m_group
|
* m_loan and m_savings_account are connected to an m_office thru either
|
||||||
* If both it means it relates to an m_client that is in a group (still an m_client account)
|
* an m_client or an m_group If both it means it relates to an m_client
|
||||||
|
* that is in a group (still an m_client account)
|
||||||
*/
|
*/
|
||||||
if (appTable.equalsIgnoreCase("m_loan")) {
|
if (appTable.equalsIgnoreCase("m_loan")) {
|
||||||
scopedSQL = "select distinctrow x.* from (" +
|
scopedSQL = "select distinctrow x.* from ("
|
||||||
" (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId from m_loan l " +
|
+ " (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId from m_loan l "
|
||||||
" join m_client c on c.id = l.client_id " +
|
+ " join m_client c on c.id = l.client_id "
|
||||||
" join m_office o on o.id = c.office_id and o.hierarchy like '" +
|
+ " join m_office o on o.id = c.office_id and o.hierarchy like '"
|
||||||
currentUser.getOffice().getHierarchy() + "%'" +
|
+ currentUser.getOffice().getHierarchy()
|
||||||
" where l.id = " + appTableId + ")" +
|
+ "%'"
|
||||||
" union all " +
|
+ " where l.id = "
|
||||||
" (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId from m_loan l " +
|
+ appTableId
|
||||||
" join m_group g on g.id = l.group_id " +
|
+ ")"
|
||||||
" join m_office o on o.id = g.office_id and o.hierarchy like '" +
|
+ " union all "
|
||||||
currentUser.getOffice().getHierarchy() + "%'" +
|
+ " (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId from m_loan l "
|
||||||
" where l.id = " + appTableId + ")" +
|
+ " join m_group g on g.id = l.group_id " + " join m_office o on o.id = g.office_id and o.hierarchy like '"
|
||||||
" ) x";
|
+ currentUser.getOffice().getHierarchy() + "%'" + " where l.id = " + appTableId + ")" + " ) x";
|
||||||
}
|
}
|
||||||
if (appTable.equalsIgnoreCase("m_savings_account")) {
|
if (appTable.equalsIgnoreCase("m_savings_account")) {
|
||||||
scopedSQL = "select distinctrow x.* from (" +
|
scopedSQL = "select distinctrow x.* from ("
|
||||||
" (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId from m_savings_account s " +
|
+ " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId from m_savings_account s "
|
||||||
" join m_client c on c.id = s.client_id " +
|
+ " join m_client c on c.id = s.client_id "
|
||||||
" join m_office o on o.id = c.office_id and o.hierarchy like '" +
|
+ " join m_office o on o.id = c.office_id and o.hierarchy like '"
|
||||||
currentUser.getOffice().getHierarchy() + "%'" +
|
+ currentUser.getOffice().getHierarchy()
|
||||||
" where s.id = " + appTableId+ ")" +
|
+ "%'"
|
||||||
" union all " +
|
+ " where s.id = "
|
||||||
" (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId from m_savings_account s " +
|
+ appTableId
|
||||||
" join m_group g on g.id = s.group_id " +
|
+ ")"
|
||||||
" join m_office o on o.id = g.office_id and o.hierarchy like '" +
|
+ " union all "
|
||||||
currentUser.getOffice().getHierarchy() + "%'" +
|
+ " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId from m_savings_account s "
|
||||||
" where s.id = " + appTableId+ ")" +
|
+ " join m_group g on g.id = s.group_id " + " join m_office o on o.id = g.office_id and o.hierarchy like '"
|
||||||
" ) x";
|
+ currentUser.getOffice().getHierarchy() + "%'" + " where s.id = " + appTableId + ")" + " ) x";
|
||||||
}
|
}
|
||||||
if (appTable.equalsIgnoreCase("m_client")) {
|
if (appTable.equalsIgnoreCase("m_client")) {
|
||||||
scopedSQL = "select o.id as officeId, null as groupId, c.id as clientId, null as savingsId, null as loanId from m_client c " +
|
scopedSQL = "select o.id as officeId, null as groupId, c.id as clientId, null as savingsId, null as loanId from m_client c "
|
||||||
" join m_office o on o.id = c.office_id and o.hierarchy like '" +
|
+ " join m_office o on o.id = c.office_id and o.hierarchy like '" + currentUser.getOffice().getHierarchy() + "%'"
|
||||||
currentUser.getOffice().getHierarchy() + "%'" +
|
+ " where c.id = " + appTableId;
|
||||||
" where c.id = " + appTableId;
|
|
||||||
}
|
}
|
||||||
if (appTable.equalsIgnoreCase("m_group")) {
|
if (appTable.equalsIgnoreCase("m_group")) {
|
||||||
scopedSQL = "select o.id as officeId, g.id as groupId, null as clientId, null as savingsId, null as loanId from m_group g " +
|
scopedSQL = "select o.id as officeId, g.id as groupId, null as clientId, null as savingsId, null as loanId from m_group g "
|
||||||
" join m_office o on o.id = g.office_id and o.hierarchy like '" +
|
+ " join m_office o on o.id = g.office_id and o.hierarchy like '" + currentUser.getOffice().getHierarchy() + "%'"
|
||||||
currentUser.getOffice().getHierarchy() + "%'" +
|
+ " where g.id = " + appTableId;
|
||||||
" where g.id = " + appTableId;
|
|
||||||
}
|
}
|
||||||
if (appTable.equalsIgnoreCase("m_office")) {
|
if (appTable.equalsIgnoreCase("m_office")) {
|
||||||
scopedSQL = "select o.id as officeId, null as groupId, null as clientId, null as savingsId, null as loanId from m_office o " +
|
scopedSQL = "select o.id as officeId, null as groupId, null as clientId, null as savingsId, null as loanId from m_office o "
|
||||||
" where o.hierarchy like '" +
|
+ " where o.hierarchy like '" + currentUser.getOffice().getHierarchy() + "%'" + " and o.id = " + appTableId;
|
||||||
currentUser.getOffice().getHierarchy() + "%'" +
|
|
||||||
" and o.id = " + appTableId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scopedSQL == null) { throw new PlatformDataIntegrityException("error.msg.invalid.dataScopeCriteria",
|
if (scopedSQL == null) { throw new PlatformDataIntegrityException("error.msg.invalid.dataScopeCriteria", "Application Table: "
|
||||||
"Application Table: " + appTable + " not catered for in data Scoping"); }
|
+ appTable + " not catered for in data Scoping"); }
|
||||||
|
|
||||||
return scopedSQL;
|
return scopedSQL;
|
||||||
|
|
||||||
@@ -635,9 +1017,7 @@ logger.info("data scoped sql: " + sql);
|
|||||||
if (key.equals(grs.getColumnHeaders().get(i).getColumnName())) {
|
if (key.equals(grs.getColumnHeaders().get(i).getColumnName())) {
|
||||||
columnValue = columnValues.get(i);
|
columnValue = columnValues.get(i);
|
||||||
|
|
||||||
if (notTheSame(columnValue, keyValue, colType)) {
|
if (notTheSame(columnValue, keyValue, colType)) { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-39
@@ -5,36 +5,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.documentmanagement.api;
|
package org.mifosplatform.infrastructure.documentmanagement.api;
|
||||||
|
|
||||||
import java.io.File;
|
import com.sun.jersey.core.header.FormDataContentDisposition;
|
||||||
import java.io.InputStream;
|
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||||
import java.util.Arrays;
|
import com.sun.jersey.multipart.FormDataParam;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.HeaderParam;
|
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.PathParam;
|
|
||||||
import javax.ws.rs.Produces;
|
|
||||||
import javax.ws.rs.core.Context;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
|
||||||
import javax.ws.rs.core.UriInfo;
|
|
||||||
|
|
||||||
import org.mifosplatform.infrastructure.core.api.ApiConstants;
|
|
||||||
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
|
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
|
||||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
|
||||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||||
import org.mifosplatform.infrastructure.documentmanagement.service.DocumentReadPlatformService;
|
import org.mifosplatform.infrastructure.documentmanagement.service.DocumentReadPlatformService;
|
||||||
import org.mifosplatform.infrastructure.documentmanagement.service.DocumentWritePlatformService;
|
import org.mifosplatform.infrastructure.documentmanagement.service.DocumentWritePlatformService;
|
||||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
@@ -42,9 +22,17 @@ 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;
|
||||||
|
|
||||||
import com.sun.jersey.core.header.FormDataContentDisposition;
|
import javax.ws.rs.*;
|
||||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
import javax.ws.rs.core.Context;
|
||||||
import com.sun.jersey.multipart.FormDataParam;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Path("{entityType}/{entityId}/documents")
|
@Path("{entityType}/{entityId}/documents")
|
||||||
@Component
|
@Component
|
||||||
@@ -95,8 +83,6 @@ public class DocumentManagementApiResource {
|
|||||||
@FormDataParam("file") final FormDataContentDisposition fileDetails, @FormDataParam("file") final FormDataBodyPart bodyPart,
|
@FormDataParam("file") final FormDataContentDisposition fileDetails, @FormDataParam("file") final FormDataBodyPart bodyPart,
|
||||||
@FormDataParam("name") final String name, @FormDataParam("description") final String description) {
|
@FormDataParam("name") final String name, @FormDataParam("description") final String description) {
|
||||||
|
|
||||||
FileUtils.validateFileSizeWithinPermissibleRange(fileSize, name, ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: also need to have a backup and stop reading from stream after
|
* TODO: also need to have a backup and stop reading from stream after
|
||||||
* max size is reached to protect against malicious clients
|
* max size is reached to protect against malicious clients
|
||||||
@@ -124,8 +110,6 @@ public class DocumentManagementApiResource {
|
|||||||
@FormDataParam("file") final FormDataBodyPart bodyPart, @FormDataParam("name") final String name,
|
@FormDataParam("file") final FormDataBodyPart bodyPart, @FormDataParam("name") final String name,
|
||||||
@FormDataParam("description") final String description) {
|
@FormDataParam("description") final String description) {
|
||||||
|
|
||||||
FileUtils.validateFileSizeWithinPermissibleRange(fileSize, name, ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
|
|
||||||
|
|
||||||
final Set<String> modifiedParams = new HashSet<String>();
|
final Set<String> modifiedParams = new HashSet<String>();
|
||||||
modifiedParams.add("name");
|
modifiedParams.add("name");
|
||||||
modifiedParams.add("description");
|
modifiedParams.add("description");
|
||||||
@@ -166,9 +150,6 @@ public class DocumentManagementApiResource {
|
|||||||
|
|
||||||
final DocumentData documentData = this.documentReadPlatformService.retrieveDocument(entityType, entityId, documentId);
|
final DocumentData documentData = this.documentReadPlatformService.retrieveDocument(entityType, entityId, documentId);
|
||||||
|
|
||||||
// we do not want to send document location as a part of the response
|
|
||||||
documentData.setLocation(null);
|
|
||||||
|
|
||||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
return this.toApiJsonSerializer.serialize(settings, documentData, this.RESPONSE_DATA_PARAMETERS);
|
return this.toApiJsonSerializer.serialize(settings, documentData, this.RESPONSE_DATA_PARAMETERS);
|
||||||
}
|
}
|
||||||
@@ -182,11 +163,10 @@ public class DocumentManagementApiResource {
|
|||||||
|
|
||||||
this.context.authenticatedUser().validateHasReadPermission(this.SystemEntityType);
|
this.context.authenticatedUser().validateHasReadPermission(this.SystemEntityType);
|
||||||
|
|
||||||
final DocumentData documentData = this.documentReadPlatformService.retrieveDocument(entityType, entityId, documentId);
|
final FileData fileData = this.documentReadPlatformService.retrieveFileData(entityType, entityId, documentId);
|
||||||
final File file = new File(documentData.fileLocation());
|
final ResponseBuilder response = Response.ok(fileData.file());
|
||||||
final ResponseBuilder response = Response.ok(file);
|
response.header("Content-Disposition", "attachment; filename=\"" + fileData.name() + "\"");
|
||||||
response.header("Content-Disposition", "attachment; filename=\"" + documentData.fileName() + "\"");
|
response.header("Content-Type", fileData.contentType());
|
||||||
response.header("Content-Type", documentData.contentType());
|
|
||||||
|
|
||||||
return response.build();
|
return response.build();
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-52
@@ -3,9 +3,8 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
* 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/.
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.client.api;
|
package org.mifosplatform.infrastructure.documentmanagement.api;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
@@ -22,19 +21,16 @@ import javax.ws.rs.core.Response;
|
|||||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.infrastructure.core.api.ApiConstants;
|
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils;
|
||||||
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_DATA_URI_SUFFIX;
|
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils.IMAGE_FILE_EXTENSION;
|
||||||
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_FILE_EXTENSION;
|
import org.mifosplatform.infrastructure.documentmanagement.data.ImageData;
|
||||||
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_MIME_TYPE;
|
import org.mifosplatform.infrastructure.documentmanagement.service.ImageReadPlatformService;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.service.ImageWritePlatformService;
|
||||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||||
import org.mifosplatform.portfolio.client.exception.ImageNotFoundException;
|
|
||||||
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
|
|
||||||
import org.mifosplatform.portfolio.client.service.ClientWritePlatformService;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -44,22 +40,26 @@ import com.sun.jersey.core.header.FormDataContentDisposition;
|
|||||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||||
import com.sun.jersey.multipart.FormDataParam;
|
import com.sun.jersey.multipart.FormDataParam;
|
||||||
|
|
||||||
@Path("/clients/{clientId}/images")
|
/**
|
||||||
|
* TODO Vishwas need to change the url to entity/entityId/images to make the
|
||||||
|
* image management API's generic for other entities like staff etc
|
||||||
|
**/
|
||||||
|
@Path("{clients}/{clientId}/images")
|
||||||
@Component
|
@Component
|
||||||
@Scope("singleton")
|
@Scope("singleton")
|
||||||
public class ClientImagesApiResource {
|
public class ImagesApiResource {
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final ClientReadPlatformService clientReadPlatformService;
|
private final ImageReadPlatformService imageReadPlatformService;
|
||||||
private final ClientWritePlatformService clientWritePlatformService;
|
private final ImageWritePlatformService imageWritePlatformService;
|
||||||
private final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer;
|
private final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ClientImagesApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService,
|
public ImagesApiResource(final PlatformSecurityContext context, final ImageReadPlatformService readPlatformService,
|
||||||
final ClientWritePlatformService clientWritePlatformService, final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer) {
|
final ImageWritePlatformService imageWritePlatformService, final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.clientReadPlatformService = readPlatformService;
|
this.imageReadPlatformService = readPlatformService;
|
||||||
this.clientWritePlatformService = clientWritePlatformService;
|
this.imageWritePlatformService = imageWritePlatformService;
|
||||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,12 +76,11 @@ public class ClientImagesApiResource {
|
|||||||
// TODO: vishwas might need more advances validation (like reading magic
|
// TODO: vishwas might need more advances validation (like reading magic
|
||||||
// number) for handling malicious clients
|
// number) for handling malicious clients
|
||||||
// and clients not setting mime type
|
// and clients not setting mime type
|
||||||
FileUtils.validateClientImageNotEmpty(fileDetails.getFileName());
|
ContentRepositoryUtils.validateClientImageNotEmpty(fileDetails.getFileName());
|
||||||
FileUtils.validateImageMimeType(bodyPart.getMediaType().toString());
|
ContentRepositoryUtils.validateImageMimeType(bodyPart.getMediaType().toString());
|
||||||
FileUtils.validateFileSizeWithinPermissibleRange(fileSize, fileDetails.getFileName(), ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
|
|
||||||
|
|
||||||
final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
|
final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
|
||||||
inputStream);
|
inputStream, fileSize);
|
||||||
|
|
||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
}
|
}
|
||||||
@@ -94,9 +93,9 @@ public class ClientImagesApiResource {
|
|||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String addNewClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
|
public String addNewClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
|
||||||
|
|
||||||
final Base64EncodedImage base64EncodedImage = FileUtils.extractImageFromDataURL(jsonRequestBody);
|
final Base64EncodedImage base64EncodedImage = ContentRepositoryUtils.extractImageFromDataURL(jsonRequestBody);
|
||||||
|
|
||||||
final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage);
|
final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage);
|
||||||
|
|
||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
}
|
}
|
||||||
@@ -111,20 +110,17 @@ public class ClientImagesApiResource {
|
|||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
||||||
|
|
||||||
final ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
|
final ImageData imageData = this.imageReadPlatformService.retrieveClientImage(clientId);
|
||||||
|
|
||||||
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
|
|
||||||
|
|
||||||
// TODO: Need a better way of determining image type
|
// TODO: Need a better way of determining image type
|
||||||
String imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.JPEG.getValue();
|
String imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.JPEG.getValue();
|
||||||
if (StringUtils.endsWith(clientData.imageKey(), IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
if (StringUtils.endsWith(imageData.location(), ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||||
imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.GIF.getValue();
|
imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.GIF.getValue();
|
||||||
} else if (StringUtils.endsWith(clientData.imageKey(), IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
} else if (StringUtils.endsWith(imageData.location(), ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||||
imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.PNG.getValue();
|
imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.PNG.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeFromFile(clientData.imageKey());
|
String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeBytes(imageData.getContent());
|
||||||
|
|
||||||
return clientImageAsBase64Text;
|
return clientImageAsBase64Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,26 +130,15 @@ public class ClientImagesApiResource {
|
|||||||
public Response downloadClientImage(@PathParam("clientId") final Long clientId) {
|
public Response downloadClientImage(@PathParam("clientId") final Long clientId) {
|
||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
||||||
final ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
|
final ImageData imageData = this.imageReadPlatformService.retrieveClientImage(clientId);
|
||||||
|
|
||||||
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
|
ResponseBuilder response = Response.ok(imageData.getContent());
|
||||||
|
response.header("Content-Disposition", "attachment; filename=\"" + imageData.getEntityDisplayName() + IMAGE_FILE_EXTENSION.JPEG
|
||||||
File image = new File(clientData.imageKey());
|
+ "\"");
|
||||||
String imageName = image.getName();
|
|
||||||
ResponseBuilder response = Response.ok(image);
|
|
||||||
response.header("Content-Disposition", "attachment; filename=\"" + imageName + "\"");
|
|
||||||
|
|
||||||
// TODO: Need a better way of determining image type
|
// TODO: Need a better way of determining image type
|
||||||
|
|
||||||
// determine image type
|
response.header("Content-Type", imageData.contentType());
|
||||||
String contentType = IMAGE_MIME_TYPE.JPEG.getValue();
|
|
||||||
if (StringUtils.endsWith(imageName, IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
|
||||||
contentType = IMAGE_MIME_TYPE.GIF.getValue();
|
|
||||||
} else if (StringUtils.endsWith(imageName, IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
|
||||||
contentType = IMAGE_MIME_TYPE.PNG.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
response.header("Content-Type", contentType);
|
|
||||||
return response.build();
|
return response.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +172,7 @@ public class ClientImagesApiResource {
|
|||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String deleteClientImage(@PathParam("clientId") final Long clientId) {
|
public String deleteClientImage(@PathParam("clientId") final Long clientId) {
|
||||||
this.clientWritePlatformService.deleteClientImage(clientId);
|
this.imageWritePlatformService.deleteClientImage(clientId);
|
||||||
return this.toApiJsonSerializer.serialize(new CommandProcessingResult(clientId));
|
return this.toApiJsonSerializer.serialize(new CommandProcessingResult(clientId));
|
||||||
}
|
}
|
||||||
|
|
||||||
+9
@@ -22,6 +22,7 @@ public class DocumentCommand {
|
|||||||
private Long size;
|
private Long size;
|
||||||
private String type;
|
private String type;
|
||||||
private String location;
|
private String location;
|
||||||
|
private Integer storageType;
|
||||||
|
|
||||||
private final Set<String> modifiedParameters;
|
private final Set<String> modifiedParameters;
|
||||||
|
|
||||||
@@ -95,6 +96,14 @@ public class DocumentCommand {
|
|||||||
this.location = location;
|
this.location = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getStorageType() {
|
||||||
|
return this.storageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorageType(Integer storageType) {
|
||||||
|
this.storageType = storageType;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isNameChanged() {
|
public boolean isNameChanged() {
|
||||||
return this.modifiedParameters.contains("name");
|
return this.modifiedParameters.contains("name");
|
||||||
}
|
}
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
package org.mifosplatform.infrastructure.documentmanagement.contentrepository;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.ImageData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||||
|
|
||||||
|
public interface ContentRepository {
|
||||||
|
|
||||||
|
public StorageType type = null;
|
||||||
|
|
||||||
|
// TODO:Vishwas Need to move these settings to the Database
|
||||||
|
public static final Integer MAX_FILE_UPLOAD_SIZE_IN_MB = 5;
|
||||||
|
|
||||||
|
// TODO:Vishwas Need to move these settings to the Database
|
||||||
|
public static final Integer MAX_IMAGE_UPLOAD_SIZE_IN_MB = 1;
|
||||||
|
|
||||||
|
public abstract String saveFile(InputStream uploadedInputStream, DocumentCommand documentCommand);
|
||||||
|
|
||||||
|
public abstract void deleteFile(String fileName, String documentPath);
|
||||||
|
|
||||||
|
public abstract FileData fetchFile(DocumentData documentData);
|
||||||
|
|
||||||
|
public abstract String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize);
|
||||||
|
|
||||||
|
public abstract String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName);
|
||||||
|
|
||||||
|
public abstract void deleteImage(final Long resourceId, final String location);
|
||||||
|
|
||||||
|
public abstract ImageData fetchImage(ImageData imageData);
|
||||||
|
|
||||||
|
public abstract StorageType getStorageType();
|
||||||
|
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package org.mifosplatform.infrastructure.documentmanagement.contentrepository;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData;
|
||||||
|
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
|
import org.mifosplatform.infrastructure.configuration.service.ExternalServicesReadPlatformService;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ContentRepositoryFactory {
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
private ExternalServicesReadPlatformService externalServicesReadPlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ContentRepositoryFactory(final ApplicationContext applicationContext,
|
||||||
|
final ExternalServicesReadPlatformService externalServicesReadPlatformService) {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
this.externalServicesReadPlatformService = externalServicesReadPlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentRepository getRepository() {
|
||||||
|
ConfigurationDomainService configurationDomainServiceJpa = applicationContext.getBean("configurationDomainServiceJpa",
|
||||||
|
ConfigurationDomainService.class);
|
||||||
|
if (configurationDomainServiceJpa.isAmazonS3Enabled()) { return createS3DocumentStore(); }
|
||||||
|
return new FileSystemContentRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentRepository getRepository(StorageType documentStoreType) {
|
||||||
|
if (documentStoreType == StorageType.FILE_SYSTEM) { return new FileSystemContentRepository(); }
|
||||||
|
return createS3DocumentStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContentRepository createS3DocumentStore() {
|
||||||
|
S3CredentialsData s3CredentialsData = externalServicesReadPlatformService.getS3Credentials();
|
||||||
|
return new S3ContentRepository(s3CredentialsData.getBucketName(), s3CredentialsData.getSecretKey(),
|
||||||
|
s3CredentialsData.getAccessKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
-129
@@ -1,15 +1,5 @@
|
|||||||
/**
|
package org.mifosplatform.infrastructure.documentmanagement.contentrepository;
|
||||||
* 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.infrastructure.core.service;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -20,17 +10,11 @@ import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
|||||||
import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException;
|
import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException;
|
||||||
import org.mifosplatform.infrastructure.core.exception.ImageUploadException;
|
import org.mifosplatform.infrastructure.core.exception.ImageUploadException;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
|
import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.lowagie.text.pdf.codec.Base64;
|
public class ContentRepositoryUtils {
|
||||||
|
|
||||||
public class FileUtils {
|
public static Random random = new Random();
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(FileUtils.class);
|
|
||||||
|
|
||||||
public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx";
|
|
||||||
|
|
||||||
public static enum IMAGE_MIME_TYPE {
|
public static enum IMAGE_MIME_TYPE {
|
||||||
GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png");
|
GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png");
|
||||||
@@ -44,7 +28,7 @@ public class FileUtils {
|
|||||||
public String getValue() {
|
public String getValue() {
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
public static enum IMAGE_FILE_EXTENSION {
|
public static enum IMAGE_FILE_EXTENSION {
|
||||||
GIF(".gif"), JPEG(".jpeg"), JPG(".jpg"), PNG(".png");
|
GIF(".gif"), JPEG(".jpeg"), JPG(".jpg"), PNG(".png");
|
||||||
@@ -58,7 +42,7 @@ public class FileUtils {
|
|||||||
public String getValue() {
|
public String getValue() {
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
public static enum IMAGE_DATA_URI_SUFFIX {
|
public static enum IMAGE_DATA_URI_SUFFIX {
|
||||||
GIF("data:" + IMAGE_MIME_TYPE.GIF.getValue() + ";base64,"), JPEG("data:" + IMAGE_MIME_TYPE.JPEG.getValue() + ";base64,"), PNG(
|
GIF("data:" + IMAGE_MIME_TYPE.GIF.getValue() + ";base64,"), JPEG("data:" + IMAGE_MIME_TYPE.JPEG.getValue() + ";base64,"), PNG(
|
||||||
@@ -73,107 +57,6 @@ public class FileUtils {
|
|||||||
public String getValue() {
|
public String getValue() {
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
public static Random random = new Random();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a random String
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String generateRandomString() {
|
|
||||||
String characters = "abcdefghijklmnopqrstuvwxyz123456789";
|
|
||||||
int length = generateRandomNumber();
|
|
||||||
char[] text = new char[length];
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
text[i] = characters.charAt(random.nextInt(characters.length()));
|
|
||||||
}
|
|
||||||
return new String(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a random number between 5 to 16
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static int generateRandomNumber() {
|
|
||||||
Random randomGenerator = new Random();
|
|
||||||
return randomGenerator.nextInt(11) + 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate the directory path for storing the new document
|
|
||||||
*
|
|
||||||
* @param entityType
|
|
||||||
* @param entityId
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String generateFileParentDirectory(String entityType, Long entityId) {
|
|
||||||
return FileUtils.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim()
|
|
||||||
+ File.separator + "documents" + File.separator + entityType + File.separator + entityId + File.separator
|
|
||||||
+ FileUtils.generateRandomString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate directory path for storing new Image
|
|
||||||
*/
|
|
||||||
public static String generateClientImageParentDirectory(final Long resourceId) {
|
|
||||||
return FileUtils.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim()
|
|
||||||
+ File.separator + "images" + File.separator + "clients" + File.separator + resourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param uploadedInputStream
|
|
||||||
* @param uploadedFileLocation
|
|
||||||
* @return
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public static String saveToFileSystem(InputStream uploadedInputStream, String uploadedFileLocation, String fileName) throws IOException {
|
|
||||||
String fileLocation = uploadedFileLocation + File.separator + fileName;
|
|
||||||
OutputStream out = new FileOutputStream(new File(fileLocation));
|
|
||||||
int read = 0;
|
|
||||||
byte[] bytes = new byte[1024];
|
|
||||||
|
|
||||||
while ((read = uploadedInputStream.read(bytes)) != -1) {
|
|
||||||
out.write(bytes, 0, read);
|
|
||||||
}
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
return fileLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param base64EncodedImage
|
|
||||||
* @param uploadedFileLocation
|
|
||||||
* @param fileName
|
|
||||||
* @return
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public static String saveToFileSystem(Base64EncodedImage base64EncodedImage, String uploadedFileLocation, String fileName)
|
|
||||||
throws IOException {
|
|
||||||
String fileLocation = uploadedFileLocation + File.separator + fileName + base64EncodedImage.getFileExtension();
|
|
||||||
OutputStream out = new FileOutputStream(new File(fileLocation));
|
|
||||||
byte[] imgBytes = Base64.decode(base64EncodedImage.getBase64EncodedString());
|
|
||||||
out.write(imgBytes);
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
return fileLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param fileSize
|
|
||||||
* @param name
|
|
||||||
*/
|
|
||||||
public static void validateFileSizeWithinPermissibleRange(Long fileSize, String name, int maxFileSize) {
|
|
||||||
/**
|
|
||||||
* Using Content-Length gives me size of the entire request, which is
|
|
||||||
* good enough for now for a fast fail as the length of the rest of the
|
|
||||||
* content i.e name and description while compared to the uploaded file
|
|
||||||
* size is negligible
|
|
||||||
**/
|
|
||||||
if (fileSize != null && ((fileSize / (1024 * 1024)) > maxFileSize)) { throw new DocumentManagementException(name, fileSize,
|
|
||||||
maxFileSize); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -210,6 +93,17 @@ public class FileUtils {
|
|||||||
return new Base64EncodedImage(base64EncodedString, fileExtension);
|
return new Base64EncodedImage(base64EncodedString, fileExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void validateFileSizeWithinPermissibleRange(Long fileSize, String name) {
|
||||||
|
/**
|
||||||
|
* Using Content-Length gives me size of the entire request, which is
|
||||||
|
* good enough for now for a fast fail as the length of the rest of the
|
||||||
|
* content i.e name and description while compared to the uploaded file
|
||||||
|
* size is negligible
|
||||||
|
**/
|
||||||
|
if (fileSize != null && ((fileSize / (1024 * 1024)) > ContentRepository.MAX_FILE_UPLOAD_SIZE_IN_MB)) { throw new ContentManagementException(
|
||||||
|
name, fileSize, ContentRepository.MAX_FILE_UPLOAD_SIZE_IN_MB); }
|
||||||
|
}
|
||||||
|
|
||||||
public static void validateClientImageNotEmpty(String imageFileName) {
|
public static void validateClientImageNotEmpty(String imageFileName) {
|
||||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
if (imageFileName == null) {
|
if (imageFileName == null) {
|
||||||
@@ -223,12 +117,28 @@ public class FileUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteClientImage(final Long resourceId, final String location) {
|
/**
|
||||||
File fileToBeDeleted = new File(location);
|
* Generate a random String
|
||||||
boolean fileDeleted = fileToBeDeleted.delete();
|
*
|
||||||
if (!fileDeleted) {
|
* @return
|
||||||
// no need to throw an Error, simply log a warning
|
*/
|
||||||
logger.warn("Unable to delete image associated with clients with Id " + resourceId);
|
public static String generateRandomString() {
|
||||||
|
String characters = "abcdefghijklmnopqrstuvwxyz123456789";
|
||||||
|
int length = generateRandomNumber();
|
||||||
|
char[] text = new char[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
text[i] = characters.charAt(random.nextInt(characters.length()));
|
||||||
}
|
}
|
||||||
|
return new String(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random number between 5 to 16
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int generateRandomNumber() {
|
||||||
|
Random randomGenerator = new Random();
|
||||||
|
return randomGenerator.nextInt(11) + 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.documentmanagement.contentrepository;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.ImageData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.lowagie.text.pdf.codec.Base64;
|
||||||
|
|
||||||
|
public class FileSystemContentRepository implements ContentRepository {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(FileSystemContentRepository.class);
|
||||||
|
|
||||||
|
public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String saveFile(InputStream uploadedInputStream, DocumentCommand documentCommand) {
|
||||||
|
String fileName = documentCommand.getFileName();
|
||||||
|
String uploadDocumentLocation = generateFileParentDirectory(documentCommand.getParentEntityType(),
|
||||||
|
documentCommand.getParentEntityId());
|
||||||
|
|
||||||
|
ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(documentCommand.getSize(), fileName);
|
||||||
|
makeDirectories(uploadDocumentLocation);
|
||||||
|
|
||||||
|
String fileLocation = uploadDocumentLocation + File.separator + fileName;
|
||||||
|
|
||||||
|
writeFileToFileSystem(fileName, uploadedInputStream, fileLocation);
|
||||||
|
return fileLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) {
|
||||||
|
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||||
|
|
||||||
|
ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(fileSize, imageName);
|
||||||
|
makeDirectories(uploadImageLocation);
|
||||||
|
|
||||||
|
String fileLocation = uploadImageLocation + File.separator + imageName;
|
||||||
|
|
||||||
|
writeFileToFileSystem(imageName, uploadedInputStream, fileLocation);
|
||||||
|
return fileLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) {
|
||||||
|
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||||
|
|
||||||
|
makeDirectories(uploadImageLocation);
|
||||||
|
|
||||||
|
String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension();
|
||||||
|
try {
|
||||||
|
OutputStream out = new FileOutputStream(new File(fileLocation));
|
||||||
|
byte[] imgBytes = Base64.decode(base64EncodedImage.getBase64EncodedString());
|
||||||
|
out.write(imgBytes);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new ContentManagementException(imageName, ioe.getMessage());
|
||||||
|
}
|
||||||
|
return fileLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteImage(final Long resourceId, final String location) {
|
||||||
|
final boolean fileDeleted = deleteFile(location);
|
||||||
|
if (!fileDeleted) {
|
||||||
|
// no need to throw an Error, simply log a warning
|
||||||
|
logger.warn("Unable to delete image associated with clients with Id " + resourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteFile(String fileName, String documentPath) {
|
||||||
|
final boolean fileDeleted = deleteFile(documentPath);
|
||||||
|
if (!fileDeleted) { throw new ContentManagementException(fileName, null); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteFile(String documentPath) {
|
||||||
|
final File fileToBeDeleted = new File(documentPath);
|
||||||
|
return fileToBeDeleted.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StorageType getStorageType() {
|
||||||
|
return StorageType.FILE_SYSTEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileData fetchFile(DocumentData documentData) {
|
||||||
|
File file = new File(documentData.fileLocation());
|
||||||
|
return new FileData(file, documentData.fileName(), documentData.contentType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageData fetchImage(ImageData imageData) {
|
||||||
|
File file = new File(imageData.location());
|
||||||
|
imageData.updateContent(file);
|
||||||
|
return imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the directory path for storing the new document
|
||||||
|
*
|
||||||
|
* @param entityType
|
||||||
|
* @param entityId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String generateFileParentDirectory(String entityType, Long entityId) {
|
||||||
|
return FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator
|
||||||
|
+ ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() + File.separator + "documents" + File.separator
|
||||||
|
+ entityType + File.separator + entityId + File.separator + ContentRepositoryUtils.generateRandomString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate directory path for storing new Image
|
||||||
|
*/
|
||||||
|
private String generateClientImageParentDirectory(final Long resourceId) {
|
||||||
|
return FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator
|
||||||
|
+ ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() + File.separator + "images" + File.separator
|
||||||
|
+ "clients" + File.separator + resourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively create the directory if it does not exist *
|
||||||
|
*/
|
||||||
|
private void makeDirectories(String uploadDocumentLocation) {
|
||||||
|
if (!new File(uploadDocumentLocation).isDirectory()) {
|
||||||
|
new File(uploadDocumentLocation).mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFileToFileSystem(String fileName, InputStream uploadedInputStream, String fileLocation) {
|
||||||
|
try {
|
||||||
|
OutputStream out = new FileOutputStream(new File(fileLocation));
|
||||||
|
int read = 0;
|
||||||
|
byte[] bytes = new byte[1024];
|
||||||
|
|
||||||
|
while ((read = uploadedInputStream.read(bytes)) != -1) {
|
||||||
|
out.write(bytes, 0, read);
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
throw new ContentManagementException(fileName, ioException.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+157
@@ -0,0 +1,157 @@
|
|||||||
|
package org.mifosplatform.infrastructure.documentmanagement.contentrepository;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.data.ImageData;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.amazonaws.AmazonClientException;
|
||||||
|
import com.amazonaws.AmazonServiceException;
|
||||||
|
import com.amazonaws.auth.BasicAWSCredentials;
|
||||||
|
import com.amazonaws.services.s3.AmazonS3;
|
||||||
|
import com.amazonaws.services.s3.AmazonS3Client;
|
||||||
|
import com.amazonaws.services.s3.model.DeleteObjectRequest;
|
||||||
|
import com.amazonaws.services.s3.model.GetObjectRequest;
|
||||||
|
import com.amazonaws.services.s3.model.ObjectMetadata;
|
||||||
|
import com.amazonaws.services.s3.model.PutObjectRequest;
|
||||||
|
import com.amazonaws.services.s3.model.S3Object;
|
||||||
|
import com.lowagie.text.pdf.codec.Base64;
|
||||||
|
|
||||||
|
public class S3ContentRepository implements ContentRepository {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(S3ContentRepository.class);
|
||||||
|
|
||||||
|
private final String s3BucketName;
|
||||||
|
private AmazonS3 s3Client;
|
||||||
|
|
||||||
|
public S3ContentRepository(String bucketName, String secretKey, String accessKey) {
|
||||||
|
this.s3BucketName = bucketName;
|
||||||
|
this.s3Client = new AmazonS3Client(new BasicAWSCredentials(accessKey, secretKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String saveFile(InputStream toUpload, DocumentCommand documentCommand) {
|
||||||
|
String fileName = documentCommand.getFileName();
|
||||||
|
ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(documentCommand.getSize(), fileName);
|
||||||
|
|
||||||
|
String uploadDocFolder = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId());
|
||||||
|
String uploadDocFullPath = uploadDocFolder + File.separator + fileName;
|
||||||
|
|
||||||
|
uploadDocument(fileName, toUpload, uploadDocFullPath);
|
||||||
|
return uploadDocFullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteFile(String documentName, String documentPath) {
|
||||||
|
try {
|
||||||
|
deleteObjectFromS3(documentPath);
|
||||||
|
} catch (AmazonClientException ace) {
|
||||||
|
throw new ContentManagementException(documentName, ace.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String saveImage(InputStream toUploadInputStream, Long resourceId, String imageName, Long fileSize) {
|
||||||
|
ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(fileSize, imageName);
|
||||||
|
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||||
|
String fileLocation = uploadImageLocation + File.separator + imageName;
|
||||||
|
|
||||||
|
uploadDocument(imageName, toUploadInputStream, fileLocation);
|
||||||
|
return fileLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) {
|
||||||
|
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||||
|
String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension();
|
||||||
|
InputStream toUploadInputStream = new ByteArrayInputStream(Base64.decode(base64EncodedImage.getBase64EncodedString()));
|
||||||
|
|
||||||
|
uploadDocument(imageName, toUploadInputStream, fileLocation);
|
||||||
|
return fileLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteImage(Long resourceId, String location) {
|
||||||
|
try {
|
||||||
|
deleteObjectFromS3(location);
|
||||||
|
} catch (AmazonServiceException ase) {
|
||||||
|
deleteObjectAmazonServiceExceptionMessage(ase);
|
||||||
|
logger.warn("Unable to delete image associated with clients with Id " + resourceId);
|
||||||
|
} catch (AmazonClientException ace) {
|
||||||
|
deleteObjectAmazonClientExceptionMessage(ace);
|
||||||
|
logger.warn("Unable to delete image associated with clients with Id " + resourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StorageType getStorageType() {
|
||||||
|
return StorageType.S3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileData fetchFile(DocumentData documentData) throws DocumentNotFoundException {
|
||||||
|
FileData fileData = null;
|
||||||
|
String fileName = documentData.fileName();
|
||||||
|
try {
|
||||||
|
logger.info("Downloading an object");
|
||||||
|
S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, documentData.fileLocation()));
|
||||||
|
fileData = new FileData(s3object.getObjectContent(), fileName, documentData.contentType());
|
||||||
|
} catch (AmazonClientException ace) {
|
||||||
|
logger.error(ace.getMessage());
|
||||||
|
throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId());
|
||||||
|
}
|
||||||
|
return fileData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageData fetchImage(ImageData imageData) {
|
||||||
|
S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, imageData.location()));
|
||||||
|
imageData.updateContent(s3object.getObjectContent());
|
||||||
|
return imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteObjectAmazonClientExceptionMessage(AmazonClientException ace) {
|
||||||
|
String message = "Caught an AmazonClientException." + "Error Message: " + ace.getMessage();
|
||||||
|
logger.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteObjectAmazonServiceExceptionMessage(AmazonServiceException ase) {
|
||||||
|
String message = "Caught an AmazonServiceException." + "Error Message: " + ase.getMessage() + "HTTP Status Code: "
|
||||||
|
+ ase.getStatusCode() + "AWS Error Code: " + ase.getErrorCode() + "Error Type: " + ase.getErrorType()
|
||||||
|
+ "Request ID: " + ase.getRequestId();
|
||||||
|
logger.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateFileParentDirectory(String entityType, Long entityId) {
|
||||||
|
return "documents" + File.separator + entityType + File.separator + entityId + File.separator
|
||||||
|
+ ContentRepositoryUtils.generateRandomString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateClientImageParentDirectory(Long resourceId) {
|
||||||
|
return "images" + File.separator + "clients" + File.separator + resourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteObjectFromS3(String location) {
|
||||||
|
s3Client.deleteObject(new DeleteObjectRequest(s3BucketName, location));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadDocument(String filename, InputStream inputStream, String s3UploadLocation) throws ContentManagementException {
|
||||||
|
try {
|
||||||
|
logger.info("Uploading a new object to S3 from a file to " + s3UploadLocation);
|
||||||
|
s3Client.putObject(new PutObjectRequest(this.s3BucketName, s3UploadLocation, inputStream, new ObjectMetadata()));
|
||||||
|
} catch (AmazonClientException ace) {
|
||||||
|
String message = ace.getMessage();
|
||||||
|
throw new ContentManagementException(filename, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-7
@@ -5,16 +5,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.documentmanagement.data;
|
package org.mifosplatform.infrastructure.documentmanagement.data;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable data object represent document being managed on platform.
|
* Immutable data object represent document being managed on platform.
|
||||||
*/
|
*/
|
||||||
public class DocumentData {
|
public class DocumentData {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final Long id;
|
private final Long id;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final String parentEntityType;
|
private final String parentEntityType;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final Long parentEntityId;
|
private final Long parentEntityId;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private final String name;
|
private final String name;
|
||||||
@@ -24,10 +23,11 @@ public class DocumentData {
|
|||||||
private final String type;
|
private final String type;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private final String description;
|
private final String description;
|
||||||
private String location;
|
private final String location;
|
||||||
|
private final Integer storageType;
|
||||||
|
|
||||||
public DocumentData(final Long id, final String parentEntityType, final Long parentEntityId, final String name, final String fileName,
|
public DocumentData(final Long id, final String parentEntityType, final Long parentEntityId, final String name, final String fileName,
|
||||||
final Long size, final String type, final String description, final String location) {
|
final Long size, final String type, final String description, final String location, Integer storageType) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.parentEntityType = parentEntityType;
|
this.parentEntityType = parentEntityType;
|
||||||
this.parentEntityId = parentEntityId;
|
this.parentEntityId = parentEntityId;
|
||||||
@@ -37,6 +37,7 @@ public class DocumentData {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
|
this.storageType = storageType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String contentType() {
|
public String contentType() {
|
||||||
@@ -51,8 +52,20 @@ public class DocumentData {
|
|||||||
return this.location;
|
return this.location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocation(final String location) {
|
public StorageType storageType() {
|
||||||
this.location = location;
|
return StorageType.fromInt(storageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentEntityType() {
|
||||||
|
return this.parentEntityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getParentEntityId() {
|
||||||
|
return this.parentEntityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
package org.mifosplatform.infrastructure.documentmanagement.data;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class FileData {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(FileData.class);
|
||||||
|
|
||||||
|
private final File file;
|
||||||
|
private final String fileName;
|
||||||
|
private final String contentType;
|
||||||
|
private final InputStream inputStream;
|
||||||
|
|
||||||
|
public FileData(File file, String fileName, String contentType) {
|
||||||
|
this.file = file;
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.contentType = contentType;
|
||||||
|
this.inputStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileData(InputStream inputStream, String fileName, String contentType) {
|
||||||
|
this.file = null;
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String contentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream file() {
|
||||||
|
try {
|
||||||
|
if (inputStream == null) { return new FileInputStream(file); }
|
||||||
|
return inputStream;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
logger.error(e.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
package org.mifosplatform.infrastructure.documentmanagement.data;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils;
|
||||||
|
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||||
|
|
||||||
|
public class ImageData {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Long imageId;
|
||||||
|
private final String location;
|
||||||
|
private final Integer storageType;
|
||||||
|
private final String entityDisplayName;
|
||||||
|
|
||||||
|
private File file;
|
||||||
|
private String contentType;
|
||||||
|
private InputStream inputStream;
|
||||||
|
|
||||||
|
public ImageData(Long imageId, String location, Integer storageType, String entityDisplayName) {
|
||||||
|
this.imageId = imageId;
|
||||||
|
this.location = location;
|
||||||
|
this.storageType = storageType;
|
||||||
|
this.entityDisplayName = entityDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getContent() {
|
||||||
|
// TODO Vishwas Fix error handling
|
||||||
|
try {
|
||||||
|
if (inputStream == null) {
|
||||||
|
FileInputStream fileInputStream = new FileInputStream(file);
|
||||||
|
return IOUtils.toByteArray(fileInputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IOUtils.toByteArray(inputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String setImageContentType() {
|
||||||
|
String contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.JPEG.getValue();
|
||||||
|
|
||||||
|
if (file != null) {
|
||||||
|
String fileName = file.getName();
|
||||||
|
|
||||||
|
if (StringUtils.endsWith(fileName, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||||
|
contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.GIF.getValue();
|
||||||
|
} else if (StringUtils.endsWith(fileName, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||||
|
contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.PNG.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String contentType() {
|
||||||
|
return this.contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StorageType storageType() {
|
||||||
|
return StorageType.fromInt(storageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return this.file.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String location() {
|
||||||
|
return this.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateContent(InputStream objectContent) {
|
||||||
|
this.inputStream = objectContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateContent(File file) {
|
||||||
|
this.file = file;
|
||||||
|
setImageContentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityDisplayName() {
|
||||||
|
return this.entityDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
-4
@@ -41,15 +41,18 @@ public class Document extends AbstractPersistable<Long> {
|
|||||||
@Column(name = "location", length = 500)
|
@Column(name = "location", length = 500)
|
||||||
private String location;
|
private String location;
|
||||||
|
|
||||||
|
@Column(name = "storage_type_enum")
|
||||||
|
private Integer storageType;
|
||||||
|
|
||||||
public Document() {}
|
public Document() {}
|
||||||
|
|
||||||
public static Document createNew(final String parentEntityType, final Long parentEntityId, final String name, final String fileName,
|
public static Document createNew(final String parentEntityType, final Long parentEntityId, final String name, final String fileName,
|
||||||
final Long size, final String type, final String description, final String location) {
|
final Long size, final String type, final String description, final String location, final StorageType storageType) {
|
||||||
return new Document(parentEntityType, parentEntityId, name, fileName, size, type, description, location);
|
return new Document(parentEntityType, parentEntityId, name, fileName, size, type, description, location, storageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Document(final String parentEntityType, final Long parentEntityId, final String name, final String fileName, final Long size,
|
private Document(final String parentEntityType, final Long parentEntityId, final String name, final String fileName, final Long size,
|
||||||
final String type, final String description, final String location) {
|
final String type, final String description, final String location, final StorageType storageType) {
|
||||||
this.parentEntityType = StringUtils.defaultIfEmpty(parentEntityType, null);
|
this.parentEntityType = StringUtils.defaultIfEmpty(parentEntityType, null);
|
||||||
this.parentEntityId = parentEntityId;
|
this.parentEntityId = parentEntityId;
|
||||||
this.name = StringUtils.defaultIfEmpty(name, null);
|
this.name = StringUtils.defaultIfEmpty(name, null);
|
||||||
@@ -58,6 +61,7 @@ public class Document extends AbstractPersistable<Long> {
|
|||||||
this.type = StringUtils.defaultIfEmpty(type, null);
|
this.type = StringUtils.defaultIfEmpty(type, null);
|
||||||
this.description = StringUtils.defaultIfEmpty(description, null);
|
this.description = StringUtils.defaultIfEmpty(description, null);
|
||||||
this.location = StringUtils.defaultIfEmpty(location, null);
|
this.location = StringUtils.defaultIfEmpty(location, null);
|
||||||
|
this.storageType = storageType.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(final DocumentCommand command) {
|
public void update(final DocumentCommand command) {
|
||||||
@@ -79,7 +83,6 @@ public class Document extends AbstractPersistable<Long> {
|
|||||||
if (command.isSizeChanged()) {
|
if (command.isSizeChanged()) {
|
||||||
this.size = command.getSize();
|
this.size = command.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getParentEntityType() {
|
public String getParentEntityType() {
|
||||||
@@ -146,4 +149,7 @@ public class Document extends AbstractPersistable<Long> {
|
|||||||
this.location = location;
|
this.location = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StorageType storageType() {
|
||||||
|
return StorageType.fromInt(this.storageType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.documentmanagement.domain;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_image")
|
||||||
|
public final class Image extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@Column(name = "location", length = 500)
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
@Column(name = "storage_type_enum")
|
||||||
|
private Integer storageType;
|
||||||
|
|
||||||
|
public Image(String location, StorageType storageType) {
|
||||||
|
this.location = location;
|
||||||
|
this.storageType = storageType.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Image() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocation() {
|
||||||
|
return this.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getStorageType() {
|
||||||
|
return this.storageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocation(String location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorageType(Integer storageType) {
|
||||||
|
this.storageType = storageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.documentmanagement.domain;
|
||||||
|
|
||||||
|
import org.mifosplatform.portfolio.client.domain.Client;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface ImageRepository extends JpaRepository<Image, Long>, JpaSpecificationExecutor<Client> {}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user