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
repaymentEvery repaymentFrequencyType<br> e.g. 10
(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>
</tr>
<tr class=alt>
@@ -5476,15 +5476,15 @@ Request Body:
<td class=fielddesc>Used like: interestRatePerPeriod%
interestRateFrequencyType - interestType<br> e.g. 12.0000%
<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>
</tr>
<tr class=alt>
<td>amortizationType</td>
</tr>
<tr>
<td class=fielddesc><span>Example Values:</span> Equal
principle payments, Equal installments</td>
<td class=fielddesc><span>Example Values:</span> 0=Equal
principle payments, 1=Equal installments</td>
</tr>
</td>
</tr>
@@ -5495,7 +5495,7 @@ Request Body:
<td class=fielddesc>Used like: interestRatePerPeriod%
interestRateFrequencyType - interestType<br> e.g. 12.0000%
Per year - <b>Declining Balance</b> <br>
<span>Example Values:</span> Declining Balance, Flat
<span>Example Values:</span> 0=Declining Balance, 1=Flat
</td>
</tr>
</td>
@@ -5504,8 +5504,8 @@ Request Body:
<td>interestCalculationPeriodType</td>
</tr>
<tr>
<td class=fielddesc><span>Example Values:</span> Same as
repayment period, Daily</td>
<td class=fielddesc><span>Example Values:</span> 0=Daily,
1=Same as repayment period</td>
</tr>
</td>
</tr>
@@ -5514,8 +5514,8 @@ Request Body:
<td>accountingRule</td>
</tr>
<tr>
<td class=fielddesc><span>Example Values:</span>NONE(1),
CASH_BASED(2), ACCRUAL_BASED(3)</td>
<td class=fielddesc><span>Example Values:</span>1=NONE,
2=CASH_BASED, 3=ACCRUAL_BASED</td>
</tr>
</td>
</tr>
@@ -5541,61 +5541,55 @@ GET https://DomainName/api/v1/loanproducts
</code>
<code class="method-response">
[
{
"id": 1,
"name": "A new loan product for me",
"currency": {
"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": {
"id": 1,
"code": "accountingRuleType.none",
"value": "NONE"
}
{
"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",
"accountingRule": {
"id": 1,
"code": "accountingRuleType.none",
"value": "NONE"
}
}
]
</code>
</div>
@@ -5625,17 +5619,12 @@ GET https://DomainName/api/v1/loanproducts/template
<code class="method-response">
{
"currency": {
"code": "USD",
"name": "US Dollar",
"code": "UGX",
"name": "Uganda Shilling",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"loanTermFrequencyType": {
"id": 2,
"code": "loanTermFrequency.periodFrequencyType.months",
"value": "Months"
"displaySymbol": "USh",
"nameCode": "currency.UGX",
"displayLabel": "Uganda Shilling (USh)"
},
"repaymentFrequencyType": {
"id": 2,
@@ -5662,43 +5651,51 @@ GET https://DomainName/api/v1/loanproducts/template
"code": "interestCalculationPeriodType.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": [
{
"code": "USD",
"name": "US Dollar",
"code": "UGX",
"name": "Uganda Shilling",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
"displaySymbol": "USh",
"nameCode": "currency.UGX",
"displayLabel": "Uganda Shilling (USh)"
}
],
"fundOptions": [
"repaymentFrequencyTypeOptions": [
{
"id": 0,
"code": "repaymentFrequency.periodFrequencyType.days",
"value": "Days"
},
{
"id": 1,
"name": "EU Agri Fund"
}
],
"transactionProcessingStrategyOptions": [
{
"id": 1,
"code": "mifos-standard-strategy",
"name": "Mifos style"
"code": "repaymentFrequency.periodFrequencyType.weeks",
"value": "Weeks"
},
{
"id": 2,
"code": "heavensfamily-strategy",
"name": "Heavensfamily"
"code": "repaymentFrequency.periodFrequencyType.months",
"value": "Months"
}
],
"interestRateFrequencyTypeOptions": [
{
"id": 2,
"code": "interestRateFrequency.periodFrequencyType.months",
"value": "Per month"
},
{
"id": 3,
"code": "creocore-strategy",
"name": "Creocore"
},
{
"id": 4,
"code": "rbi-india-strategy",
"name": "RBI (India)"
"code": "interestRateFrequency.periodFrequencyType.years",
"value": "Per year"
}
],
"amortizationTypeOptions": [
@@ -5737,58 +5734,274 @@ GET https://DomainName/api/v1/loanproducts/template
"value": "Same as repayment period"
}
],
"repaymentFrequencyTypeOptions": [
{
"id": 0,
"code": "repaymentFrequency.periodFrequencyType.days",
"value": "Days"
},
{
"id": 1,
"code": "repaymentFrequency.periodFrequencyType.weeks",
"value": "Weeks"
},
"transactionProcessingStrategyOptions": [
{
"id": 2,
"code": "repaymentFrequency.periodFrequencyType.months",
"value": "Months"
}
],
"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"
"code": "heavensfamily-strategy",
"name": "Heavensfamily"
}
],
"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,
"name": "Loan service fee",
@@ -5820,234 +6033,11 @@ GET https://DomainName/api/v1/loanproducts/template
}
}
],
"accountingOptions": {
"assetAccountOptions": [
{
"id": 1,
"name": "Fund Source For Loans",
"glCode": "100001",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 2,
"name": "Interest Receivable",
"glCode": "100002",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 3,
"name": "Fees Receivable",
"glCode": "100003",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 4,
"name": "Penalties Receivable",
"glCode": "100004",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 1,
"code": "accountType.asset",
"value": "ASSET"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "better duddette"
}
],
"expenseAccountOptions": [
{
"id": 5,
"name": "Written Off Losses",
"glCode": "500005",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 5,
"code": "accountType.expense",
"value": "EXPENSE"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
}
],
"incomeAccountOptions": [
{
"id": 6,
"name": "Income from Interest",
"glCode": "400001",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 7,
"name": "Income from Fees",
"glCode": "400002",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
},
{
"id": 8,
"name": "Income from Penalties",
"glCode": "400003",
"disabled": false,
"manualEntriesAllowed": true,
"type": {
"id": 4,
"code": "accountType.income",
"value": "INCOME"
},
"usage": {
"id": 1,
"code": "accountUsage.detail",
"value": "DETAIL"
},
"description": "Desc"
}
]
},
"accountingMappings": {}
}
</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">
{
"accountingRule": {
"id": 1,
"name": "A new loan product for me",
"currency": {
"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": "accountingRuleType.none",
"value": "NONE"
}
}
</code>
</div>
@@ -6074,11 +6064,17 @@ GET https://DomainName/api/v1/loanproducts/{productId}
</tr>
<tr class=alt>
<td>name, currencyCode, digitsAfterDecimal, principal,
inArrearsTolerance, repaymentFrequencyType, repaymentEvery,
numberOfRepayments, interestRatePerPeriod,
interestRateFrequencyType, amortizationType, interestType,
interestCalculationPeriodType, accountingRule</td>
numberOfRepayments, repaymentEvery, repaymentFrequencyType,
interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType,
interestCalculationPeriodType, transactionProcessingStrategyId, accountingRule</td>
</tr>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>inArrearsTolerance, charges</td>
</tr>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Additional Mandatory Fields for Cash(2) based accounting</div></td>
</tr>
@@ -6107,7 +6103,7 @@ POST loanproducts
Content-Type: application/json
Request Body:
{
"name": "A new loan product for me",
"name": "LP with Charges but no accounting)",
"currencyCode": "USD",
"locale": "en_GB",
"digitsAfterDecimal": "2",
@@ -6115,12 +6111,14 @@ Request Body:
"numberOfRepayments": "12",
"repaymentEvery": "1",
"repaymentFrequencyType": 2,
"transactionProcessingStrategyId": 1,
"interestRatePerPeriod": "1.75",
"interestRateFrequencyType": 2,
"amortizationType": 1,
"interestType": 0,
"interestCalculationPeriodType": 1,
"inArrearsTolerance": "0",
"transactionProcessingStrategyId": 1,
"charges":[{"id":"1"},{"id":"1"}],
"accountingRule": 1
}
</code>
@@ -6134,7 +6132,7 @@ POST loanproducts
Content-Type: application/json
Request Body:
{
"name": "A new loan product for me",
"name": "LP Cash Accounting",
"currencyCode": "USD",
"locale": "en_GB",
"digitsAfterDecimal": "2",
@@ -6148,13 +6146,13 @@ Request Body:
"amortizationType": 1,
"interestType": 0,
"interestCalculationPeriodType": 1,
"accountingRule":"3",
"fundSourceAccountId":"15",
"loanPortfolioAccountId":"16",
"interestOnLoanAccountId":"14",
"incomeFromFeeAccountId":"13",
"incomeFromPenaltyAccountId":"12",
"writeOffAccountId":"11"
"accountingRule":"2",
"fundSourceAccountId":"4",
"loanPortfolioAccountId":"8",
"interestOnLoanAccountId":"34",
"incomeFromFeeAccountId":"37",
"incomeFromPenaltyAccountId":"35",
"writeOffAccountId":"41"
}
</code>
<code class="method-response">
@@ -6167,7 +6165,7 @@ POST loanproducts
Content-Type: application/json
Request Body:
{
"name": "A new loan product for me",
"name": "LP Accrual Accounting",
"currencyCode": "USD",
"locale": "en_GB",
"digitsAfterDecimal": "2",
@@ -6182,15 +6180,15 @@ Request Body:
"interestType": 0,
"interestCalculationPeriodType": 1,
"accountingRule":"3",
"fundSourceAccountId":"15",
"loanPortfolioAccountId":"16",
"receivableInterestAccountId":"10",
"receivableFeeAccountId":"9",
"receivablePenaltyAccountId":"8",
"interestOnLoanAccountId":"14",
"incomeFromFeeAccountId":"13",
"incomeFromPenaltyAccountId":"12",
"writeOffAccountId":"11"
"fundSourceAccountId":"4",
"loanPortfolioAccountId":"8",
"receivableInterestAccountId":"9",
"receivableFeeAccountId":"11",
"receivablePenaltyAccountId":"10",
"interestOnLoanAccountId":"34",
"incomeFromFeeAccountId":"37",
"incomeFromPenaltyAccountId":"35",
"writeOffAccountId":"41"
}
</code>
<code class="method-response">
@@ -6823,7 +6821,8 @@ Request Body:
"locale": "en",
"amount": "230.56",
"chargeTimeType": "1",
"chargeCalculationType": "1"
"chargeCalculationType": "1",
"active": true
}
</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)
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.service.PlatformPasswordEncoder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
/**
@@ -39,8 +40,8 @@ public final class JsonCommand {
private final Long apptableId;
private final Long datatableId;
private final Long codeId;
private String supportedEntityType;
private Long supportedEntityId;
private final String supportedEntityType;
private final Long supportedEntityId;
private final String transactionId;
public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper,
@@ -86,6 +87,15 @@ public final class JsonCommand {
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() {
return this.commandId;
}
@@ -347,6 +357,10 @@ public final class JsonCommand {
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,
final PlatformPasswordEncoder platformPasswordEncoder, final Long saltValue) {
boolean isChanged = false;
@@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterExcep
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
@@ -46,7 +47,7 @@ public class FromJsonHelper {
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);
}
@@ -93,6 +94,10 @@ public class FromJsonHelper {
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) {
return helperDelegator.extractArrayNamed(parameterName, element, new HashSet<String>());
}
@@ -109,7 +114,7 @@ public class FromJsonHelper {
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>());
}
@@ -32,7 +32,7 @@ import com.google.gson.JsonPrimitive;
* Helper class to extract values of json named attributes.
*/
public class JsonParserHelper {
public boolean parameterExists(final String parameterName, final JsonElement element) {
return element.getAsJsonObject().has(parameterName);
}
@@ -137,19 +137,21 @@ public class JsonParserHelper {
}
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
* specific formatted strings Ex "1,000" etc
* Please note that this method does not support extracting Integers from
* locale specific formatted strings Ex "1,000" etc
*
* @param parameterName
* @param element
* @param parametersPassedInRequest
* @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;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
@@ -211,6 +213,19 @@ public class JsonParserHelper {
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
*/
@@ -391,8 +406,8 @@ public class JsonParserHelper {
} catch (NumberFormatException e) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer", "The parameter "
+ parameterName + " has value: " + numericalValueFormatted + " which is invalid integer.", parameterName, numericalValueFormatted);
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer", "The parameter " + parameterName
+ " has value: " + numericalValueFormatted + " which is invalid integer.", parameterName, numericalValueFormatted);
dataValidationErrors.add(error);
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) {
if (clientApplicationLocale == null) {
@@ -11,11 +11,12 @@ import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
/**
* Immutable data object for charge data.
*/
public class ChargeData {
public class ChargeData implements Comparable<ChargeData> {
private final Long id;
private final String name;
@@ -72,36 +73,8 @@ public class ChargeData {
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
public boolean equals(Object obj) {
public boolean equals(final Object obj) {
ChargeData chargeData = (ChargeData) obj;
return this.id.equals(chargeData.id);
}
@@ -110,4 +83,16 @@ public class ChargeData {
public int 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.UniqueConstraint;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.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.service.ChargeEnumerations;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.springframework.data.jpa.domain.AbstractPersistable;
@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,
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.amount = amount;
this.currencyCode = currencyCode;
@@ -83,6 +90,24 @@ public class Charge extends AbstractPersistable<Long> {
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() {
return name;
}
@@ -180,8 +205,9 @@ public class Charge extends AbstractPersistable<Long> {
actualChanges.put(activeParamName, 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;
}
@@ -196,4 +222,15 @@ public class Charge extends AbstractPersistable<Long> {
this.deleted = true;
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>();
for (ChargeData charge : product.charges()) {
charges.add(LoanChargeData.newLoanChargeDetails(charge.getId(), charge.getName(), charge.getCurrency(), charge.getAmount(),
charge.getChargeTimeType(), charge.getChargeCalculationType(), charge.isPenalty()));
charges.add(charge.toLoanChargeData());
}
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 {
private final Set<String> LOAN_PRODUCT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "description", "fundId",
"fundName", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "principal", "inArrearsTolerance",
"numberOfRepayments", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentFrequencyType",
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "charges", "createdOn",
"lastModifedOn", "currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions",
"chargeOptions", "accountingOptions", "accountingRule","accountingRuleOptions","accountingMappings","accountingMappingOptions"));
"fundName", "currency", "principal", "numberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "interestRatePerPeriod",
"interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType", "interestCalculationPeriodType",
"inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "charges", "accountingRule",
"accountingMappings", "fundOptions", "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions",
"amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions",
"accountingMappingOptions"));
private final String resourceNameForPermissions = "LOANPRODUCT";
@@ -129,7 +130,7 @@ public class LoanProductsApiResource {
@Path("template")
@Consumes({ 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);
@@ -154,7 +155,8 @@ public class LoanProductsApiResource {
Map<String, Object> accountingMappings = null;
if (loanProduct.hasAccountingEnabled()) {
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct.accountingRuleType().getId().intValue());
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct
.accountingRuleType().getId().intValue());
loanProduct = LoanProductData.withAccountingMappings(loanProduct, accountingMappings);
}
@@ -182,7 +184,6 @@ public class LoanProductsApiResource {
final boolean feeChargesOnly = true;
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
chargeOptions.removeAll(productData.charges());
if (chargeOptions.isEmpty()) {
chargeOptions = null;
}
@@ -192,7 +193,6 @@ public class LoanProductsApiResource {
final List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
final List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService
.retrieveLoanInterestRateCalculatedInPeriodOptions();
final List<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
final List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
final List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService
.retrieveInterestRateFrequencyTypeOptions();
@@ -204,29 +204,29 @@ public class LoanProductsApiResource {
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService
.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);
if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null;
}
accountOptions.put("assetAccountOptions", assetAccountOptions);
List<GLAccountData> incomeAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME);
if (incomeAccountOptions.isEmpty()) {
incomeAccountOptions = null;
}
accountOptions.put("incomeAccountOptions", incomeAccountOptions);
List<GLAccountData> expenseAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE);
if (expenseAccountOptions.isEmpty()) {
expenseAccountOptions = null;
}
accountOptions.put("expenseAccountOptions", expenseAccountOptions);
List<EnumOptionData> accountingRuleTypeOptions = dropdownReadPlatformService.retrieveAccountingRuleTypeOptions();
return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions,accountingMappings);
interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions,
transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions, accountingMappings);
}
}
@@ -33,47 +33,46 @@ public class LoanProductData {
private final String description;
private final Long fundId;
private final String fundName;
// terms
private final CurrencyData currency;
private final BigDecimal principal;
private final Integer numberOfRepayments;
private final Integer loanTermFrequency;
private final Integer repaymentEvery;
private final EnumOptionData loanTermFrequencyType;
private final EnumOptionData repaymentFrequencyType;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
private final BigDecimal interestRatePerPeriod;
private final EnumOptionData interestRateFrequencyType;
private final BigDecimal annualInterestRate;
// settings
private final EnumOptionData amortizationType;
private final EnumOptionData interestType;
private final EnumOptionData interestCalculationPeriodType;
private final BigDecimal inArrearsTolerance;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
private final EnumOptionData accountingRule;
// charges
private final Collection<ChargeData> charges;
// accounting
private final EnumOptionData accountingRule;
@SuppressWarnings("unused")
private final Map<String, Object> accountingMappings;
// template related
private final Collection<CurrencyData> currencyOptions;
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> interestTypeOptions;
private final List<EnumOptionData> interestCalculationPeriodTypeOptions;
private final List<EnumOptionData> repaymentFrequencyTypeOptions;
private final List<EnumOptionData> interestRateFrequencyTypeOptions;
private final List<EnumOptionData> loanTermFrequencyTypeOptions;
private final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions;
private final Collection<ChargeData> chargeOptions;
// accounting related template fields
private final List<EnumOptionData> accountingRuleOptions;
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.
*/
@@ -83,11 +82,9 @@ public class LoanProductData {
final BigDecimal principal = null;
final BigDecimal tolerance = null;
final Integer numberOfRepayments = null;
final Integer loanTermFrequency = null;
final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null;
final BigDecimal annualInterestRate = null;
final EnumOptionData loanTermFrequencyType = null;
final EnumOptionData repaymentFrequencyType = null;
final EnumOptionData interestRateFrequencyType = null;
final EnumOptionData amortizationType = null;
@@ -100,10 +97,10 @@ public class LoanProductData {
final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = null;
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyName, charges, accountingType);
}
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
@@ -114,11 +111,9 @@ public class LoanProductData {
final BigDecimal principal = null;
final BigDecimal tolerance = null;
final Integer numberOfRepayments = null;
final Integer loanTermFrequency = null;
final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null;
final BigDecimal annualInterestRate = null;
final EnumOptionData loanTermFrequencyType = LoanEnumerations.loanTermFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(AmortizationMethod.EQUAL_INSTALLMENTS);
@@ -132,25 +127,24 @@ public class LoanProductData {
final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = LoanEnumerations.accountingRuleType(AccountingRuleType.NONE);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyName, charges, accountingType);
}
public static LoanProductData withAccountingMappings(final LoanProductData productData, final Map<String, Object> accountingMappings) {
return new LoanProductData(productData, productData.chargeOptions, productData.currencyOptions,
productData.amortizationTypeOptions, productData.interestTypeOptions, productData.interestCalculationPeriodTypeOptions,
productData.loanTermFrequencyTypeOptions, productData.repaymentFrequencyTypeOptions,
productData.interestRateFrequencyTypeOptions, productData.fundOptions, productData.transactionProcessingStrategyOptions,
productData.accountingMappingOptions, productData.accountingRuleOptions, accountingMappings);
productData.repaymentFrequencyTypeOptions, productData.interestRateFrequencyTypeOptions, productData.fundOptions,
productData.transactionProcessingStrategyOptions, productData.accountingMappingOptions, productData.accountingRuleOptions,
accountingMappings);
}
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 Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate,
final EnumOptionData loanTermFrequencyType, final EnumOptionData repaymentFrequencyType,
final BigDecimal principal, final BigDecimal tolerance, final Integer numberOfRepayments, final Integer repaymentEvery,
final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType,
final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName,
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
@@ -162,11 +156,11 @@ public class LoanProductData {
this.principal = principal;
this.inArrearsTolerance = tolerance;
this.numberOfRepayments = numberOfRepayments;
this.loanTermFrequency = loanTermFrequency;
// this.loanTermFrequency = loanTermFrequency;
this.repaymentEvery = repaymentEvery;
this.interestRatePerPeriod = interestRatePerPeriod;
this.annualInterestRate = annualInterestRate;
this.loanTermFrequencyType = loanTermFrequencyType;
// this.loanTermFrequencyType = loanTermFrequencyType;
this.repaymentFrequencyType = repaymentFrequencyType;
this.interestRateFrequencyType = interestRateFrequencyType;
this.amortizationType = amortizationType;
@@ -186,7 +180,7 @@ public class LoanProductData {
this.amortizationTypeOptions = null;
this.interestTypeOptions = null;
this.interestCalculationPeriodTypeOptions = null;
this.loanTermFrequencyTypeOptions = null;
// this.loanTermFrequencyTypeOptions = null;
this.repaymentFrequencyTypeOptions = null;
this.interestRateFrequencyTypeOptions = null;
@@ -198,31 +192,29 @@ public class LoanProductData {
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> amortizationTypeOptions,
final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions,
final List<EnumOptionData> loanTermFrequencyTypeOptions, final List<EnumOptionData> repaymentFrequencyTypeOptions,
final List<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions,
final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions,
final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
final Map<String, Object> accountingMappings) {
this.id = productData.id;
this.name = productData.name;
this.description = productData.description;
this.fundId = productData.fundId;
this.fundName = productData.fundName;
this.principal = productData.principal;
this.inArrearsTolerance = productData.inArrearsTolerance;
this.numberOfRepayments = productData.numberOfRepayments;
this.loanTermFrequency = productData.loanTermFrequency;
this.repaymentEvery = productData.repaymentEvery;
this.interestRatePerPeriod = productData.interestRatePerPeriod;
this.annualInterestRate = productData.annualInterestRate;
this.loanTermFrequencyType = productData.loanTermFrequencyType;
this.repaymentFrequencyType = productData.repaymentFrequencyType;
this.interestRateFrequencyType = productData.interestRateFrequencyType;
this.amortizationType = productData.amortizationType;
this.interestType = productData.interestType;
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.accountingRule = productData.accountingRule;
@@ -234,17 +226,32 @@ public class LoanProductData {
this.currency = productData.currency;
}
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.interestTypeOptions = interestTypeOptions;
this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
this.loanTermFrequencyTypeOptions = loanTermFrequencyTypeOptions;
this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions;
this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions;
this.accountingMappingOptions = accountingMappingOptions;
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) {
@@ -315,10 +322,6 @@ public class LoanProductData {
return numberOfRepayments;
}
public Integer getLoanTermFrequency() {
return loanTermFrequency;
}
public Integer getRepaymentEvery() {
return repaymentEvery;
}
@@ -331,10 +334,6 @@ public class LoanProductData {
return annualInterestRate;
}
public EnumOptionData getLoanTermFrequencyType() {
return loanTermFrequencyType;
}
public EnumOptionData getRepaymentFrequencyType() {
return repaymentFrequencyType;
}
@@ -359,10 +358,6 @@ public class LoanProductData {
return fundOptions;
}
public Collection<TransactionProcessingStrategyData> getTransactionProcessingStrategyOptions() {
return transactionProcessingStrategyOptions;
}
public List<EnumOptionData> getAmortizationTypeOptions() {
return amortizationTypeOptions;
}
@@ -383,10 +378,6 @@ public class LoanProductData {
return interestRateFrequencyTypeOptions;
}
public List<EnumOptionData> getLoanTermFrequencyTypeOptions() {
return loanTermFrequencyTypeOptions;
}
public Collection<ChargeData> getChargeOptions() {
return chargeOptions;
}
@@ -5,13 +5,14 @@
*/
package org.mifosplatform.portfolio.loanproduct.data;
/**
* Immutable data object representing a transaction strategy option for a loan.
*/
public final class TransactionProcessingStrategyData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final String code;
@SuppressWarnings("unused")
private 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.name = name;
}
public Long id() {
return this.id;
}
public String name() {
return this.name;
}
}
@@ -6,7 +6,7 @@
package org.mifosplatform.portfolio.loanproduct.domain;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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.springframework.data.jpa.domain.AbstractPersistable;
import com.google.gson.JsonArray;
/**
* Loan products allow for categorisation of an organisations loans into
* 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.
*/
@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> {
@ManyToOne
@@ -50,7 +52,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@Column(name = "name", nullable = false , unique = true)
@Column(name = "name", nullable = false, unique = true)
private String name;
@Column(name = "description")
@@ -58,7 +60,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
@ManyToMany
@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
private final LoanProductRelatedDetail loanProductRelatedDetail;
@@ -67,7 +69,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
private Integer accountingRule;
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 description = command.stringValueOfParameterNamed("description");
@@ -108,7 +110,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
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) {
this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy;
@@ -136,10 +138,6 @@ public class LoanProduct extends AbstractPersistable<Long> {
return this.loanProductRelatedDetail.getCurrency();
}
public Set<Charge> getCharges() {
return charges;
}
public void update(final Fund fund) {
this.fund = fund;
}
@@ -148,8 +146,17 @@ public class LoanProduct extends AbstractPersistable<Long> {
this.transactionProcessingStrategy = strategy;
}
public void update(final Set<Charge> charges) {
this.charges = charges;
public boolean update(final List<Charge> newProductCharges) {
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() {
@@ -202,30 +209,17 @@ public class LoanProduct extends AbstractPersistable<Long> {
}
final String chargesParamName = "charges";
if (command.isChangeInArrayParameterNamed(chargesParamName, getChargesIds())) {
final String[] newValue = command.arrayValueOfParameterNamed(chargesParamName);
actualChanges.put(chargesParamName, newValue);
if (command.hasParameter(chargesParamName)) {
JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName);
if (jsonArray != null) {
}
actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName));
}
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() {
return AccountingRuleType.CASH_BASED.getValue().equals(this.accountingRule);
}
@@ -35,10 +35,10 @@ public final class LoanProductCommandFromApiJsonDeserializer {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "description", "fundId",
"transactionProcessingStrategyId", "currencyCode", "digitsAfterDecimal", "principal", "inArrearsTolerance",
"interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", "interestRateFrequencyType",
"amortizationType", "interestType", "interestCalculationPeriodType", "charges", "locale", "accountingRule",
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("locale", "name", "description", "fundId",
"currencyCode", "digitsAfterDecimal", "principal", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType",
"interestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"inArrearsTolerance", "transactionProcessingStrategyId", "charges", "accountingRule",
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
@@ -74,6 +74,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
}
// terms
final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element);
baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3);
@@ -83,12 +84,6 @@ public final class LoanProductCommandFromApiJsonDeserializer {
final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
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);
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero();
@@ -99,14 +94,14 @@ public final class LoanProductCommandFromApiJsonDeserializer {
.extractIntegerNamed("repaymentFrequencyType", element, Locale.getDefault());
baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero();
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
// settings
final Integer amortizationType = fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
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()
.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
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
@@ -176,7 +178,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(String json) {
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
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 repaymentEvery = JdbcSupport.getInteger(rs, "repaidEvery");
final Integer accountingTypeId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = LoanEnumerations.accountingRuleType(accountingTypeId);
final Integer loanTermFrequency = repaymentEvery * numberOfRepayments;
final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = LoanEnumerations.accountingRuleType(accountingRuleId);
final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod");
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
final int repaymentFrequencyTypeId = JdbcSupport.getInteger(rs, "repaymentPeriodFrequency");
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeId);
final EnumOptionData loanTermFrequencyType = LoanEnumerations.loanTermFrequencyType(repaymentFrequencyTypeId);
final int amortizationTypeId = JdbcSupport.getInteger(rs, "amortizationMethod");
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeId);
@@ -160,10 +157,10 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final EnumOptionData interestCalculationPeriodType = LoanEnumerations
.interestCalculationPeriodType(interestCalculationPeriodTypeId);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
transactionStrategyId, transactionStrategyName, this.charges, accountingRuleType);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionStrategyId, transactionStrategyName,
this.charges, accountingRuleType);
}
}
@@ -5,9 +5,9 @@
*/
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.Set;
import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
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.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.charge.domain.Charge;
import org.mifosplatform.portfolio.charge.domain.ChargeRepository;
import org.mifosplatform.portfolio.charge.exception.ChargeIsNotActiveException;
import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException;
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.fund.domain.FundRepository;
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.LoanProductNotFoundException;
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.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@Service
public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService {
@@ -49,7 +49,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final AprCalculator aprCalculator;
private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final ChargeRepository chargeRepository;
private final ChargeRepositoryWrapper chargeRepository;
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
@Autowired
@@ -57,7 +57,8 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final LoanProductCommandFromApiJsonDeserializer fromApiJsonDeserializer, final LoanProductRepository loanProductRepository,
final AprCalculator aprCalculator, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final ChargeRepository chargeRepository, final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) {
final ChargeRepositoryWrapper chargeRepository,
final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) {
this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.loanProductRepository = loanProductRepository;
@@ -78,15 +79,13 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
this.fromApiJsonDeserializer.validateForCreate(command.json());
// associating fund with loan product at creation is optional for
// now.
final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId"));
final Long transactionProcessingStrategyId = command.longValueOfParameterNamed("transactionProcessingStrategyId");
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
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,
this.aprCalculator);
@@ -131,7 +130,6 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
public CommandProcessingResult updateLoanProduct(final Long loanProductId, final JsonCommand command) {
try {
this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForUpdate(command.json());
@@ -141,8 +139,6 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
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")) {
final Long fundId = (Long) changes.get("fundId");
final Fund fund = findFundByIdIfProvided(fundId);
@@ -156,10 +152,13 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
}
if (changes.containsKey("charges")) {
final Set<Charge> charges = this.assembleSetOfCharges(command, product.getCurrency().getCode());
product.update(charges);
final List<Charge> productCharges = this.assembleListOfProductCharges(command, product.getCurrency().getCode());
boolean updated = product.update(productCharges);
if (!updated) {
changes.remove("charges");
}
}
// accounting related changes
boolean accountingTypeChanged = changes.containsKey("accountingRule");
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 String[] chargesArray = command.arrayValueOfParameterNamed("charges");
final List<Charge> charges = new ArrayList<Charge>();
String loanProductCurrencyCode = command.stringValueOfParameterNamed("currencyCode");
if (loanProductCurrencyCode == null) {
loanProductCurrencyCode = currencyCode;
}
if (!ObjectUtils.isEmpty(chargesArray)) {
for (final String chargeId : chargesArray) {
if (command.parameterExists("charges")) {
JsonArray chargesArray = command.arrayOfParameterNamed("charges");
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final Long id = Long.valueOf(chargeId);
final Charge charge = this.chargeRepository.findOne(id);
if (charge == null || charge.isDeleted()) { throw new ChargeNotFoundException(id); }
if (!charge.isActive()) { throw new ChargeIsNotActiveException(id, charge.getName()); }
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
if (jsonObject.has("id")) {
final Long id = jsonObject.get("id").getAsLong();
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);
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
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");
throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.name", "Loan product with 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);
@@ -234,5 +241,4 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve);
}
}