MIFOSX-748: update api documentation, resolve null pointers when creating charge with empty request.

This commit is contained in:
Keith Woodlock
2013-10-19 21:48:27 +01:00
parent 961e581f63
commit 0f2685c63c
6 changed files with 514 additions and 452 deletions
+376 -347
View File
@@ -1506,6 +1506,10 @@
<td>loans/{loanId}/transactions/template</td> <td>loans/{loanId}/transactions/template</td>
<td><a href="#loans_repaymenttemplate_etc">Retrieve Loan Transaction Template</a></td> <td><a href="#loans_repaymenttemplate_etc">Retrieve Loan Transaction Template</a></td>
</tr> </tr>
<tr>
<td>charges/template</td>
<td><a href="#charges_template">Retrieve Charge Template</a></td>
</tr>
<tr> <tr>
<td>offices/template</td> <td>offices/template</td>
<td><a href="#offices_template">Retrieve Office Template</a></td> <td><a href="#offices_template">Retrieve Office Template</a></td>
@@ -10980,388 +10984,413 @@ Request Body:
</div> </div>
<!--Charges section starts here--> <!--Charges section starts here-->
<a id="charges" name="charges" class="old-syle-anchor">&nbsp;</a> <a id="charges" name="charges" class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h3>Charges</h3> <h3>Charges</h3>
<p> <p>
Its typical for MFIs to add extra costs for their financial Its typical for MFIs to add extra costs for their financial
products. There are typically <b>Fees</b> or <b>Penalties</b>. products. These are typically <b>Fees</b> or <b>Penalties</b>.
</p> </p>
<p>Charges is what we use to model both fees and penalties. <p>A <strong><i>Charge</i></strong> on mifos platform is what we use to model both <b>Fees</b> and <b>Penalties</b>.</p>
For now charges can only be associated with loan products / loans.</p> <p>At present we support defining charges for use in both loan and saving products.</p>
<table class=matrixHeading> <table class=matrixHeading>
<tr class="matrixHeadingBG"> <tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td> <td><div class="mifosXHeading2">Field Descriptions</div></td>
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>name</td> <td>name</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc>Name associated with this charge.</td> <td class=fielddesc>Name associated with this charge.</td>
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>active</td> <td>chargeAppliesTo</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc>Boolean flag determines if the Charge is currently active.</td> <td class=fielddesc>Enumeration for indicating whether charge is to be applicable for loans or savings.<br/>
</tr> 1=Loans,
<tr class=alt> 2=Savings
<td>penalty</td> <p>Once a charge definiton has been created, this attribute cannot be changed at any point.</p>
</tr> </td>
<tr> </tr>
<td class=fielddesc>Boolean flag determines if the Charge is a penalty.</td> <tr class=alt>
</tr> <td>active</td>
<tr class=alt> </tr>
<td>currency</td> <tr>
</tr> <td class=fielddesc>Boolean flag determines if the charge is currently active.</td>
<tr> </tr>
<td class=fielddesc>Details of the currency associated with this charge. <tr class=alt>
Refer <a href="#charges_template">Charges Template</a> for additional details</td> <td>penalty</td>
</tr> </tr>
<tr class=alt> <tr>
<td>amount</td> <td class=fielddesc>Boolean flag determines if the charge is a <b>penalty</b>. If false the charge is considered a <b>Fee</b></td>
</tr> </tr>
<tr> <tr class=alt>
<td class=fielddesc>Field holds the details of the actual amount to be collected <td>currencyCode</td>
in the case of "Flat Charges" or the percentage amount to be collected in </tr>
case of "Percentage Amount Charges". Refer <a href="#charges_template">Charges Template</a> <tr>
for details of the Available Charge Types</td> <td class=fielddesc>A three letter ISO code of currency.</td>
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>chargeTimeType</td> <td>chargeCalculationType</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc>The time at which the charge becomes Due. <td class=fielddesc>Enumeration for indicating whether charge amount is <b>flat</b> or <b>percentage</b> based:<br/>
Refer <a href="#charges_template">Charges Template</a> for additional details<br/> 1=Flat,
1 = Disbursement : Applicable for loans and deducted at the time of loan disbursement.<br/> 2=% of Amount
2 = Specified Due Date : Adhoc charge can be applied for loans and savings on a specified due date.<br/> <p>Used in combination with <i>amount</i> parameter e.g 1 <b>% of Amount</b> or 3.50 <b>Flat</b></p>
3 = Savings Activation : This charge is applicable for savings account and charged at the time of account activation. This charge will be autodeducted once the account is activated with sufficient opening balance.<br/> <p>For loans, % of Amount refers to the loan principal disbursed.</p>
5 = Withdrawal fee : Charge applied to every withdrawal of savings account.<br/> <p>For savings, % of Amount can be used with 'withrawal fees' and refers to the amount withdrawn.</p>
6 = Annual Fee : Recurring charge applied annually for savings on a specified Month and day.<br/> </td>
7 = Monthly Fee : Recurring charge applied on regular interval of months for savings. </tr>
</td> <tr class=alt>
</tr> <td>amount</td>
<tr class=alt> </tr>
<td>chargeAppliesTo</td> <tr>
</tr> <td class=fielddesc>The charge amount. Used in combination with <i>chargeCalculationType</i> parameter. e.g <b>1</b> % of Amount or <b>3.50</b> Flat</td>
<tr> </tr>
<td class=fielddesc>The Entity for which the charge can be applied (Loans, Savings etc). <tr class=alt>
Refer <a href="#charges_template">Charges Template</a> for additional details</td> <td>minCap</td>
</tr> </tr>
<tr class=alt> <tr>
<td>chargeCalculationType</td> <td class=fielddesc><b>Optional</b>: Can be used when a '% of Amount' value is used for <i>chargeCalculationType</i>.
</tr> <p>Used to enforce a minimum charge amount in cases where the calculated amount is less than the minimum amount provided.</p>
<tr> <p>e.g. A 2% fee with a minimum cap of 500:</br>
<td class=fielddesc>Rules for calculating the amount for a particular Charge. In case of a 5000 loan, the 2% is 100, and therefore automatically 500 is used as the value of the charge.
Refer <a href="#charges_template">Charges Template</a> for additional details</td> </p>
</tr> </td>
<tr class=alt> </tr>
<td>feeOnMonthDay</td> <tr class=alt>
</tr> <td>maxCap</td>
<tr> </tr>
<td class=fielddesc>Used along with <i>monthDayFormat</i> to indicate the recurring charge due date starting on a given day of the month and follow specified <i>feeInterval</i>. e.g. fee due date starting on <b>10 May</b> with <i>feeInterval</i> of two (2) months then a recurring fee on 10th of every two months is applied to savings account. <i>monthDayFormat</i> indicates day and month formatting used e.g. "dd MMM" for 10 May <br/> <tr>
This field is mandatory if <i>chargeTimeType</i> is of type <b>Monthly Fee</b> <td class=fielddesc><b>Optional</b>: Can be used when a '% of Amount' value is used for <i>chargeCalculationType</i>.
</td> <p>Used to enforce a maximum charge amount in cases where the calculated amount is greater than the maximum amount provided.</p>
</tr> <p>e.g. A 2% fee, with a maximum of 2000.:</br>
<tr class=alt> In case of a 150000 loan, the 2% adds up to 3000 charge, and therefore the maximum cap of 2000 will be used.
<td>feeInterval</td> </p>
</tr> </td>
<tr> </tr>
<td class=fielddesc>Fee to be applied on a specified interval (number of months).<br/> <tr class=alt>
This field is mandatory if <i>chargeTimeType</i> is of type <b>Monthly Fee</b> <td>chargeTimeType</td>
</td> </tr>
</tr> <tr>
<tr class=alt> <td class=fielddesc>An enumeration indicating the time at which the charge becomes due:<br/>
<td>minCap</td> <p>
</tr> 1 = Disbursement : Applicable for loans and deducted at the time of loan disbursement.<br/>
<tr> 2 = Specified Due Date : Adhoc charge can be applied for loans and savings on a specified due date.<br/>
<td class=fielddesc>minimum cap: a 2% fee with a minimum cap of 500<br/> 3 = Savings Activation : This charge is applicable for savings account and charged at the time of account activation. This charge will be autodeducted once the account is activated with sufficient opening balance.<br/>
In case of a 5000 loan, the 2% is 100, and therefore automatically 500 is used as the value of the charge.<br/> 5 = Withdrawal fee : Charge applied to every withdrawal of savings account.<br/>
This field is optional and can be used along when ChargeCalculationType equals (2) PERCENT_OF_AMOUNT 6 = Annual Fee : Recurring charge applied annually for savings on a specified Month and day. see <i>feeOnMonthDay</i><br/>
</td> 7 = Monthly Fee : Recurring charge applied on regular interval of months for savings. see <i>feeOnMonthDay</i> and <i>feeInterval</i>
</p>
</td>
</tr>
<tr class=alt>
<td>chargePaymentMode</td>
</tr>
<tr>
<td class=fielddesc><b>Applicable to loan charges only:</b> Enumeration for indicating whether charge is to be paid through an <i>Account Transfer</i> from savings or through <i>Regular</i> payment mode.<br/>
0=Regular,
1=Account Transfer
</td>
</tr>
<tr class=alt>
<td>feeOnMonthDay</td>
</tr>
<tr>
<td class=fielddesc>Used along with <i>monthDayFormat</i> to indicate the recurring charge due date starting on a given day of the month and follow specified <i>feeInterval</i>. e.g. fee due date starting on <b>10 May</b> with <i>feeInterval</i> of two (2) months then a recurring fee on 10th of every two months is applied to savings account. <i>monthDayFormat</i> indicates day and month formatting used e.g. "dd MMM" for 10 May <br/>
This field is mandatory if <i>chargeTimeType</i> is of type <b>Monthly Fee</b>
</td>
</tr>
<tr class=alt>
<td>feeInterval</td>
</tr>
<tr>
<td class=fielddesc>Fee to be applied on a specified interval (number of months).<br/>
This field is mandatory if <i>chargeTimeType</i> is of type <b>Monthly Fee</b>
</td>
</tr>
</table>
</div>
</div>
</tr> <a id="charges_template" name="charges_template" class="old-syle-anchor">&nbsp;</a>
<tr class=alt> <div class="method-section">
<td>maxCap</td> <div class="method-description">
</tr> <h4>Retrieve Charge Template</h4>
<tr> <p>This is a convenience resource. It can be useful when
<td class=fielddesc>maximum cap: A 2% fee, with a maximum of 2000. <br/> building maintenance user interface screens for client
In case of a 150000 loan, the 2% adds up to 3000 charge, and therefore the maximum cap of 2000 will be used. <br /> applications. The template data returned consists of any or all
This field is optional and can be used along when ChargeCalculationType equals (2) PERCENT_OF_AMOUNT of:
</td> <ul>
</tr> <li class=normalli>Field Defaults</li>
</table> <li class=normalli>Allowed Value Lists</li>
</div> </ul>
</div> </p>
<p>Example Request:</p>
<a id="charges_template" name="charges_template" <div class=apiClick>charges/template</div>
class="old-syle-anchor">&nbsp;</a> </div>
<div class="method-section"> <div class="method-example">
<div class="method-description"> <code class="method-declaration">
<h4>Retrieve Charges Details 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>charges/template</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/charges/template GET https://DomainName/api/v1/charges/template
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"active": false, "active": false,
"penalty": false, "penalty": false,
"currencyOptions": "currencyOptions": [
[ {
{ "code": "USD",
"code": "USD", "name": "US Dollar",
"name": "US Dollar", "decimalPlaces": 2,
"decimalPlaces": 2, "displaySymbol": "$",
"displaySymbol": "$", "nameCode": "currency.USD",
"nameCode": "currency.USD", "displayLabel": "US Dollar ($)"
"displayLabel": "US Dollar ($)" }
} ],
], "chargeCalculationTypeOptions": [
"chargeCalculationTypeOptions": {
[ "id": 1,
{ "code": "chargeCalculationType.flat",
"id": 1, "value": "Flat"
"code": "chargeCalculationType.flat", },
"value": "Flat" {
}, "id": 2,
{ "code": "chargeCalculationType.percent.of.amount",
"id": 2, "value": "% Amount"
"code": "chargeCalculationType.percent.of.amount", }
"value": "% Amount" ],
} "chargeAppliesToOptions": [
], {
"chargeAppliesToOptions": "id": 1,
[ "code": "chargeAppliesTo.loan",
{ "value": "Loan"
"id": 1, },
"code": "chargeAppliesTo.loan", {
"value": "Loan" "id": 2,
}, "code": "chargeAppliesTo.savings",
{ "value": "Savings"
"id": 2, }
"code": "chargeAppliesTo.savings", ],
"value": "Savings" "chargeTimeTypeOptions": [
} {
], "id": 1,
"chargeTimeTypeOptions": "code": "chargeTimeType.disbursement",
[ "value": "Disbursement"
{ },
"id": 1, {
"code": "chargeTimeType.disbursement", "id": 2,
"value": "Disbursement" "code": "chargeTimeType.specifiedDueDate",
}, "value": "Specified due date"
{ },
"id": 2, {
"code": "chargeTimeType.specifiedDueDate", "id": 3,
"value": "Specified due date" "code": "chargeTimeType.savingsActivation",
}, "value": "Savings Activation"
{ },
"id": 3, {
"code": "chargeTimeType.savingsActivation", "id": 5,
"value": "Savings Activation" "code": "chargeTimeType.withdrawalFee",
}, "value": "Withdrawal Fee"
{ },
"id": 5, {
"code": "chargeTimeType.withdrawalFee", "id": 6,
"value": "Withdrawal Fee" "code": "chargeTimeType.annualFee",
}, "value": "Annual Fee"
{ },
"id": 6, {
"code": "chargeTimeType.annualFee", "id": 7,
"value": "Annual Fee" "code": "chargeTimeType.monthlyFee",
}, "value": "Monthly Fee"
{ }
"id": 7, ],
"code": "chargeTimeType.monthlyFee", "chargePaymetModeOptions": [
"value": "Monthly Fee" {
} "id": 0,
], "code": "chargepaymentmode.regular",
"chargePaymetModeOptions": "value": "Regular"
[ },
{ {
"id":0, "id": 1,
"code":"chargepaymentmode.regular", "code": "chargepaymentmode.accounttransfer",
"value":"chargepaymentmode.regular" "value": "Account transfer"
}, }
{ ],
"id":1, "loanChargeCalculationTypeOptions": [
"code":"chargepaymentmode.accounttransfer", {
"value":"chargepaymentmode.accounttransfer" "id": 1,
} "code": "chargeCalculationType.flat",
], "value": "Flat"
"loanChargeCalculationTypeOptions" : [ { "code" : "chargeCalculationType.flat", },
"id" : 1, {
"value" : "Flat" "id": 2,
}, "code": "chargeCalculationType.percent.of.amount",
{ "code" : "chargeCalculationType.percent.of.amount", "value": "% Amount"
"id" : 2, }
"value" : "% Amount" ],
} "loanChargeTimeTypeOptions": [
], {
"loanChargeTimeTypeOptions" : [ { "code" : "chargeTimeType.disbursement", "id": 1,
"id" : 1, "code": "chargeTimeType.disbursement",
"value" : "Disbursement" "value": "Disbursement"
}, },
{ "code" : "chargeTimeType.specifiedDueDate", {
"id" : 2, "id": 2,
"value" : "Specified due date" "code": "chargeTimeType.specifiedDueDate",
} "value": "Specified due date"
], }
"savingsChargeCalculationTypeOptions" : [ { "code" : "chargeCalculationType.flat", ],
"id" : 1, "savingsChargeCalculationTypeOptions": [
"value" : "Flat" {
}, "id": 1,
{ "code" : "chargeCalculationType.percent.of.amount", "code": "chargeCalculationType.flat",
"id" : 2, "value": "Flat"
"value" : "% Amount" },
} {
], "id": 2,
"savingsChargeTimeTypeOptions" : [ { "code" : "chargeTimeType.specifiedDueDate", "code": "chargeCalculationType.percent.of.amount",
"id" : 2, "value": "% Amount"
"value" : "Specified due date" }
}, ],
{ "code" : "chargeTimeType.savingsActivation", "savingsChargeTimeTypeOptions": [
"id" : 3, {
"value" : "Savings Activation" "id": 2,
}, "code": "chargeTimeType.specifiedDueDate",
{ "code" : "chargeTimeType.withdrawalFee", "value": "Specified due date"
"id" : 5, },
"value" : "Withdrawal Fee" {
}, "id": 3,
{ "code" : "chargeTimeType.annualFee", "code": "chargeTimeType.savingsActivation",
"id" : 6, "value": "Savings Activation"
"value" : "Annual Fee" },
}, {
{ "code" : "chargeTimeType.monthlyFee", "id": 5,
"id" : 7, "code": "chargeTimeType.withdrawalFee",
"value" : "Monthly Fee" "value": "Withdrawal Fee"
} },
] {
"id": 6,
"code": "chargeTimeType.annualFee",
"value": "Annual Fee"
},
{
"id": 7,
"code": "chargeTimeType.monthlyFee",
"value": "Monthly Fee"
}
]
} }
</code> </code>
</div> </div>
</div> </div>
<a id="charges_create" name="charges_create" class="old-syle-anchor">&nbsp;</a> <a id="charges_create" name="charges_create" class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h4>Create/Define a Charge</h4> <h4>Create/Define a Charge</h4>
<p>Define a new charge specific to loan or saving that can later be associated with loan <p>Define a new charge that can later be associated with loans and savings through their respective product definitions or directly on each account instance.</p>
products, loans Savings product or Savings account.</p> </div>
</div> <div class="method-example">
<div class="method-example"> <code class="method-declaration">
<code class="method-declaration">
POST https://DomainName/api/v1/charges POST https://DomainName/api/v1/charges
</code> </code>
<code class="method-request"> <code class="method-request">
POST charges POST charges
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"name": "Loan service fee", "name": "Loan service fee",
"chargeAppliesTo": 1, "chargeAppliesTo": 1,
"currencyCode": "USD", "currencyCode": "USD",
"locale": "en", "locale": "en",
"amount": "230.56", "amount": "230.56",
"chargeTimeType": "1", "chargeTimeType": "1",
"chargeCalculationType": "1", "chargeCalculationType": "1",
"chargePaymentMode": "1", "chargePaymentMode": "1",
"active": true "active": true
} }
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"resourceId": 1 "resourceId": 1
} }
</code> </code>
<code class="method-request"> <code class="method-request">
POST charges POST charges
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"locale": "en", "locale": "en",
"name": "Default Penalty", "name": "Default Penalty",
"amount": "2", "amount": "2",
"currencyCode": "USD", "currencyCode": "USD",
"chargeAppliesTo": "1", "chargeAppliesTo": "1",
"chargeTimeType": "2", "chargeTimeType": "2",
"chargeCalculationType": "2", "chargeCalculationType": "2",
"chargePaymentMode": "1", "chargePaymentMode": "1",
"active": "true", "active": "true",
"penalty": "true", "penalty": "true",
"minCap": 50.00, "minCap": 50.00,
"maxCap": 100.00 "maxCap": 100.00
} }
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"resourceId": 4 "resourceId": 4
} }
</code> </code>
<code class="method-request"> <code class="method-request">
POST charges POST charges
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"locale": "en", "locale": "en",
"name": "Annuaul Fee", "name": "Annuaul Fee",
"amount": "20", "amount": "20",
"currencyCode": "USD", "currencyCode": "USD",
"chargeAppliesTo": "2", "chargeAppliesTo": "2",
"chargeTimeType": "6", "chargeTimeType": "6",
"chargeCalculationType": "1", "chargeCalculationType": "1",
"chargePaymentMode": "1", "chargePaymentMode": "1",
"active": "true", "active": "true",
"penalty": "false" "penalty": "false"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"resourceId": 5 "resourceId": 5
} }
</code> </code>
<code class="method-request"> <code class="method-request">
POST charges POST charges
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"locale": "en", "locale": "en",
"name": "Quarterly Fee", "name": "Quarterly Fee",
"amount": "5", "amount": "5",
"currencyCode": "USD", "currencyCode": "USD",
"chargeAppliesTo": "2", "chargeAppliesTo": "2",
"chargeTimeType": "7", "chargeTimeType": "7",
"feeOnMonthDay": "10 May", "feeOnMonthDay": "10 May",
"monthDayFormat": "dd MMM", "monthDayFormat": "dd MMM",
"feeInterval": "4", "feeInterval": "4",
"chargeCalculationType": "1", "chargeCalculationType": "1",
"active": "true", "active": "true",
"penalty": "false" "penalty": "false"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"resourceId": 6 "resourceId": 6
} }
</code> </code>
</div> </div>
</div> </div>
<a id="charges_list" name="charges_list" class="old-syle-anchor">&nbsp;</a> <a id="charges_list" name="charges_list" class="old-syle-anchor">&nbsp;</a>
<div class="method-section"> <div class="method-section">
@@ -5,10 +5,12 @@
*/ */
package org.mifosplatform.portfolio.charge.domain; package org.mifosplatform.portfolio.charge.domain;
public enum ChargeAppliesTo { public enum ChargeAppliesTo {
INVALID(0, "chargeAppliesTo.invalid"), // INVALID(0, "chargeAppliesTo.invalid"), //
LOAN(1, "chargeAppliesTo.loan"), SAVINGS(2, "chargeAppliesTo.savings"); LOAN(1, "chargeAppliesTo.loan"), //
SAVINGS(2, "chargeAppliesTo.savings");
private final Integer value; private final Integer value;
private final String code; private final String code;
@@ -26,27 +28,23 @@ public enum ChargeAppliesTo {
return this.code; return this.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) { public static ChargeAppliesTo fromInt(final Integer chargeAppliesTo) {
ChargeAppliesTo chargeAppliesToType = ChargeAppliesTo.INVALID; ChargeAppliesTo chargeAppliesToType = ChargeAppliesTo.INVALID;
switch (chargeAppliesTo) {
case 1: if (chargeAppliesTo != null) {
chargeAppliesToType = LOAN; switch (chargeAppliesTo) {
break; case 1:
case 2: chargeAppliesToType = LOAN;
chargeAppliesToType = SAVINGS; break;
break; case 2:
default: chargeAppliesToType = SAVINGS;
chargeAppliesToType = INVALID; break;
break; default:
chargeAppliesToType = INVALID;
break;
}
} }
return chargeAppliesToType; return chargeAppliesToType;
} }
@@ -57,4 +55,8 @@ public enum ChargeAppliesTo {
public boolean isSavingsCharge() { public boolean isSavingsCharge() {
return this.value.equals(ChargeAppliesTo.SAVINGS.getValue()); return this.value.equals(ChargeAppliesTo.SAVINGS.getValue());
} }
public static Object[] validValues() {
return new Object[] {ChargeAppliesTo.LOAN.getValue(), ChargeAppliesTo.SAVINGS.getValue()};
}
} }
@@ -29,6 +29,10 @@ public enum ChargeCalculationType {
return this.code; return this.code;
} }
public static Object[] validValues() {
return new Integer[] { ChargeCalculationType.FLAT.getValue(), ChargeCalculationType.PERCENT_OF_AMOUNT.getValue() };
}
public static ChargeCalculationType fromInt(final Integer chargeCalculation) { public static ChargeCalculationType fromInt(final Integer chargeCalculation) {
ChargeCalculationType chargeCalculationType = ChargeCalculationType.INVALID; ChargeCalculationType chargeCalculationType = ChargeCalculationType.INVALID;
switch (chargeCalculation) { switch (chargeCalculation) {
@@ -21,6 +21,10 @@ public enum ChargePaymentMode {
return this.code; return this.code;
} }
public static Object[] validValues() {
return new Integer[] { ChargePaymentMode.REGULAR.getValue(), ChargePaymentMode.ACCOUNT_TRANSFER.getValue() };
}
public static ChargePaymentMode fromInt(final Integer paymentMode) { public static ChargePaymentMode fromInt(final Integer paymentMode) {
ChargePaymentMode chargeAppliesToType = ChargePaymentMode.REGULAR; ChargePaymentMode chargeAppliesToType = ChargePaymentMode.REGULAR;
switch (paymentMode) { switch (paymentMode) {
@@ -8,9 +8,11 @@ package org.mifosplatform.portfolio.charge.domain;
public enum ChargeTimeType { public enum ChargeTimeType {
INVALID(0, "chargeTimeType.invalid"), // INVALID(0, "chargeTimeType.invalid"), //
DISBURSEMENT(1, "chargeTimeType.disbursement"), //only for loan charges DISBURSEMENT(1, "chargeTimeType.disbursement"), // only for loan charges
SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate"), // for loan and savings charges SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate"), // for loan and
SAVINGS_ACTIVATION(3, "chargeTimeType.savingsActivation"), // only for savings // savings charges
SAVINGS_ACTIVATION(3, "chargeTimeType.savingsActivation"), // only for
// savings
SAVINGS_CLOSURE(4, "chargeTimeType.savingsClosure"), // only for savings SAVINGS_CLOSURE(4, "chargeTimeType.savingsClosure"), // only for savings
WITHDRAWAL_FEE(5, "chargeTimeType.withdrawalFee"), // only for savings WITHDRAWAL_FEE(5, "chargeTimeType.withdrawalFee"), // only for savings
ANNUAL_FEE(6, "chargeTimeType.annualFee"), // only for savings ANNUAL_FEE(6, "chargeTimeType.annualFee"), // only for savings
@@ -32,45 +34,45 @@ public enum ChargeTimeType {
return this.code; return this.code;
} }
public static int minValue() { public static Object[] validLoanValues() {
return 1; return new Integer[] { ChargeTimeType.DISBURSEMENT.getValue(), ChargeTimeType.SPECIFIED_DUE_DATE.getValue() };
} }
public static int maxValue() { public static Object[] validSavingsValues() {
int max = 1;// Default it to one (1) return new Integer[] { ChargeTimeType.SPECIFIED_DUE_DATE.getValue(), ChargeTimeType.SAVINGS_ACTIVATION.getValue(),
for (ChargeTimeType chargeTimeType : ChargeTimeType.values()) { ChargeTimeType.SAVINGS_CLOSURE.getValue(), ChargeTimeType.WITHDRAWAL_FEE.getValue(), ChargeTimeType.ANNUAL_FEE.getValue(),
max = (chargeTimeType.getValue() > max) ? chargeTimeType.getValue() : max; ChargeTimeType.MONTHLY_FEE.getValue() };
}
return max;
} }
public static ChargeTimeType fromInt(final Integer chargeTime) { public static ChargeTimeType fromInt(final Integer chargeTime) {
ChargeTimeType chargeTimeType = ChargeTimeType.INVALID; ChargeTimeType chargeTimeType = ChargeTimeType.INVALID;
switch (chargeTime) { if (chargeTime != null) {
case 1: switch (chargeTime) {
chargeTimeType = DISBURSEMENT; case 1:
break; chargeTimeType = DISBURSEMENT;
case 2: break;
chargeTimeType = SPECIFIED_DUE_DATE; case 2:
break; chargeTimeType = SPECIFIED_DUE_DATE;
case 3: break;
chargeTimeType = SAVINGS_ACTIVATION; case 3:
break; chargeTimeType = SAVINGS_ACTIVATION;
case 4: break;
chargeTimeType = SAVINGS_CLOSURE; case 4:
break; chargeTimeType = SAVINGS_CLOSURE;
case 5: break;
chargeTimeType = WITHDRAWAL_FEE; case 5:
break; chargeTimeType = WITHDRAWAL_FEE;
case 6: break;
chargeTimeType = ANNUAL_FEE; case 6:
break; chargeTimeType = ANNUAL_FEE;
case 7: break;
chargeTimeType = MONTHLY_FEE; case 7:
break; chargeTimeType = MONTHLY_FEE;
default: break;
chargeTimeType = INVALID; default:
break; chargeTimeType = INVALID;
break;
}
} }
return chargeTimeType; return chargeTimeType;
} }
@@ -90,11 +92,11 @@ public enum ChargeTimeType {
public boolean isSavingsClosure() { public boolean isSavingsClosure() {
return this.value.equals(ChargeTimeType.SAVINGS_CLOSURE.getValue()); return this.value.equals(ChargeTimeType.SAVINGS_CLOSURE.getValue());
} }
public boolean isWithdrawalFee() { public boolean isWithdrawalFee() {
return this.value.equals(ChargeTimeType.WITHDRAWAL_FEE.getValue()); return this.value.equals(ChargeTimeType.WITHDRAWAL_FEE.getValue());
} }
public boolean isAnnualFee() { public boolean isAnnualFee() {
return this.value.equals(ChargeTimeType.ANNUAL_FEE.getValue()); return this.value.equals(ChargeTimeType.ANNUAL_FEE.getValue());
} }
@@ -102,7 +104,7 @@ public enum ChargeTimeType {
public boolean isMonthlyFee() { public boolean isMonthlyFee() {
return this.value.equals(ChargeTimeType.MONTHLY_FEE.getValue()); return this.value.equals(ChargeTimeType.MONTHLY_FEE.getValue());
} }
public boolean isAllowedLoanChargeTime() { public boolean isAllowedLoanChargeTime() {
return isTimeOfDisbursement() || isOnSpecifiedDueDate(); return isTimeOfDisbursement() || isOnSpecifiedDueDate();
} }
@@ -23,6 +23,8 @@ import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.charge.domain.ChargeAppliesTo; import org.mifosplatform.portfolio.charge.domain.ChargeAppliesTo;
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -38,7 +40,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
*/ */
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "amount", "locale", "currencyCode", private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "amount", "locale", "currencyCode",
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty", "currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty",
"active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat","minCap","maxCap")); "active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat", "minCap", "maxCap"));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -58,6 +60,50 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
final JsonElement element = this.fromApiJsonHelper.parse(json); final JsonElement element = this.fromApiJsonHelper.parse(json);
final Integer chargeAppliesTo = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeAppliesTo", element);
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull();
if (chargeAppliesTo != null) {
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).isOneOfTheseValues(ChargeAppliesTo.validValues());
}
final ChargeAppliesTo appliesTo = ChargeAppliesTo.fromInt(chargeAppliesTo);
if (appliesTo.isLoanCharge()) {
// loan applicable validation
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeTimeType", element);
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull();
if (chargeTimeType != null) {
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).isOneOfTheseValues(ChargeTimeType.validLoanValues());
}
final Integer chargePaymentMode = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargePaymentMode", element);
baseDataValidator.reset().parameter("chargePaymentMode").value(chargePaymentMode).notNull().isOneOfTheseValues(ChargePaymentMode.validValues());
if (chargePaymentMode != null) {
baseDataValidator.reset().parameter("chargePaymentMode").value(chargePaymentMode).isOneOfTheseValues(ChargePaymentMode.validValues());
}
} else if (appliesTo.isSavingsCharge()) {
// savings applicable validation
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeTimeType", element);
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull();
if (chargeTimeType != null) {
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).isOneOfTheseValues(ChargeTimeType.validSavingsValues());
}
final ChargeTimeType ctt = ChargeTimeType.fromInt(chargeTimeType);
if (ctt.isMonthlyFee()) {
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element);
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull();
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12);
}
if (ctt.isAnnualFee()) {
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element);
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull();
}
}
final String name = this.fromApiJsonHelper.extractStringNamed("name", element); final String name = this.fromApiJsonHelper.extractStringNamed("name", element);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100); baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100);
@@ -67,24 +113,6 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element.getAsJsonObject()); final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element.getAsJsonObject());
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount(); baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
final Integer chargeAppliesTo = this.fromApiJsonHelper.extractIntegerNamed("chargeAppliesTo", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull()
.inMinMaxRange(ChargeAppliesTo.minValue(), ChargeAppliesTo.maxValue());
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerNamed("chargeTimeType", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull()
.inMinMaxRange(ChargeTimeType.minValue(), ChargeTimeType.maxValue());
final Integer chargeCalculationType = this.fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 4);
ChargeAppliesTo appliesTo = ChargeAppliesTo.fromInt(chargeAppliesTo);
if (appliesTo.isLoanCharge()) {
final Integer chargePaymentMode = this.fromApiJsonHelper.extractIntegerNamed("chargePaymentMode", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargePaymentMode").value(chargePaymentMode).notNull().inMinMaxRange(0, 1);
}
if (this.fromApiJsonHelper.parameterExists("penalty", element)) { if (this.fromApiJsonHelper.parameterExists("penalty", element)) {
final Boolean penalty = this.fromApiJsonHelper.extractBooleanNamed("penalty", element); final Boolean penalty = this.fromApiJsonHelper.extractBooleanNamed("penalty", element);
baseDataValidator.reset().parameter("penalty").value(penalty).notNull(); baseDataValidator.reset().parameter("penalty").value(penalty).notNull();
@@ -94,26 +122,18 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
final Boolean active = this.fromApiJsonHelper.extractBooleanNamed("active", element); final Boolean active = this.fromApiJsonHelper.extractBooleanNamed("active", element);
baseDataValidator.reset().parameter("active").value(active).notNull(); baseDataValidator.reset().parameter("active").value(active).notNull();
} }
final ChargeTimeType ctt = ChargeTimeType.fromInt(chargeTimeType); final Integer chargeCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeCalculationType", element);
if(ctt.isMonthlyFee()){ baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull();
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element); if (chargeCalculationType != null) {
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull(); baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).isOneOfTheseValues(ChargeCalculationType.validValues());
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12);
}
if(ctt.isAnnualFee()){
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element);
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull();
} }
if(this.fromApiJsonHelper.parameterExists("minCap",element)){ if (this.fromApiJsonHelper.parameterExists("minCap", element)) {
final BigDecimal minCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("minCap", element.getAsJsonObject()); final BigDecimal minCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("minCap", element.getAsJsonObject());
baseDataValidator.reset().parameter("minCap").value(minCap).notNull().positiveAmount(); baseDataValidator.reset().parameter("minCap").value(minCap).notNull().positiveAmount();
} }
if(this.fromApiJsonHelper.parameterExists("maxCap",element)){ if (this.fromApiJsonHelper.parameterExists("maxCap", element)) {
final BigDecimal maxCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("maxCap", element.getAsJsonObject()); final BigDecimal maxCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("maxCap", element.getAsJsonObject());
baseDataValidator.reset().parameter("maxCap").value(maxCap).notNull().positiveAmount(); baseDataValidator.reset().parameter("maxCap").value(maxCap).notNull().positiveAmount();
} }
@@ -131,6 +151,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge"); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
final JsonElement element = this.fromApiJsonHelper.parse(json); final JsonElement element = this.fromApiJsonHelper.parse(json);
if (this.fromApiJsonHelper.parameterExists("name", element)) { if (this.fromApiJsonHelper.parameterExists("name", element)) {
final String name = this.fromApiJsonHelper.extractStringNamed("name", element); final String name = this.fromApiJsonHelper.extractStringNamed("name", element);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100); baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100);
@@ -146,33 +167,34 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount(); baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
} }
if(this.fromApiJsonHelper.parameterExists("minCap",element)){ if (this.fromApiJsonHelper.parameterExists("minCap", element)) {
final BigDecimal minCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("minCap", element.getAsJsonObject()); final BigDecimal minCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("minCap", element.getAsJsonObject());
baseDataValidator.reset().parameter("minCap").value(minCap).notNull().positiveAmount(); baseDataValidator.reset().parameter("minCap").value(minCap).notNull().positiveAmount();
} }
if(this.fromApiJsonHelper.parameterExists("maxCap",element)){ if (this.fromApiJsonHelper.parameterExists("maxCap", element)) {
final BigDecimal maxCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("maxCap", element.getAsJsonObject()); final BigDecimal maxCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("maxCap", element.getAsJsonObject());
baseDataValidator.reset().parameter("maxCap").value(maxCap).notNull().positiveAmount(); baseDataValidator.reset().parameter("maxCap").value(maxCap).notNull().positiveAmount();
} }
if (this.fromApiJsonHelper.parameterExists("chargeAppliesTo", element)) { if (this.fromApiJsonHelper.parameterExists("chargeAppliesTo", element)) {
final Integer chargeAppliesTo = this.fromApiJsonHelper.extractIntegerNamed("chargeAppliesTo", element, Locale.getDefault()); final Integer chargeAppliesTo = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeAppliesTo", element);
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull() baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull()
.inMinMaxRange(ChargeAppliesTo.minValue(), ChargeAppliesTo.maxValue()); .isOneOfTheseValues(ChargeAppliesTo.validValues());
} }
if (this.fromApiJsonHelper.parameterExists("chargeTimeType", element)) { if (this.fromApiJsonHelper.parameterExists("chargeTimeType", element)) {
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerNamed("chargeTimeType", element, Locale.getDefault());
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeTimeType", element);
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull() baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull()
.inMinMaxRange(ChargeTimeType.minValue(), ChargeTimeType.maxValue()); .isOneOfTheseValues(ChargeTimeType.validLoanValues(), ChargeTimeType.validSavingsValues());
} }
if (this.fromApiJsonHelper.parameterExists("feeOnMonthDay", element)) { if (this.fromApiJsonHelper.parameterExists("feeOnMonthDay", element)) {
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element); final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element);
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull(); baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull();
} }
if (this.fromApiJsonHelper.parameterExists("feeInterval", element)) { if (this.fromApiJsonHelper.parameterExists("feeInterval", element)) {
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault()); final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12); baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12);
@@ -198,11 +220,11 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
final Boolean active = this.fromApiJsonHelper.extractBooleanNamed("active", element); final Boolean active = this.fromApiJsonHelper.extractBooleanNamed("active", element);
baseDataValidator.reset().parameter("active").value(active).notNull(); baseDataValidator.reset().parameter("active").value(active).notNull();
} }
if(this.fromApiJsonHelper.parameterExists("minCap",element)){ if (this.fromApiJsonHelper.parameterExists("minCap", element)) {
final BigDecimal minCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("minCap", element.getAsJsonObject()); final BigDecimal minCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("minCap", element.getAsJsonObject());
baseDataValidator.reset().parameter("minCap").value(minCap).notNull().positiveAmount(); baseDataValidator.reset().parameter("minCap").value(minCap).notNull().positiveAmount();
} }
if(this.fromApiJsonHelper.parameterExists("maxCap",element)){ if (this.fromApiJsonHelper.parameterExists("maxCap", element)) {
final BigDecimal maxCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("maxCap", element.getAsJsonObject()); final BigDecimal maxCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("maxCap", element.getAsJsonObject());
baseDataValidator.reset().parameter("maxCap").value(maxCap).notNull().positiveAmount(); baseDataValidator.reset().parameter("maxCap").value(maxCap).notNull().positiveAmount();
} }
@@ -211,7 +233,6 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
} }
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
"Validation errors exist.", dataValidationErrors); }
} }
} }