MIFOSX-232: WIP - split out terms and settings related fields on loan product along with charges and accounting related inputs.

This commit is contained in:
Keith Woodlock
2013-03-07 13:35:52 +00:00
parent 619ec9e8cc
commit 76d2dd6209
15 changed files with 773 additions and 619 deletions
+395 -396
View File
@@ -5466,7 +5466,7 @@ Request Body:
<td class=fielddesc>Used like: numberOfRepayments Every <td class=fielddesc>Used like: numberOfRepayments Every
repaymentEvery repaymentFrequencyType<br> e.g. 10 repaymentEvery repaymentFrequencyType<br> e.g. 10
(repayments) Every 12 <b>Weeks</b> <br> (repayments) Every 12 <b>Weeks</b> <br>
<span>Example Values:</span> Days, Weeks, Months <span>Example Values:</span> 0=Days, 1=Weeks, 2=Months
</td> </td>
</tr> </tr>
<tr class=alt> <tr class=alt>
@@ -5476,15 +5476,15 @@ Request Body:
<td class=fielddesc>Used like: interestRatePerPeriod% <td class=fielddesc>Used like: interestRatePerPeriod%
interestRateFrequencyType - interestType<br> e.g. 12.0000% interestRateFrequencyType - interestType<br> e.g. 12.0000%
<b>Per year</b> - Declining Balance <br> <b>Per year</b> - Declining Balance <br>
<span>Example Values:</span> Per month, Per year <span>Example Values:</span> 2=Per month, 3=Per year
</td> </td>
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>amortizationType</td> <td>amortizationType</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc><span>Example Values:</span> Equal <td class=fielddesc><span>Example Values:</span> 0=Equal
principle payments, Equal installments</td> principle payments, 1=Equal installments</td>
</tr> </tr>
</td> </td>
</tr> </tr>
@@ -5495,7 +5495,7 @@ Request Body:
<td class=fielddesc>Used like: interestRatePerPeriod% <td class=fielddesc>Used like: interestRatePerPeriod%
interestRateFrequencyType - interestType<br> e.g. 12.0000% interestRateFrequencyType - interestType<br> e.g. 12.0000%
Per year - <b>Declining Balance</b> <br> Per year - <b>Declining Balance</b> <br>
<span>Example Values:</span> Declining Balance, Flat <span>Example Values:</span> 0=Declining Balance, 1=Flat
</td> </td>
</tr> </tr>
</td> </td>
@@ -5504,8 +5504,8 @@ Request Body:
<td>interestCalculationPeriodType</td> <td>interestCalculationPeriodType</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc><span>Example Values:</span> Same as <td class=fielddesc><span>Example Values:</span> 0=Daily,
repayment period, Daily</td> 1=Same as repayment period</td>
</tr> </tr>
</td> </td>
</tr> </tr>
@@ -5514,8 +5514,8 @@ Request Body:
<td>accountingRule</td> <td>accountingRule</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc><span>Example Values:</span>NONE(1), <td class=fielddesc><span>Example Values:</span>1=NONE,
CASH_BASED(2), ACCRUAL_BASED(3)</td> 2=CASH_BASED, 3=ACCRUAL_BASED</td>
</tr> </tr>
</td> </td>
</tr> </tr>
@@ -5541,61 +5541,55 @@ GET https://DomainName/api/v1/loanproducts
</code> </code>
<code class="method-response"> <code class="method-response">
[ [
{ {
"id": 1, "id": 1,
"name": "A new loan product for me", "name": "LP with Charges but no accounting)",
"currency": { "currency": {
"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 ($)"
}, },
"principal": 100000, "principal": 100000,
"numberOfRepayments": 12, "numberOfRepayments": 12,
"loanTermFrequency": 12, "repaymentEvery": 1,
"repaymentEvery": 1, "repaymentFrequencyType": {
"loanTermFrequencyType": { "id": 2,
"id": 2, "code": "repaymentFrequency.periodFrequencyType.months",
"code": "loanTermFrequency.periodFrequencyType.months", "value": "Months"
"value": "Months" },
}, "interestRatePerPeriod": 1.75,
"repaymentFrequencyType": { "interestRateFrequencyType": {
"id": 2, "id": 2,
"code": "repaymentFrequency.periodFrequencyType.months", "code": "interestRateFrequency.periodFrequencyType.months",
"value": "Months" "value": "Per month"
}, },
"transactionProcessingStrategyId": 1, "annualInterestRate": 21,
"transactionProcessingStrategyName": "Mifos style", "amortizationType": {
"interestRatePerPeriod": 1.75, "id": 1,
"interestRateFrequencyType": { "code": "amortizationType.equal.installments",
"id": 2, "value": "Equal installments"
"code": "interestRateFrequency.periodFrequencyType.months", },
"value": "Per month" "interestType": {
}, "id": 0,
"annualInterestRate": 21, "code": "interestType.declining.balance",
"amortizationType": { "value": "Declining Balance"
"id": 1, },
"code": "amortizationType.equal.installments", "interestCalculationPeriodType": {
"value": "Equal installments" "id": 1,
}, "code": "interestCalculationPeriodType.same.as.repayment.period",
"interestType": { "value": "Same as repayment period"
"id": 0, },
"code": "interestType.declining.balance", "transactionProcessingStrategyId": 1,
"value": "Declining Balance" "transactionProcessingStrategyName": "Mifos style",
}, "accountingRule": {
"interestCalculationPeriodType": { "id": 1,
"id": 1, "code": "accountingRuleType.none",
"code": "interestCalculationPeriodType.same.as.repayment.period", "value": "NONE"
"value": "Same as repayment period"
},
"accountingRule": {
"id": 1,
"code": "accountingRuleType.none",
"value": "NONE"
}
} }
}
] ]
</code> </code>
</div> </div>
@@ -5625,17 +5619,12 @@ GET https://DomainName/api/v1/loanproducts/template
<code class="method-response"> <code class="method-response">
{ {
"currency": { "currency": {
"code": "USD", "code": "UGX",
"name": "US Dollar", "name": "Uganda Shilling",
"decimalPlaces": 2, "decimalPlaces": 2,
"displaySymbol": "$", "displaySymbol": "USh",
"nameCode": "currency.USD", "nameCode": "currency.UGX",
"displayLabel": "US Dollar ($)" "displayLabel": "Uganda Shilling (USh)"
},
"loanTermFrequencyType": {
"id": 2,
"code": "loanTermFrequency.periodFrequencyType.months",
"value": "Months"
}, },
"repaymentFrequencyType": { "repaymentFrequencyType": {
"id": 2, "id": 2,
@@ -5662,43 +5651,51 @@ GET https://DomainName/api/v1/loanproducts/template
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"accountingRule": 1, "transactionProcessingStrategyId": 2,
"transactionProcessingStrategyName": "Heavensfamily",
"accountingRule": {
"id": 1,
"code": "accountingRuleType.none",
"value": "NONE"
},
"accountingMappings": {},
"currencyOptions": [ "currencyOptions": [
{ {
"code": "USD", "code": "UGX",
"name": "US Dollar", "name": "Uganda Shilling",
"decimalPlaces": 2, "decimalPlaces": 2,
"displaySymbol": "$", "displaySymbol": "USh",
"nameCode": "currency.USD", "nameCode": "currency.UGX",
"displayLabel": "US Dollar ($)" "displayLabel": "Uganda Shilling (USh)"
} }
], ],
"fundOptions": [ "repaymentFrequencyTypeOptions": [
{
"id": 0,
"code": "repaymentFrequency.periodFrequencyType.days",
"value": "Days"
},
{ {
"id": 1, "id": 1,
"name": "EU Agri Fund" "code": "repaymentFrequency.periodFrequencyType.weeks",
} "value": "Weeks"
],
"transactionProcessingStrategyOptions": [
{
"id": 1,
"code": "mifos-standard-strategy",
"name": "Mifos style"
}, },
{ {
"id": 2, "id": 2,
"code": "heavensfamily-strategy", "code": "repaymentFrequency.periodFrequencyType.months",
"name": "Heavensfamily" "value": "Months"
}
],
"interestRateFrequencyTypeOptions": [
{
"id": 2,
"code": "interestRateFrequency.periodFrequencyType.months",
"value": "Per month"
}, },
{ {
"id": 3, "id": 3,
"code": "creocore-strategy", "code": "interestRateFrequency.periodFrequencyType.years",
"name": "Creocore" "value": "Per year"
},
{
"id": 4,
"code": "rbi-india-strategy",
"name": "RBI (India)"
} }
], ],
"amortizationTypeOptions": [ "amortizationTypeOptions": [
@@ -5737,58 +5734,274 @@ GET https://DomainName/api/v1/loanproducts/template
"value": "Same as repayment period" "value": "Same as repayment period"
} }
], ],
"repaymentFrequencyTypeOptions": [ "transactionProcessingStrategyOptions": [
{
"id": 0,
"code": "repaymentFrequency.periodFrequencyType.days",
"value": "Days"
},
{
"id": 1,
"code": "repaymentFrequency.periodFrequencyType.weeks",
"value": "Weeks"
},
{ {
"id": 2, "id": 2,
"code": "repaymentFrequency.periodFrequencyType.months", "code": "heavensfamily-strategy",
"value": "Months" "name": "Heavensfamily"
}
],
"interestRateFrequencyTypeOptions": [
{
"id": 2,
"code": "interestRateFrequency.periodFrequencyType.months",
"value": "Per month"
},
{
"id": 3,
"code": "interestRateFrequency.periodFrequencyType.years",
"value": "Per year"
}
],
"loanTermFrequencyTypeOptions": [
{
"id": 0,
"code": "loanTermFrequency.periodFrequencyType.days",
"value": "Days"
},
{
"id": 1,
"code": "loanTermFrequency.periodFrequencyType.weeks",
"value": "Weeks"
},
{
"id": 2,
"code": "loanTermFrequency.periodFrequencyType.months",
"value": "Months"
},
{
"id": 3,
"code": "loanTermFrequency.periodFrequencyType.years",
"value": "Years"
} }
], ],
"chargeOptions": [ "chargeOptions": [
{
"id": 1,
"name": "Bank Fee (per installment)",
"active": true,
"penalty": false,
"currency": {
"code": "UGX",
"name": "Uganda Shilling",
"decimalPlaces": 2,
"displaySymbol": "USh",
"nameCode": "currency.UGX",
"displayLabel": "Uganda Shilling (USh)"
},
"amount": 1500,
"chargeTimeType": {
"id": 2,
"code": "chargeTimeType.specifiedDueDate",
"value": "Specified due date"
},
"chargeAppliesTo": {
"id": 1,
"code": "chargeAppliesTo.loan",
"value": "Loan"
},
"chargeCalculationType": {
"id": 1,
"code": "chargeCalculationType.flat",
"value": "Flat"
}
}
],
"accountingRuleOptions": [
{
"id": 1,
"code": "accountingRuleType.none",
"value": "NONE"
},
{
"id": 2,
"code": "accountingRuleType.cash",
"value": "CASH BASED"
},
{
"id": 3,
"code": "accountingRuleType.accrual",
"value": "ACCRUAL BASED"
}
],
"accountingMappingOptions": {
"assetAccountOptions": [
{
"id": 2,
"name": "Cash in Valut 1",
"glCode": "11101",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
}
},
{
"id": 4,
"name": "Centenary Opening Account",
"glCode": "11201",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
}
}
],
"expenseAccountOptions": [
{
"id": 41,
"name": "Water Charges",
"glCode": "42102",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 5,
"code": "accountType.expense",
"value": "EXPENSE"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
}
},
{
"id": 42,
"name": "Electricity Charges",
"glCode": "42103",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 5,
"code": "accountType.expense",
"value": "EXPENSE"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
}
},
{
"id": 97,
"name": "Miscellaneous",
"glCode": "42905",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 5,
"code": "accountType.expense",
"value": "EXPENSE"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
}
}
],
"incomeAccountOptions": [
{
"id": 34,
"name": "Interest on Loans",
"glCode": "31101",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
}
},
{
"id": 35,
"name": "Late Payment Interest",
"glCode": "31102",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
}
},
{
"id": 37,
"name": "Collected Bank Fees Receivable",
"glCode": "6201",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
}
}
]
}
}
</code>
</div>
</div>
<a id="loanproducts_retrieve" name="loanproducts_retrieve"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve a Loan Product</h4>
<p>Example Requests:</p>
<div class=apiClick>loanproducts/1</div>
<br>
<br>
<div class=apiClick>loanproducts/1?template=true</div>
<br>
<br>
<div class=apiClick>loanproducts/1?fields=name,description,numberOfRepayments</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/loanproducts/{productId}
</code>
<code class="method-response">
{
"id": 1,
"name": "LP with Charges but no accounting)",
"currency": {
"code": "USD",
"name": "US Dollar",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"principal": 100000,
"numberOfRepayments": 12,
"repaymentEvery": 1,
"repaymentFrequencyType": {
"id": 2,
"code": "repaymentFrequency.periodFrequencyType.months",
"value": "Months"
},
"interestRatePerPeriod": 1.75,
"interestRateFrequencyType": {
"id": 2,
"code": "interestRateFrequency.periodFrequencyType.months",
"value": "Per month"
},
"annualInterestRate": 21,
"amortizationType": {
"id": 1,
"code": "amortizationType.equal.installments",
"value": "Equal installments"
},
"interestType": {
"id": 0,
"code": "interestType.declining.balance",
"value": "Declining Balance"
},
"interestCalculationPeriodType": {
"id": 1,
"code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period"
},
"transactionProcessingStrategyId": 1,
"transactionProcessingStrategyName": "Mifos style",
"charges": [
{ {
"id": 1, "id": 1,
"name": "Loan service fee", "name": "Loan service fee",
@@ -5820,234 +6033,11 @@ GET https://DomainName/api/v1/loanproducts/template
} }
} }
], ],
"accountingOptions": { "accountingRule": {
"assetAccountOptions": [
{
"id": 1,
"name": "Fund Source For Loans",
"glCode": "100001",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 2,
"name": "Interest Receivable",
"glCode": "100002",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 3,
"name": "Fees Receivable",
"glCode": "100003",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 4,
"name": "Penalties Receivable",
"glCode": "100004",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "better duddette"
}
],
"expenseAccountOptions": [
{
"id": 5,
"name": "Written Off Losses",
"glCode": "500005",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 5,
"code": "accountType.expense",
"value": "EXPENSE"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
}
],
"incomeAccountOptions": [
{
"id": 6,
"name": "Income from Interest",
"glCode": "400001",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 7,
"name": "Income from Fees",
"glCode": "400002",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 8,
"name": "Income from Penalties",
"glCode": "400003",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
}
]
},
"accountingMappings": {}
}
</code>
</div>
</div>
<a id="loanproducts_retrieve" name="loanproducts_retrieve"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve a Loan Product</h4>
<p>Example Requests:</p>
<div class=apiClick>loanproducts/1</div>
<br>
<br>
<div class=apiClick>loanproducts/1?template=true</div>
<br>
<br>
<div class=apiClick>loanproducts/1?fields=name,description,numberOfRepayments</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/loanproducts/{productId}
</code>
<code class="method-response">
{
"id": 1, "id": 1,
"name": "A new loan product for me", "code": "accountingRuleType.none",
"currency": { "value": "NONE"
"code": "USD", }
"name": "US Dollar",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"principal": 100000,
"numberOfRepayments": 12,
"loanTermFrequency": 12,
"repaymentEvery": 1,
"loanTermFrequencyType": {
"id": 2,
"code": "loanTermFrequency.periodFrequencyType.months",
"value": "Months"
},
"repaymentFrequencyType": {
"id": 2,
"code": "repaymentFrequency.periodFrequencyType.months",
"value": "Months"
},
"transactionProcessingStrategyId": 1,
"transactionProcessingStrategyName": "Mifos style",
"interestRatePerPeriod": 1.75,
"interestRateFrequencyType": {
"id": 2,
"code": "interestRateFrequency.periodFrequencyType.months",
"value": "Per month"
},
"annualInterestRate": 21,
"amortizationType": {
"id": 1,
"code": "amortizationType.equal.installments",
"value": "Equal installments"
},
"interestType": {
"id": 0,
"code": "interestType.declining.balance",
"value": "Declining Balance"
},
"interestCalculationPeriodType": {
"id": 1,
"code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period"
},
"accountingRule": 1,
"charges": []
} }
</code> </code>
</div> </div>
@@ -6074,11 +6064,17 @@ GET https://DomainName/api/v1/loanproducts/{productId}
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>name, currencyCode, digitsAfterDecimal, principal, <td>name, currencyCode, digitsAfterDecimal, principal,
inArrearsTolerance, repaymentFrequencyType, repaymentEvery, numberOfRepayments, repaymentEvery, repaymentFrequencyType,
numberOfRepayments, interestRatePerPeriod, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyId, accountingRule</td>
interestCalculationPeriodType, accountingRule</td>
</tr> </tr>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>inArrearsTolerance, charges</td>
</tr>
<tr class="matrixHeadingBG"> <tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Additional Mandatory Fields for Cash(2) based accounting</div></td> <td><div class="mifosXHeading2">Additional Mandatory Fields for Cash(2) based accounting</div></td>
</tr> </tr>
@@ -6107,7 +6103,7 @@ POST loanproducts
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"name": "A new loan product for me", "name": "LP with Charges but no accounting)",
"currencyCode": "USD", "currencyCode": "USD",
"locale": "en_GB", "locale": "en_GB",
"digitsAfterDecimal": "2", "digitsAfterDecimal": "2",
@@ -6115,12 +6111,14 @@ Request Body:
"numberOfRepayments": "12", "numberOfRepayments": "12",
"repaymentEvery": "1", "repaymentEvery": "1",
"repaymentFrequencyType": 2, "repaymentFrequencyType": 2,
"transactionProcessingStrategyId": 1,
"interestRatePerPeriod": "1.75", "interestRatePerPeriod": "1.75",
"interestRateFrequencyType": 2, "interestRateFrequencyType": 2,
"amortizationType": 1, "amortizationType": 1,
"interestType": 0, "interestType": 0,
"interestCalculationPeriodType": 1, "interestCalculationPeriodType": 1,
"inArrearsTolerance": "0",
"transactionProcessingStrategyId": 1,
"charges":[{"id":"1"},{"id":"1"}],
"accountingRule": 1 "accountingRule": 1
} }
</code> </code>
@@ -6134,7 +6132,7 @@ POST loanproducts
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"name": "A new loan product for me", "name": "LP Cash Accounting",
"currencyCode": "USD", "currencyCode": "USD",
"locale": "en_GB", "locale": "en_GB",
"digitsAfterDecimal": "2", "digitsAfterDecimal": "2",
@@ -6148,13 +6146,13 @@ Request Body:
"amortizationType": 1, "amortizationType": 1,
"interestType": 0, "interestType": 0,
"interestCalculationPeriodType": 1, "interestCalculationPeriodType": 1,
"accountingRule":"3", "accountingRule":"2",
"fundSourceAccountId":"15", "fundSourceAccountId":"4",
"loanPortfolioAccountId":"16", "loanPortfolioAccountId":"8",
"interestOnLoanAccountId":"14", "interestOnLoanAccountId":"34",
"incomeFromFeeAccountId":"13", "incomeFromFeeAccountId":"37",
"incomeFromPenaltyAccountId":"12", "incomeFromPenaltyAccountId":"35",
"writeOffAccountId":"11" "writeOffAccountId":"41"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -6167,7 +6165,7 @@ POST loanproducts
Content-Type: application/json Content-Type: application/json
Request Body: Request Body:
{ {
"name": "A new loan product for me", "name": "LP Accrual Accounting",
"currencyCode": "USD", "currencyCode": "USD",
"locale": "en_GB", "locale": "en_GB",
"digitsAfterDecimal": "2", "digitsAfterDecimal": "2",
@@ -6182,15 +6180,15 @@ Request Body:
"interestType": 0, "interestType": 0,
"interestCalculationPeriodType": 1, "interestCalculationPeriodType": 1,
"accountingRule":"3", "accountingRule":"3",
"fundSourceAccountId":"15", "fundSourceAccountId":"4",
"loanPortfolioAccountId":"16", "loanPortfolioAccountId":"8",
"receivableInterestAccountId":"10", "receivableInterestAccountId":"9",
"receivableFeeAccountId":"9", "receivableFeeAccountId":"11",
"receivablePenaltyAccountId":"8", "receivablePenaltyAccountId":"10",
"interestOnLoanAccountId":"14", "interestOnLoanAccountId":"34",
"incomeFromFeeAccountId":"13", "incomeFromFeeAccountId":"37",
"incomeFromPenaltyAccountId":"12", "incomeFromPenaltyAccountId":"35",
"writeOffAccountId":"11" "writeOffAccountId":"41"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -6823,7 +6821,8 @@ Request Body:
"locale": "en", "locale": "en",
"amount": "230.56", "amount": "230.56",
"chargeTimeType": "1", "chargeTimeType": "1",
"chargeCalculationType": "1" "chargeCalculationType": "1",
"active": true
} }
</code> </code>
<code class="method-response">{ "resourceId": 1 }</code> <code class="method-response">{ "resourceId": 1 }</code>
@@ -116,4 +116,106 @@ from x_registered_table r;
insert into m_permission(grouping, `code`, entity_name, action_name) insert into m_permission(grouping, `code`, entity_name, action_name)
select 'datatable', concat('DELETE_', r.registered_table_name), r.registered_table_name, 'DELETE' select 'datatable', concat('DELETE_', r.registered_table_name), r.registered_table_name, 'DELETE'
from x_registered_table r; from x_registered_table r;
-- ==== Chart of Accounts =====
truncate `acc_gl_account`;
INSERT INTO `acc_gl_account` VALUES
(1,'Petty Cash Balances',NULL,'11100',0,1,2,1,NULL),
(2,'Cash in Valut 1',NULL,'11101',0,1,1,1,NULL),
(3,'Bank Balances',NULL,'11200',0,1,2,1,NULL),
(4,'Centenary Opening Account',NULL,'11201',0,1,1,1,NULL),
(5,'Centenary Expense Account',NULL,'11202',0,1,1,1,NULL),
(6,'Centenary USD Account',NULL,'11203',0,1,1,1,NULL),
(7,'Loans and Advances',NULL,'13100',0,1,2,1,NULL),
(8,'Loans to Clients',NULL,'13101',0,1,1,1,NULL),
(9,'Outstanding Interest',NULL,'13102',0,1,1,1,NULL),
(10,'Outstanding Late Payment Interest',NULL,'13103',0,1,1,1,NULL),
(11,'Outstanding Bank Fees to be collected',NULL,'13104',0,1,1,1,NULL),
(12,'WriteOff Accounts',NULL,'13200',0,1,2,1,NULL),
(13,'Write-offs (use for funds coming in)',NULL,'13201',0,1,1,1,NULL),
(14,'Write-offs outstanding principal',NULL,'13202',0,1,1,1,NULL),
(15,'Write-offs outstanding interest',NULL,'13203',0,1,1,1,NULL),
(16,'Write-offs collected bank fees',NULL,'13204',0,1,1,1,NULL),
(17,'Write-offs hardware/furniture',NULL,'13205',0,1,1,1,NULL),
(18,'Fixed Assets',NULL,'14100',0,1,2,1,NULL),
(19,'Office Equipment',NULL,'14101',0,1,1,1,NULL),
(20,'Suspense Items (unidentified deposits)',NULL,'15000',0,1,2,1,NULL),
(21,'Assets',NULL,'10000',0,1,2,1,NULL),
(22,'Liabilities',NULL,'20000',0,1,2,2,NULL),
(23,'Shares Account',NULL,'26100',0,1,2,2,NULL),
(24,'Shares Captial',NULL,'26101',0,1,1,2,NULL),
(25,'Donated Equity',NULL,'26300',0,1,2,2,NULL),
(26,'Donated Equity Ameropa Foundation',NULL,'26301',0,1,1,2,NULL),
(27,'Donated Equity e.h',NULL,'26302',0,1,1,2,NULL),
(28,'Overpaid Amounts',NULL,'27000',0,1,2,2,NULL),
(29,'Loss Provision',NULL,'28000',0,1,2,2,NULL),
(30,'Provision Outstanding Principal',NULL,'28001',0,1,1,2,NULL),
(31,'Provision Oustanding Interest',NULL,'28002',0,1,1,2,NULL),(32,'Income',NULL,'30000',0,1,2,4,NULL),
(33,'Interest Income from Loans',NULL,'31100',0,1,2,4,NULL),
(34,'Interest on Loans',NULL,'31101',0,1,1,4,NULL),
(35,'Late Payment Interest',NULL,'31102',0,1,1,4,NULL),
(36,'Income from Micro credit & Lending Activities',NULL,'31300',0,1,2,4,NULL),
(37,'Collected Bank Fees Receivable',NULL,'6201',0,1,1,4,NULL),
(38,'Deposits from Loans Write Off',NULL,'31400',0,1,2,4,NULL),
(39,'Expenditure',NULL,'40000',0,1,2,5,NULL),
(40,'Office Expenditure Account',NULL,'42100',0,1,2,5,NULL),
(41,'Water Charges',NULL,'42102',0,1,1,5,NULL),
(42,'Electricity Charges',NULL,'42103',0,1,1,5,NULL),
(43,'Printing and Stationary',NULL,'42105',0,1,1,5,NULL),
(44,'Office Rent',NULL,'42107',0,1,1,5,NULL),
(45,'Marketing Expense',NULL,'42109',0,1,1,5,NULL),
(46,'Office utilities',NULL,'42112',0,1,1,5,'(supplies, toiletries, kitchen)'),
(47,'Furniture',NULL,'42113',0,1,1,5,NULL),
(48,'CEDA Meeting Expense',NULL,'42114',0,1,1,5,NULL),
(49,'Employee Personal Expsense Account',NULL,'42200',0,1,2,5,NULL),
(50,'Salary Alice',NULL,'42201',0,1,1,5,NULL),
(51,'Salary Irene',NULL,'42202',0,1,1,5,NULL),
(52,'Salary Richard',NULL,'42203',0,1,1,5,NULL),
(53,'Salary Loan Officer TBA',NULL,'42204',0,1,1,5,NULL),
(54,'Medical Insurance Alice & Family',NULL,'42205',0,1,1,5,NULL),
(55,'Medical Insurance Irene',NULL,'42206',0,1,1,5,NULL),
(56,'Medical Insurance Richard',NULL,'42207',0,1,1,5,NULL),
(57,'Medical Insurance Loan Officer TBA',NULL,'42208',0,1,1,5,NULL),
(58,'PAYE all employees',NULL,'42209',0,1,1,5,NULL),
(59,'NSSF all employees',NULL,'42210',0,1,1,5,NULL),
(60,'Lunch Allowances all employees',NULL,'42211',0,1,1,5,NULL),
(61,'IT software and maintenance',NULL,'42300',0,1,2,5,NULL),
(62,'Mifos maintenance contract 12 months',NULL,'42301',0,1,1,5,NULL),
(63,'VPS Contract 12 months',NULL,'42302',0,1,1,5,NULL),
(64,'Bulk SMS Service',NULL,'42303',0,1,1,5,NULL),
(65,'Support Accounting Software',NULL,'42304',0,1,1,5,NULL),
(66,'Mifos Instance Setup',NULL,'42305',0,1,1,5,NULL),
(67,'Misc support expense',NULL,'42306',0,1,1,5,NULL),
(68,'Warid Mobile Line',NULL,'42307',0,1,1,5,NULL),
(69,'Landline',NULL,'42308',0,1,1,5,NULL),
(70,'Modem Alice',NULL,'42309',0,1,1,5,NULL),
(71,'Modem Irene',NULL,'42310',0,1,1,5,NULL),
(72,'Modem Richard',NULL,'42311',0,1,1,5,NULL),
(73,'Repairs',NULL,'42312',0,1,1,5,NULL),
(74,'Airtime Expenses',NULL,'42400',0,1,2,5,NULL),
(75,'Airtime Alice',NULL,'42401',0,1,1,5,NULL),
(76,'Airtime Richard',NULL,'42402',0,1,1,5,NULL),
(77,'Airtime Loan Office TBA',NULL,'42403',0,1,1,5,NULL),
(78,'Special Airtime Alice',NULL,'42404',0,1,1,5,NULL),
(79,'Transportation',NULL,'42500',0,1,2,5,NULL),
(80,'Flat monthly transportation cost',NULL,'42501',0,1,1,5,NULL),
(81,'Faciliation cost for Richard',NULL,'42502',0,1,1,5,NULL),
(82,'Faciliation cost for Loan Officer TBA',NULL,'42503',0,1,1,5,NULL),
(83,'Consultancy Expenses',NULL,'42600',0,1,2,5,NULL),
(84,'Audit Fees',NULL,'42601',0,1,1,5,NULL),
(85,'Legal Fees',NULL,'42602',0,1,1,5,NULL),
(86,'Miscellaneous Expenses Account',NULL,'42700',0,1,2,5,NULL),
(87,'MFI License',NULL,'42703',0,1,1,5,NULL),
(88,'Sundy Expenses',NULL,'42704',0,1,1,5,NULL),
(89,'Bank Fees',NULL,'42800',0,1,2,5,NULL),
(90,'Bank Charges Operating Account',NULL,'42801',0,1,1,5,NULL),
(91,'Bank Charges Expense Account',NULL,'42802',0,1,1,5,NULL),
(92,'E.H Account',NULL,'42900',0,1,2,5,NULL),
(93,'Airtime',NULL,'42901',0,1,1,5,NULL),
(94,'Modem',NULL,'42902',0,1,1,5,NULL),
(95,'Meals',NULL,'42903',0,1,1,5,NULL),
(96,'Transportation',NULL,'42904',0,1,1,5,NULL),
(97,'Miscellaneous',NULL,'42905',0,1,1,5,NULL);
@@ -17,6 +17,7 @@ import org.mifosplatform.infrastructure.security.domain.BasicPasswordEncodablePl
import org.mifosplatform.infrastructure.security.domain.PlatformUser; import org.mifosplatform.infrastructure.security.domain.PlatformUser;
import org.mifosplatform.infrastructure.security.service.PlatformPasswordEncoder; import org.mifosplatform.infrastructure.security.service.PlatformPasswordEncoder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
/** /**
@@ -39,8 +40,8 @@ public final class JsonCommand {
private final Long apptableId; private final Long apptableId;
private final Long datatableId; private final Long datatableId;
private final Long codeId; private final Long codeId;
private String supportedEntityType; private final String supportedEntityType;
private Long supportedEntityId; private final Long supportedEntityId;
private final String transactionId; private final String transactionId;
public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper,
@@ -86,6 +87,15 @@ public final class JsonCommand {
return this.parsedCommand; return this.parsedCommand;
} }
public String jsonFragment(final String paramName) {
String jsonFragment = null;
if (this.parsedCommand.getAsJsonObject().has(paramName)) {
JsonElement fragment = this.parsedCommand.getAsJsonObject().get(paramName);
jsonFragment = this.fromApiJsonHelper.toJson(fragment);
}
return jsonFragment;
}
public Long commandId() { public Long commandId() {
return this.commandId; return this.commandId;
} }
@@ -347,6 +357,10 @@ public final class JsonCommand {
return this.fromApiJsonHelper.extractArrayNamed(parameterName, parsedCommand); return this.fromApiJsonHelper.extractArrayNamed(parameterName, parsedCommand);
} }
public JsonArray arrayOfParameterNamed(final String parameterName) {
return this.fromApiJsonHelper.extractJsonArrayNamed(parameterName, parsedCommand);
}
public boolean isChangeInPasswordParameterNamed(final String parameterName, final String existingValue, public boolean isChangeInPasswordParameterNamed(final String parameterName, final String existingValue,
final PlatformPasswordEncoder platformPasswordEncoder, final Long saltValue) { final PlatformPasswordEncoder platformPasswordEncoder, final Long saltValue) {
boolean isChanged = false; boolean isChanged = false;
@@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterExcep
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
@@ -46,7 +47,7 @@ public class FromJsonHelper {
return this.gsonConverter.fromJson(json, typeOfMap); return this.gsonConverter.fromJson(json, typeOfMap);
} }
public <T> T fromJson(final String json, Class<T> classOfT) { public <T> T fromJson(final String json, final Class<T> classOfT) {
return this.gsonConverter.fromJson(json, classOfT); return this.gsonConverter.fromJson(json, classOfT);
} }
@@ -93,6 +94,10 @@ public class FromJsonHelper {
return helperDelegator.extractLongNamed(parameterName, element, parametersPassedInRequest); return helperDelegator.extractLongNamed(parameterName, element, parametersPassedInRequest);
} }
public JsonArray extractJsonArrayNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractJsonArrayNamed(parameterName, element);
}
public String[] extractArrayNamed(final String parameterName, final JsonElement element) { public String[] extractArrayNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractArrayNamed(parameterName, element, new HashSet<String>()); return helperDelegator.extractArrayNamed(parameterName, element, new HashSet<String>());
} }
@@ -109,7 +114,7 @@ public class FromJsonHelper {
return helperDelegator.extractBooleanNamed(parameterName, element, parametersPassedInRequest); return helperDelegator.extractBooleanNamed(parameterName, element, parametersPassedInRequest);
} }
public LocalDate extractLocalDateNamed(final String parameterName, JsonElement element) { public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<String>()); return helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<String>());
} }
@@ -32,7 +32,7 @@ import com.google.gson.JsonPrimitive;
* Helper class to extract values of json named attributes. * Helper class to extract values of json named attributes.
*/ */
public class JsonParserHelper { public class JsonParserHelper {
public boolean parameterExists(final String parameterName, final JsonElement element) { public boolean parameterExists(final String parameterName, final JsonElement element) {
return element.getAsJsonObject().has(parameterName); return element.getAsJsonObject().has(parameterName);
} }
@@ -137,19 +137,21 @@ public class JsonParserHelper {
} }
return value; return value;
} }
/** Method used to extract integers from unformatted strings. /**
* Ex: "1" , "100002" etc * Method used to extract integers from unformatted strings. Ex: "1" ,
* "100002" etc
* *
* Please note that this method does not support extracting Integers from locale * Please note that this method does not support extracting Integers from
* specific formatted strings Ex "1,000" etc * locale specific formatted strings Ex "1,000" etc
* *
* @param parameterName * @param parameterName
* @param element * @param element
* @param parametersPassedInRequest * @param parametersPassedInRequest
* @return * @return
*/ */
public Integer extractIntegerSansLocaleNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInRequest) { public Integer extractIntegerSansLocaleNamed(final String parameterName, final JsonElement element,
final Set<String> parametersPassedInRequest) {
Integer intValue = null; Integer intValue = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject(); JsonObject object = element.getAsJsonObject();
@@ -211,6 +213,19 @@ public class JsonParserHelper {
return arrayValue; return arrayValue;
} }
public JsonArray extractJsonArrayNamed(final String parameterName, final JsonElement element) {
JsonArray jsonArray = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
if (object.has(parameterName)) {
jsonArray = object.get(parameterName).getAsJsonArray();
}
}
return jsonArray;
}
/** /**
* Used with the local date is in array format * Used with the local date is in array format
*/ */
@@ -391,8 +406,8 @@ public class JsonParserHelper {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer", "The parameter " ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer", "The parameter " + parameterName
+ parameterName + " has value: " + numericalValueFormatted + " which is invalid integer.", parameterName, numericalValueFormatted); + " has value: " + numericalValueFormatted + " which is invalid integer.", parameterName, numericalValueFormatted);
dataValidationErrors.add(error); dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
@@ -400,7 +415,6 @@ public class JsonParserHelper {
} }
} }
public BigDecimal convertFrom(final String numericalValueFormatted, final String parameterName, final Locale clientApplicationLocale) { public BigDecimal convertFrom(final String numericalValueFormatted, final String parameterName, final Locale clientApplicationLocale) {
if (clientApplicationLocale == null) { if (clientApplicationLocale == null) {
@@ -11,11 +11,12 @@ import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
/** /**
* Immutable data object for charge data. * Immutable data object for charge data.
*/ */
public class ChargeData { public class ChargeData implements Comparable<ChargeData> {
private final Long id; private final Long id;
private final String name; private final String name;
@@ -72,36 +73,8 @@ public class ChargeData {
this.chargeTimeTypeOptions = chargeTimeTypeOptions; this.chargeTimeTypeOptions = chargeTimeTypeOptions;
} }
public Long getId() {
return id;
}
public String getName() {
return name;
}
public boolean isPenalty() {
return this.penalty;
}
public BigDecimal getAmount() {
return amount;
}
public CurrencyData getCurrency() {
return currency;
}
public EnumOptionData getChargeTimeType() {
return chargeTimeType;
}
public EnumOptionData getChargeCalculationType() {
return chargeCalculationType;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(final Object obj) {
ChargeData chargeData = (ChargeData) obj; ChargeData chargeData = (ChargeData) obj;
return this.id.equals(chargeData.id); return this.id.equals(chargeData.id);
} }
@@ -110,4 +83,16 @@ public class ChargeData {
public int hashCode() { public int hashCode() {
return this.id.hashCode(); return this.id.hashCode();
} }
@Override
public int compareTo(final ChargeData obj) {
if (obj == null) { return -1; }
return obj.id.compareTo(this.id);
}
public LoanChargeData toLoanChargeData() {
return LoanChargeData.newLoanChargeDetails(this.id, this.name, this.currency, this.amount, this.chargeTimeType,
this.chargeCalculationType, this.penalty);
}
} }
@@ -14,8 +14,15 @@ import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint; 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.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
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.ChargeDueAtDisbursementCannotBePenaltyException;
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity @Entity
@@ -71,7 +78,7 @@ public class Charge extends AbstractPersistable<Long> {
} }
private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo, private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, boolean active) { final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, final boolean active) {
this.name = name; this.name = name;
this.amount = amount; this.amount = amount;
this.currencyCode = currencyCode; this.currencyCode = currencyCode;
@@ -83,6 +90,24 @@ public class Charge extends AbstractPersistable<Long> {
if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); } if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
} }
@Override
public boolean equals(final Object obj) {
if (obj == null) { return false; }
if (obj == this) { return true; }
if (obj.getClass() != getClass()) { return false; }
LoanCharge rhs = (LoanCharge) obj;
return new EqualsBuilder().appendSuper(super.equals(obj)) //
.append(this.getId(), rhs.getId()) //
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(3, 5) //
.append(this.getId()) //
.toHashCode();
}
public String getName() { public String getName() {
return name; return name;
} }
@@ -180,8 +205,9 @@ public class Charge extends AbstractPersistable<Long> {
actualChanges.put(activeParamName, newValue); actualChanges.put(activeParamName, newValue);
this.active = newValue; this.active = newValue;
} }
if (this.penalty && ChargeTimeType.fromInt(this.chargeTime).isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); } if (this.penalty && ChargeTimeType.fromInt(this.chargeTime).isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(
name); }
return actualChanges; return actualChanges;
} }
@@ -196,4 +222,15 @@ public class Charge extends AbstractPersistable<Long> {
this.deleted = true; this.deleted = true;
this.name = this.getId() + "_" + this.name; this.name = this.getId() + "_" + this.name;
} }
public ChargeData toData() {
EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(this.chargeTime);
EnumOptionData chargeAppliesTo = ChargeEnumerations.chargeAppliesTo(this.chargeAppliesTo);
EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation);
CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, null, null);
return ChargeData.instance(this.getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
penalty, active);
}
} }
@@ -353,8 +353,7 @@ public class LoanAccountData {
final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>(); final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>();
for (ChargeData charge : product.charges()) { for (ChargeData charge : product.charges()) {
charges.add(LoanChargeData.newLoanChargeDetails(charge.getId(), charge.getName(), charge.getCurrency(), charge.getAmount(), charges.add(charge.toLoanChargeData());
charge.getChargeTimeType(), charge.getChargeCalculationType(), charge.isPenalty()));
} }
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId, return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
@@ -57,12 +57,13 @@ import org.springframework.stereotype.Component;
public class LoanProductsApiResource { public class LoanProductsApiResource {
private final Set<String> LOAN_PRODUCT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "description", "fundId", private final Set<String> LOAN_PRODUCT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "description", "fundId",
"fundName", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "principal", "inArrearsTolerance", "fundName", "currency", "principal", "numberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "interestRatePerPeriod",
"numberOfRepayments", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentFrequencyType", "interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType", "interestCalculationPeriodType",
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "charges", "createdOn", "inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "charges", "accountingRule",
"lastModifedOn", "currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "accountingMappings", "fundOptions", "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"chargeOptions", "accountingOptions", "accountingRule","accountingRuleOptions","accountingMappings","accountingMappingOptions")); "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions",
"accountingMappingOptions"));
private final String resourceNameForPermissions = "LOANPRODUCT"; private final String resourceNameForPermissions = "LOANPRODUCT";
@@ -129,7 +130,7 @@ public class LoanProductsApiResource {
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) { public String retrieveTemplate(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
@@ -154,7 +155,8 @@ public class LoanProductsApiResource {
Map<String, Object> accountingMappings = null; Map<String, Object> accountingMappings = null;
if (loanProduct.hasAccountingEnabled()) { if (loanProduct.hasAccountingEnabled()) {
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct.accountingRuleType().getId().intValue()); accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct
.accountingRuleType().getId().intValue());
loanProduct = LoanProductData.withAccountingMappings(loanProduct, accountingMappings); loanProduct = LoanProductData.withAccountingMappings(loanProduct, accountingMappings);
} }
@@ -182,7 +184,6 @@ public class LoanProductsApiResource {
final boolean feeChargesOnly = true; final boolean feeChargesOnly = true;
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly); Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
chargeOptions.removeAll(productData.charges());
if (chargeOptions.isEmpty()) { if (chargeOptions.isEmpty()) {
chargeOptions = null; chargeOptions = null;
} }
@@ -192,7 +193,6 @@ public class LoanProductsApiResource {
final List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions(); final List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
final List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService final List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService
.retrieveLoanInterestRateCalculatedInPeriodOptions(); .retrieveLoanInterestRateCalculatedInPeriodOptions();
final List<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
final List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions(); final List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
final List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService final List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService
.retrieveInterestRateFrequencyTypeOptions(); .retrieveInterestRateFrequencyTypeOptions();
@@ -204,29 +204,29 @@ public class LoanProductsApiResource {
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService
.retreiveTransactionProcessingStrategies(); .retreiveTransactionProcessingStrategies();
Map<String, List<GLAccountData>> accountOptions= new HashMap<String, List<GLAccountData>>(); Map<String, List<GLAccountData>> accountOptions = new HashMap<String, List<GLAccountData>>();
List<GLAccountData> assetAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET); List<GLAccountData> assetAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET);
if (assetAccountOptions.isEmpty()) { if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null; assetAccountOptions = null;
} }
accountOptions.put("assetAccountOptions", assetAccountOptions); accountOptions.put("assetAccountOptions", assetAccountOptions);
List<GLAccountData> incomeAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME); List<GLAccountData> incomeAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME);
if (incomeAccountOptions.isEmpty()) { if (incomeAccountOptions.isEmpty()) {
incomeAccountOptions = null; incomeAccountOptions = null;
} }
accountOptions.put("incomeAccountOptions", incomeAccountOptions); accountOptions.put("incomeAccountOptions", incomeAccountOptions);
List<GLAccountData> expenseAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE); List<GLAccountData> expenseAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE);
if (expenseAccountOptions.isEmpty()) { if (expenseAccountOptions.isEmpty()) {
expenseAccountOptions = null; expenseAccountOptions = null;
} }
accountOptions.put("expenseAccountOptions", expenseAccountOptions); accountOptions.put("expenseAccountOptions", expenseAccountOptions);
List<EnumOptionData> accountingRuleTypeOptions = dropdownReadPlatformService.retrieveAccountingRuleTypeOptions(); List<EnumOptionData> accountingRuleTypeOptions = dropdownReadPlatformService.retrieveAccountingRuleTypeOptions();
return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions, return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions,
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions,accountingMappings); transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions, accountingMappings);
} }
} }
@@ -33,47 +33,46 @@ public class LoanProductData {
private final String description; private final String description;
private final Long fundId; private final Long fundId;
private final String fundName; private final String fundName;
// terms
private final CurrencyData currency; private final CurrencyData currency;
private final BigDecimal principal; private final BigDecimal principal;
private final Integer numberOfRepayments; private final Integer numberOfRepayments;
private final Integer loanTermFrequency;
private final Integer repaymentEvery; private final Integer repaymentEvery;
private final EnumOptionData loanTermFrequencyType;
private final EnumOptionData repaymentFrequencyType; private final EnumOptionData repaymentFrequencyType;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
private final BigDecimal interestRatePerPeriod; private final BigDecimal interestRatePerPeriod;
private final EnumOptionData interestRateFrequencyType; private final EnumOptionData interestRateFrequencyType;
private final BigDecimal annualInterestRate; private final BigDecimal annualInterestRate;
// settings
private final EnumOptionData amortizationType; private final EnumOptionData amortizationType;
private final EnumOptionData interestType; private final EnumOptionData interestType;
private final EnumOptionData interestCalculationPeriodType; private final EnumOptionData interestCalculationPeriodType;
private final BigDecimal inArrearsTolerance; private final BigDecimal inArrearsTolerance;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
private final EnumOptionData accountingRule; // charges
private final Collection<ChargeData> charges; private final Collection<ChargeData> charges;
// accounting
private final EnumOptionData accountingRule;
@SuppressWarnings("unused")
private final Map<String, Object> accountingMappings;
// template related // template related
private final Collection<CurrencyData> currencyOptions;
private final Collection<FundData> fundOptions; private final Collection<FundData> fundOptions;
private final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions; private final Collection<CurrencyData> currencyOptions;
private final List<EnumOptionData> repaymentFrequencyTypeOptions;
private final List<EnumOptionData> interestRateFrequencyTypeOptions;
private final List<EnumOptionData> amortizationTypeOptions; private final List<EnumOptionData> amortizationTypeOptions;
private final List<EnumOptionData> interestTypeOptions; private final List<EnumOptionData> interestTypeOptions;
private final List<EnumOptionData> interestCalculationPeriodTypeOptions; private final List<EnumOptionData> interestCalculationPeriodTypeOptions;
private final List<EnumOptionData> repaymentFrequencyTypeOptions; private final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions;
private final List<EnumOptionData> interestRateFrequencyTypeOptions;
private final List<EnumOptionData> loanTermFrequencyTypeOptions;
private final Collection<ChargeData> chargeOptions; private final Collection<ChargeData> chargeOptions;
// accounting related template fields
private final List<EnumOptionData> accountingRuleOptions; private final List<EnumOptionData> accountingRuleOptions;
private final Map<String, List<GLAccountData>> accountingMappingOptions; private final Map<String, List<GLAccountData>> accountingMappingOptions;
// accounting related mapping fields
@SuppressWarnings("unused")
private final Map<String, Object> accountingMappings;
/** /**
* Used when returning lookup information about loan product for dropdowns. * Used when returning lookup information about loan product for dropdowns.
*/ */
@@ -83,11 +82,9 @@ public class LoanProductData {
final BigDecimal principal = null; final BigDecimal principal = null;
final BigDecimal tolerance = null; final BigDecimal tolerance = null;
final Integer numberOfRepayments = null; final Integer numberOfRepayments = null;
final Integer loanTermFrequency = null;
final Integer repaymentEvery = null; final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null; final BigDecimal interestRatePerPeriod = null;
final BigDecimal annualInterestRate = null; final BigDecimal annualInterestRate = null;
final EnumOptionData loanTermFrequencyType = null;
final EnumOptionData repaymentFrequencyType = null; final EnumOptionData repaymentFrequencyType = null;
final EnumOptionData interestRateFrequencyType = null; final EnumOptionData interestRateFrequencyType = null;
final EnumOptionData amortizationType = null; final EnumOptionData amortizationType = null;
@@ -100,10 +97,10 @@ public class LoanProductData {
final Collection<ChargeData> charges = null; final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = null; final EnumOptionData accountingType = null;
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency, return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType, interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType); transactionProcessingStrategyName, charges, accountingType);
} }
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() { public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
@@ -114,11 +111,9 @@ public class LoanProductData {
final BigDecimal principal = null; final BigDecimal principal = null;
final BigDecimal tolerance = null; final BigDecimal tolerance = null;
final Integer numberOfRepayments = null; final Integer numberOfRepayments = null;
final Integer loanTermFrequency = null;
final Integer repaymentEvery = null; final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null; final BigDecimal interestRatePerPeriod = null;
final BigDecimal annualInterestRate = null; final BigDecimal annualInterestRate = null;
final EnumOptionData loanTermFrequencyType = LoanEnumerations.loanTermFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(PeriodFrequencyType.MONTHS); final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(PeriodFrequencyType.MONTHS); final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(AmortizationMethod.EQUAL_INSTALLMENTS); final EnumOptionData amortizationType = LoanEnumerations.amortizationType(AmortizationMethod.EQUAL_INSTALLMENTS);
@@ -132,25 +127,24 @@ public class LoanProductData {
final Collection<ChargeData> charges = null; final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = LoanEnumerations.accountingRuleType(AccountingRuleType.NONE); final EnumOptionData accountingType = LoanEnumerations.accountingRuleType(AccountingRuleType.NONE);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency, return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType, interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType); transactionProcessingStrategyName, charges, accountingType);
} }
public static LoanProductData withAccountingMappings(final LoanProductData productData, final Map<String, Object> accountingMappings) { public static LoanProductData withAccountingMappings(final LoanProductData productData, final Map<String, Object> accountingMappings) {
return new LoanProductData(productData, productData.chargeOptions, productData.currencyOptions, return new LoanProductData(productData, productData.chargeOptions, productData.currencyOptions,
productData.amortizationTypeOptions, productData.interestTypeOptions, productData.interestCalculationPeriodTypeOptions, productData.amortizationTypeOptions, productData.interestTypeOptions, productData.interestCalculationPeriodTypeOptions,
productData.loanTermFrequencyTypeOptions, productData.repaymentFrequencyTypeOptions, productData.repaymentFrequencyTypeOptions, productData.interestRateFrequencyTypeOptions, productData.fundOptions,
productData.interestRateFrequencyTypeOptions, productData.fundOptions, productData.transactionProcessingStrategyOptions, productData.transactionProcessingStrategyOptions, productData.accountingMappingOptions, productData.accountingRuleOptions,
productData.accountingMappingOptions, productData.accountingRuleOptions, accountingMappings); accountingMappings);
} }
public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency, public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency,
final BigDecimal principal, final BigDecimal tolerance, final Integer numberOfRepayments, final Integer loanTermFrequency, final BigDecimal principal, final BigDecimal tolerance, final Integer numberOfRepayments, final Integer repaymentEvery,
final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate, final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType,
final EnumOptionData loanTermFrequencyType, final EnumOptionData repaymentFrequencyType,
final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType, final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName, final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName,
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName, final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
@@ -162,11 +156,11 @@ public class LoanProductData {
this.principal = principal; this.principal = principal;
this.inArrearsTolerance = tolerance; this.inArrearsTolerance = tolerance;
this.numberOfRepayments = numberOfRepayments; this.numberOfRepayments = numberOfRepayments;
this.loanTermFrequency = loanTermFrequency; // this.loanTermFrequency = loanTermFrequency;
this.repaymentEvery = repaymentEvery; this.repaymentEvery = repaymentEvery;
this.interestRatePerPeriod = interestRatePerPeriod; this.interestRatePerPeriod = interestRatePerPeriod;
this.annualInterestRate = annualInterestRate; this.annualInterestRate = annualInterestRate;
this.loanTermFrequencyType = loanTermFrequencyType; // this.loanTermFrequencyType = loanTermFrequencyType;
this.repaymentFrequencyType = repaymentFrequencyType; this.repaymentFrequencyType = repaymentFrequencyType;
this.interestRateFrequencyType = interestRateFrequencyType; this.interestRateFrequencyType = interestRateFrequencyType;
this.amortizationType = amortizationType; this.amortizationType = amortizationType;
@@ -186,7 +180,7 @@ public class LoanProductData {
this.amortizationTypeOptions = null; this.amortizationTypeOptions = null;
this.interestTypeOptions = null; this.interestTypeOptions = null;
this.interestCalculationPeriodTypeOptions = null; this.interestCalculationPeriodTypeOptions = null;
this.loanTermFrequencyTypeOptions = null; // this.loanTermFrequencyTypeOptions = null;
this.repaymentFrequencyTypeOptions = null; this.repaymentFrequencyTypeOptions = null;
this.interestRateFrequencyTypeOptions = null; this.interestRateFrequencyTypeOptions = null;
@@ -198,31 +192,29 @@ public class LoanProductData {
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions, public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> amortizationTypeOptions, final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> amortizationTypeOptions,
final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions, final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions,
final List<EnumOptionData> loanTermFrequencyTypeOptions, final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions,
final List<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<FundData> fundOptions, final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions, final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions, final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
final Map<String, Object> accountingMappings) { final Map<String, Object> accountingMappings) {
this.id = productData.id; this.id = productData.id;
this.name = productData.name; this.name = productData.name;
this.description = productData.description; this.description = productData.description;
this.fundId = productData.fundId;
this.fundName = productData.fundName;
this.principal = productData.principal; this.principal = productData.principal;
this.inArrearsTolerance = productData.inArrearsTolerance; this.inArrearsTolerance = productData.inArrearsTolerance;
this.numberOfRepayments = productData.numberOfRepayments; this.numberOfRepayments = productData.numberOfRepayments;
this.loanTermFrequency = productData.loanTermFrequency;
this.repaymentEvery = productData.repaymentEvery; this.repaymentEvery = productData.repaymentEvery;
this.interestRatePerPeriod = productData.interestRatePerPeriod; this.interestRatePerPeriod = productData.interestRatePerPeriod;
this.annualInterestRate = productData.annualInterestRate; this.annualInterestRate = productData.annualInterestRate;
this.loanTermFrequencyType = productData.loanTermFrequencyType;
this.repaymentFrequencyType = productData.repaymentFrequencyType; this.repaymentFrequencyType = productData.repaymentFrequencyType;
this.interestRateFrequencyType = productData.interestRateFrequencyType; this.interestRateFrequencyType = productData.interestRateFrequencyType;
this.amortizationType = productData.amortizationType; this.amortizationType = productData.amortizationType;
this.interestType = productData.interestType; this.interestType = productData.interestType;
this.interestCalculationPeriodType = productData.interestCalculationPeriodType; this.interestCalculationPeriodType = productData.interestCalculationPeriodType;
this.fundId = productData.fundId;
this.fundName = productData.fundName;
this.transactionProcessingStrategyId = productData.transactionProcessingStrategyId;
this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName;
this.charges = nullIfEmpty(productData.charges()); this.charges = nullIfEmpty(productData.charges());
this.accountingRule = productData.accountingRule; this.accountingRule = productData.accountingRule;
@@ -234,17 +226,32 @@ public class LoanProductData {
this.currency = productData.currency; this.currency = productData.currency;
} }
this.fundOptions = fundOptions; this.fundOptions = fundOptions;
this.transactionProcessingStrategyOptions = transactionProcessingStrategyOptions; this.transactionProcessingStrategyOptions = transactionStrategyOptions;
if (this.transactionProcessingStrategyOptions != null && this.transactionProcessingStrategyOptions.size() == 1) {
final List<TransactionProcessingStrategyData> listOfOptions = new ArrayList<TransactionProcessingStrategyData>(
this.transactionProcessingStrategyOptions);
this.transactionProcessingStrategyId = listOfOptions.get(0).id();
this.transactionProcessingStrategyName = listOfOptions.get(0).name();
} else {
this.transactionProcessingStrategyId = productData.transactionProcessingStrategyId;
this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName;
}
this.amortizationTypeOptions = amortizationTypeOptions; this.amortizationTypeOptions = amortizationTypeOptions;
this.interestTypeOptions = interestTypeOptions; this.interestTypeOptions = interestTypeOptions;
this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions; this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
this.loanTermFrequencyTypeOptions = loanTermFrequencyTypeOptions;
this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions; this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions;
this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions; this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions;
this.accountingMappingOptions = accountingMappingOptions; this.accountingMappingOptions = accountingMappingOptions;
this.accountingRuleOptions = accountingRuleOptions; this.accountingRuleOptions = accountingRuleOptions;
this.accountingMappings = accountingMappings;
if (accountingMappings == null || accountingMappings.isEmpty()) {
this.accountingMappings = null;
} else {
this.accountingMappings = accountingMappings;
}
} }
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) { private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
@@ -315,10 +322,6 @@ public class LoanProductData {
return numberOfRepayments; return numberOfRepayments;
} }
public Integer getLoanTermFrequency() {
return loanTermFrequency;
}
public Integer getRepaymentEvery() { public Integer getRepaymentEvery() {
return repaymentEvery; return repaymentEvery;
} }
@@ -331,10 +334,6 @@ public class LoanProductData {
return annualInterestRate; return annualInterestRate;
} }
public EnumOptionData getLoanTermFrequencyType() {
return loanTermFrequencyType;
}
public EnumOptionData getRepaymentFrequencyType() { public EnumOptionData getRepaymentFrequencyType() {
return repaymentFrequencyType; return repaymentFrequencyType;
} }
@@ -359,10 +358,6 @@ public class LoanProductData {
return fundOptions; return fundOptions;
} }
public Collection<TransactionProcessingStrategyData> getTransactionProcessingStrategyOptions() {
return transactionProcessingStrategyOptions;
}
public List<EnumOptionData> getAmortizationTypeOptions() { public List<EnumOptionData> getAmortizationTypeOptions() {
return amortizationTypeOptions; return amortizationTypeOptions;
} }
@@ -383,10 +378,6 @@ public class LoanProductData {
return interestRateFrequencyTypeOptions; return interestRateFrequencyTypeOptions;
} }
public List<EnumOptionData> getLoanTermFrequencyTypeOptions() {
return loanTermFrequencyTypeOptions;
}
public Collection<ChargeData> getChargeOptions() { public Collection<ChargeData> getChargeOptions() {
return chargeOptions; return chargeOptions;
} }
@@ -5,13 +5,14 @@
*/ */
package org.mifosplatform.portfolio.loanproduct.data; package org.mifosplatform.portfolio.loanproduct.data;
/**
* Immutable data object representing a transaction strategy option for a loan.
*/
public final class TransactionProcessingStrategyData { public final class TransactionProcessingStrategyData {
@SuppressWarnings("unused")
private final Long id; private final Long id;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final String code; private final String code;
@SuppressWarnings("unused")
private final String name; private final String name;
public TransactionProcessingStrategyData(final Long id, final String code, final String name) { public TransactionProcessingStrategyData(final Long id, final String code, final String name) {
@@ -19,4 +20,12 @@ public final class TransactionProcessingStrategyData {
this.code = code; this.code = code;
this.name = name; this.name = name;
} }
public Long id() {
return this.id;
}
public String name() {
return this.name;
}
} }
@@ -6,7 +6,7 @@
package org.mifosplatform.portfolio.loanproduct.domain; package org.mifosplatform.portfolio.loanproduct.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -29,6 +29,8 @@ import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
import com.google.gson.JsonArray;
/** /**
* Loan products allow for categorisation of an organisations loans into * Loan products allow for categorisation of an organisations loans into
* something meaningful to them. * something meaningful to them.
@@ -39,7 +41,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
* They allow for constraints to be added at product level. * They allow for constraints to be added at product level.
*/ */
@Entity @Entity
@Table(name = "m_product_loan" , uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name")}) @Table(name = "m_product_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") })
public class LoanProduct extends AbstractPersistable<Long> { public class LoanProduct extends AbstractPersistable<Long> {
@ManyToOne @ManyToOne
@@ -50,7 +52,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true) @JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy; private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@Column(name = "name", nullable = false , unique = true) @Column(name = "name", nullable = false, unique = true)
private String name; private String name;
@Column(name = "description") @Column(name = "description")
@@ -58,7 +60,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
@ManyToMany @ManyToMany
@JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) @JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
private Set<Charge> charges; private List<Charge> charges;
@Embedded @Embedded
private final LoanProductRelatedDetail loanProductRelatedDetail; private final LoanProductRelatedDetail loanProductRelatedDetail;
@@ -67,7 +69,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
private Integer accountingRule; private Integer accountingRule;
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy, public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
final Set<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) { final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
final String name = command.stringValueOfParameterNamed("name"); final String name = command.stringValueOfParameterNamed("name");
final String description = command.stringValueOfParameterNamed("description"); final String description = command.stringValueOfParameterNamed("description");
@@ -108,7 +110,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod, final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final Set<Charge> charges, final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges,
final AccountingRuleType accountingRuleType) { final AccountingRuleType accountingRuleType) {
this.fund = fund; this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy; this.transactionProcessingStrategy = transactionProcessingStrategy;
@@ -136,10 +138,6 @@ public class LoanProduct extends AbstractPersistable<Long> {
return this.loanProductRelatedDetail.getCurrency(); return this.loanProductRelatedDetail.getCurrency();
} }
public Set<Charge> getCharges() {
return charges;
}
public void update(final Fund fund) { public void update(final Fund fund) {
this.fund = fund; this.fund = fund;
} }
@@ -148,8 +146,17 @@ public class LoanProduct extends AbstractPersistable<Long> {
this.transactionProcessingStrategy = strategy; this.transactionProcessingStrategy = strategy;
} }
public void update(final Set<Charge> charges) { public boolean update(final List<Charge> newProductCharges) {
this.charges = charges; boolean updated = false;
if (this.charges != null) {
final Set<Charge> setOfCharges = new HashSet<Charge>(this.charges);
updated = setOfCharges.addAll(newProductCharges);
if (updated) {
this.charges = newProductCharges;
}
}
return updated;
} }
public Integer getAccountingType() { public Integer getAccountingType() {
@@ -202,30 +209,17 @@ public class LoanProduct extends AbstractPersistable<Long> {
} }
final String chargesParamName = "charges"; final String chargesParamName = "charges";
if (command.isChangeInArrayParameterNamed(chargesParamName, getChargesIds())) { if (command.hasParameter(chargesParamName)) {
final String[] newValue = command.arrayValueOfParameterNamed(chargesParamName); JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName);
actualChanges.put(chargesParamName, newValue); if (jsonArray != null) {
}
actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName));
} }
return actualChanges; return actualChanges;
} }
private String[] getChargesIds() {
final List<String> chargeIds = new ArrayList<String>();
if (this.charges != null) {
for (Charge charge : this.charges) {
chargeIds.add(charge.getId().toString());
}
}
return chargeIds.toArray(new String[chargeIds.size()]);
}
public boolean isAccountingEnabled() {
return !AccountingRuleType.NONE.getValue().equals(this.accountingRule);
}
public boolean isCashBasedAccountingEnabled() { public boolean isCashBasedAccountingEnabled() {
return AccountingRuleType.CASH_BASED.getValue().equals(this.accountingRule); return AccountingRuleType.CASH_BASED.getValue().equals(this.accountingRule);
} }
@@ -35,10 +35,10 @@ public final class LoanProductCommandFromApiJsonDeserializer {
/** /**
* The parameters supported for this command. * The parameters supported for this command.
*/ */
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "description", "fundId", private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("locale", "name", "description", "fundId",
"transactionProcessingStrategyId", "currencyCode", "digitsAfterDecimal", "principal", "inArrearsTolerance", "currencyCode", "digitsAfterDecimal", "principal", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType",
"interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", "interestRateFrequencyType", "interestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"amortizationType", "interestType", "interestCalculationPeriodType", "charges", "locale", "accountingRule", "inArrearsTolerance", "transactionProcessingStrategyId", "charges", "accountingRule",
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
@@ -74,6 +74,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero(); baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
} }
// terms
final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element); final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element);
baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3); baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3);
@@ -83,12 +84,6 @@ public final class LoanProductCommandFromApiJsonDeserializer {
final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element); final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount(); baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element); final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero();
@@ -99,14 +94,14 @@ public final class LoanProductCommandFromApiJsonDeserializer {
.extractIntegerNamed("repaymentFrequencyType", element, Locale.getDefault()); .extractIntegerNamed("repaymentFrequencyType", element, Locale.getDefault());
baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3); baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element); final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull() baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
.integerGreaterThanZero();
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element, final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
Locale.getDefault()); Locale.getDefault());
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3); baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
// settings
final Integer amortizationType = fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault()); final Integer amortizationType = fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1); baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1);
@@ -118,6 +113,13 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(interestCalculationPeriodType).notNull() baseDataValidator.reset().parameter("interestCalculationPeriodType").value(interestCalculationPeriodType).notNull()
.inMinMaxRange(0, 1); .inMinMaxRange(0, 1);
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero();
// accounting related data validation // accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault()); final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3); baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
@@ -176,7 +178,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
public void validateForUpdate(String json) { public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType(); final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
@@ -134,19 +134,16 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments"); final Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments");
final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaidEvery"); final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaidEvery");
final Integer accountingTypeId = JdbcSupport.getInteger(rs, "accountingType"); final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = LoanEnumerations.accountingRuleType(accountingTypeId); final EnumOptionData accountingRuleType = LoanEnumerations.accountingRuleType(accountingRuleId);
final Integer loanTermFrequency = repaymentEvery * numberOfRepayments;
final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod"); final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod");
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate"); final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
final int repaymentFrequencyTypeId = JdbcSupport.getInteger(rs, "repaymentPeriodFrequency"); final int repaymentFrequencyTypeId = JdbcSupport.getInteger(rs, "repaymentPeriodFrequency");
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeId); final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeId);
final EnumOptionData loanTermFrequencyType = LoanEnumerations.loanTermFrequencyType(repaymentFrequencyTypeId);
final int amortizationTypeId = JdbcSupport.getInteger(rs, "amortizationMethod"); final int amortizationTypeId = JdbcSupport.getInteger(rs, "amortizationMethod");
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeId); final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeId);
@@ -160,10 +157,10 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final EnumOptionData interestCalculationPeriodType = LoanEnumerations final EnumOptionData interestCalculationPeriodType = LoanEnumerations
.interestCalculationPeriodType(interestCalculationPeriodTypeId); .interestCalculationPeriodType(interestCalculationPeriodTypeId);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency, return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType, interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, interestType, interestCalculationPeriodType, fundId, fundName, transactionStrategyId, transactionStrategyName,
transactionStrategyId, transactionStrategyName, this.charges, accountingRuleType); this.charges, accountingRuleType);
} }
} }
@@ -5,9 +5,9 @@
*/ */
package org.mifosplatform.portfolio.loanproduct.service; package org.mifosplatform.portfolio.loanproduct.service;
import java.util.HashSet; import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService; import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
@@ -16,9 +16,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.charge.domain.Charge; import org.mifosplatform.portfolio.charge.domain.Charge;
import org.mifosplatform.portfolio.charge.domain.ChargeRepository; import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.mifosplatform.portfolio.charge.exception.ChargeIsNotActiveException;
import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException;
import org.mifosplatform.portfolio.fund.domain.Fund; import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.fund.domain.FundRepository; import org.mifosplatform.portfolio.fund.domain.FundRepository;
import org.mifosplatform.portfolio.fund.exception.FundNotFoundException; import org.mifosplatform.portfolio.fund.exception.FundNotFoundException;
@@ -31,13 +29,15 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingS
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException; import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException; import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.slf4j.Logger; import com.google.gson.JsonArray;
import org.slf4j.LoggerFactory; import com.google.gson.JsonObject;
@Service @Service
public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService { public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService {
@@ -49,7 +49,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final AprCalculator aprCalculator; private final AprCalculator aprCalculator;
private final FundRepository fundRepository; private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository; private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final ChargeRepository chargeRepository; private final ChargeRepositoryWrapper chargeRepository;
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
@Autowired @Autowired
@@ -57,7 +57,8 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final LoanProductCommandFromApiJsonDeserializer fromApiJsonDeserializer, final LoanProductRepository loanProductRepository, final LoanProductCommandFromApiJsonDeserializer fromApiJsonDeserializer, final LoanProductRepository loanProductRepository,
final AprCalculator aprCalculator, final FundRepository fundRepository, final AprCalculator aprCalculator, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository, final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final ChargeRepository chargeRepository, final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) { final ChargeRepositoryWrapper chargeRepository,
final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) {
this.context = context; this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.loanProductRepository = loanProductRepository; this.loanProductRepository = loanProductRepository;
@@ -78,15 +79,13 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
this.fromApiJsonDeserializer.validateForCreate(command.json()); this.fromApiJsonDeserializer.validateForCreate(command.json());
// associating fund with loan product at creation is optional for
// now.
final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId")); final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId"));
final Long transactionProcessingStrategyId = command.longValueOfParameterNamed("transactionProcessingStrategyId"); final Long transactionProcessingStrategyId = command.longValueOfParameterNamed("transactionProcessingStrategyId");
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId); final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
final String currencyCode = command.stringValueOfParameterNamed("currencyCode"); final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
final Set<Charge> charges = this.assembleSetOfCharges(command, currencyCode); final List<Charge> charges = this.assembleListOfProductCharges(command, currencyCode);
final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command, final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command,
this.aprCalculator); this.aprCalculator);
@@ -131,7 +130,6 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
public CommandProcessingResult updateLoanProduct(final Long loanProductId, final JsonCommand command) { public CommandProcessingResult updateLoanProduct(final Long loanProductId, final JsonCommand command) {
try { try {
this.context.authenticatedUser(); this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForUpdate(command.json()); this.fromApiJsonDeserializer.validateForUpdate(command.json());
@@ -141,8 +139,6 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final Map<String, Object> changes = product.update(command, this.aprCalculator); final Map<String, Object> changes = product.update(command, this.aprCalculator);
// associating fund with loan product at creation is optional for
// now.
if (changes.containsKey("fundId")) { if (changes.containsKey("fundId")) {
final Long fundId = (Long) changes.get("fundId"); final Long fundId = (Long) changes.get("fundId");
final Fund fund = findFundByIdIfProvided(fundId); final Fund fund = findFundByIdIfProvided(fundId);
@@ -156,10 +152,13 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
} }
if (changes.containsKey("charges")) { if (changes.containsKey("charges")) {
final Set<Charge> charges = this.assembleSetOfCharges(command, product.getCurrency().getCode()); final List<Charge> productCharges = this.assembleListOfProductCharges(command, product.getCurrency().getCode());
product.update(charges); boolean updated = product.update(productCharges);
if (!updated) {
changes.remove("charges");
}
} }
// accounting related changes // accounting related changes
boolean accountingTypeChanged = changes.containsKey("accountingRule"); boolean accountingTypeChanged = changes.containsKey("accountingRule");
final Map<String, Object> accountingMappingChanges = accountMappingWritePlatformService.updateLoanProductToGLAccountMapping( final Map<String, Object> accountingMappingChanges = accountMappingWritePlatformService.updateLoanProductToGLAccountMapping(
@@ -183,29 +182,33 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
} }
private Set<Charge> assembleSetOfCharges(final JsonCommand command, final String currencyCode) { private List<Charge> assembleListOfProductCharges(final JsonCommand command, final String currencyCode) {
final Set<Charge> charges = new HashSet<Charge>(); final List<Charge> charges = new ArrayList<Charge>();
final String[] chargesArray = command.arrayValueOfParameterNamed("charges");
String loanProductCurrencyCode = command.stringValueOfParameterNamed("currencyCode"); String loanProductCurrencyCode = command.stringValueOfParameterNamed("currencyCode");
if (loanProductCurrencyCode == null) { if (loanProductCurrencyCode == null) {
loanProductCurrencyCode = currencyCode; loanProductCurrencyCode = currencyCode;
} }
if (!ObjectUtils.isEmpty(chargesArray)) { if (command.parameterExists("charges")) {
for (final String chargeId : chargesArray) { JsonArray chargesArray = command.arrayOfParameterNamed("charges");
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final Long id = Long.valueOf(chargeId); final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
final Charge charge = this.chargeRepository.findOne(id); if (jsonObject.has("id")) {
if (charge == null || charge.isDeleted()) { throw new ChargeNotFoundException(id); } final Long id = jsonObject.get("id").getAsLong();
if (!charge.isActive()) { throw new ChargeIsNotActiveException(id, charge.getName()); }
if (!loanProductCurrencyCode.equals(charge.getCurrencyCode())) { final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
String errorMessage = "Charge and Loan Product must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.loan.product", errorMessage); if (!loanProductCurrencyCode.equals(charge.getCurrencyCode())) {
String errorMessage = "Charge and Loan Product must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.loan.product", errorMessage);
}
charges.add(charge);
}
} }
charges.add(charge);
} }
} }
@@ -224,6 +227,10 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final String name = command.stringValueOfParameterNamed("name"); final String name = command.stringValueOfParameterNamed("name");
throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.name", "Loan product with name `" + name throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.name", "Loan product with name `" + name
+ "` already exists", "name", name); + "` already exists", "name", name);
} else if (realCause.getMessage().contains("Duplicate entry")) {
Object[] args = null;
throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.charge",
"Loan product may only have one charge of each type.`", "charges", args);
} }
logAsErrorUnexpectedDataIntegrityException(dve); logAsErrorUnexpectedDataIntegrityException(dve);
@@ -234,5 +241,4 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve); logger.error(dve.getMessage(), dve);
} }
} }