support fee configuration for savings
This commit is contained in:
+569
-2
@@ -11062,6 +11062,11 @@ GET https://DomainName/api/v1/charges/template
|
||||
"id": 1,
|
||||
"code": "chargeAppliesTo.loan",
|
||||
"value": "Loan"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"code": "chargeAppliesTo.savings",
|
||||
"value": "Savings"
|
||||
}
|
||||
],
|
||||
"chargeTimeTypeOptions":
|
||||
@@ -12324,7 +12329,7 @@ Content-Type: application/json
|
||||
<br/>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG"><td><div class="mifosXHeading2">Optional Fields</div></td></tr>
|
||||
<tr class=alt><td>minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,withdrawalFeeForTransfers, annualFeeAmount, annualFeeOnMonthDay, monthDayFormat, paymentChannelToFundSourceMappings</tr>
|
||||
<tr class=alt><td>minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,withdrawalFeeForTransfers, annualFeeAmount, annualFeeOnMonthDay, monthDayFormat, paymentChannelToFundSourceMappings, charges</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
@@ -12344,7 +12349,8 @@ Request Body:
|
||||
"interestPostingPeriodType":4,
|
||||
"interestCalculationType": 1,
|
||||
"interestCalculationDaysInYearType": "365",
|
||||
"accountingRule":"1"
|
||||
"accountingRule":"1",
|
||||
"charges":[{"id":"1"}]
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
@@ -12890,6 +12896,78 @@ Content-Type: application/json
|
||||
"code": "savingsWithdrawalFeesType.percent.of.amount",
|
||||
"value": "% of Amount"
|
||||
}
|
||||
],
|
||||
"chargeOptions": [
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Savings charge 1",
|
||||
"active": true,
|
||||
"penalty": false,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 200,
|
||||
"chargeTimeType": {
|
||||
"id": 2,
|
||||
"code": "chargeTimeType.specifiedDueDate",
|
||||
"value": "Specified due date"
|
||||
},
|
||||
"chargeAppliesTo": {
|
||||
"id": 2,
|
||||
"code": "chargeAppliesTo.savings",
|
||||
"value": "Savings"
|
||||
},
|
||||
"chargeCalculationType": {
|
||||
"id": 1,
|
||||
"code": "chargeCalculationType.flat",
|
||||
"value": "Flat"
|
||||
},
|
||||
"chargePaymentMode": {
|
||||
"id": 0,
|
||||
"code": "chargepaymentmode.regular",
|
||||
"value": "chargepaymentmode.regular"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Savings charge 2",
|
||||
"active": true,
|
||||
"penalty": false,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 300,
|
||||
"chargeTimeType": {
|
||||
"id": 2,
|
||||
"code": "chargeTimeType.specifiedDueDate",
|
||||
"value": "Specified due date"
|
||||
},
|
||||
"chargeAppliesTo": {
|
||||
"id": 2,
|
||||
"code": "chargeAppliesTo.savings",
|
||||
"value": "Savings"
|
||||
},
|
||||
"chargeCalculationType": {
|
||||
"id": 1,
|
||||
"code": "chargeCalculationType.flat",
|
||||
"value": "Flat"
|
||||
},
|
||||
"chargePaymentMode": {
|
||||
"id": 0,
|
||||
"code": "chargepaymentmode.regular",
|
||||
"value": "chargepaymentmode.regular"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
</code>
|
||||
@@ -13395,6 +13473,7 @@ Request Body:
|
||||
<dd><br>Associations are just extra pieces of data that you might or might not want to retrieve.<br><br></dd>
|
||||
<dd><b>'all':</b> Gets data related to all associations e.g. ?associations=all.</dd>
|
||||
<dd><b>'transactions':</b> Gets data related to transactions on the account e.g. ?associations=transactions</dd>
|
||||
<dd><b>'charges':</b>Savings Account charges data.</dd>
|
||||
</dl>
|
||||
<p>Example Requests : </p>
|
||||
<div class=apiClick>savingsaccounts/1</div>
|
||||
@@ -13812,6 +13891,462 @@ No Request Body:
|
||||
</div>
|
||||
<!-- end of savings accounts api -->
|
||||
|
||||
<!-- start of Savings charges content-->
|
||||
<a id="savings_charges" name="savings_charges" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h3>Savings Charges</h3>
|
||||
<p>Its typical for MFIs to add maintenance and operating charges. They can be either Fees or Penalties.
|
||||
</p>
|
||||
<p>Savings Charges are instances of <a href="#charges">Charges</a> and
|
||||
represent either fees and penalties for savings products. Refer <a href="#charges">Charges</a>
|
||||
for documentation of the various properties of a charge, Only additional properties (
|
||||
specific to the context of a Charge being associated with a Savings account) are
|
||||
described here</p>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>amountPaid</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>The Total amount which has been paid for this Charge
|
||||
</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>amountWaived</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>The Total amount that has been waived for this Charge
|
||||
</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>amountWrittenOff</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Total amount written off from this Charge
|
||||
</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>amountOutstanding</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>The Total outstanding amount for this Charge
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_list" name="savings_charges_list"
|
||||
class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>List Loan Charges</h4>
|
||||
<p>Example Requests:</p>
|
||||
<div class=apiClick>savingsaccounts/1/charges</div>
|
||||
<br>
|
||||
<br>
|
||||
<div class=apiClick>savingsaccounts/1/charges?fields=name,amountOrPercentage</div>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
GET https://DomainName/api/v1/savingsaccounts/{accountId}/charges
|
||||
</code>
|
||||
<code class="method-response">
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"chargeId": 3,
|
||||
"name": "Savings account maintenance fee",
|
||||
"chargeTimeType": {
|
||||
"id": 1,
|
||||
"code": "chargeTimeType.specifiedDueDate",
|
||||
"value": "Specified due date"
|
||||
},
|
||||
"chargeCalculationType": {
|
||||
"id": 1,
|
||||
"code": "chargeCalculationType.flat",
|
||||
"value": "Flat"
|
||||
},
|
||||
"percentage": 0,
|
||||
"amountPercentageAppliedTo": 0,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 100,
|
||||
"amountPaid": 0,
|
||||
"amountWaived": 0,
|
||||
"amountWrittenOff": 0,
|
||||
"amountOutstanding": 100,
|
||||
"amountOrPercentage": 100,
|
||||
"penalty": false
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"chargeId": 4,
|
||||
"name": "Pass book Fee",
|
||||
"chargeTimeType": {
|
||||
"id": 2,
|
||||
"code": "chargeTimeType.specifiedDueDate",
|
||||
"value": "Specified due date"
|
||||
},
|
||||
"dueDate": [
|
||||
2013,
|
||||
3,
|
||||
29
|
||||
],
|
||||
"chargeCalculationType": {
|
||||
"id": 1,
|
||||
"code": "chargeCalculationType.flat",
|
||||
"value": "Flat"
|
||||
},
|
||||
"percentage": 0,
|
||||
"amountPercentageAppliedTo": 0,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 100,
|
||||
"amountPaid": 0,
|
||||
"amountWaived": 0,
|
||||
"amountWrittenOff": 0,
|
||||
"amountOutstanding": 100,
|
||||
"amountOrPercentage": 100,
|
||||
"penalty": false
|
||||
}
|
||||
]
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_template" name="savings_charges_template"
|
||||
class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Retrieve Savings Charges Template</h4>
|
||||
<p>This is a convenience resource. It can be useful when
|
||||
building maintenance user interface screens for client
|
||||
applications. The template data returned consists of any or all
|
||||
of:
|
||||
<ul>
|
||||
<li class=normalli>Field Defaults</li>
|
||||
<li class=normalli>Allowed Value Lists</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>Example Request:</p>
|
||||
<div class=apiClick>savingsaccounts/1/charges/template</div>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
GET https://DomainName/api/v1/savingsaccounts/{accountId}/charges/template
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"amountPaid": 0,
|
||||
"amountWaived": 0,
|
||||
"amountWrittenOff": 0,
|
||||
"chargeOptions": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Passbook Fee",
|
||||
"active": true,
|
||||
"penalty": false,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 100,
|
||||
"chargeTimeType": {
|
||||
"id": 2,
|
||||
"code": "chargeTimeType.specifiedDueDate",
|
||||
"value": "Specified due date"
|
||||
},
|
||||
"chargeAppliesTo": {
|
||||
"id": 1,
|
||||
"code": "chargeAppliesTo.savings",
|
||||
"value": "Savings"
|
||||
},
|
||||
"chargeCalculationType": {
|
||||
"id": 1,
|
||||
"code": "chargeCalculationType.flat",
|
||||
"value": "Flat"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Chequebook fee",
|
||||
"active": true,
|
||||
"penalty": true,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 1,
|
||||
"chargeTimeType": {
|
||||
"id": 2,
|
||||
"code": "chargeTimeType.specifiedDueDate",
|
||||
"value": "Specified due date"
|
||||
},
|
||||
"chargeAppliesTo": {
|
||||
"id": 1,
|
||||
"code": "chargeAppliesTo.savings",
|
||||
"value": "Savings"
|
||||
},
|
||||
"chargeCalculationType": {
|
||||
"id": 1,
|
||||
"code": "chargeCalculationType.flat",
|
||||
"value": "Flat"
|
||||
}
|
||||
}
|
||||
],
|
||||
"penalty": false
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_retrieve" name="savings_charges_retrieve"
|
||||
class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Retrieve a Savings account Charge</h4>
|
||||
<p>Example Requests:</p>
|
||||
<div class=apiClick>/savingsaccounts/1/charges/5</div>
|
||||
<br>
|
||||
<br>
|
||||
<div class=apiClick>/savingsaccounts/1/charges/5?fields=name,amountOrPercentage</div>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
GET https://DomainName/api/v1/savingsaccounts/{accountId}/charges/{savingsAccountChargeId}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"id": 1,
|
||||
"chargeId": 1,
|
||||
"name": "Passbook fee",
|
||||
"chargeTimeType": {
|
||||
"id": 1,
|
||||
"code": "chargeTimeType.specifiedDueDate",
|
||||
"value": "Specified due date"
|
||||
},
|
||||
"chargeCalculationType": {
|
||||
"id": 1,
|
||||
"code": "chargeCalculationType.flat",
|
||||
"value": "Flat"
|
||||
},
|
||||
"percentage": 0,
|
||||
"amountPercentageAppliedTo": 0,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 100,
|
||||
"amountPaid": 0,
|
||||
"amountWaived": 0,
|
||||
"amountWrittenOff": 0,
|
||||
"amountOutstanding": 100,
|
||||
"amountOrPercentage": 100,
|
||||
"penalty": false
|
||||
} </code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_create" name="savings_charges_create" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Create a Savings account Charge</h4>
|
||||
<table class=matrixHeading>
|
||||
<tr class="matrixHeadingBG">
|
||||
<td><div class="mifosXHeading2">Mandatory Fields
|
||||
for Savings account Charges</div></td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>chargeId, amount, dueDate</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
POST https://DomainName/api/v1/savingsaccounts/{accountId}/charges
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST /savingsaccounts/1/charges
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"chargeId": "2",
|
||||
"locale": "en",
|
||||
"amount": "100",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"dueDate": "29 April 2013"
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"officeId": 1,
|
||||
"clientId": 1,
|
||||
"savingsId": 1,
|
||||
"resourceId": 6
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_update" name="savings_charges_update"
|
||||
class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Update a Savings account Charge</h4>
|
||||
<p>Currently Savings account Charges may be updated only if the Savings account
|
||||
is not yet approved.
|
||||
</p>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
PUT https://DomainName/api/v1/savingsaccounts/{accountId}/charges/{savingsAccountChargeId}
|
||||
</code>
|
||||
<code class="method-request">
|
||||
PUT savingsaccounts/1/charges/6
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"locale": "en",
|
||||
"amount": "60",
|
||||
"dueDate": "27 March 2013"
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"officeId": 1,
|
||||
"clientId": 1,
|
||||
"savingsId": 1,
|
||||
"resourceId": 6,
|
||||
"changes": {
|
||||
"dueDate": "27 March 2013",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"locale": "en",
|
||||
"amount": 60.0
|
||||
}
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_delete" name="savings_charges_delete" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Delete a Savings account Charge</h4>
|
||||
<p>
|
||||
<b>Note:</b>Currently, A Savings account Charge may only be removed from Savings that are not
|
||||
yet approved.
|
||||
</p>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
DELETE https://DomainName/api/v1/savingsaccounts/{accountId}/charges/{savingsAccountChargeId}
|
||||
</code>
|
||||
<code class="method-request">
|
||||
DELETE savingsaccounts/1/charges/2
|
||||
Content-Type: application/json
|
||||
No Request Body:
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"officeId": 1,
|
||||
"clientId": 1,
|
||||
"savingsId": 1,
|
||||
"resourceId": 2
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_pay" name="savings_charges_pay" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Pay a Savings account Charge</h4>
|
||||
<p>
|
||||
A charge will be paid when savings account is active and having sufficient balance.
|
||||
</p>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
POST https://DomainName/api/v1/savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=paycharge
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST savingsaccounts/1/charges/2?command=paycharge
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"locale": "en",
|
||||
"amount": "60",
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"officeId": 1,
|
||||
"clientId": 1,
|
||||
"savingsId": 1,
|
||||
"resourceId": 2
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_waive" name="savings_charges_waive" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Waive off a Savings account Charge</h4>
|
||||
<p>
|
||||
Outstanding charge amount will be waived off.
|
||||
</p>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
POST https://DomainName/api/v1/savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=waive
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST savingsaccounts/1/charges/2?command=waive
|
||||
Content-Type: application/json
|
||||
No Request Body:
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"officeId": 1,
|
||||
"clientId": 1,
|
||||
"savingsId": 1,
|
||||
"resourceId": 2
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of Savings account charges -->
|
||||
<!-- account transfers -->
|
||||
<a id="accounttransfers" name="accounttransfers" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
@@ -19949,6 +20484,38 @@ GET https://DomainName/api/v1/search?query=000000111
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="alt">
|
||||
<td><a href="#savings_charges">Savings Account Charges</a></td>
|
||||
<td>savingsaccounts/{accountId}/charges</td>
|
||||
<td><a href="#savings_charges_create">Add a Savings Account Charge</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>savingsaccounts/{accountId}/charges/{savingsAccountChargeId}</td>
|
||||
<td></td>
|
||||
<td><a href="#savings_charges_retrieve">Retrieve a Savings Account Charge</a></td>
|
||||
<td><a href="#savings_charges_update">Modify a Savings Account Charge</a></td>
|
||||
<td><a href="#savings_charges_delete">Delete a Savings Account Charge</a></td>
|
||||
</tr>
|
||||
<tr class="alt">
|
||||
<td></td>
|
||||
<td>savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=paycharge</td>
|
||||
<td><a href="#savings_charges_pay">Pay a Savings Account Charge</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="alt">
|
||||
<td></td>
|
||||
<td>savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=waive</td>
|
||||
<td><a href="#savings_charges_waive">Waive a Savings Account Charge</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="alt">
|
||||
<td><a href="#accounttransfers">Account Transfers</a></td>
|
||||
<td>accounttransfers/</td>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
CREATE TABLE `m_savings_account_charge` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`savings_account_id` BIGINT(20) NOT NULL,
|
||||
`charge_id` BIGINT(20) NOT NULL,
|
||||
`is_penalty` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`charge_time_enum` SMALLINT(5) NOT NULL,
|
||||
`due_for_collection_as_of_date` DATE NULL DEFAULT NULL,
|
||||
`charge_calculation_enum` SMALLINT(5) NOT NULL,
|
||||
`calculation_percentage` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
`calculation_on_amount` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
`amount` DECIMAL(19,6) NOT NULL,
|
||||
`amount_paid_derived` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
`amount_waived_derived` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
`amount_writtenoff_derived` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
`amount_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT '0.000000',
|
||||
`is_paid_derived` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`waived` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `charge_id` (`charge_id`),
|
||||
INDEX `m_savings_account_charge_ibfk_2` (`savings_account_id`),
|
||||
CONSTRAINT `m_savings_account_charge_ibfk_1` FOREIGN KEY (`charge_id`) REFERENCES `m_charge` (`id`),
|
||||
CONSTRAINT `m_savings_account_charge_ibfk_2` FOREIGN KEY (`savings_account_id`) REFERENCES `m_savings_account` (`id`)
|
||||
)
|
||||
ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
CREATE TABLE `m_savings_product_charge` (
|
||||
`savings_product_id` BIGINT(20) NOT NULL,
|
||||
`charge_id` BIGINT(20) NOT NULL,
|
||||
PRIMARY KEY (`savings_product_id`, `charge_id`),
|
||||
INDEX `charge_id` (`charge_id`),
|
||||
CONSTRAINT `m_savings_product_charge_ibfk_1` FOREIGN KEY (`charge_id`) REFERENCES `m_charge` (`id`),
|
||||
CONSTRAINT `m_savings_product_charge_ibfk_2` FOREIGN KEY (`savings_product_id`) REFERENCES `m_savings_product` (`id`)
|
||||
)
|
||||
ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_SAVINGSACCOUNTCHARGE', 'SAVINGSACCOUNTCHARGE', 'CREATE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'CREATE_SAVINGSACCOUNTCHARGE_CHECKER', 'SAVINGSACCOUNTCHARGE', 'CREATE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_SAVINGSACCOUNTCHARGE', 'SAVINGSACCOUNTCHARGE', 'UPDATE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'UPDATE_SAVINGSACCOUNTCHARGE_CHECKER', 'SAVINGSACCOUNTCHARGE', 'UPDATE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_SAVINGSACCOUNTCHARGE', 'SAVINGSACCOUNTCHARGE', 'DELETE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'DELETE_SAVINGSACCOUNTCHARGE_CHECKER', 'SAVINGSACCOUNTCHARGE', 'DELETE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'WAIVE_SAVINGSACCOUNTCHARGE', 'SAVINGSACCOUNTCHARGE', 'WAIVE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'WAIVE_SAVINGSACCOUNTCHARGE_CHECKER', 'SAVINGSACCOUNTCHARGE', 'WAIVE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'PAY_SAVINGSACCOUNTCHARGE', 'SAVINGSACCOUNTCHARGE', 'PAY', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ( 'portfolio', 'PAY_SAVINGSACCOUNTCHARGE_CHECKER', 'SAVINGSACCOUNTCHARGE', 'PAY', 0);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE `m_savings_account_charge_paid_by` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`savings_account_transaction_id` BIGINT(20) NOT NULL,
|
||||
`savings_account_charge_id` BIGINT(20) NOT NULL,
|
||||
`amount` DECIMAL(19,6) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `FK__m_savings_account_transaction` (`savings_account_transaction_id`),
|
||||
INDEX `FK__m_savings_account_charge` (`savings_account_charge_id`),
|
||||
CONSTRAINT `FK__m_savings_account_charge` FOREIGN KEY (`savings_account_charge_id`) REFERENCES `m_savings_account_charge` (`id`),
|
||||
CONSTRAINT `FK__m_savings_account_transaction` FOREIGN KEY (`savings_account_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB;
|
||||
+25
-1
@@ -445,7 +445,7 @@ public class CommandWrapper {
|
||||
public boolean isSavingsAccountResource() {
|
||||
return this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
|
||||
}
|
||||
|
||||
|
||||
public boolean isRejectionOfSavingsAccountApplication() {
|
||||
return this.actionName.equalsIgnoreCase("REJECT") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
|
||||
}
|
||||
@@ -498,6 +498,30 @@ public class CommandWrapper {
|
||||
return this.actionName.equalsIgnoreCase("ADJUSTTRANSACTION") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
|
||||
}
|
||||
|
||||
public boolean isSavingsAccountChargeResource() {
|
||||
return this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE");
|
||||
}
|
||||
|
||||
public boolean isAddSavingsAccountCharge() {
|
||||
return this.actionName.equalsIgnoreCase("CREATE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE");
|
||||
}
|
||||
|
||||
public boolean isDeleteSavingsAccountCharge() {
|
||||
return isDeleteOperation() && this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE");
|
||||
}
|
||||
|
||||
public boolean isUpdateSavingsAccountCharge() {
|
||||
return this.actionName.equalsIgnoreCase("UPDATE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE");
|
||||
}
|
||||
|
||||
public boolean isWaiveSavingsAccountCharge() {
|
||||
return this.actionName.equalsIgnoreCase("WAIVE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE");
|
||||
}
|
||||
|
||||
public boolean isPaySavingsAccountCharge() {
|
||||
return this.actionName.equalsIgnoreCase("PAY") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE");
|
||||
}
|
||||
|
||||
public boolean isCalendarResource() {
|
||||
return this.entityName.equalsIgnoreCase("CALENDAR");
|
||||
}
|
||||
|
||||
+46
@@ -941,6 +941,52 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/savingsaccounts/" + accountId + "?command=applyAnnualFees";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createSavingsAccountCharge(final Long savingsAccountId) {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "SAVINGSACCOUNTCHARGE";
|
||||
this.savingsId = savingsAccountId;
|
||||
this.href = "/savingsaccounts/" + savingsAccountId + "/charges";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateSavingsAccountCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "SAVINGSACCOUNTCHARGE";
|
||||
this.entityId = savingsAccountChargeId;
|
||||
this.savingsId = savingsAccountId;
|
||||
this.href = "/savingsaccounts/" + savingsAccountId + "/charges/" + savingsAccountChargeId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder waiveSavingsAccountCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
|
||||
this.actionName = "WAIVE";
|
||||
this.entityName = "SAVINGSACCOUNTCHARGE";
|
||||
this.entityId = savingsAccountChargeId;
|
||||
this.savingsId = savingsAccountId;
|
||||
this.href = "/savingsaccounts/" + savingsAccountId + "/charges/" + savingsAccountChargeId;
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder paySavingsAccountCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
|
||||
this.actionName = "PAY";
|
||||
this.entityName = "SAVINGSACCOUNTCHARGE";
|
||||
this.entityId = savingsAccountChargeId;
|
||||
this.savingsId = savingsAccountId;
|
||||
this.href = "/savingsaccounts/" + savingsAccountId + "/charges/" + savingsAccountChargeId;
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteSavingsAccountCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "SAVINGSACCOUNTCHARGE";
|
||||
this.entityId = savingsAccountChargeId;
|
||||
this.savingsId = savingsAccountId;
|
||||
this.href = "/savingsaccounts/" + savingsAccountId + "/charges/" + savingsAccountChargeId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createCalendar(final String supportedEntityType, final Long supportedEntityId) {
|
||||
this.actionName = "CREATE";
|
||||
|
||||
+14
@@ -429,6 +429,20 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
} else {
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
}
|
||||
} else if (wrapper.isSavingsAccountChargeResource()) {
|
||||
if (wrapper.isAddSavingsAccountCharge()) {
|
||||
handler = this.applicationContext.getBean("addSavingsAccountChargeCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isDeleteSavingsAccountCharge()) {
|
||||
handler = this.applicationContext.getBean("deleteSavingsAccountChargeCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isUpdateSavingsAccountCharge()) {
|
||||
handler = this.applicationContext.getBean("updateSavingsAccountChargeCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isWaiveSavingsAccountCharge()) {
|
||||
handler = this.applicationContext.getBean("waiveSavingsAccountChargeCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isPaySavingsAccountCharge()) {
|
||||
handler = this.applicationContext.getBean("paySavingsAccountChargeCommandHandler", NewCommandSourceHandler.class);
|
||||
} else {
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
}
|
||||
} else if (wrapper.isCalendarResource()) {
|
||||
if (wrapper.isCreate()) {
|
||||
handler = this.applicationContext.getBean("createCalendarCommandHandler", NewCommandSourceHandler.class);
|
||||
|
||||
+19
@@ -10,9 +10,11 @@ import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData;
|
||||
|
||||
/**
|
||||
* Immutable data object for charge data.
|
||||
@@ -125,4 +127,21 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
return LoanChargeData.newLoanChargeDetails(this.id, this.name, this.currency, this.amount, percentage, this.chargeTimeType,
|
||||
this.chargeCalculationType, this.penalty,this.chargePaymentMode);
|
||||
}
|
||||
|
||||
public SavingsAccountChargeData toSavingsAccountChargeData(){
|
||||
|
||||
final Long savingsChargeId = null;
|
||||
final BigDecimal amountPaid = BigDecimal.ZERO;
|
||||
final BigDecimal amountWaived = BigDecimal.ZERO;
|
||||
final BigDecimal amountWrittenOff = BigDecimal.ZERO;
|
||||
final BigDecimal amountOutstanding = BigDecimal.ZERO;
|
||||
final BigDecimal percentage = BigDecimal.ZERO;
|
||||
final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final LocalDate dueAsOfDate = null;
|
||||
|
||||
return SavingsAccountChargeData.instance(savingsChargeId, this.id, this.name, this.currency, this.amount, amountPaid, amountWaived,
|
||||
amountWrittenOff, amountOutstanding, this.chargeTimeType, dueAsOfDate, this.chargeCalculationType, percentage,
|
||||
amountPercentageAppliedTo, chargeOptions, this.penalty);
|
||||
}
|
||||
}
|
||||
+85
-4
@@ -6,7 +6,9 @@
|
||||
package org.mifosplatform.portfolio.charge.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.Column;
|
||||
@@ -17,10 +19,14 @@ import javax.persistence.UniqueConstraint;
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeDueAtDisbursementCannotBePenaltyException;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeParameterUpdateNotSupportedException;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
@@ -93,7 +99,33 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
this.penalty = penalty;
|
||||
this.active = active;
|
||||
this.chargePaymentMode = paymentMode.getValue();
|
||||
if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charges");
|
||||
|
||||
if (isSavingsCharge()) {
|
||||
|
||||
if (!isAllowedSavingsChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.savings");
|
||||
}
|
||||
|
||||
if (!isAllowedSavingsChargeCalculationType()) {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.calculation.type.for.savings");
|
||||
}
|
||||
|
||||
} else if (isLoanCharge()) {
|
||||
|
||||
if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
|
||||
|
||||
if (!isAllowedLoanChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.loan");
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,12 +177,35 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public boolean isLoanCharge(){
|
||||
return ChargeAppliesTo.fromInt(this.chargeAppliesTo).isLoanCharge();
|
||||
}
|
||||
|
||||
public boolean isAllowedLoanChargeTime(){
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isAllowedLoanChargeTime();
|
||||
}
|
||||
|
||||
public boolean isSavingsCharge(){
|
||||
return ChargeAppliesTo.fromInt(this.chargeAppliesTo).isSavingsCharge();
|
||||
}
|
||||
|
||||
public boolean isAllowedSavingsChargeTime(){
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isAllowedSavingsChargeTime();
|
||||
}
|
||||
|
||||
public boolean isAllowedSavingsChargeCalculationType(){
|
||||
return ChargeCalculationType.fromInt(this.chargeCalculation).isAllowedSavingsChargeCalculationType();
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
|
||||
|
||||
final String localeAsInput = command.locale();
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charges");
|
||||
|
||||
final String nameParamName = "name";
|
||||
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
|
||||
@@ -180,14 +235,31 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
actualChanges.put(chargeTimeParamName, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.chargeTime = ChargeTimeType.fromInt(newValue).getValue();
|
||||
|
||||
if (isSavingsCharge()) {
|
||||
if (!isAllowedSavingsChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.savings");
|
||||
}
|
||||
} else if (isLoanCharge()) {
|
||||
if (!isAllowedLoanChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.loan");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final String chargeAppliesToParamName = "chargeAppliesTo";
|
||||
if (command.isChangeInIntegerParameterNamed(chargeAppliesToParamName, this.chargeAppliesTo)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(chargeAppliesToParamName);
|
||||
/*final Integer newValue = command.integerValueOfParameterNamed(chargeAppliesToParamName);
|
||||
actualChanges.put(chargeAppliesToParamName, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.chargeAppliesTo = ChargeAppliesTo.fromInt(newValue).getValue();
|
||||
this.chargeAppliesTo = ChargeAppliesTo.fromInt(newValue).getValue();*/
|
||||
|
||||
//AA: Do not allow to change chargeAppliesTo.
|
||||
final String errorMessage = "Update of Charge applies to is not supported";
|
||||
throw new ChargeParameterUpdateNotSupportedException("charge.applies.to", errorMessage);
|
||||
}
|
||||
|
||||
final String chargeCalculationParamName = "chargeCalculationType";
|
||||
@@ -196,6 +268,13 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
actualChanges.put(chargeCalculationParamName, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.chargeCalculation = ChargeCalculationType.fromInt(newValue).getValue();
|
||||
|
||||
if (isSavingsCharge()) {
|
||||
if (!isAllowedSavingsChargeCalculationType()) {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.calculation.type.for.savings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String paymentModeParamName = "chargePaymentMode";
|
||||
@@ -223,7 +302,9 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
if (this.penalty && ChargeTimeType.fromInt(this.chargeTime).isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(
|
||||
name); }
|
||||
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
|
||||
+21
-1
@@ -8,7 +8,8 @@ package org.mifosplatform.portfolio.charge.domain;
|
||||
public enum ChargeAppliesTo {
|
||||
|
||||
INVALID(0, "chargeAppliesTo.invalid"), //
|
||||
LOAN(1, "chargeAppliesTo.loan");
|
||||
LOAN(1, "chargeAppliesTo.loan"),
|
||||
SAVINGS(2, "chargeAppliesTo.savings");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -26,16 +27,35 @@ public enum ChargeAppliesTo {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static int minValue(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int maxValue(){
|
||||
return 2;//modify whenever a new value added
|
||||
}
|
||||
|
||||
public static ChargeAppliesTo fromInt(final Integer chargeAppliesTo) {
|
||||
ChargeAppliesTo chargeAppliesToType = ChargeAppliesTo.INVALID;
|
||||
switch (chargeAppliesTo) {
|
||||
case 1:
|
||||
chargeAppliesToType = LOAN;
|
||||
break;
|
||||
case 2:
|
||||
chargeAppliesToType = SAVINGS;
|
||||
break;
|
||||
default:
|
||||
chargeAppliesToType = INVALID;
|
||||
break;
|
||||
}
|
||||
return chargeAppliesToType;
|
||||
}
|
||||
|
||||
public boolean isLoanCharge(){
|
||||
return this.value.equals(ChargeAppliesTo.LOAN.getValue());
|
||||
}
|
||||
|
||||
public boolean isSavingsCharge(){
|
||||
return this.value.equals(ChargeAppliesTo.SAVINGS.getValue());
|
||||
}
|
||||
}
|
||||
+12
@@ -47,4 +47,16 @@ public enum ChargeCalculationType {
|
||||
}
|
||||
return chargeCalculationType;
|
||||
}
|
||||
|
||||
public boolean isPercentageOfAmount(){
|
||||
return this.value.equals(ChargeCalculationType.PERCENT_OF_AMOUNT.getValue());
|
||||
}
|
||||
|
||||
public boolean isFlat(){
|
||||
return this.value.equals(ChargeCalculationType.FLAT.getValue());
|
||||
}
|
||||
|
||||
public boolean isAllowedSavingsChargeCalculationType(){
|
||||
return this.isFlat();
|
||||
}
|
||||
}
|
||||
+38
-1
@@ -5,11 +5,14 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.charge.domain;
|
||||
|
||||
|
||||
public enum ChargeTimeType {
|
||||
|
||||
INVALID(0, "chargeTimeType.invalid"), //
|
||||
DISBURSEMENT(1, "chargeTimeType.disbursement"), //
|
||||
SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate");
|
||||
SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate"),
|
||||
MONTHLY(3, "chargeTimeType.monthly"),
|
||||
YEARLY(4, "chargeTimeType.yearly");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -27,6 +30,14 @@ public enum ChargeTimeType {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static int minValue(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int maxValue(){
|
||||
return 4;
|
||||
}
|
||||
|
||||
public static ChargeTimeType fromInt(final Integer chargeTime) {
|
||||
ChargeTimeType chargeTimeType = ChargeTimeType.INVALID;
|
||||
switch (chargeTime) {
|
||||
@@ -36,6 +47,12 @@ public enum ChargeTimeType {
|
||||
case 2:
|
||||
chargeTimeType = SPECIFIED_DUE_DATE;
|
||||
break;
|
||||
case 3:
|
||||
chargeTimeType = MONTHLY;
|
||||
break;
|
||||
case 4:
|
||||
chargeTimeType = YEARLY;
|
||||
break;
|
||||
default:
|
||||
chargeTimeType = INVALID;
|
||||
break;
|
||||
@@ -46,4 +63,24 @@ public enum ChargeTimeType {
|
||||
public boolean isTimeOfDisbursement() {
|
||||
return ChargeTimeType.DISBURSEMENT.getValue().equals(this.value);
|
||||
}
|
||||
|
||||
public boolean isOnSpecifiedDueDate() {
|
||||
return this.value.equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
}
|
||||
|
||||
public boolean isMonthly(){
|
||||
return this.value.equals(ChargeTimeType.MONTHLY.getValue());
|
||||
}
|
||||
|
||||
public boolean isYearly(){
|
||||
return this.value.equals(ChargeTimeType.YEARLY.getValue());
|
||||
}
|
||||
|
||||
public boolean isAllowedLoanChargeTime(){
|
||||
return this.isTimeOfDisbursement() || this.isOnSpecifiedDueDate();
|
||||
}
|
||||
|
||||
public boolean isAllowedSavingsChargeTime(){
|
||||
return this.isOnSpecifiedDueDate() || this.isMonthly() || this.isYearly();
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package org.mifosplatform.portfolio.charge.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
|
||||
public class ChargeCannotBeAppliedToException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ChargeCannotBeAppliedToException(String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
|
||||
super("error.msg.charge.cannot.be.applied.to" + postFix, defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.charge.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeException} thrown when Calendar resources are not found.
|
||||
*/
|
||||
public class ChargeParameterUpdateNotSupportedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ChargeParameterUpdateNotSupportedException(String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
super("error.msg.charge.update.of." + postFix + ".is.not.supported", defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.charge.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class SavingsAccountChargeCannotBeWaivedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
/*** enum of reasons of why Savings Account Charge cannot be waived **/
|
||||
public static enum SAVINGS_ACCOUNT_CHARGE_CANNOT_BE_WAIVED_REASON {
|
||||
ALREADY_PAID, ALREADY_WAIVED, SAVINGS_ACCOUNT_NOT_ACTIVE, SAVINGS_ACCOUNT_CLOSED;
|
||||
|
||||
public String errorMessage() {
|
||||
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||
return "This savings account charge has been completely paid";
|
||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||
return "This savings account charge has already been waived";
|
||||
} else if (name().toString().equalsIgnoreCase("SAVINGS_ACCOUNT_NOT_ACTIVE")) {
|
||||
return "This savings account charge cannot be waived as the Savings account associated with it is not active";
|
||||
} else if (name().toString().equalsIgnoreCase("SAVINGS_ACCOUNT_CLOSED")) { return "This savings account charge cannot be waived as the Savings account associated with it is closed"; }
|
||||
return name().toString();
|
||||
}
|
||||
|
||||
public String errorCode() {
|
||||
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||
return "error.msg.savings.account.charge.already.paid";
|
||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||
return "error.msg.savings.account.charge.already.waived";
|
||||
} else if (name().toString().equalsIgnoreCase("SAVINGS_ACCOUNT_NOT_ACTIVE")) {
|
||||
return "error.msg.savings.account.charge.associated.savings.account.not.active";
|
||||
} else if (name().toString().equalsIgnoreCase("SAVINGS_ACCOUNT_CLOSED")) { return "error.msg.savings.account.charge.associated.savings.account.closed"; }
|
||||
return name().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public SavingsAccountChargeCannotBeWaivedException(final SAVINGS_ACCOUNT_CHARGE_CANNOT_BE_WAIVED_REASON reason,
|
||||
final Long savingsAccountChargeId) {
|
||||
super(reason.errorCode(), reason.errorMessage(), savingsAccountChargeId);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.charge.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* {@link AbstractPlatformDomainRuleException} thrown when savings account charge does not
|
||||
* exist.
|
||||
*/
|
||||
public class SavingsAccountChargeNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public SavingsAccountChargeNotFoundException(final Long id) {
|
||||
super("error.msg.savings.account.charge.id.invalid", "Savings Account charge with identifier " + id + " does not exist", id);
|
||||
}
|
||||
|
||||
public SavingsAccountChargeNotFoundException(final Long id, final Long savingsAccountId) {
|
||||
super("error.msg.savings.account.charge.id.invalid.for.given.savings.account", "Savings Account charge with identifier " + id + " does not exist for Savings Account " + savingsAccountId,
|
||||
id, savingsAccountId);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.charge.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
/**
|
||||
* {@link AbstractPlatformDomainRuleException} thrown when currency mismatch occurs
|
||||
*/
|
||||
public class SavingsAccountChargeWithoutMandatoryFieldException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public SavingsAccountChargeWithoutMandatoryFieldException(final String entity, final String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
super("error.msg."+entity+"."+postFix+".cannot.be.blank", defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
|
||||
}
|
||||
+6
-4
@@ -21,6 +21,8 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeAppliesTo;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -65,10 +67,10 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
|
||||
|
||||
final Integer chargeAppliesTo = fromApiJsonHelper.extractIntegerNamed("chargeAppliesTo", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull().inMinMaxRange(ChargeAppliesTo.minValue(), ChargeAppliesTo.maxValue());
|
||||
|
||||
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull().inMinMaxRange(1, 2);
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull().inMinMaxRange(ChargeTimeType.minValue(), ChargeTimeType.maxValue());
|
||||
|
||||
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 4);
|
||||
@@ -116,12 +118,12 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
|
||||
if (fromApiJsonHelper.parameterExists("chargeAppliesTo", element)) {
|
||||
final Integer chargeAppliesTo = fromApiJsonHelper.extractIntegerNamed("chargeAppliesTo", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull().inMinMaxRange(ChargeAppliesTo.minValue(), ChargeAppliesTo.maxValue());
|
||||
}
|
||||
|
||||
if (fromApiJsonHelper.parameterExists("chargeTimeType", element)) {
|
||||
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull().inMinMaxRange(1, 2);
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull().inMinMaxRange(ChargeTimeType.minValue(), ChargeTimeType.maxValue());
|
||||
}
|
||||
|
||||
if (fromApiJsonHelper.parameterExists("chargeCalculationType", element)) {
|
||||
|
||||
+16
-4
@@ -5,11 +5,10 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.charge.service;
|
||||
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargeAppliesTo;
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargeCalculationType;
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargeTimeType;
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargePaymentMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -35,12 +34,25 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveApplicableToTypes() {
|
||||
return Arrays.asList(chargeAppliesTo(ChargeAppliesTo.LOAN));
|
||||
List<EnumOptionData> chargeAppliesToTypes = new ArrayList<EnumOptionData>();
|
||||
for (ChargeAppliesTo chargeAppliesTo : ChargeAppliesTo.values()) {
|
||||
if(ChargeAppliesTo.INVALID.equals(chargeAppliesTo)) continue;
|
||||
chargeAppliesToTypes.add(ChargeEnumerations.chargeAppliesTo(chargeAppliesTo));
|
||||
}
|
||||
return chargeAppliesToTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveCollectionTimeTypes() {
|
||||
return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE));
|
||||
List<EnumOptionData> chargeTimeTypes = new ArrayList<EnumOptionData>();
|
||||
for (ChargeTimeType chargeTimeType : ChargeTimeType.values()) {
|
||||
if (ChargeTimeType.INVALID.equals(chargeTimeType)
|
||||
|| ChargeTimeType.MONTHLY.equals(chargeTimeType) // To be implemented for Savings
|
||||
|| ChargeTimeType.YEARLY.equals(chargeTimeType)) // To be implemented for Savings
|
||||
continue;
|
||||
chargeTimeTypes.add(ChargeEnumerations.chargeTimeType(chargeTimeType));
|
||||
}
|
||||
return chargeTimeTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
@@ -28,6 +28,14 @@ public class ChargeEnumerations {
|
||||
optionData = new EnumOptionData(ChargeTimeType.SPECIFIED_DUE_DATE.getValue().longValue(),
|
||||
ChargeTimeType.SPECIFIED_DUE_DATE.getCode(), "Specified due date");
|
||||
break;
|
||||
case MONTHLY:
|
||||
optionData = new EnumOptionData(ChargeTimeType.MONTHLY.getValue().longValue(),
|
||||
ChargeTimeType.MONTHLY.getCode(), "Monthly");
|
||||
break;
|
||||
case YEARLY:
|
||||
optionData = new EnumOptionData(ChargeTimeType.YEARLY.getValue().longValue(),
|
||||
ChargeTimeType.YEARLY.getCode(), "Yearly");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid");
|
||||
break;
|
||||
@@ -45,6 +53,9 @@ public class ChargeEnumerations {
|
||||
case LOAN:
|
||||
optionData = new EnumOptionData(ChargeAppliesTo.LOAN.getValue().longValue(), ChargeAppliesTo.LOAN.getCode(), "Loan");
|
||||
break;
|
||||
case SAVINGS:
|
||||
optionData = new EnumOptionData(ChargeAppliesTo.SAVINGS.getValue().longValue(), ChargeAppliesTo.SAVINGS.getCode(), "Savings");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargeAppliesTo.INVALID.getValue().longValue(), ChargeAppliesTo.INVALID.getCode(),
|
||||
"Invalid");
|
||||
|
||||
+6
@@ -22,4 +22,10 @@ public interface ChargeReadPlatformService {
|
||||
Collection<ChargeData> retrieveLoanApplicablePenalties();
|
||||
|
||||
Collection<ChargeData> retrieveLoanProductCharges(Long loanProductId);
|
||||
|
||||
Collection<ChargeData> retrieveSavingsAccountApplicableCharges(boolean feeChargesOnly);
|
||||
|
||||
Collection<ChargeData> retrieveSavingsAccountApplicablePenalties();
|
||||
|
||||
Collection<ChargeData> retrieveSavingsProductCharges(Long savingsProductId);
|
||||
}
|
||||
|
||||
+42
@@ -141,6 +141,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
public String loanProductChargeSchema() {
|
||||
return chargeSchema() + " join m_product_loan_charge plc on plc.charge_id = c.id";
|
||||
}
|
||||
|
||||
public String savingsProductChargeSchema() {
|
||||
return chargeSchema() + " join m_savings_product_charge spc on spc.charge_id = c.id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
@@ -179,4 +183,42 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ChargeData> retrieveSavingsAccountApplicableCharges(boolean feeChargesOnly) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
|
||||
String sql = "select " + rm.chargeSchema()
|
||||
+ " where c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=? order by c.name ";
|
||||
if (feeChargesOnly) {
|
||||
sql = "select " + rm.chargeSchema()
|
||||
+ " where c.is_deleted=0 and c.is_active=1 and c.is_penalty=0 and c.charge_applies_to_enum=? order by c.name ";
|
||||
}
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { ChargeAppliesTo.SAVINGS.getValue() });
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ChargeData> retrieveSavingsAccountApplicablePenalties() {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
|
||||
String sql = "select " + rm.chargeSchema()
|
||||
+ " where c.is_deleted=0 and c.is_active=1 and c.is_penalty=1 and c.charge_applies_to_enum=? order by c.name ";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { ChargeAppliesTo.SAVINGS.getValue() });
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ChargeData> retrieveSavingsProductCharges(Long savingsProductId) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
|
||||
final String sql = "select " + rm.savingsProductChargeSchema() + " where c.is_deleted=0 and c.is_active=1 and spc.savings_product_id=?";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { savingsProductId });
|
||||
}
|
||||
|
||||
}
|
||||
+28
-12
@@ -19,6 +19,8 @@ public enum SavingsAccountTransactionType {
|
||||
INTEREST_POSTING(3, "savingsAccountTransactionType.interestPosting"), //
|
||||
WITHDRAWAL_FEE(4, "savingsAccountTransactionType.withdrawalFee"), //
|
||||
ANNUAL_FEE(5, "savingsAccountTransactionType.annualFee"), //
|
||||
WAIVE_CHARGES(6, "savingsAccountTransactionType.waiveCharges"),
|
||||
APPLY_CHARGES(7, "savingsAccountTransactionType.applyCharges"),
|
||||
INITIATE_TRANSFER(12, "savingsAccountTransactionType.initiateTransfer"), //
|
||||
APPROVE_TRANSFER(13, "savingsAccountTransactionType.approveTransfer"), //
|
||||
WITHDRAW_TRANSFER(14, "savingsAccountTransactionType.withdrawTransfer"), //
|
||||
@@ -44,37 +46,43 @@ public enum SavingsAccountTransactionType {
|
||||
|
||||
if (transactionType == null) { return SavingsAccountTransactionType.INVALID; }
|
||||
|
||||
SavingsAccountTransactionType loanTransactionType = SavingsAccountTransactionType.INVALID;
|
||||
SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.INVALID;
|
||||
switch (transactionType) {
|
||||
case 1:
|
||||
loanTransactionType = SavingsAccountTransactionType.DEPOSIT;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.DEPOSIT;
|
||||
break;
|
||||
case 2:
|
||||
loanTransactionType = SavingsAccountTransactionType.WITHDRAWAL;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.WITHDRAWAL;
|
||||
break;
|
||||
case 3:
|
||||
loanTransactionType = SavingsAccountTransactionType.INTEREST_POSTING;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.INTEREST_POSTING;
|
||||
break;
|
||||
case 4:
|
||||
loanTransactionType = SavingsAccountTransactionType.WITHDRAWAL_FEE;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.WITHDRAWAL_FEE;
|
||||
break;
|
||||
case 5:
|
||||
loanTransactionType = SavingsAccountTransactionType.ANNUAL_FEE;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.ANNUAL_FEE;
|
||||
break;
|
||||
case 6:
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.WAIVE_CHARGES;
|
||||
break;
|
||||
case 7:
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.APPLY_CHARGES;
|
||||
break;
|
||||
case 12:
|
||||
loanTransactionType = SavingsAccountTransactionType.INITIATE_TRANSFER;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.INITIATE_TRANSFER;
|
||||
break;
|
||||
case 13:
|
||||
loanTransactionType = SavingsAccountTransactionType.APPROVE_TRANSFER;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.APPROVE_TRANSFER;
|
||||
break;
|
||||
case 14:
|
||||
loanTransactionType = SavingsAccountTransactionType.WITHDRAW_TRANSFER;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.WITHDRAW_TRANSFER;
|
||||
break;
|
||||
case 15:
|
||||
loanTransactionType = SavingsAccountTransactionType.REJECT_TRANSFER;
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.REJECT_TRANSFER;
|
||||
break;
|
||||
}
|
||||
return loanTransactionType;
|
||||
return savingsAccountTransactionType;
|
||||
}
|
||||
|
||||
public boolean isDeposit() {
|
||||
@@ -96,9 +104,17 @@ public enum SavingsAccountTransactionType {
|
||||
public boolean isAnnualFee() {
|
||||
return this.value.equals(SavingsAccountTransactionType.ANNUAL_FEE.getValue());
|
||||
}
|
||||
|
||||
public boolean isCharge() {
|
||||
return this.value.equals(SavingsAccountTransactionType.APPLY_CHARGES.getValue());
|
||||
}
|
||||
|
||||
public boolean isWaiveCharge() {
|
||||
return this.value.equals(SavingsAccountTransactionType.WAIVE_CHARGES.getValue());
|
||||
}
|
||||
|
||||
public boolean isDebit() {
|
||||
return isWithdrawal() || isWithdrawalFee() || isAnnualFee();
|
||||
return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isCharge();
|
||||
}
|
||||
|
||||
public boolean isCredit() {
|
||||
|
||||
+37
-2
@@ -18,6 +18,8 @@ public class SavingsApiConstants {
|
||||
public static final String SAVINGS_PRODUCT_RESOURCE_NAME = "savingsproduct";
|
||||
public static final String SAVINGS_ACCOUNT_RESOURCE_NAME = "savingsaccount";
|
||||
public static final String SAVINGS_ACCOUNT_TRANSACTION_RESOURCE_NAME = "savingsaccount.transaction";
|
||||
public static final String SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME = "savingsaccountcharge";
|
||||
|
||||
|
||||
// actions
|
||||
public static String summitalAction = ".summital";
|
||||
@@ -32,10 +34,13 @@ public class SavingsApiConstants {
|
||||
public static String applyAnnualFeeTransactionAction = ".applyannualfee";
|
||||
public static String adjustTransactionAction = ".adjusttransaction";
|
||||
public static String closeAction = ".close";
|
||||
public static String payChargeTransactionAction = ".paycharge";
|
||||
|
||||
// command
|
||||
public static String COMMAND_UNDO_TRANSACTION = "undo";
|
||||
public static String COMMAND_ADJUST_TRANSACTION = "modify";
|
||||
public static String COMMAND_WAIVE_CHARGE= "waive";
|
||||
public static String COMMAND_PAY_CHARGE= "paycharge";
|
||||
|
||||
// general
|
||||
public static final String localeParamName = "locale";
|
||||
@@ -93,6 +98,26 @@ public class SavingsApiConstants {
|
||||
public static final String runningBalanceParamName = "runningBalance";
|
||||
public static final String reversedParamName = "reversed";
|
||||
public static final String dateParamName = "date";
|
||||
|
||||
//charges parameters
|
||||
public static final String chargeIdParamName = "chargeId";
|
||||
public static final String chargesParamName = "charges";
|
||||
public static final String savingsAccountChargeIdParamName = "savingsAccountChargeId";
|
||||
public static final String chargeNameParamName = "name";
|
||||
public static final String penaltyParamName = "penalty";
|
||||
public static final String chargeTimeTypeParamName = "chargeTimeType";
|
||||
public static final String dueAsOfDateParamName = "dueDate";
|
||||
public static final String chargeCalculationTypeParamName = "chargeCalculationType";
|
||||
public static final String percentageParamName = "percentage";
|
||||
public static final String amountPercentageAppliedToParamName = "amountPercentageAppliedTo";
|
||||
public static final String currencyParamName = "currency";
|
||||
public static final String amountWaivedParamName = "amountWaived";
|
||||
public static final String amountWrittenOffParamName = "amountWrittenOff";
|
||||
public static final String amountOutstandingParamName = "amountOutstanding";
|
||||
public static final String amountOrPercentageParamName = "amountOrPercentage";
|
||||
public static final String amountParamName = "amount";
|
||||
public static final String amountPaidParamName = "amountPaid";
|
||||
public static final String chargeOptionsParamName = "chargeOptions";
|
||||
|
||||
public static final String noteParamName = "note";
|
||||
|
||||
@@ -102,7 +127,8 @@ public class SavingsApiConstants {
|
||||
interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
|
||||
minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
|
||||
withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, annualFeeAmountParamName,
|
||||
annualFeeOnMonthDayParamName, accountingRuleParamName, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
|
||||
annualFeeOnMonthDayParamName, accountingRuleParamName, chargesParamName,
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
|
||||
@@ -128,7 +154,7 @@ public class SavingsApiConstants {
|
||||
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
|
||||
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
|
||||
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName,
|
||||
withdrawalFeeForTransfersParamName, annualFeeAmountParamName, annualFeeOnMonthDayParamName));
|
||||
withdrawalFeeForTransfersParamName, annualFeeAmountParamName, annualFeeOnMonthDayParamName, chargesParamName));
|
||||
|
||||
/**
|
||||
* These parameters will match the class level parameters of
|
||||
@@ -158,4 +184,13 @@ public class SavingsApiConstants {
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, closedOnDateParamName, noteParamName));
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(chargeIdParamName,
|
||||
savingsAccountChargeIdParamName, chargeNameParamName, penaltyParamName, chargeTimeTypeParamName, dueAsOfDateParamName,
|
||||
chargeCalculationTypeParamName, percentageParamName, amountPercentageAppliedToParamName, currencyParamName,
|
||||
amountWaivedParamName, amountWrittenOffParamName, amountOutstandingParamName, amountOrPercentageParamName, amountParamName, amountPaidParamName,
|
||||
chargeOptionsParamName));
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
|
||||
chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName));
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.api;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.COMMAND_WAIVE_CHARGE;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.COMMAND_PAY_CHARGE;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
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.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.commands.domain.CommandWrapper;
|
||||
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
||||
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/savingsaccounts/{savingsAccountId}/charges")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class SavingsAccountChargesApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
|
||||
private final DefaultToApiJsonSerializer<SavingsAccountChargeData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountChargesApiResource(final PlatformSecurityContext context,
|
||||
final ChargeReadPlatformService chargeReadPlatformService,
|
||||
final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService,
|
||||
final DefaultToApiJsonSerializer<SavingsAccountChargeData> toApiJsonSerializer,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
|
||||
this.context = context;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveAllSavingsAccountCharges(@PathParam("savingsAccountId") final Long savingsAccountId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
|
||||
|
||||
final Collection<SavingsAccountChargeData> savingsAccountCharges = this.savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountCharges(savingsAccountId);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, savingsAccountCharges,
|
||||
SavingsApiConstants.SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveTemplate(@Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
|
||||
|
||||
final boolean feeChargesOnly = false;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicableCharges(feeChargesOnly);
|
||||
final SavingsAccountChargeData savingsAccountChargeTemplate = SavingsAccountChargeData.template(chargeOptions);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, savingsAccountChargeTemplate, SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{savingsAccountChargeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveSavingsAccountCharge(@PathParam("savingsAccountId") final Long savingsAccountId,
|
||||
@PathParam("savingsAccountChargeId") final Long savingsAccountChargeId, @Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
|
||||
|
||||
final SavingsAccountChargeData savingsAccountCharge = this.savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountChargeDetails(savingsAccountChargeId, savingsAccountId);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, savingsAccountCharge, SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String addSavingsAccountCharge(@PathParam("savingsAccountId") final Long savingsAccountId, final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createSavingsAccountCharge(savingsAccountId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{savingsAccountChargeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String updateSavingsAccountCharge(@PathParam("savingsAccountId") final Long savingsAccountId,
|
||||
@PathParam("savingsAccountChargeId") final Long savingsAccountChargeId, final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.updateSavingsAccountCharge(savingsAccountId, savingsAccountChargeId).withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{savingsAccountChargeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String waiveSavingsAccountCharge(@PathParam("savingsAccountId") final Long savingsAccountId,
|
||||
@PathParam("savingsAccountChargeId") final Long savingsAccountChargeId, @QueryParam("command") final String commandParam,
|
||||
final String apiRequestBodyAsJson) {
|
||||
|
||||
String json = "";
|
||||
if (is(commandParam, COMMAND_WAIVE_CHARGE)) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.waiveSavingsAccountCharge(savingsAccountId, savingsAccountChargeId).withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
json = this.toApiJsonSerializer.serialize(result);
|
||||
} else if (is(commandParam, COMMAND_PAY_CHARGE)) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.paySavingsAccountCharge(savingsAccountId, savingsAccountChargeId).withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
json = this.toApiJsonSerializer.serialize(result);
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam, COMMAND_PAY_CHARGE, COMMAND_WAIVE_CHARGE);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{savingsAccountChargeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String deleteSavingsAccountCharge(@PathParam("savingsAccountId") final Long savingsAccountId,
|
||||
@PathParam("savingsAccountChargeId") final Long savingsAccountChargeId) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteSavingsAccountCharge(savingsAccountId,
|
||||
savingsAccountChargeId).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
}
|
||||
+17
-3
@@ -38,8 +38,10 @@ import org.mifosplatform.infrastructure.core.service.Page;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.group.service.SearchParameters;
|
||||
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@@ -56,17 +58,19 @@ public class SavingsAccountsApiResource {
|
||||
private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountsApiResource(final SavingsAccountReadPlatformService savingsAccountReadPlatformService,
|
||||
final PlatformSecurityContext context, final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper) {
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper, final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService) {
|
||||
this.savingsAccountReadPlatformService = savingsAccountReadPlatformService;
|
||||
this.context = context;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -143,12 +147,13 @@ public class SavingsAccountsApiResource {
|
||||
final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo, final Set<String> mandatoryResponseParameters) {
|
||||
|
||||
Collection<SavingsAccountTransactionData> transactions = null;
|
||||
Collection<SavingsAccountChargeData> charges = null;
|
||||
|
||||
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (!associationParameters.isEmpty()) {
|
||||
|
||||
if (associationParameters.contains("all")) {
|
||||
associationParameters.addAll(Arrays.asList("transactions"));
|
||||
associationParameters.addAll(Arrays.asList("transactions", "charges"));
|
||||
}
|
||||
|
||||
if (associationParameters.contains("transactions")) {
|
||||
@@ -159,6 +164,15 @@ public class SavingsAccountsApiResource {
|
||||
transactions = currentTransactions;
|
||||
}
|
||||
}
|
||||
|
||||
if (associationParameters.contains("charges")) {
|
||||
mandatoryResponseParameters.add("charges");
|
||||
final Collection<SavingsAccountChargeData> currentCharges = this.savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountCharges(accountId);
|
||||
if (!CollectionUtils.isEmpty(currentCharges)) {
|
||||
charges = currentCharges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SavingsAccountData templateData = null;
|
||||
@@ -168,7 +182,7 @@ public class SavingsAccountsApiResource {
|
||||
savingsAccount.productId(), staffInSelectedOfficeOnly);
|
||||
}
|
||||
|
||||
return SavingsAccountData.withTemplateOptions(savingsAccount, templateData, transactions);
|
||||
return SavingsAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges);
|
||||
}
|
||||
|
||||
@PUT
|
||||
|
||||
+22
-4
@@ -41,6 +41,8 @@ import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSeria
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
|
||||
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
|
||||
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
|
||||
@@ -54,6 +56,7 @@ import org.mifosplatform.portfolio.savings.service.SavingsProductReadPlatformSer
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Path("/savingsproducts")
|
||||
@Component
|
||||
@@ -70,6 +73,7 @@ public class SavingsProductsApiResource {
|
||||
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsProductsApiResource(final SavingsProductReadPlatformService savingProductReadPlatformService,
|
||||
@@ -79,7 +83,8 @@ public class SavingsProductsApiResource {
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper, final CodeValueReadPlatformService codeValueReadPlatformService,
|
||||
final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService,
|
||||
final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService) {
|
||||
final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService) {
|
||||
this.savingProductReadPlatformService = savingProductReadPlatformService;
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.currencyReadPlatformService = currencyReadPlatformService;
|
||||
@@ -90,6 +95,7 @@ public class SavingsProductsApiResource {
|
||||
this.codeValueReadPlatformService = codeValueReadPlatformService;
|
||||
this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService;
|
||||
this.accountMappingReadPlatformService = accountMappingReadPlatformService;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
}
|
||||
|
||||
@POST
|
||||
@@ -141,6 +147,10 @@ public class SavingsProductsApiResource {
|
||||
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
SavingsProductData savingProductData = this.savingProductReadPlatformService.retrieveOne(productId);
|
||||
|
||||
final Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId);
|
||||
|
||||
savingProductData = SavingsProductData.withCharges(savingProductData, charges);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
@@ -220,19 +230,27 @@ public class SavingsProductsApiResource {
|
||||
.retrieveAccountingRuleTypeOptions();
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions = accountingDropdownReadPlatformService
|
||||
.retrieveAccountMappingOptionsForSavingsProducts();
|
||||
|
||||
|
||||
//charges
|
||||
final boolean feeChargesOnly = true;
|
||||
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicableCharges(feeChargesOnly);
|
||||
chargeOptions = CollectionUtils.isEmpty(chargeOptions) ? null : chargeOptions;
|
||||
|
||||
Collection<ChargeData> penaltyOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicablePenalties();
|
||||
penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions;
|
||||
|
||||
SavingsProductData savingsProductToReturn = null;
|
||||
if (savingsProduct != null) {
|
||||
savingsProductToReturn = SavingsProductData.withTemplate(savingsProduct, currencyOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions);
|
||||
accountingMappingOptions, chargeOptions, penaltyOptions);
|
||||
} else {
|
||||
savingsProductToReturn = SavingsProductData.template(currency, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions);
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions);
|
||||
}
|
||||
|
||||
return savingsProductToReturn;
|
||||
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
|
||||
/**
|
||||
* Immutable data object for Savings Account charge data.
|
||||
*/
|
||||
public class SavingsAccountChargeData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long chargeId;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData chargeTimeType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final LocalDate dueDate;
|
||||
|
||||
private final EnumOptionData chargeCalculationType;
|
||||
|
||||
private final BigDecimal percentage;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountPercentageAppliedTo;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final CurrencyData currency;
|
||||
|
||||
private final BigDecimal amount;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountPaid;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountWaived;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountWrittenOff;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountOutstanding;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountOrPercentage;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<ChargeData> chargeOptions;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean penalty;
|
||||
|
||||
public static SavingsAccountChargeData template(final Collection<ChargeData> chargeOptions) {
|
||||
final Long id = null;
|
||||
final Long chargeId = null;
|
||||
final String name = null;
|
||||
final CurrencyData currency = null;
|
||||
final BigDecimal amount = BigDecimal.ZERO;
|
||||
final BigDecimal amountPaid = BigDecimal.ZERO;
|
||||
final BigDecimal amountWaived = BigDecimal.ZERO;
|
||||
final BigDecimal amountWrittenOff = BigDecimal.ZERO;
|
||||
final BigDecimal amountOutstanding = BigDecimal.ZERO;
|
||||
final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
|
||||
final EnumOptionData chargeTimeType = null;
|
||||
final EnumOptionData chargeCalculationType = null;
|
||||
final BigDecimal percentage = BigDecimal.ZERO;
|
||||
final boolean penalty = false;
|
||||
final LocalDate dueAsOfDate = null;
|
||||
|
||||
return new SavingsAccountChargeData(id, chargeId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions, penalty);
|
||||
}
|
||||
|
||||
public static SavingsAccountChargeData instance(final Long id, final Long chargeId, final String name, final CurrencyData currency,
|
||||
final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff,
|
||||
final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate,
|
||||
final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo,
|
||||
final Collection<ChargeData> chargeOptions, final boolean penalty) {
|
||||
|
||||
return new SavingsAccountChargeData(id, chargeId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage,
|
||||
amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff,
|
||||
amountOutstanding, chargeOptions, penalty);
|
||||
}
|
||||
|
||||
private SavingsAccountChargeData(Long id, Long chargeId, String name, EnumOptionData chargeTimeType, LocalDate dueAsOfDate,
|
||||
EnumOptionData chargeCalculationType, BigDecimal percentage, BigDecimal amountPercentageAppliedTo, CurrencyData currency,
|
||||
BigDecimal amount, BigDecimal amountPaid, BigDecimal amountWaived, BigDecimal amountWrittenOff, BigDecimal amountOutstanding,
|
||||
Collection<ChargeData> chargeOptions, boolean penalty) {
|
||||
this.id = id;
|
||||
this.chargeId = chargeId;
|
||||
this.name = name;
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.dueDate = dueAsOfDate;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.percentage = percentage;
|
||||
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
|
||||
this.currency = currency;
|
||||
this.amount = amount;
|
||||
this.amountPaid = amountPaid;
|
||||
this.amountWaived = amountWaived;
|
||||
this.amountWrittenOff = amountWrittenOff;
|
||||
this.amountOutstanding = amountOutstanding;
|
||||
this.amountOrPercentage = this.getAmountOrPercentage();
|
||||
this.chargeOptions = chargeOptions;
|
||||
this.penalty = penalty;
|
||||
}
|
||||
|
||||
private BigDecimal getAmountOrPercentage() {
|
||||
return (this.chargeCalculationType != null) && (chargeCalculationType.getId().intValue() > 1) ? this.percentage : this.amount;
|
||||
}
|
||||
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.data;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.serialization.FromJsonHelper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@Component
|
||||
public class SavingsAccountChargeDataValidator {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountChargeDataValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
public void validateAdd(final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
|
||||
|
||||
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||
final Long chargeId = fromApiJsonHelper.extractLongNamed(chargeIdParamName, element);
|
||||
baseDataValidator.reset().parameter(chargeIdParamName).value(chargeId).notNull().integerGreaterThanZero();
|
||||
|
||||
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(amountParamName, element);
|
||||
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
|
||||
|
||||
if (fromApiJsonHelper.parameterExists(dueAsOfDateParamName, element)) {
|
||||
fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateUpdate(final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
|
||||
|
||||
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||
|
||||
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(amountParamName, element);
|
||||
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
|
||||
|
||||
if (fromApiJsonHelper.parameterExists(dueAsOfDateParamName, element)) {
|
||||
fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
+23
-9
@@ -15,6 +15,7 @@ import org.joda.time.MonthDay;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.staff.data.StaffData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
|
||||
/**
|
||||
* Immutable data object representing a savings account.
|
||||
@@ -54,6 +55,7 @@ public class SavingsAccountData {
|
||||
private final SavingsAccountSummaryData summary;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<SavingsAccountTransactionData> transactions;
|
||||
private final Collection<SavingsAccountChargeData> charges;
|
||||
|
||||
// template
|
||||
private final Collection<SavingsProductData> productOptions;
|
||||
@@ -64,6 +66,7 @@ public class SavingsAccountData {
|
||||
private final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions;
|
||||
private final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions;
|
||||
private final Collection<EnumOptionData> withdrawalFeeTypeOptions;
|
||||
private final Collection<ChargeData> chargeOptions;
|
||||
|
||||
public static SavingsAccountData instance(final Long id, final String accountNo, final String externalId, final Long groupId,
|
||||
final String groupName, final Long clientId, final String clientName, final Long productId, final String productName,
|
||||
@@ -86,6 +89,8 @@ public class SavingsAccountData {
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
|
||||
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
|
||||
return new SavingsAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||
fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType,
|
||||
@@ -93,7 +98,7 @@ public class SavingsAccountData {
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers,
|
||||
annualFeeAmount, annualFeeOnMonthDay, annualFeeNextDueDate, summary, transactions, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions);
|
||||
}
|
||||
|
||||
public static SavingsAccountData lookup(final Long accountId, final String accountNo) {
|
||||
@@ -108,16 +113,18 @@ public class SavingsAccountData {
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
|
||||
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
|
||||
return new SavingsAccountData(accountId, accountNo, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, false, null, null, null, null, transactions, productOptions,
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions);
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template,
|
||||
final Collection<SavingsAccountTransactionData> transactions) {
|
||||
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges) {
|
||||
|
||||
if (template == null) {
|
||||
final Collection<SavingsProductData> productOptions = null;
|
||||
@@ -128,10 +135,11 @@ public class SavingsAccountData {
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
|
||||
return withTemplateOptions(account, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions);
|
||||
}
|
||||
|
||||
return new SavingsAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
|
||||
@@ -143,7 +151,7 @@ public class SavingsAccountData {
|
||||
account.annualFeeAmount, account.annualFeeOnMonthDay, account.annualFeeNextDueDate, account.summary, transactions, template.productOptions,
|
||||
template.fieldOfficerOptions, template.interestCompoundingPeriodTypeOptions, template.interestPostingPeriodTypeOptions,
|
||||
template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions,
|
||||
template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions);
|
||||
template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
|
||||
@@ -153,7 +161,7 @@ public class SavingsAccountData {
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions,
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<SavingsAccountTransactionData> transactions) {
|
||||
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions) {
|
||||
|
||||
return new SavingsAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
|
||||
account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldOfficerId,
|
||||
@@ -164,7 +172,7 @@ public class SavingsAccountData {
|
||||
account.annualFeeAmount, account.annualFeeOnMonthDay, account.annualFeeNextDueDate, account.summary, transactions, productOptions,
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions);
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
|
||||
@@ -207,13 +215,16 @@ public class SavingsAccountData {
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
|
||||
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
|
||||
return new SavingsAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,withdrawalFeeForTransfers,
|
||||
annualFeeAmount, annualFeeOnMonthDay, annualFeeNextDueDate, summary, transactions, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions);
|
||||
}
|
||||
|
||||
private SavingsAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName,
|
||||
@@ -230,7 +241,7 @@ public class SavingsAccountData {
|
||||
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions,
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions) {
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions, final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
this.externalId = externalId;
|
||||
@@ -271,6 +282,9 @@ public class SavingsAccountData {
|
||||
this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions;
|
||||
this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions;
|
||||
this.withdrawalFeeTypeOptions = withdrawalFeeTypeOptions;
|
||||
|
||||
this.charges = charges;//charges associated with Savings account
|
||||
this.chargeOptions = chargeOptions;//charges available for adding to Savings account
|
||||
}
|
||||
|
||||
public Long id() {
|
||||
|
||||
+7
-2
@@ -10,7 +10,7 @@ import java.math.BigDecimal;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
|
||||
/**
|
||||
* Immutable data object representing loan summary information.
|
||||
* Immutable data object representing Savings Account summary information.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SavingsAccountSummaryData {
|
||||
@@ -23,10 +23,13 @@ public class SavingsAccountSummaryData {
|
||||
private final BigDecimal totalInterestEarned;
|
||||
private final BigDecimal totalInterestPosted;
|
||||
private final BigDecimal accountBalance;
|
||||
private final BigDecimal totalFeeCharge;
|
||||
private final BigDecimal totalPenaltyCharge;
|
||||
|
||||
public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals,
|
||||
final BigDecimal totalWithdrawalFees, final BigDecimal totalAnnualFees, final BigDecimal totalInterestEarned,
|
||||
final BigDecimal totalInterestPosted, final BigDecimal accountBalance) {
|
||||
final BigDecimal totalInterestPosted, final BigDecimal accountBalance, final BigDecimal totalFeeCharge,
|
||||
final BigDecimal totalPenaltyCharge) {
|
||||
this.currency = currency;
|
||||
this.totalDeposits = totalDeposits;
|
||||
this.totalWithdrawals = totalWithdrawals;
|
||||
@@ -35,5 +38,7 @@ public class SavingsAccountSummaryData {
|
||||
this.totalInterestEarned = totalInterestEarned;
|
||||
this.totalInterestPosted = totalInterestPosted;
|
||||
this.accountBalance = accountBalance;
|
||||
this.totalFeeCharge = totalFeeCharge;
|
||||
this.totalPenaltyCharge = totalPenaltyCharge;
|
||||
}
|
||||
}
|
||||
+41
-19
@@ -17,6 +17,7 @@ import org.mifosplatform.accounting.producttoaccountmapping.data.PaymentTypeToGL
|
||||
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
|
||||
/**
|
||||
* Immutable data object represent a savings product.
|
||||
@@ -46,28 +47,23 @@ public class SavingsProductData {
|
||||
private final Map<String, Object> accountingMappings;
|
||||
private final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings;
|
||||
|
||||
// charges
|
||||
private final Collection<ChargeData> charges;
|
||||
|
||||
// template
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<CurrencyData> currencyOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> interestPostingPeriodTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> interestCalculationTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> withdrawalFeeTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<CodeValueData> paymentTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> accountingRuleOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Map<String, List<GLAccountData>> accountingMappingOptions;
|
||||
|
||||
private final Collection<ChargeData> chargeOptions;
|
||||
private final Collection<ChargeData> penaltyOptions;
|
||||
|
||||
public static SavingsProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
|
||||
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
|
||||
final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule,
|
||||
@@ -77,7 +73,7 @@ public class SavingsProductData {
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<CodeValueData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions) {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions, final Collection<ChargeData> penaltyOptions) {
|
||||
|
||||
final Long id = null;
|
||||
final String name = null;
|
||||
@@ -93,6 +89,7 @@ public class SavingsProductData {
|
||||
final MonthDay annualFeeMonthDay = null;
|
||||
final Map<String, Object> accountingMappings = null;
|
||||
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
|
||||
final Collection<ChargeData> charges = null;
|
||||
|
||||
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
@@ -100,9 +97,22 @@ public class SavingsProductData {
|
||||
accountingRule, annualFeeAmount, annualFeeMonthDay, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions);
|
||||
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData withCharges(final SavingsProductData product, final Collection<ChargeData> charges) {
|
||||
return new SavingsProductData(product.id, product.name, product.description, product.currency, product.nominalAnnualInterestRate,
|
||||
product.interestCompoundingPeriodType, product.interestPostingPeriodType, product.interestCalculationType,
|
||||
product.interestCalculationDaysInYearType, product.minRequiredOpeningBalance, product.lockinPeriodFrequency,
|
||||
product.lockinPeriodFrequencyType, product.withdrawalFeeAmount, product.withdrawalFeeType,
|
||||
product.withdrawalFeeForTransfers, product.accountingRule, product.annualFeeAmount, product.annualFeeOnMonthDay,
|
||||
product.accountingMappings, product.paymentChannelToFundSourceMappings, product.currencyOptions,
|
||||
product.interestCompoundingPeriodTypeOptions, product.interestPostingPeriodTypeOptions,
|
||||
product.interestCalculationTypeOptions, product.interestCalculationDaysInYearTypeOptions,
|
||||
product.lockinPeriodFrequencyTypeOptions, product.withdrawalFeeTypeOptions, product.paymentTypeOptions,
|
||||
product.accountingRuleOptions, product.accountingMappingOptions, charges, product.chargeOptions, product.penaltyOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link SavingsProductData} that contains and exist
|
||||
* {@link SavingsProductData} data with further template data for dropdowns.
|
||||
@@ -114,7 +124,7 @@ public class SavingsProductData {
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<CodeValueData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions) {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions, final Collection<ChargeData> penaltyOptions) {
|
||||
|
||||
return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.description, existingProduct.currency,
|
||||
existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
|
||||
@@ -126,7 +136,7 @@ public class SavingsProductData {
|
||||
existingProduct.paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions);
|
||||
accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct,
|
||||
@@ -142,6 +152,8 @@ public class SavingsProductData {
|
||||
final Collection<CodeValueData> paymentTypeOptions = null;
|
||||
final Collection<EnumOptionData> accountingRuleOptions = null;
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final Collection<ChargeData> penaltyOptions = null;
|
||||
|
||||
return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.description, existingProduct.currency,
|
||||
existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
|
||||
@@ -152,7 +164,7 @@ public class SavingsProductData {
|
||||
existingProduct.annualFeeOnMonthDay, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions);
|
||||
accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData instance(final Long id, final String name, final String description, final CurrencyData currency,
|
||||
@@ -176,6 +188,9 @@ public class SavingsProductData {
|
||||
final Collection<CodeValueData> paymentTypeOptions = null;
|
||||
final Collection<EnumOptionData> accountingRuleOptions = null;
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final Collection<ChargeData> penaltyOptions = null;
|
||||
final Collection<ChargeData> charges = null;
|
||||
|
||||
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
@@ -183,7 +198,7 @@ public class SavingsProductData {
|
||||
annualFeeAmount, annualFeeOnMonthDay, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions);
|
||||
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData lookup(final Long id, final String name) {
|
||||
@@ -217,6 +232,9 @@ public class SavingsProductData {
|
||||
final Collection<CodeValueData> paymentTypeOptions = null;
|
||||
final Collection<EnumOptionData> accountingRuleOptions = null;
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
|
||||
final Collection<ChargeData> charges = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final Collection<ChargeData> penaltyOptions = null;
|
||||
|
||||
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
@@ -224,7 +242,7 @@ public class SavingsProductData {
|
||||
annualFeeAmount, annualFeeMonthDay, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions);
|
||||
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions);
|
||||
}
|
||||
|
||||
private SavingsProductData(final Long id, final String name, final String description, final CurrencyData currency,
|
||||
@@ -241,7 +259,7 @@ public class SavingsProductData {
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<CodeValueData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions) {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> charges, final Collection<ChargeData> chargeOptions, final Collection<ChargeData> penaltyOptions) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
@@ -278,6 +296,10 @@ public class SavingsProductData {
|
||||
this.accountingMappings = accountingMappings;
|
||||
}
|
||||
this.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings;
|
||||
|
||||
this.charges = charges;//charges associated with Savings product
|
||||
this.chargeOptions = chargeOptions;//charges available for adding to Savings product
|
||||
this.penaltyOptions = penaltyOptions;//penalties available for adding to Savings product
|
||||
}
|
||||
|
||||
public boolean hasAccountingEnabled() {
|
||||
|
||||
+229
-3
@@ -22,10 +22,12 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
@@ -62,6 +64,8 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.organisation.office.domain.Office;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
@@ -78,6 +82,9 @@ import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceE
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_savings_account", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "sa_account_no_UNIQUE"),
|
||||
@@ -242,6 +249,10 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true)
|
||||
private final List<SavingsAccountTransaction> transactions = new ArrayList<SavingsAccountTransaction>();
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true)
|
||||
private Set<SavingsAccountCharge> charges = new HashSet<SavingsAccountCharge>();
|
||||
|
||||
@Transient
|
||||
private boolean accountNumberRequiresAutoGeneration = false;
|
||||
@@ -262,13 +273,15 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay) {
|
||||
final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
|
||||
final Set<SavingsAccountCharge> savingsAccountCharges) {
|
||||
|
||||
final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
|
||||
return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate,
|
||||
interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
|
||||
withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeApplicableForTransfer, annualFeeAmount, annualFeeOnMonthDay);
|
||||
withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeApplicableForTransfer, annualFeeAmount, annualFeeOnMonthDay,
|
||||
savingsAccountCharges);
|
||||
}
|
||||
|
||||
private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer,
|
||||
@@ -279,7 +292,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay) {
|
||||
final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
|
||||
final Set<SavingsAccountCharge> savingsAccountCharges) {
|
||||
this.client = client;
|
||||
this.group = group;
|
||||
this.product = product;
|
||||
@@ -317,6 +331,12 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth();
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(savingsAccountCharges)) {
|
||||
this.charges = associateChargesWithThisSavingsAccount(savingsAccountCharges);
|
||||
//TODO:Add charges details to savings summary
|
||||
//this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
this.summary = new SavingsAccountSummary();
|
||||
}
|
||||
|
||||
@@ -1074,12 +1094,23 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
this.annualFeeOnMonth = monthOfYear;
|
||||
}
|
||||
}
|
||||
|
||||
//charges
|
||||
final String chargesParamName = "charges";
|
||||
if (command.hasParameter(chargesParamName)) {
|
||||
JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName);
|
||||
if (jsonArray != null) {
|
||||
actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName));
|
||||
}
|
||||
}
|
||||
|
||||
// set period type to null if frequency is null
|
||||
if (this.annualFeeAmount == null) {
|
||||
this.annualFeeOnDay = null;
|
||||
this.annualFeeOnMonth = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
validateLockinDetails();
|
||||
validateWithdrawalFeeDetails();
|
||||
@@ -1457,6 +1488,13 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
.withMonthOfYear(this.annualFeeOnMonth).withDayOfMonth(this.annualFeeOnDay).plusYears(1);
|
||||
this.annualFeeNextDueDate = newAnnualFeeNextDueDate.toDate();
|
||||
}
|
||||
} else if (transactionToUndo.isCharge()) {
|
||||
//undo charge
|
||||
final Set<SavingsAccountChargePaidBy> chargesPaidBy = transactionToUndo.getSavingsAccountChargesPaid();
|
||||
for (SavingsAccountChargePaidBy savingsAccountChargePaidBy : chargesPaidBy) {
|
||||
final SavingsAccountCharge chargeToUndo = savingsAccountChargePaidBy.getSavingsAccountCharge();
|
||||
chargeToUndo.resetPaidAmount(currency);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1809,5 +1847,193 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
public void setStatus(final Integer status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
private Set<SavingsAccountCharge> associateChargesWithThisSavingsAccount(final Set<SavingsAccountCharge> savingsAccountCharges) {
|
||||
for (final SavingsAccountCharge savingsAccountCharge : savingsAccountCharges) {
|
||||
savingsAccountCharge.update(this);
|
||||
}
|
||||
return savingsAccountCharges;
|
||||
}
|
||||
|
||||
public boolean update(final Set<SavingsAccountCharge> newSavingsAccountCharges) {
|
||||
if (newSavingsAccountCharges == null) return false;
|
||||
|
||||
if(this.charges == null) this.charges = new HashSet<SavingsAccountCharge>();
|
||||
this.charges.clear();
|
||||
this.charges.addAll(associateChargesWithThisSavingsAccount(newSavingsAccountCharges));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasCurrencyCodeOf(final String matchingCurrencyCode) {
|
||||
//TODO AA: revisit
|
||||
if(this.currency == null) return false;
|
||||
return this.currency.getCode().equalsIgnoreCase(matchingCurrencyCode);
|
||||
}
|
||||
|
||||
public void removeCharge(final SavingsAccountCharge charge){
|
||||
this.charges.remove(charge);
|
||||
}
|
||||
|
||||
public void waiveCharge(final Long savingsAccountChargeId, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + "dueDate");
|
||||
|
||||
if (isClosed()) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.closed");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
if (isNotActive()) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.not.active");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = this.getCharge(savingsAccountChargeId);
|
||||
|
||||
// validate charge is not already paid or waived
|
||||
if(savingsAccountCharge.isWaived()){
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.already.waived");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}else if(savingsAccountCharge.isPaid()){
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.paid");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
//waive charge
|
||||
final Money amountWaived = savingsAccountCharge.waive(this.getCurrency());
|
||||
//TODO : AA revisit the code
|
||||
/*Money feeChargesWaived = amountWaived;
|
||||
Money penaltyChargesWaived = Money.zero(this.getCurrency());
|
||||
if (savingsAccountCharge.isPenaltyCharge()) {
|
||||
penaltyChargesWaived = amountWaived;
|
||||
feeChargesWaived = Money.zero(this.getCurrency());
|
||||
}*/
|
||||
|
||||
//persist an entry in transaction details
|
||||
SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, this.office(), savingsAccountCharge.getDueLocalDate(), amountWaived);
|
||||
|
||||
this.transactions.add(chargeTransaction);
|
||||
|
||||
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||
final MathContext mc = MathContext.DECIMAL64;
|
||||
calculateInterestUsing(mc, savingsAccountCharge.getDueLocalDate());
|
||||
}
|
||||
|
||||
public void addCharge(final DateTimeFormatter formatter, final SavingsAccountCharge savingsAccountCharge, final Charge chargeDefinition){
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + "dueDate");
|
||||
|
||||
if (isClosed()) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.closed");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
if (isNotActive()) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.not.active");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
if (!this.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.currency.and.charge.currency.not.same");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
final LocalDate chargeDueDate = savingsAccountCharge.getDueLocalDate();
|
||||
|
||||
if (chargeDueDate != null && chargeDueDate.isBefore(getActivationLocalDate())) {
|
||||
baseDataValidator.reset().parameter("dueDate").value(getActivationLocalDate().toString(formatter))
|
||||
.failWithCodeNoParameterAddedToErrorCode("before.activationDate");
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
|
||||
//add new charge to savings account
|
||||
getCharges().add(savingsAccountCharge);
|
||||
|
||||
}
|
||||
|
||||
public void payCharge(final Long savingsAccountChargeId, final BigDecimal amountPaid, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + "dueDate");
|
||||
|
||||
if (isClosed()) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.closed");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
if (isNotActive()) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.not.active");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = this.getCharge(savingsAccountChargeId);
|
||||
|
||||
// validate charge is not already paid or waived
|
||||
if(savingsAccountCharge.isWaived()){
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.already.waived");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}else if(savingsAccountCharge.isPaid()){
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.paid");
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
final Money chargePaid = savingsAccountCharge.pay(this.getCurrency(), amountPaid);
|
||||
|
||||
final LocalDate transactionDate = (savingsAccountCharge.getDueLocalDate() == null) ? DateUtils.getLocalDateOfTenant()
|
||||
: savingsAccountCharge.getDueLocalDate();//If null reset to current date
|
||||
//persist an entry in transaction details
|
||||
SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, this.office(), transactionDate, chargePaid);
|
||||
|
||||
final SavingsAccountChargePaidBy chargePaidBy = SavingsAccountChargePaidBy.instance(chargeTransaction, savingsAccountCharge, amountPaid);
|
||||
chargeTransaction.getSavingsAccountChargesPaid().add(chargePaidBy);
|
||||
this.transactions.add(chargeTransaction);
|
||||
|
||||
final MathContext mc = MathContext.DECIMAL64;
|
||||
if (isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
|
||||
final LocalDate today = DateUtils.getLocalDateOfTenant();
|
||||
postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds);
|
||||
} else {
|
||||
final LocalDate today = DateUtils.getLocalDateOfTenant();
|
||||
calculateInterestUsing(mc, today);
|
||||
}
|
||||
|
||||
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||
|
||||
validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.payChargeTransactionAction);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private SavingsAccountCharge getCharge(Long savingsAccountChargeId) {
|
||||
SavingsAccountCharge charge = null;
|
||||
for (SavingsAccountCharge existingCharge : this.charges) {
|
||||
if(existingCharge.getId().equals(savingsAccountChargeId)) {
|
||||
charge = existingCharge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(charge == null){
|
||||
throw new SavingsAccountChargeNotFoundException(savingsAccountChargeId, getId());
|
||||
}
|
||||
|
||||
return charge;
|
||||
}
|
||||
|
||||
private Set<SavingsAccountCharge> getCharges(){
|
||||
return (this.charges == null) ? new HashSet<SavingsAccountCharge>() : this.charges;
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -27,6 +27,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawal
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.MonthDay;
|
||||
@@ -66,19 +67,22 @@ public class SavingsAccountAssembler {
|
||||
private final StaffRepositoryWrapper staffRepository;
|
||||
private final SavingsProductRepository savingProductRepository;
|
||||
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
||||
private final SavingsAccountChargeAssembler savingsAccountChargeAssembler;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
|
||||
final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository,
|
||||
final StaffRepositoryWrapper staffRepository, final SavingsProductRepository savingProductRepository,
|
||||
final SavingsAccountRepositoryWrapper savingsAccountRepository, final FromJsonHelper fromApiJsonHelper) {
|
||||
final SavingsAccountRepositoryWrapper savingsAccountRepository,
|
||||
final SavingsAccountChargeAssembler savingsAccountChargeAssembler, final FromJsonHelper fromApiJsonHelper) {
|
||||
this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper;
|
||||
this.clientRepository = clientRepository;
|
||||
this.groupRepository = groupRepository;
|
||||
this.staffRepository = staffRepository;
|
||||
this.savingProductRepository = savingProductRepository;
|
||||
this.savingsAccountRepository = savingsAccountRepository;
|
||||
this.savingsAccountChargeAssembler = savingsAccountChargeAssembler;
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
@@ -231,12 +235,14 @@ public class SavingsAccountAssembler {
|
||||
} else {
|
||||
monthDayOfAnnualFee = product.monthDayOfAnnualFee();
|
||||
}
|
||||
|
||||
final Set<SavingsAccountCharge> charges = this.savingsAccountChargeAssembler.fromParsedJson(element);
|
||||
|
||||
final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo,
|
||||
externalId, accountType, submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, iswithdrawalFeeApplicableForTransfer, annualFeeAmount,
|
||||
monthDayOfAnnualFee);
|
||||
monthDayOfAnnualFee, charges);
|
||||
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
|
||||
|
||||
account.validateNewApplicationState(DateUtils.getLocalDateOfTenant());
|
||||
|
||||
+503
@@ -0,0 +1,503 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dateFormatParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeWithoutMandatoryFieldException;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_savings_account_charge")
|
||||
public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "savings_account_id", referencedColumnName = "id", nullable = false)
|
||||
private SavingsAccount savingsAccount;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable = false)
|
||||
private Charge charge;
|
||||
|
||||
@Column(name = "charge_time_enum", nullable = false)
|
||||
private Integer chargeTime;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "due_for_collection_as_of_date")
|
||||
private Date dueDate;
|
||||
|
||||
@Column(name = "charge_calculation_enum")
|
||||
private Integer chargeCalculation;
|
||||
|
||||
@Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal percentage;
|
||||
|
||||
@Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal amountPercentageAppliedTo;
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(name = "amount_paid_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal amountPaid;
|
||||
|
||||
@Column(name = "amount_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal amountWaived;
|
||||
|
||||
@Column(name = "amount_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal amountWrittenOff;
|
||||
|
||||
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amountOutstanding;
|
||||
|
||||
@Column(name = "is_penalty", nullable = false)
|
||||
private boolean penaltyCharge = false;
|
||||
|
||||
@Column(name = "is_paid_derived", nullable = false)
|
||||
private boolean paid = false;
|
||||
|
||||
@Column(name = "waived", nullable = false)
|
||||
private boolean waived = false;
|
||||
|
||||
public static SavingsAccountCharge createNewFromJson(final SavingsAccount savingsAccount, final Charge chargeDefinition, final JsonCommand command) {
|
||||
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||
final LocalDate dueDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
|
||||
final ChargeTimeType chargeTime = null;
|
||||
final ChargeCalculationType chargeCalculation = null;
|
||||
|
||||
return new SavingsAccountCharge(savingsAccount, chargeDefinition, amount, chargeTime, chargeCalculation, dueDate);
|
||||
}
|
||||
|
||||
//TODO AA: refactor method signature
|
||||
public static SavingsAccountCharge createNewWithoutSavingsAccount(final Charge chargeDefinition, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate) {
|
||||
return new SavingsAccountCharge(null, chargeDefinition, amount, chargeTime, chargeCalculation, dueDate);
|
||||
}
|
||||
|
||||
protected SavingsAccountCharge() {
|
||||
//
|
||||
}
|
||||
|
||||
public SavingsAccountCharge(final SavingsAccount savingsAccount, final Charge chargeDefinition, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate) {
|
||||
|
||||
this.savingsAccount = savingsAccount;
|
||||
this.charge = chargeDefinition;
|
||||
this.penaltyCharge = chargeDefinition.isPenalty();
|
||||
this.chargeTime = (chargeTime == null) ? chargeDefinition.getChargeTime() : chargeTime.getValue();
|
||||
|
||||
if (isOnSpecifiedDueDate()) {
|
||||
if (dueDate == null) {
|
||||
final String defaultUserMessage = "Savings Account charge is missing due date.";
|
||||
throw new SavingsAccountChargeWithoutMandatoryFieldException("savingsaccount.charge", dueAsOfDateParamName, defaultUserMessage, chargeDefinition.getId(),
|
||||
chargeDefinition.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.dueDate = (dueDate == null) ? null : dueDate.toDate();
|
||||
|
||||
this.chargeCalculation = chargeDefinition.getChargeCalculation();
|
||||
if (chargeCalculation != null) {
|
||||
this.chargeCalculation = chargeCalculation.getValue();
|
||||
}
|
||||
|
||||
BigDecimal chargeAmount = chargeDefinition.getAmount();
|
||||
if (amount != null) {
|
||||
chargeAmount = amount;
|
||||
}
|
||||
|
||||
final BigDecimal transactionAmount = new BigDecimal(0);//percentage based fee is not yet supported for savings
|
||||
populateDerivedFields(transactionAmount, chargeAmount);
|
||||
this.paid = determineIfFullyPaid();
|
||||
}
|
||||
|
||||
private void populateDerivedFields(final BigDecimal transactionAmount, final BigDecimal chargeAmount) {
|
||||
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
case INVALID:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
case FLAT:
|
||||
this.percentage = null;
|
||||
this.amount = chargeAmount;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = chargeAmount;
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
this.percentage = chargeAmount;
|
||||
this.amountPercentageAppliedTo = transactionAmount;
|
||||
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void markAsFullyPaid() {
|
||||
this.amountPaid = this.amount;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.paid = true;
|
||||
}
|
||||
|
||||
public void resetToOriginal(final MonetaryCurrency currency) {
|
||||
this.amountPaid = BigDecimal.ZERO;
|
||||
this.amountWaived = BigDecimal.ZERO;
|
||||
this.amountWrittenOff = BigDecimal.ZERO;
|
||||
this.amountOutstanding = calculateAmountOutstanding(currency);
|
||||
this.paid = false;
|
||||
this.waived = false;
|
||||
}
|
||||
|
||||
public void resetPaidAmount(final MonetaryCurrency currency) {
|
||||
this.amountPaid = BigDecimal.ZERO;
|
||||
this.amountOutstanding = calculateAmountOutstanding(currency);
|
||||
this.paid = false;
|
||||
}
|
||||
|
||||
public Money waive(final MonetaryCurrency currency) {
|
||||
this.amountWaived = this.amountOutstanding;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.paid = false;
|
||||
this.waived = true;
|
||||
return getAmountWaived(currency);
|
||||
}
|
||||
|
||||
public Money pay(final MonetaryCurrency currency, final BigDecimal chargePaid) {
|
||||
|
||||
Money amountPaidToDate = Money.of(currency, this.amountPaid);
|
||||
Money amountPaid = Money.of(currency, chargePaid);
|
||||
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
|
||||
amountPaidToDate = amountPaidToDate.plus(amountPaid);
|
||||
amountOutstanding = amountOutstanding.minus(chargePaid);
|
||||
this.amountPaid = amountPaidToDate.getAmount();
|
||||
this.amountOutstanding = amountOutstanding.getAmount();
|
||||
this.paid = determineIfFullyPaid();
|
||||
return amountPaid;
|
||||
}
|
||||
|
||||
private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) {
|
||||
return getAmount(currency).minus(getAmountWaived(currency)).minus(getAmountPaid(currency)).getAmount();
|
||||
}
|
||||
|
||||
public void update(final SavingsAccount savingsAccount) {
|
||||
this.savingsAccount = savingsAccount;
|
||||
}
|
||||
|
||||
public void update(final BigDecimal amount, final LocalDate dueDate) {
|
||||
final BigDecimal transactionAmount = BigDecimal.ZERO;//percentage based fee calculation is not yet supported for Savings
|
||||
if (dueDate != null) {
|
||||
this.dueDate = dueDate.toDate();
|
||||
}
|
||||
|
||||
if (amount != null) {
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
case INVALID:
|
||||
break;
|
||||
case FLAT:
|
||||
this.amount = amount;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
this.percentage = amount;
|
||||
this.amountPercentageAppliedTo = transactionAmount;
|
||||
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = amount;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = amount;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
|
||||
|
||||
final String dateFormatAsInput = command.dateFormat();
|
||||
final String localeAsInput = command.locale();
|
||||
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(dueAsOfDateParamName, getDueLocalDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(dueAsOfDateParamName);
|
||||
actualChanges.put(dueAsOfDateParamName, valueAsInput);
|
||||
actualChanges.put(dateFormatParamName, dateFormatAsInput);
|
||||
actualChanges.put(localeParamName, localeAsInput);
|
||||
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
|
||||
this.dueDate = newValue.toDate();
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||
actualChanges.put(amountParamName, newValue);
|
||||
actualChanges.put(localeParamName, localeAsInput);
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
case INVALID:
|
||||
break;
|
||||
case FLAT:
|
||||
this.amount = newValue;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
this.percentage = newValue;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = newValue;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = newValue;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public boolean isSpecifiedDueDate() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE);
|
||||
}
|
||||
|
||||
private boolean isGreaterThanZero(final BigDecimal value) {
|
||||
return value.compareTo(BigDecimal.ZERO) == 1;
|
||||
}
|
||||
|
||||
public LocalDate getDueLocalDate() {
|
||||
LocalDate dueDate = null;
|
||||
if (this.dueDate != null) {
|
||||
dueDate = new LocalDate(this.dueDate);
|
||||
}
|
||||
return dueDate;
|
||||
}
|
||||
|
||||
private boolean determineIfFullyPaid() {
|
||||
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
|
||||
}
|
||||
|
||||
private BigDecimal calculateOutstanding() {
|
||||
|
||||
BigDecimal amountPaidLocal = BigDecimal.ZERO;
|
||||
if (this.amountPaid != null) {
|
||||
amountPaidLocal = this.amountPaid;
|
||||
}
|
||||
|
||||
BigDecimal amountWaivedLocal = BigDecimal.ZERO;
|
||||
if (this.amountWaived != null) {
|
||||
amountWaivedLocal = this.amountWaived;
|
||||
}
|
||||
|
||||
BigDecimal amountWrittenOffLocal = BigDecimal.ZERO;
|
||||
if (this.amountWrittenOff != null) {
|
||||
amountWrittenOffLocal = this.amountWrittenOff;
|
||||
}
|
||||
|
||||
final BigDecimal totalAccountedFor = amountPaidLocal.add(amountWaivedLocal).add(amountWrittenOffLocal);
|
||||
|
||||
return this.amount.subtract(totalAccountedFor);
|
||||
}
|
||||
|
||||
private BigDecimal percentageOf(final BigDecimal value, final BigDecimal percentage) {
|
||||
|
||||
BigDecimal percentageOf = BigDecimal.ZERO;
|
||||
|
||||
if (isGreaterThanZero(value)) {
|
||||
final MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
|
||||
BigDecimal multiplicand = percentage.divide(BigDecimal.valueOf(100l), mc);
|
||||
percentageOf = value.multiply(multiplicand, mc);
|
||||
}
|
||||
|
||||
return percentageOf;
|
||||
}
|
||||
|
||||
public BigDecimal amount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public boolean isFeeCharge() {
|
||||
return !this.penaltyCharge;
|
||||
}
|
||||
|
||||
public boolean isPenaltyCharge() {
|
||||
return this.penaltyCharge;
|
||||
}
|
||||
|
||||
public boolean isNotFullyPaid() {
|
||||
return !isPaid();
|
||||
}
|
||||
|
||||
public boolean isPaid() {
|
||||
return this.paid;
|
||||
}
|
||||
|
||||
public boolean isWaived() {
|
||||
return this.waived;
|
||||
}
|
||||
|
||||
public boolean isPaidOrPartiallyPaid(final MonetaryCurrency currency) {
|
||||
|
||||
final Money amountWaivedOrWrittenOff = getAmountWaived(currency).plus(getAmountWrittenOff(currency));
|
||||
return Money.of(currency, this.amountPaid).plus(amountWaivedOrWrittenOff).isGreaterThanZero();
|
||||
}
|
||||
|
||||
public Money getAmount(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.amount);
|
||||
}
|
||||
|
||||
private Money getAmountPaid(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.amountPaid);
|
||||
}
|
||||
|
||||
public Money getAmountWaived(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.amountWaived);
|
||||
}
|
||||
|
||||
public Money getAmountWrittenOff(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.amountWrittenOff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param incrementBy
|
||||
* Amount used to pay off this charge
|
||||
* @return Actual amount paid on this charge
|
||||
*/
|
||||
public Money updatePaidAmountBy(final Money incrementBy) {
|
||||
|
||||
Money amountPaidToDate = Money.of(incrementBy.getCurrency(), this.amountPaid);
|
||||
Money amountOutstanding = Money.of(incrementBy.getCurrency(), this.amountOutstanding);
|
||||
|
||||
Money amountPaidOnThisCharge = Money.zero(incrementBy.getCurrency());
|
||||
if (incrementBy.isGreaterThanOrEqualTo(amountOutstanding)) {
|
||||
amountPaidOnThisCharge = amountOutstanding;
|
||||
amountPaidToDate = amountPaidToDate.plus(amountOutstanding);
|
||||
this.amountPaid = amountPaidToDate.getAmount();
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
} else {
|
||||
amountPaidOnThisCharge = incrementBy;
|
||||
amountPaidToDate = amountPaidToDate.plus(incrementBy);
|
||||
this.amountPaid = amountPaidToDate.getAmount();
|
||||
|
||||
Money amountExpected = Money.of(incrementBy.getCurrency(), this.amount);
|
||||
this.amountOutstanding = amountExpected.minus(amountPaidToDate).getAmount();
|
||||
}
|
||||
|
||||
this.paid = determineIfFullyPaid();
|
||||
|
||||
return amountPaidOnThisCharge;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.charge.getName();
|
||||
}
|
||||
|
||||
public String currencyCode() {
|
||||
return this.charge.getCurrencyCode();
|
||||
}
|
||||
|
||||
public Charge getCharge() {
|
||||
return this.charge;
|
||||
}
|
||||
|
||||
public boolean isOnSpecifiedDueDate(){
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isOnSpecifiedDueDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == null) { return false; }
|
||||
if (obj == this) { return true; }
|
||||
if (obj.getClass() != getClass()) { return false; }
|
||||
SavingsAccountCharge rhs = (SavingsAccountCharge) obj;
|
||||
return new EqualsBuilder().appendSuper(super.equals(obj)) //
|
||||
.append(this.getId(), rhs.getId()) //
|
||||
.append(this.charge.getId(), rhs.charge.getId()) //
|
||||
.append(this.amount, rhs.amount) //
|
||||
.append(this.getDueLocalDate(), rhs.getDueLocalDate()) //
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(3, 5) //
|
||||
.append(this.getId()) //
|
||||
.append(this.charge.getId()) //
|
||||
.append(this.amount).append(this.getDueLocalDate()) //
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeCalculationTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeTimeTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.idParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeCannotBeAppliedToException;
|
||||
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@Service
|
||||
public class SavingsAccountChargeAssembler {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final SavingsAccountChargeRepository savingsAccountChargeRepository;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountChargeAssembler(final FromJsonHelper fromApiJsonHelper, final ChargeRepositoryWrapper chargeRepository,
|
||||
final SavingsAccountChargeRepository savingsAccountChargeRepository) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.savingsAccountChargeRepository = savingsAccountChargeRepository;
|
||||
}
|
||||
|
||||
public Set<SavingsAccountCharge> fromParsedJson(final JsonElement element) {
|
||||
|
||||
final Set<SavingsAccountCharge> savingsAccountCharges = new HashSet<SavingsAccountCharge>();
|
||||
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
if (topLevelJsonElement.has(chargesParamName) && topLevelJsonElement.get(chargesParamName).isJsonArray()) {
|
||||
final JsonArray array = topLevelJsonElement.get(chargesParamName).getAsJsonArray();
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
|
||||
final JsonObject savingsChargeElement = array.get(i).getAsJsonObject();
|
||||
|
||||
final Long id = fromApiJsonHelper.extractLongNamed(idParamName, savingsChargeElement);
|
||||
final Long chargeId = fromApiJsonHelper.extractLongNamed(chargeIdParamName, savingsChargeElement);
|
||||
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed(amountParamName, savingsChargeElement, locale);
|
||||
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed(chargeTimeTypeParamName, savingsChargeElement, locale);
|
||||
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed(chargeCalculationTypeParamName, savingsChargeElement,
|
||||
locale);
|
||||
final LocalDate dueDate = fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, savingsChargeElement,
|
||||
dateFormat, locale);
|
||||
|
||||
if (id == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
|
||||
if(!chargeDefinition.isSavingsCharge()){
|
||||
String errorMessage = "Charge with identifier " + chargeDefinition.getId() + " cannot be applied to Savings product.";
|
||||
throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, chargeDefinition.getId());
|
||||
}
|
||||
|
||||
ChargeTimeType chargeTime = null;
|
||||
if (chargeTimeType != null) {
|
||||
ChargeTimeType.fromInt(chargeTimeType);
|
||||
}
|
||||
ChargeCalculationType chargeCalculation = null;
|
||||
if (chargeCalculationType != null) {
|
||||
ChargeCalculationType.fromInt(chargeCalculationType);
|
||||
}
|
||||
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount(chargeDefinition, amount, chargeTime, chargeCalculation, dueDate);
|
||||
savingsAccountCharges.add(savingsAccountCharge);
|
||||
} else {
|
||||
final Long savingsAccountChargeId = id;
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOne(savingsAccountChargeId);
|
||||
if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(savingsAccountChargeId); }
|
||||
|
||||
savingsAccountCharge.update(amount, dueDate);
|
||||
|
||||
savingsAccountCharges.add(savingsAccountCharge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return savingsAccountCharges;
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_savings_account_charge_paid_by")
|
||||
public class SavingsAccountChargePaidBy extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "savings_account_transaction_id", nullable = false)
|
||||
private SavingsAccountTransaction savingsAccountTransaction;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "savings_account_charge_id", nullable = false)
|
||||
private SavingsAccountCharge savingsAccountCharge;
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
protected SavingsAccountChargePaidBy() {
|
||||
|
||||
}
|
||||
|
||||
public static SavingsAccountChargePaidBy instance(final SavingsAccountTransaction savingsAccountTransaction, final SavingsAccountCharge savingsAccountCharge, final BigDecimal amount){
|
||||
return new SavingsAccountChargePaidBy(savingsAccountTransaction, savingsAccountCharge, amount);
|
||||
}
|
||||
|
||||
private SavingsAccountChargePaidBy(SavingsAccountTransaction savingsAccountTransaction, SavingsAccountCharge savingsAccountCharge, BigDecimal amount) {
|
||||
this.savingsAccountTransaction = savingsAccountTransaction;
|
||||
this.savingsAccountCharge = savingsAccountCharge;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public SavingsAccountTransaction getSavingsAccountTransaction() {
|
||||
return this.savingsAccountTransaction;
|
||||
}
|
||||
|
||||
public void setSavingsAccountTransaction(SavingsAccountTransaction savingsAccountTransaction) {
|
||||
this.savingsAccountTransaction = savingsAccountTransaction;
|
||||
}
|
||||
|
||||
public SavingsAccountCharge getSavingsAccountCharge() {
|
||||
return this.savingsAccountCharge;
|
||||
}
|
||||
|
||||
public void setSavingsAccountCharge(SavingsAccountCharge savingsAccountCharge) {
|
||||
this.savingsAccountCharge = savingsAccountCharge;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public boolean isFeeCharge(){
|
||||
return (this.savingsAccountCharge == null) ? false : this.savingsAccountCharge.isFeeCharge();
|
||||
}
|
||||
|
||||
public boolean isPenaltyCharge(){
|
||||
return (this.savingsAccountCharge == null) ? false : this.savingsAccountCharge.isPenaltyCharge();
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface SavingsAccountChargeRepository extends JpaRepository<SavingsAccountCharge, Long>, JpaSpecificationExecutor<SavingsAccountCharge> {
|
||||
SavingsAccountCharge findByIdAndSavingsAccountId(Long id, Long savingsAccountId);
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Wrapper for {@link SavingsAccountChargeRepository} that adds NULL checking and Error
|
||||
* handling capabilities
|
||||
* </p>
|
||||
*/
|
||||
@Service
|
||||
public class SavingsAccountChargeRepositoryWrapper {
|
||||
|
||||
private final SavingsAccountChargeRepository repository;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountChargeRepositoryWrapper(final SavingsAccountChargeRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public SavingsAccountCharge findOneWithNotFoundDetection(final Long id) {
|
||||
final SavingsAccountCharge savingsAccountCharge = this.repository.findOne(id);
|
||||
if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(id); }
|
||||
return savingsAccountCharge;
|
||||
}
|
||||
|
||||
public SavingsAccountCharge findOneWithNotFoundDetection(final Long id, final Long savingsAccountId) {
|
||||
final SavingsAccountCharge savingsAccountCharge = this.repository.findByIdAndSavingsAccountId(id, savingsAccountId);
|
||||
if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(id); }
|
||||
return savingsAccountCharge;
|
||||
}
|
||||
|
||||
public void save(final SavingsAccountCharge savingsAccountCharge) {
|
||||
this.repository.save(savingsAccountCharge);
|
||||
}
|
||||
|
||||
public void saveAndFlush(final SavingsAccountCharge savingsAccountCharge) {
|
||||
this.repository.saveAndFlush(savingsAccountCharge);
|
||||
}
|
||||
|
||||
public void delete(final SavingsAccountCharge savingsAccountCharge) {
|
||||
this.repository.delete(savingsAccountCharge);
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -36,6 +36,12 @@ public final class SavingsAccountSummary {
|
||||
|
||||
@Column(name = "total_withdrawal_fees_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalWithdrawalFees;
|
||||
|
||||
@Column(name = "total_fees_charge_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalFeeCharge;
|
||||
|
||||
@Column(name = "total_penalty_charge_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalPenaltyCharge;
|
||||
|
||||
@Column(name = "total_annual_fees_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalAnnualFees;
|
||||
@@ -55,9 +61,11 @@ public final class SavingsAccountSummary {
|
||||
this.totalInterestPosted = wrapper.calculateTotalInterestPosted(currency, transactions);
|
||||
this.totalWithdrawalFees = wrapper.calculateTotalWithdrawalFees(currency, transactions);
|
||||
this.totalAnnualFees = wrapper.calculateTotalAnnualFees(currency, transactions);
|
||||
this.totalFeeCharge = wrapper.calculateTotalFeesCharge(currency, transactions);
|
||||
this.totalPenaltyCharge = wrapper.calculateTotalPenaltyCharge(currency, transactions);
|
||||
|
||||
this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals)
|
||||
.minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).getAmount();
|
||||
.minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).minus(totalFeeCharge).minus(totalPenaltyCharge).getAmount();
|
||||
}
|
||||
|
||||
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, final List<PostingPeriod> allPostingPeriods) {
|
||||
|
||||
+61
-2
@@ -7,17 +7,23 @@ package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
@@ -30,6 +36,7 @@ import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumDat
|
||||
import org.mifosplatform.portfolio.savings.domain.interest.EndOfDayBalance;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* All monetary transactions against a savings account are modelled through this
|
||||
@@ -76,7 +83,11 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "balance_number_of_days_derived", nullable = false)
|
||||
private Integer balanceNumberOfDays;
|
||||
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccountTransaction", orphanRemoval = true)
|
||||
private Set<SavingsAccountChargePaidBy> savingsAccountChargesPaid = new HashSet<SavingsAccountChargePaidBy>();
|
||||
|
||||
protected SavingsAccountTransaction() {
|
||||
this.dateOf = null;
|
||||
this.typeOf = null;
|
||||
@@ -116,6 +127,20 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount,
|
||||
isReversed);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
|
||||
final Money amount) {
|
||||
final boolean isReversed = false;
|
||||
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.APPLY_CHARGES.getValue(), date, amount,
|
||||
isReversed);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransaction waiver(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
|
||||
final Money amount) {
|
||||
final boolean isReversed = false;
|
||||
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount,
|
||||
isReversed);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date) {
|
||||
final boolean isReversed = false;
|
||||
@@ -386,6 +411,40 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public boolean isDebit() {
|
||||
return isWithdrawal() || isWithdrawalFeeAndNotReversed() || isAnnualFeeAndNotReversed();
|
||||
return isWithdrawal() || isWithdrawalFeeAndNotReversed() || isAnnualFeeAndNotReversed() || isCharge();
|
||||
}
|
||||
|
||||
public boolean isCharge() {
|
||||
return SavingsAccountTransactionType.fromInt(this.typeOf).isCharge();
|
||||
}
|
||||
|
||||
public boolean isFeeCharge() {
|
||||
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
|
||||
return (isCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false;
|
||||
}
|
||||
|
||||
public boolean isPenaltyCharge() {
|
||||
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
|
||||
return (isCharge() && chargePaidBy != null) ? chargePaidBy.isPenaltyCharge() : false;
|
||||
}
|
||||
|
||||
public boolean isFeeChargeAndNotReversed(){
|
||||
return isFeeCharge() && isNotReversed();
|
||||
}
|
||||
|
||||
public boolean isPenaltyChargeAndNotReversed(){
|
||||
return isPenaltyCharge() && isNotReversed();
|
||||
}
|
||||
|
||||
private SavingsAccountChargePaidBy getSavingsAccountChargePaidBy(){
|
||||
if(!CollectionUtils.isEmpty(this.savingsAccountChargesPaid)){
|
||||
return this.savingsAccountChargesPaid.iterator().next();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<SavingsAccountChargePaidBy> getSavingsAccountChargesPaid() {
|
||||
return this.savingsAccountChargesPaid;
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -68,4 +68,25 @@ public final class SavingsAccountTransactionSummaryWrapper {
|
||||
}
|
||||
return total.getAmountDefaultedToNullIfZero();
|
||||
}
|
||||
|
||||
public BigDecimal calculateTotalFeesCharge(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
|
||||
Money total = Money.zero(currency);
|
||||
for (SavingsAccountTransaction transaction : transactions) {
|
||||
if (transaction.isFeeChargeAndNotReversed() && transaction.isNotReversed()) {
|
||||
total = total.plus(transaction.getAmount(currency));
|
||||
}
|
||||
}
|
||||
return total.getAmountDefaultedToNullIfZero();
|
||||
}
|
||||
|
||||
public BigDecimal calculateTotalPenaltyCharge(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
|
||||
Money total = Money.zero(currency);
|
||||
for (SavingsAccountTransaction transaction : transactions) {
|
||||
if (transaction.isPenaltyChargeAndNotReversed() && transaction.isNotReversed()) {
|
||||
total = total.plus(transaction.getAmount(currency));
|
||||
}
|
||||
}
|
||||
return total.getAmountDefaultedToNullIfZero();
|
||||
}
|
||||
|
||||
}
|
||||
+47
-3
@@ -10,6 +10,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PR
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
|
||||
@@ -30,13 +31,18 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawal
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.joda.time.MonthDay;
|
||||
@@ -47,6 +53,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
|
||||
@@ -55,6 +62,8 @@ import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_savings_product")
|
||||
public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
@@ -132,6 +141,10 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "annual_fee_on_day", nullable = true)
|
||||
private Integer annualFeeOnDay;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "m_savings_product_charge", joinColumns = @JoinColumn(name = "savings_product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
|
||||
private Set<Charge> charges;
|
||||
|
||||
public static SavingsProduct createNew(final String name, final String description, final MonetaryCurrency currency,
|
||||
final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
|
||||
@@ -139,12 +152,12 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final boolean withdrawalFeeApplicableForTransfer,
|
||||
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final AccountingRuleType accountingRuleType) {
|
||||
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final AccountingRuleType accountingRuleType, final Set<Charge> charges) {
|
||||
|
||||
return new SavingsProduct(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,withdrawalFeeApplicableForTransfer, annualFeeAmount,
|
||||
annualFeeOnMonthDay, accountingRuleType);
|
||||
annualFeeOnMonthDay, accountingRuleType, charges);
|
||||
}
|
||||
|
||||
protected SavingsProduct() {
|
||||
@@ -158,7 +171,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final boolean withdrawalFeeApplicableForTransfer,
|
||||
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final AccountingRuleType accountingRuleType) {
|
||||
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final AccountingRuleType accountingRuleType, final Set<Charge> charges) {
|
||||
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
@@ -194,6 +207,10 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
this.accountingRule = accountingRuleType.getValue();
|
||||
}
|
||||
|
||||
if (charges != null) {
|
||||
this.charges = charges;
|
||||
}
|
||||
|
||||
validateLockinDetails();
|
||||
validateWithdrawalFeeDetails();
|
||||
validateAnnualFeeDetails();
|
||||
@@ -424,6 +441,14 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
actualChanges.put(accountingRuleParamName, newValue);
|
||||
this.accountingRule = newValue;
|
||||
}
|
||||
|
||||
//charges
|
||||
if (command.hasParameter(chargesParamName)) {
|
||||
JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName);
|
||||
if (jsonArray != null) {
|
||||
actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName));
|
||||
}
|
||||
}
|
||||
|
||||
validateLockinDetails();
|
||||
validateWithdrawalFeeDetails();
|
||||
@@ -512,4 +537,23 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
public Integer getAccountingType() {
|
||||
return this.accountingRule;
|
||||
}
|
||||
|
||||
public boolean update(final Set<Charge> newSavingsProductCharges) {
|
||||
if (newSavingsProductCharges == null) return false;
|
||||
|
||||
boolean updated = false;
|
||||
if (this.charges != null) {
|
||||
final Set<Charge> currentSetOfCharges = new HashSet<Charge>(this.charges);
|
||||
final Set<Charge> newSetOfCharges = new HashSet<Charge>(newSavingsProductCharges);
|
||||
|
||||
if (!(currentSetOfCharges.equals(newSetOfCharges))) {
|
||||
updated = true;
|
||||
this.charges = newSavingsProductCharges;
|
||||
}
|
||||
} else {
|
||||
updated = true;
|
||||
this.charges = newSavingsProductCharges;
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
+58
-2
@@ -7,6 +7,9 @@ package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.idParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
|
||||
@@ -25,22 +28,39 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawal
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.MonthDay;
|
||||
import org.mifosplatform.accounting.common.AccountingRuleType;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeCannotBeAppliedToException;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
|
||||
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@Component
|
||||
public class SavingsProductAssembler {
|
||||
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
|
||||
@Autowired
|
||||
public SavingsProductAssembler(final ChargeRepositoryWrapper chargeRepository) {
|
||||
this.chargeRepository = chargeRepository;
|
||||
}
|
||||
|
||||
public SavingsProduct assemble(final JsonCommand command) {
|
||||
|
||||
final String name = command.stringValueOfParameterNamed(nameParamName);
|
||||
@@ -103,10 +123,46 @@ public class SavingsProductAssembler {
|
||||
final BigDecimal annualFeeAmount = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(annualFeeAmountParamName);
|
||||
final MonthDay monthDayOfAnnualFee = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
|
||||
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
|
||||
|
||||
|
||||
//Savings product charges
|
||||
final Set<Charge> charges = this.assembleListOfSavingsProductCharges(command, currencyCode);
|
||||
|
||||
return SavingsProduct.createNew(name, description, currency, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, iswithdrawalFeeApplicableForTransfer,
|
||||
annualFeeAmount, monthDayOfAnnualFee, accountingRuleType);
|
||||
annualFeeAmount, monthDayOfAnnualFee, accountingRuleType, charges);
|
||||
}
|
||||
|
||||
public Set<Charge> assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) {
|
||||
|
||||
final Set<Charge> charges = new HashSet<Charge>();
|
||||
|
||||
if (command.parameterExists(chargesParamName)) {
|
||||
JsonArray chargesArray = command.arrayOfParameterNamed(chargesParamName);
|
||||
if (chargesArray != null) {
|
||||
for (int i = 0; i < chargesArray.size(); i++) {
|
||||
|
||||
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
|
||||
if (jsonObject.has(idParamName)) {
|
||||
final Long id = jsonObject.get(idParamName).getAsLong();
|
||||
|
||||
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
|
||||
|
||||
if(!charge.isSavingsCharge()){
|
||||
String errorMessage = "Charge with identifier " + charge.getId() + " cannot be applied to Savings product.";
|
||||
throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, charge.getId());
|
||||
}
|
||||
|
||||
if (!savingsProductCurrencyCode.equals(charge.getCurrencyCode())) {
|
||||
String errorMessage = "Charge and Savings Product must have the same currency.";
|
||||
throw new InvalidCurrencyException("charge", "attach.to.savings.product", errorMessage);
|
||||
}
|
||||
charges.add(charge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return charges;
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeCalculationTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeTimeTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.idParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@Service
|
||||
public class SavingsProductChargeAssembler {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final SavingsAccountChargeRepository savingsAccountChargeRepository;
|
||||
|
||||
@Autowired
|
||||
public SavingsProductChargeAssembler(final FromJsonHelper fromApiJsonHelper, final ChargeRepositoryWrapper chargeRepository,
|
||||
final SavingsAccountChargeRepository savingsAccountChargeRepository) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.savingsAccountChargeRepository = savingsAccountChargeRepository;
|
||||
}
|
||||
|
||||
public Set<SavingsAccountCharge> fromParsedJson(final JsonElement element) {
|
||||
|
||||
final Set<SavingsAccountCharge> savingsAccountCharges = new HashSet<SavingsAccountCharge>();
|
||||
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
if (topLevelJsonElement.has(chargesParamName) && topLevelJsonElement.get(chargesParamName).isJsonArray()) {
|
||||
final JsonArray array = topLevelJsonElement.get(chargesParamName).getAsJsonArray();
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
|
||||
final JsonObject savingsChargeElement = array.get(i).getAsJsonObject();
|
||||
|
||||
final Long id = fromApiJsonHelper.extractLongNamed(idParamName, savingsChargeElement);
|
||||
final Long chargeId = fromApiJsonHelper.extractLongNamed(chargeIdParamName, savingsChargeElement);
|
||||
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed(amountParamName, savingsChargeElement, locale);
|
||||
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed(chargeTimeTypeParamName, savingsChargeElement, locale);
|
||||
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed(chargeCalculationTypeParamName, savingsChargeElement,
|
||||
locale);
|
||||
final LocalDate dueDate = fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, savingsChargeElement,
|
||||
dateFormat, locale);
|
||||
|
||||
if (id == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
ChargeTimeType chargeTime = null;
|
||||
if (chargeTimeType != null) {
|
||||
ChargeTimeType.fromInt(chargeTimeType);
|
||||
}
|
||||
ChargeCalculationType chargeCalculation = null;
|
||||
if (chargeCalculationType != null) {
|
||||
ChargeCalculationType.fromInt(chargeCalculationType);
|
||||
}
|
||||
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount(chargeDefinition, amount, chargeTime, chargeCalculation, dueDate);
|
||||
savingsAccountCharges.add(savingsAccountCharge);
|
||||
} else {
|
||||
final Long savingsAccountChargeId = id;
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOne(savingsAccountChargeId);
|
||||
if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(savingsAccountChargeId); }
|
||||
|
||||
savingsAccountCharge.update(amount, dueDate);
|
||||
|
||||
savingsAccountCharges.add(savingsAccountCharge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return savingsAccountCharges;
|
||||
}
|
||||
|
||||
}
|
||||
+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.portfolio.savings.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class AddSavingsAccountChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public AddSavingsAccountChargeCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.addSavingsAccountCharge(command);
|
||||
}
|
||||
}
|
||||
+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.portfolio.savings.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class DeleteSavingsAccountChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public DeleteSavingsAccountChargeCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.deleteSavingsAccountCharge(command.getSavingsId(), command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+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.portfolio.savings.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class PaySavingsAccountChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public PaySavingsAccountChargeCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.payCharge(command.getSavingsId(), command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+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.portfolio.savings.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class UpdateSavingsAccountChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateSavingsAccountChargeCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.updateSavingsAccountCharge(command);
|
||||
}
|
||||
}
|
||||
+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.portfolio.savings.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class WaiveSavingsAccountChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public WaiveSavingsAccountChargeCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.waiveCharge(command.getSavingsId(), command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+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.portfolio.savings.service;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData;
|
||||
|
||||
public interface SavingsAccountChargeReadPlatformService {
|
||||
|
||||
ChargeData retrieveSavingsAccountChargeTemplate();
|
||||
|
||||
Collection<SavingsAccountChargeData> retrieveSavingsAccountCharges(Long savingsAccountId);
|
||||
|
||||
SavingsAccountChargeData retrieveSavingsAccountChargeDetails(Long savingsAccountChargeId, Long savingsAccountId);
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccountChargeReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountChargeReadPlatformServiceImpl(final PlatformSecurityContext context,
|
||||
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource) {
|
||||
this.context = context;
|
||||
this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
private static final class SavingsAccountChargeMapper implements RowMapper<SavingsAccountChargeData> {
|
||||
|
||||
public String schema() {
|
||||
return "sc.id as id, c.id as chargeId, c.name as name, "
|
||||
+ "sc.amount as amountDue, "
|
||||
+ "sc.amount_paid_derived as amountPaid, "
|
||||
+ "sc.amount_waived_derived as amountWaived, "
|
||||
+ "sc.amount_writtenoff_derived as amountWrittenOff, "
|
||||
+ "sc.amount_outstanding_derived as amountOutstanding, "
|
||||
+ "sc.calculation_percentage as percentageOf, sc.calculation_on_amount as amountPercentageAppliedTo, "
|
||||
+ "sc.charge_time_enum as chargeTime, "
|
||||
+ "sc.is_penalty as penalty, "
|
||||
+ "sc.due_for_collection_as_of_date as dueAsOfDate, "
|
||||
+ "sc.charge_calculation_enum as chargeCalculation, "
|
||||
+ "c.currency_code as currencyCode, oc.name as currencyName, "
|
||||
+ "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
+ "oc.internationalized_name_code as currencyNameCode from m_charge c "
|
||||
+ "join m_organisation_currency oc on c.currency_code = oc.code "
|
||||
+ "join m_savings_account_charge sc on sc.charge_id = c.id ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsAccountChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
final Long chargeId = rs.getLong("chargeId");
|
||||
final String name = rs.getString("name");
|
||||
final BigDecimal amount = rs.getBigDecimal("amountDue");
|
||||
final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid");
|
||||
final BigDecimal amountWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWaived");
|
||||
final BigDecimal amountWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWrittenOff");
|
||||
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
|
||||
|
||||
final BigDecimal percentageOf = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "percentageOf");
|
||||
final BigDecimal amountPercentageAppliedTo = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPercentageAppliedTo");
|
||||
|
||||
final String currencyCode = rs.getString("currencyCode");
|
||||
final String currencyName = rs.getString("currencyName");
|
||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||
final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces");
|
||||
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||
|
||||
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMultiplesOf,
|
||||
currencyDisplaySymbol, currencyNameCode);
|
||||
|
||||
final int chargeTime = rs.getInt("chargeTime");
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
|
||||
|
||||
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||
|
||||
final int chargeCalculation = rs.getInt("chargeCalculation");
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
|
||||
final boolean penalty = rs.getBoolean("penalty");
|
||||
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
|
||||
return SavingsAccountChargeData.instance(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, chargeOptions, penalty);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChargeData retrieveSavingsAccountChargeTemplate() {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final List<EnumOptionData> allowedChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService.retrieveCalculationTypes();
|
||||
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
|
||||
|
||||
//TODO AA : revisit for merge conflict - Not sure method signature
|
||||
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsAccountChargeData retrieveSavingsAccountChargeDetails(final Long id, final Long savingsAccountId) {
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final SavingsAccountChargeMapper rm = new SavingsAccountChargeMapper();
|
||||
|
||||
final String sql = "select " + rm.schema() + " where sc.id=? and sc.savings_account_id=?";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { id, savingsAccountId });
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
throw new SavingsAccountChargeNotFoundException(savingsAccountId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SavingsAccountChargeData> retrieveSavingsAccountCharges(final Long loanId) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final SavingsAccountChargeMapper rm = new SavingsAccountChargeMapper();
|
||||
|
||||
final String sql = "select " + rm.schema() + " where sc.savings_account_id=? "
|
||||
+ " order by sc.charge_time_enum ASC, sc.due_for_collection_as_of_date ASC, sc.is_penalty ASC";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
|
||||
}
|
||||
}
|
||||
+36
-6
@@ -27,6 +27,8 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.staff.data.StaffData;
|
||||
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
|
||||
import org.mifosplatform.portfolio.account.data.AccountTransferData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
|
||||
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||
@@ -40,6 +42,7 @@ import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountApplicationTimelineData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountSummaryData;
|
||||
@@ -65,6 +68,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
private final SavingsProductReadPlatformService savingsProductReadPlatformService;
|
||||
private final StaffReadPlatformService staffReadPlatformService;
|
||||
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
|
||||
// mappers
|
||||
private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper;
|
||||
@@ -79,7 +83,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource,
|
||||
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
|
||||
final SavingsProductReadPlatformService savingProductReadPlatformService,
|
||||
final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService) {
|
||||
final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.clientReadPlatformService = clientReadPlatformService;
|
||||
@@ -91,6 +96,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
this.transactionsMapper = new SavingsAccountTransactionsMapper();
|
||||
this.savingAccountMapper = new SavingAccountMapper();
|
||||
this.annualFeeMapper = new SavingsAccountAnnualFeeMapper();
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -229,7 +235,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sa.total_annual_fees_derived as totalAnnualFees, ");
|
||||
sqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, ");
|
||||
sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, ");
|
||||
sqlBuilder.append("sa.account_balance_derived as accountBalance ");
|
||||
sqlBuilder.append("sa.account_balance_derived as accountBalance, ");
|
||||
sqlBuilder.append("sa.total_fees_charge_derived as totalFeeCharge, ");
|
||||
sqlBuilder.append("sa.total_penalty_charge_derived as totalPenaltyCharge ");
|
||||
sqlBuilder.append("from m_savings_account sa ");
|
||||
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
|
||||
@@ -372,9 +380,12 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final BigDecimal totalInterestEarned = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestEarned");
|
||||
final BigDecimal totalInterestPosted = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestPosted");
|
||||
final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance");
|
||||
|
||||
final BigDecimal totalFeeCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalFeeCharge");
|
||||
final BigDecimal totalPenaltyCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge");
|
||||
|
||||
final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
|
||||
totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance);
|
||||
totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance, totalFeeCharge,
|
||||
totalPenaltyCharge);
|
||||
|
||||
return SavingsAccountData.instance(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
@@ -430,6 +441,12 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
|
||||
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
final Collection<ChargeData> productCharges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId);
|
||||
//update charges from Product charges
|
||||
final Collection<SavingsAccountChargeData> charges = fromChargesToSavingsCharges(productCharges);
|
||||
|
||||
final boolean feeChargesOnly = false;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicableCharges(feeChargesOnly);
|
||||
|
||||
Collection<StaffData> fieldOfficerOptions = null;
|
||||
|
||||
@@ -459,7 +476,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
|
||||
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions);
|
||||
} else {
|
||||
|
||||
String clientName = null;
|
||||
@@ -483,15 +500,28 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
|
||||
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
|
||||
final boolean feeChargesOnly = true;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsAccountApplicableCharges(feeChargesOnly);
|
||||
|
||||
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions);
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private Collection<SavingsAccountChargeData> fromChargesToSavingsCharges(Collection<ChargeData> productCharges) {
|
||||
final Collection<SavingsAccountChargeData> savingsCharges = new ArrayList<SavingsAccountChargeData>();
|
||||
for (ChargeData chargeData : productCharges) {
|
||||
final SavingsAccountChargeData savingsCharge = chargeData.toSavingsAccountChargeData();
|
||||
savingsCharges.add(savingsCharge);
|
||||
}
|
||||
return savingsCharges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsAccountTransactionData retrieveDepositTransactionTemplate(final Long savingsId) {
|
||||
|
||||
|
||||
+10
@@ -41,4 +41,14 @@ public interface SavingsAccountWritePlatformService {
|
||||
void rejectSavingsTransfer(Long accountId);
|
||||
|
||||
SavingsAccountTransaction acceptSavingsTransfer(Long accountId, LocalDate transferDate, Office acceptedInOffice, Staff staff);
|
||||
|
||||
CommandProcessingResult addSavingsAccountCharge(JsonCommand command);
|
||||
|
||||
CommandProcessingResult updateSavingsAccountCharge(JsonCommand command);
|
||||
|
||||
CommandProcessingResult deleteSavingsAccountCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult waiveCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult payCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command);
|
||||
}
|
||||
+160
-1
@@ -6,6 +6,8 @@
|
||||
package org.mifosplatform.portfolio.savings.service;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
@@ -40,6 +42,8 @@ import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
||||
import org.mifosplatform.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
@@ -49,10 +53,13 @@ import org.mifosplatform.portfolio.note.domain.NoteRepository;
|
||||
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
||||
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeDataValidator;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDataValidator;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountCharge;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountDomainService;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
@@ -74,6 +81,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
|
||||
private final SavingsAccountAssembler savingAccountAssembler;
|
||||
private final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator;
|
||||
private final SavingsAccountChargeDataValidator savingsAccountChargeDataValidator;
|
||||
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
|
||||
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||
@@ -81,6 +89,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final NoteRepository noteRepository;
|
||||
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -88,17 +98,20 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
|
||||
final SavingsAccountAssembler savingAccountAssembler,
|
||||
final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator,
|
||||
final SavingsAccountChargeDataValidator savingsAccountChargeDataValidator,
|
||||
final PaymentDetailWritePlatformService paymentDetailWritePlatformService,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
|
||||
final JournalEntryWritePlatformService journalEntryWritePlatformService,
|
||||
final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository,
|
||||
final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService) {
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
|
||||
final ChargeRepositoryWrapper chargeRepository, final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository) {
|
||||
this.context = context;
|
||||
this.savingAccountRepository = savingAccountRepository;
|
||||
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
|
||||
this.savingAccountAssembler = savingAccountAssembler;
|
||||
this.savingsAccountTransactionDataValidator = savingsAccountTransactionDataValidator;
|
||||
this.savingsAccountChargeDataValidator = savingsAccountChargeDataValidator;
|
||||
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
|
||||
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
|
||||
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
|
||||
@@ -106,6 +119,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
this.noteRepository = noteRepository;
|
||||
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
|
||||
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.savingsAccountChargeRepository = savingsAccountChargeRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -553,4 +568,148 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
return acceptTransferTransaction;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult addSavingsAccountCharge(final JsonCommand command) {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
final Long savingsAccountId = command.getSavingsId();
|
||||
this.savingsAccountChargeDataValidator.validateAdd(command.json());
|
||||
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
|
||||
|
||||
final Long chargeDefinitionId = command.longValueOfParameterNamed(chargeIdParamName);
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId);
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewFromJson(savingsAccount, chargeDefinition, command);
|
||||
|
||||
savingsAccount.addCharge(fmt, savingsAccountCharge, chargeDefinition);
|
||||
|
||||
this.savingAccountRepository.saveAndFlush(savingsAccount);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(savingsAccountCharge.getId()) //
|
||||
.withOfficeId(savingsAccount.officeId()) //
|
||||
.withClientId(savingsAccount.clientId()) //
|
||||
.withGroupId(savingsAccount.groupId()) //
|
||||
.withLoanId(savingsAccountId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult updateSavingsAccountCharge(JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
this.savingsAccountChargeDataValidator.validateUpdate(command.json());
|
||||
final Long savingsAccountId = command.getSavingsId();
|
||||
final Long savingsChargeId = command.entityId();//SavingsAccount Charge entity
|
||||
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(savingsChargeId, savingsAccountId);
|
||||
final Map<String, Object> changes = savingsAccountCharge.update(command);
|
||||
this.savingsAccountChargeRepository.saveAndFlush(savingsAccountCharge);
|
||||
//TODO AA: revisit the code
|
||||
/*
|
||||
// Charges may be edited only when the loan associated with them are
|
||||
// yet to be approved (are in submitted and pending status)
|
||||
if (!loan.status().isSubmittedAndPendingApproval()) { throw new LoanChargeCannotBeUpdatedException(
|
||||
LOAN_CHARGE_CANNOT_BE_UPDATED_REASON.LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE, loanCharge.getId()); }
|
||||
*/
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(savingsChargeId) //
|
||||
//.withOfficeId(savings) //
|
||||
//.withClientId(loan.getClientId()) //
|
||||
//.withGroupId(loan.getGroupId()) //
|
||||
//.withLoanId(savingsAccountId) //
|
||||
.with(changes) //
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult waiveCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command) {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
|
||||
savingsAccount.waiveCharge(savingsAccountChargeId, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.savingAccountRepository.saveAndFlush(savingsAccount);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(savingsAccountChargeId) //
|
||||
.withOfficeId(savingsAccount.officeId()) //
|
||||
.withClientId(savingsAccount.clientId()) //
|
||||
.withGroupId(savingsAccount.groupId()) //
|
||||
.withSavingsId(savingsAccountId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult deleteSavingsAccountCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
|
||||
|
||||
//TODO AA: validate before deleting a charge
|
||||
|
||||
savingsAccount.removeCharge(savingsAccountCharge);
|
||||
this.savingAccountRepository.saveAndFlush(savingsAccount);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(savingsAccountChargeId) //
|
||||
.withOfficeId(savingsAccount.officeId()) //
|
||||
.withClientId(savingsAccount.clientId()) //
|
||||
.withGroupId(savingsAccount.groupId()) //
|
||||
.withSavingsId(savingsAccountId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult payCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
|
||||
final BigDecimal chargeAmount = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||
savingsAccount.payCharge(savingsAccountChargeId, chargeAmount, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.savingAccountRepository.saveAndFlush(savingsAccount);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(savingsAccountChargeId) //
|
||||
.withOfficeId(savingsAccount.officeId()) //
|
||||
.withClientId(savingsAccount.clientId()) //
|
||||
.withGroupId(savingsAccount.groupId()) //
|
||||
.withSavingsId(savingsAccountId) //
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+17
-1
@@ -11,6 +11,7 @@ import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
@@ -28,6 +29,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyReposit
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
|
||||
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
@@ -44,6 +46,8 @@ import org.mifosplatform.portfolio.savings.SavingsApiConstants;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountDataValidator;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountCharge;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountChargeAssembler;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsProduct;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsProductRepository;
|
||||
@@ -74,6 +78,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
private final SavingsAccountApplicationTransitionApiJsonValidator savingsAccountApplicationTransitionApiJsonValidator;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
|
||||
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||
private final SavingsAccountChargeAssembler savingsAccountChargeAssembler;
|
||||
|
||||
@Autowired
|
||||
public SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -84,7 +89,8 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
final NoteRepository noteRepository, final StaffRepositoryWrapper staffRepository,
|
||||
final SavingsAccountApplicationTransitionApiJsonValidator savingsAccountApplicationTransitionApiJsonValidator,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
|
||||
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
|
||||
final JournalEntryWritePlatformService journalEntryWritePlatformService,
|
||||
final SavingsAccountChargeAssembler savingsAccountChargeAssembler) {
|
||||
this.context = context;
|
||||
this.savingAccountRepository = savingAccountRepository;
|
||||
this.savingAccountAssembler = savingAccountAssembler;
|
||||
@@ -98,6 +104,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
this.savingsAccountApplicationTransitionApiJsonValidator = savingsAccountApplicationTransitionApiJsonValidator;
|
||||
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
|
||||
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
|
||||
this.savingsAccountChargeAssembler = savingsAccountChargeAssembler;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -225,6 +232,15 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
}
|
||||
account.update(fieldOfficer);
|
||||
}
|
||||
|
||||
if (changes.containsKey("charges")) {
|
||||
//final Set<Charge> savingsProductCharges = this.savi.assembleListOfSavingsProductCharges(command, product.currency().getCode());
|
||||
final Set<SavingsAccountCharge> charges = this.savingsAccountChargeAssembler.fromParsedJson(command.parsedJson());
|
||||
boolean updated = account.update(charges);
|
||||
if (!updated) {
|
||||
changes.remove("charges");
|
||||
}
|
||||
}
|
||||
|
||||
this.savingAccountRepository.saveAndFlush(account);
|
||||
}
|
||||
|
||||
+7
@@ -101,6 +101,13 @@ public class SavingsEnumerations {
|
||||
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getCode(), "Transfer Withdrawn");
|
||||
break;
|
||||
default:
|
||||
case APPLY_CHARGES:
|
||||
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.APPLY_CHARGES.getValue().longValue(),
|
||||
SavingsAccountTransactionType.APPLY_CHARGES.getCode(), "Apply Charge");
|
||||
break;
|
||||
case WAIVE_CHARGES:
|
||||
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.WAIVE_CHARGES.getValue().longValue(),
|
||||
SavingsAccountTransactionType.WAIVE_CHARGES.getCode(), "Waive Charge");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
|
||||
+1
@@ -17,6 +17,7 @@ import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsProductData;
|
||||
import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
+12
@@ -6,8 +6,10 @@
|
||||
package org.mifosplatform.portfolio.savings.service;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
@@ -15,6 +17,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsProductDataValidator;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsProduct;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsProductAssembler;
|
||||
@@ -109,6 +112,14 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
final Map<String, Object> changes = product.update(command);
|
||||
|
||||
if (changes.containsKey(chargesParamName)) {
|
||||
final Set<Charge> savingsProductCharges = this.savingsProductAssembler.assembleListOfSavingsProductCharges(command, product.currency().getCode());
|
||||
boolean updated = product.update(savingsProductCharges);
|
||||
if (!updated) {
|
||||
changes.remove(chargesParamName);
|
||||
}
|
||||
}
|
||||
|
||||
// accounting related changes
|
||||
boolean accountingTypeChanged = changes.containsKey(accountingRuleParamName);
|
||||
final Map<String, Object> accountingMappingChanges = accountMappingWritePlatformService.updateSavingsProductToGLAccountMapping(
|
||||
@@ -142,4 +153,5 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.withEntityId(product.getId()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user