MIFOSX-2200 Adding Provisioning Implementation

This commit is contained in:
unknown
2015-10-20 19:42:47 +05:30
parent 5deba1c71a
commit 7fabfa5cb9
77 changed files with 4752 additions and 26 deletions
+588 -1
View File
@@ -1679,6 +1679,25 @@
<td></td>
<td></td>
</tr>
<tr>
<td><a href="#provisioningentries">Provisioning Entries</a></td>
<td>provisioningentries</td>
<td><a href="#provisioningentries_create">Create Provisioning Entry</a><br> </td>
<td><a href="#provisioningentries_list">List Provisioning Entries</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>provisioningentries/{entryId}</td>P
<td></td>
<td><a href="#provisioningentry_retrieve">Retrieve Provisioning Entry</a></td>
<td><a href="#provisioningentry_recreate">Recreates Provisionin Entry</a></td>
<td><a href="#provisioningentry_addjournals">Add Provisioning Journal Entries</a></td>
</tr>
</table>
</div>
</div>
@@ -2090,7 +2109,23 @@
<td><a href="#paymenttype_update">Update Payment Type</a></td>
<td><a href="#paymnettype_delete">Delete Payment Type</a></td>
</tr>
<tr>
<td><a href="#provisioningcriteria">Provisioning Criteria</a></td>
<td>provisioningcriteria</td>
<td><a href="#create_provisioningcriteria">Create Provisioning Criteria</a></td>
<td><a href="#provisioningcriteria_list">List Provisioning Criteria</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>provisioningcriteria/{criteriaId}</td>
<td><a href="#retrieve_provisioningcriteria">Retrieve Provisioning Criteria</a></td>
<td><a href="#update_provisioningcriteria">Update Provisioning Criteria</a></td>
<td><a href="#delete_provisioningcriteria">Delete Provisioning Criteria</a></td>
<td></td>
<td></td>
</tr>
</table>
</div>
</div>
@@ -33626,6 +33661,244 @@ Request Body:
</div>
</div>
<!-- Provisioning Entries -->
<a id="provisioningentries" name="provisioningentries" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h3>Provisioning Entries</h3>
<p>This defines the Provisioning Entries for all active loan products
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td>
</tr>
<tr class=alt>
<td>date</td>
</tr>
<tr>
<td class=fielddesc>Date on which day provisioning entries should be created
</td>
</tr>
<tr class=alt>
<td>createjournalentries</td>
</tr>
<tr>
<td class=fielddesc>
Boolean variable whether to add journal entries for generated provisioning entries
</td>
</tr>
</table>
</div>
</div>
<a id="provisioningentries_create" name="provisioningentries_create"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Create new Provisioning Entries</h4>
<p>Creates a new Provisioning Entries</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
</tr>
<tr class=alt>
<td>date</td>
</tr>
<tr class=alt>
<td>dateFormat</td>
</tr>
<tr class=alt>
<td>locale</td>
</tr>
</table>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>createjournalentries</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">
POST https://DomainName/api/v1/provisioningentries
</code>
<code class="method-request">
POST provisioningentries
Content-Type: application/json
Request Body:
{
"date":"16 October 2015",
"locale":"en",
"dateFormat":"dd MMMM yyyy",
"createjournalentries":true
}
</code>
<code class="method-response">
{
"resourceId": 1
}
</code>
</div>
</div>
<a id="provisioningentries_list" name="provisioningentries_list"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>List all Provisioning Entries</h4>
<p>List all Provisioning Entries</p>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/provisioningentries
</code>
<code class="method-request">
GET provisioningentries
Content-Type: application/json
Request Body:
{
}
</code>
<code class="method-response">
{[
{"id":1, "journalEntry":true, "createdUser":"mifos", "createdDate":"16 October 2014"},
{"id":2, "journalEntry":true, "createdUser":"mifos", "createdDate":"16 October 2015"}
]
}
</code>
</div>
</div>
<a id="provisioningentry_retrieve" name="provisioningentry_retrieve"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieves a Provisioning Entry</h4>
<p>Returns the details of a generated Provisioning Entry.</p>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/provisioningentries/{privisioningEntryId}
</code>
<code class="method-request">
GET provisioningentries
Content-Type: application/json
Request Body:
{
}
</code>
<code class="method-response">
{
"id":1,
"journalEntry":true,
"createdUser":"mifos",
"createdDate":"16 October 2014",
"provisioningEntries":[
{"historyId":1,
"officeId:":1
"officeName:":"Head Office",
"currencyCode:":"USD",
"productId:":12,
"productName:":"Loan Product Name1",
"categoryId":4,
"categoryName":"LOSS",
"amountReserved":63098.29,
"liabilityAccount":4,
"liabilityCode":"Liability Account Name",
"expenseAccount":6,
"expenseCode":"Expense Account Name"
},
{"id":2,
"journalEntry":true,
"createdUser":"mifos",
"createdDate":"16 October 2014",
"provisioningEntries":[
{"historyId":1,
"officeId:":2
"officeName:":"Branch Office",
"currencyCode:":"USD",
"productId:":12,
"productName:":"Loan Product Name1",
"categoryId":2,
"categoryName":"SUB-STANDARD",
"amountReserved":63098.29,
"liabilityAccount":1,
"liabilityCode":"Liability Account Name",
"expenseAccount":2,
"expenseCode":"Expense Account Name"
}
]
}
</code>
</div>
</div>
<a id="provisioningentry_recreate" name="provisioningentry_recreate"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Recreates Provisioning Entry</h4>
<p>Recreates Provisioning Entry.</p>
</div>
<div class="method-example">
<code class="method-declaration">
POST https://DomainName/api/v1/provisioningentries/{privisioningEntryId}?command=recreateprovisioningentry
</code>
<code class="method-request">
POST provisioningentries
Content-Type: application/json
Request Body:
{
"command":"recreateprovisioningentry"
}
</code>
<code class="method-response">
{
"resourceId": 1
}
</code>
</div>
</div>
<a id="provisioningentry_addjournals" name="provisioningentry_addjournals"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieves a Provisioning Entry</h4>
<p>Returns the details of a generated Provisioning Entry.</p>
</div>
<div class="method-example">
<code class="method-declaration">
POST https://DomainName/api/v1/provisioningentries/{privisioningEntryId}?command=createjournalentry
</code>
<code class="method-request">
POST provisioningentries
Content-Type: application/json
Request Body:
{
"command":"createjournalentry"
}
</code>
<code class="method-response">
{
"resourceId": 1
}
</code>
</div>
</div>
<!-- end Periodic accrual API doc-->
<!-- start of Search API doc -->
@@ -35679,6 +35952,320 @@ No Request Body:
</code>
</div>
</div>
<!-- Provisioning -->
<a id="provisioningcriteria" name="provisioningcriteria" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h3>Provisioning Criteria</h3>
<p>This defines the Provisioning Criteria
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td>
</tr>
<tr class=alt>
<td>Provisioning Criteria Name</td>
</tr>
<tr>
<td class=fielddesc>Name the Provisioning Criteria
</td>
</tr>
<tr class=alt>
<td>Loan Products</td>
</tr>
<tr>
<td class=fielddesc>
Select all loan products for which provisioning criteria to be associated
</td>
</tr>
<tr class=alt>
<td>Provisioning Categories</td>
</tr>
<tr>
<td class=fielddesc>
Define minimum, maximum, percentage, liability account, expense account for all provisioning categories
</td>
</tr>
</table>
</div>
</div>
<a id="create_provisioningcriteria" name="create_provisioningcriteria"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Create a new Provisioning Criteria</h4>
<p>Creates a new Provisioning Criteria</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
</tr>
<tr class=alt>
<td>criteriaName</td>
</tr>
<tr class=alt>
<td>provisioningcriteria</td>
</tr>
</table>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>loanProducts</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">
POST https://DomainName/api/v1/provisioningcriteria
</code>
<code class="method-request">
POST provisioningcriteria
Content-Type: application/json
Request Body:
{
"criteriaName":"High Risk Products Criteria",
"loanProducts": [
{"id": 1,
"name": "LOAN_PRODUCT_3ODPK1",
"includeInBorrowerCycle": false},
{"id": 2,
"name": "LOAN_PRODUCT_BXW8NC",
includeInBorrowerCycle": false}
],
"provisioningcriteria": [
{"categoryId": 1,
"categoryName": "STANDARD",
"minAge": 0,
"maxAge": 3,
"provisioningPercentage": 1,
"liabilityAccount": 8,
"expenseAccount": 14},
{"categoryId": 2,
"categoryName": "SUB-STANDARD",
"minAge": 1,
"maxAge": 5,
"provisioningPercentage": 2,
"liabilityAccount": 13,
"expenseAccount": 13},
{"categoryId": 3,
"categoryName": "DOUBTFUL",
"minAge": 2,
"maxAge": 6,
"provisioningPercentage": 3,
"liabilityAccount": 9,
"expenseAccount": 10},
{"categoryId": 4,
"categoryName": "LOSS",
"minAge": 3,
"maxAge": 7,
"provisioningPercentage": 4,
"liabilityAccount": 10,
"expenseAccount": 9}]
}
</code>
<code class="method-response">
{
"resourceId": 1
}
</code>
</div>
</div>
<a id="provisioningcriteria_list" name="provisioningcriteria_list"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieves all created Provisioning Criterias</h4>
<p>Retrieves all created Provisioning Criterias</p>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/provisioningcriteria
</code>
<code class="method-request">
GET provisioningcriteria
Content-Type: application/json
Request Body:
{
}
</code>
<code class="method-response">
{
[
{"criteriaId":1,
"criteriaName":"High Risk Products Criteria",
"createdBy":"mifos"},
{"criteriaId":2,
"criteriaName":"Low Risk Products Criteria",
"createdBy":"mifos"}
]
}
</code>
</div>
</div>
<a id="retrieve_provisioningcriteria" name="retrieve_provisioningcriteria"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieves a Provisioning Criteria</h4>
<p>Retrieves a Provisioning Criteria</p>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/api/v1/provisioningcriteria/{criteriaId}
</code>
<code class="method-request">
GET provisioningcriteria/{criteriaId}
Content-Type: application/json
Request Body:
{
}
</code>
<code class="method-response">
{
"criteriaId":1
"criteriaName":"High Risk Products Criteria",
"loanProducts": [
{"id": 1,
"name": "LOAN_PRODUCT_3ODPK1",
"includeInBorrowerCycle": false},
{"id": 2,
"name": "LOAN_PRODUCT_BXW8NC",
includeInBorrowerCycle": false}
],
"provisioningcriteria": [
{"categoryId": 1,
"categoryName": "STANDARD",
"minAge": 0,
"maxAge": 3,
"provisioningPercentage": 1,
"liabilityAccount": 8,
"expenseAccount": 14},
{"categoryId": 2,
"categoryName": "SUB-STANDARD",
"minAge": 1,
"maxAge": 5,
"provisioningPercentage": 2,
"liabilityAccount": 13,
"expenseAccount": 13},
{"categoryId": 3,
"categoryName": "DOUBTFUL",
"minAge": 2,
"maxAge": 6,
"provisioningPercentage": 3,
"liabilityAccount": 9,
"expenseAccount": 10},
{"categoryId": 4,
"categoryName": "LOSS",
"minAge": 3,
"maxAge": 7,
"provisioningPercentage": 4,
"liabilityAccount": 10,
"expenseAccount": 9}]
}
</code>
</div>
</div>
<a id="update_provisioningcriteria" name="update_provisioningcriteria"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Updates a new Provisioning Criteria</h4>
<p>Updates a new Provisioning Criteria</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>criteriaName, loanProducts, provisioningcriteria</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">
PUT https://DomainName/api/v1/provisioningcriteria/{criteriaId}
</code>
<code class="method-request">
PUT provisioningcriteria/{criteriaId}
Content-Type: application/json
Request Body:
{
"criteriaName":"High Risk Products Criteria1",
"loanProducts": [
{"id": 1,
"name": "LOAN_PRODUCT_3ODPK1",
"includeInBorrowerCycle": false},
{"id": 2,
"name": "LOAN_PRODUCT_BXW8NC",
includeInBorrowerCycle": false}
],
"provisioningcriteria": [
{"categoryId": 1,
"categoryName": "STANDARD",
"minAge": 0,
"maxAge": 3,
"provisioningPercentage": 1,
"liabilityAccount": 8,
"expenseAccount": 14},
{"categoryId": 2,
"categoryName": "SUB-STANDARD",
"minAge": 1,
"maxAge": 5,
"provisioningPercentage": 2,
"liabilityAccount": 13,
"expenseAccount": 13},
{"categoryId": 3,
"categoryName": "DOUBTFUL",
"minAge": 2,
"maxAge": 6,
"provisioningPercentage": 3,
"liabilityAccount": 9,
"expenseAccount": 10},
{"categoryId": 4,
"categoryName": "LOSS",
"minAge": 3,
"maxAge": 7,
"provisioningPercentage": 4,
"liabilityAccount": 10,
"expenseAccount": 9}]
}
</code>
<code class="method-response">
{
"resourceId": 1,
"changes": {
"criteriaName": "High Risk Products Criteria1"
}
}
</code>
</div>
</div>
<a id="delete_provisioningcriteria" name="delete_provisioningcriteria"
class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Deletes Provisioning Criteria</h4>
<p>Deletes Provisioning Criteria</p>
</div>
<div class="method-example">
<code class="method-declaration">
DELETE https://DomainName/api/v1/provisioningcriteria/{criteriaId}
</code>
<code class="method-request">
DELETE provisioningcriteria/{criteriaId}
Content-Type: application/json
Request Body:
{
}
</code>
<code class="method-response">
{
"resourceId": 1,
}
</code>
</div>
</div>
<!--End of Payment Type API docs-->
@@ -0,0 +1,123 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.integrationtests.common;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mifosplatform.integrationtests.common.accounting.Account;
import org.mifosplatform.integrationtests.common.accounting.AccountHelper;
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker;
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
import org.mifosplatform.integrationtests.common.provisioning.ProvisioningHelper;
import org.mifosplatform.integrationtests.common.provisioning.ProvisioningTransactionHelper;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ProvisioningIntegrationTest {
private static final String NONE = "1";
private final static int LOANPRODUCTS_SIZE = 10;
private final static int LOANS_SIZE = 5;
private RequestSpecification requestSpec;
private ResponseSpecification responseSpec;
private AccountHelper accountHelper;
private LoanTransactionHelper loanTransactionHelper;
@Before
public void setup() {
Utils.initializeRESTAssured();
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
}
@Test
public void testCreateProvisioningCriteria() {/*
ProvisioningTransactionHelper transactionHelper = new ProvisioningTransactionHelper(requestSpec, responseSpec);
ArrayList<Integer> loanProducts = new ArrayList<>(LOANPRODUCTS_SIZE);
List<Integer> loans = new ArrayList<>();
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
for (int i = 0; i < LOANPRODUCTS_SIZE; i++) {
final Integer loanProductID = createLoanProduct(false, NONE);
loanProducts.add(loanProductID);
Assert.assertNotNull(loanProductID);
final Integer loanID = applyForLoanApplication(clientID, loanProductID, null, null, "1,00,000.00");
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
loanStatusHashMap = this.loanTransactionHelper.approveLoan("20 September 2011", loanID);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
System.out.println("-------------------------------DISBURSE LOAN-------------------------------------------");
loanStatusHashMap = this.loanTransactionHelper.disburseLoan("20 September 2011", loanID);
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
loans.add(loanID);
Assert.assertNotNull(loanID);
}
ArrayList categories = transactionHelper.retrieveAllProvisioningCategories();
Account liability = accountHelper.createLiabilityAccount() ;
Account expense = accountHelper.createExpenseAccount() ;
String json = ProvisioningHelper.createProvisioingCriteriaJson(loanProducts, categories, liability, expense);
Integer criteriaId = transactionHelper.createProvisioningCriteria(json);
Assert.assertNotNull(criteriaId);
String provisioningEntryJson = ProvisioningHelper.createProvisioningEntryJson();
Integer provisioningEntryId = transactionHelper.createProvisioningEntries(provisioningEntryJson);
Assert.assertNotNull(provisioningEntryId);
*/}
private Integer createLoanProduct(final boolean multiDisburseLoan, final String accountingRule, final Account... accounts) {
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
final String loanProductJSON = new LoanProductTestBuilder() //
.withPrincipal("1,00,000.00") //
.withNumberOfRepayments("4") //
.withRepaymentAfterEvery("1") //
.withRepaymentTypeAsMonth() //
.withinterestRatePerPeriod("1") //
.withInterestRateFrequencyTypeAsMonths() //
.withAmortizationTypeAsEqualInstallments() //
.withInterestTypeAsDecliningBalance() //
.withTranches(multiDisburseLoan) //
.withAccounting(accountingRule, accounts).build(null);
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
}
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID, List<HashMap> charges,
final String savingsId, String principal) {
System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
.withPrincipal(principal) //
.withLoanTermFrequency("4") //
.withLoanTermFrequencyAsMonths() //
.withNumberOfRepayments("4") //
.withRepaymentEveryAfter("1") //
.withRepaymentFrequencyTypeAsMonths() //
.withInterestRatePerPeriod("2") //
.withAmortizationTypeAsEqualInstallments() //
.withInterestTypeAsDecliningBalance() //
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
.withExpectedDisbursementDate("20 September 2011") //
.withSubmittedOnDate("20 September 2011") //
.withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
}
@@ -0,0 +1,78 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.integrationtests.common.provisioning;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import org.mifosplatform.integrationtests.common.accounting.Account;
import com.google.gson.Gson;
public class ProvisioningHelper {
public final static String createProvisioingCriteriaJson(ArrayList<Integer> loanProducts, ArrayList categories, Account liability,
Account expense) {
final HashMap<String, Object> map = new HashMap<>();
map.put("loanProducts", addLoanProducts(loanProducts));
map.put("provisioningcriteria", addProvisioningCategories(categories, liability, expense));
DateFormat simple = new SimpleDateFormat("dd MMMM yyyy");
String formattedString = simple.format(new Date());
String criteriaName = "General Provisioning Criteria" + formattedString;
map.put("criteriaName", criteriaName);
map.put("locale", "en");
String provisioningCriteriaCreateJson = new Gson().toJson(map);
return provisioningCriteriaCreateJson;
}
public final static String createProvisioningEntryJson() {
final HashMap<String, Object> map = new HashMap<>();
map.put("createjournalentries", Boolean.TRUE);
map.put("locale", "en");
map.put("dateFormat", "dd MMMM yyyy");
DateFormat simple = new SimpleDateFormat("dd MMMM yyyy");
map.put("date", simple.format(new Date()));
String provisioningEntryCreateJson = new Gson().toJson(map);
return provisioningEntryCreateJson;
}
private static ArrayList addLoanProducts(ArrayList<Integer> loanProducts) {
ArrayList list = new ArrayList<>();
for (int i = 0; i < loanProducts.size(); i++) {
HashMap map = new HashMap();
map.put("id", loanProducts.get(i));
list.add(map);
}
return list;
}
public static ArrayList addProvisioningCategories(ArrayList categories, Account liability, Account expense) {
ArrayList list = new ArrayList();
int minStart = 0;
int maxStart = 30;
for (int i = 0; i < categories.size(); i++) {
HashMap map = new HashMap();
HashMap category = (HashMap) categories.get(i);
map.put("categoryId", category.get("id"));
map.put("categoryName", category.get("categoryName"));
map.put("minAge", (i * 30) + 1);
if (i == categories.size() - 1) {
map.put("maxAge", 90000);
} else {
map.put("maxAge", i * 60);
}
map.put("provisioningPercentage", (i + 1) * 5.5);
map.put("liabilityAccount", liability.getAccountID());
map.put("expenseAccount", expense.getAccountID());
list.add(map);
}
return list;
}
}
@@ -0,0 +1,42 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.integrationtests.common.provisioning;
import java.util.ArrayList;
import org.mifosplatform.integrationtests.common.Utils;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
public class ProvisioningTransactionHelper {
private static final String PROVISIONING_CATEGORY_URL = "/mifosng-provider/api/v1/provisioningcategory/template?" + Utils.TENANT_IDENTIFIER ;
private static final String CREATE_PROVISIONING_CRITERIA_URL = "/mifosng-provider/api/v1/provisioningcriteria?" + Utils.TENANT_IDENTIFIER;
private static final String CREATE_PROVISIONING_ENTRY_URL = "/mifosng-provider/api/v1/provisioningentries?" + Utils.TENANT_IDENTIFIER;
private final RequestSpecification requestSpec;
private final ResponseSpecification responseSpec;
public ProvisioningTransactionHelper(RequestSpecification requestSpec, ResponseSpecification responeSpec) {
this.requestSpec = requestSpec ;
this.responseSpec = responeSpec ;
}
public ArrayList retrieveAllProvisioningCategories() {
return Utils.performServerGet(requestSpec, responseSpec, PROVISIONING_CATEGORY_URL, "");
}
public Integer createProvisioningCriteria(final String provsioningCriteriaJson) {
return Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_PROVISIONING_CRITERIA_URL, provsioningCriteriaJson, "resourceId");
}
public Integer createProvisioningEntries(final String provsioningCriteriaJson) {
return Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_PROVISIONING_ENTRY_URL, provsioningCriteriaJson, "resourceId");
}
}
@@ -26,6 +26,8 @@ import org.mifosplatform.accounting.journalentry.data.JournalEntryAssociationPar
import org.mifosplatform.accounting.journalentry.data.JournalEntryData;
import org.mifosplatform.accounting.journalentry.data.OfficeOpeningBalancesData;
import org.mifosplatform.accounting.journalentry.service.JournalEntryReadPlatformService;
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.mifosplatform.accounting.provisioning.constant.ProvisioningEntriesApiConstants;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
@@ -162,6 +164,21 @@ public class JournalEntriesApiResource {
return this.apiJsonSerializerService.serialize(result);
}
@GET
@Path("provisioning")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveJournalEntries(@QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@QueryParam("entryId") final Long entryId, @Context final UriInfo uriInfo) {
this.context.authenticatedUser();
String transactionId = "P"+entryId ;
SearchParameters params = SearchParameters.forPagination(offset, limit) ;
Page<JournalEntryData> entries = this.journalEntryReadPlatformService.retrieveAll(params, null, null, null, null, transactionId, PortfolioProductType.PROVISIONING.getValue(), null) ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, entries, RESPONSE_DATA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@@ -192,4 +192,12 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
return this.clientTransaction;
}
public Long getEntityId() {
return this.entityId ;
}
public Integer getEntityType() {
return this.entityType ;
}
}
@@ -23,4 +23,7 @@ public interface JournalEntryRepository extends JpaRepository<JournalEntry, Long
@Query("select DISTINCT j.transactionId from JournalEntry j where j.office.id = :officeId and j.glAccount.id = :contraId and j.reversed is false and j.transactionId not in (select DISTINCT je.reversalJournalEntry.transactionId from JournalEntry je where je.reversed is true)")
List<String> findNonReversedContraTansactionIds(@Param("contraId") Long contraId, @Param("officeId") Long officeId);
@Query("from JournalEntry journalEntry where journalEntry.entityId= :entityId and journalEntry.entityType = :entityType")
List<JournalEntry> findProvisioningJournalEntriesByEntityId(@Param("entityId") Long entityId, @Param("entityType") Integer entityType) ;
}
@@ -65,6 +65,7 @@ public class AccountingProcessorHelper {
public static final String LOAN_TRANSACTION_IDENTIFIER = "L";
public static final String SAVINGS_TRANSACTION_IDENTIFIER = "S";
public static final String CLIENT_TRANSACTION_IDENTIFIER = "C";
public static final String PROVISIONING_TRANSACTION_IDENTIFIER = "P" ;
private final JournalEntryRepository glJournalEntryRepository;
private final ProductToGLAccountMappingRepository accountMappingRepository;
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
@@ -660,6 +661,33 @@ public class AccountingProcessorHelper {
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
public void createProvisioningDebitJournalEntry(Date transactionDate, Long provisioningentryId, Office office, String currencyCode, GLAccount account,BigDecimal amount) {
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
PaymentDetail paymentDetail = null ;
final boolean manualEntry = false;
String modifiedTransactionId = PROVISIONING_TRANSACTION_IDENTIFIER + provisioningentryId;
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.PROVISIONING.getValue(), provisioningentryId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
public void createProvisioningCreditJournalEntry(Date transactionDate, Long provisioningentryId, Office office, String currencyCode, GLAccount account, BigDecimal amount) {
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
PaymentDetail paymentDetail = null ;
final boolean manualEntry = false;
String modifiedTransactionId = PROVISIONING_TRANSACTION_IDENTIFIER + provisioningentryId;
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.PROVISIONING.getValue(), provisioningentryId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
private void createDebitJournalEntryForLoan(final Office office, final String currencyCode, final GLAccount account, final Long loanId,
final String transactionId, final Date transactionDate, final BigDecimal amount) {
final boolean manualEntry = false;
@@ -22,4 +22,5 @@ public interface JournalEntryReadPlatformService {
OfficeOpeningBalancesData retrieveOfficeOpeningBalances(Long officeId, String currencyCode);
Page<JournalEntryData> retrieveJournalEntriesByEntityId(String transactionId, Long entityId, Integer entityType) ;
}
@@ -502,4 +502,19 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
associationParametersData);
}
@Override
public Page<JournalEntryData> retrieveJournalEntriesByEntityId(String transactionId, Long entityId, Integer entityType) {
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(true,
true);
try {
final GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData);
final String sql = "select " + rm.schema() + " where journalEntry.transaction_id = ? and journalEntry.entity_id = ? and journalEntry.entity_type_enum = ?";
final String sqlCountRows = "SELECT FOUND_ROWS()";
Object[] data = {transactionId, entityId, entityType} ;
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sql, data, rm);
} catch (final EmptyResultDataAccessException e) {
throw new JournalEntriesNotFoundException(entityId);
}
}
}
@@ -5,8 +5,10 @@
*/
package org.mifosplatform.accounting.journalentry.service;
import java.util.Date;
import java.util.Map;
import org.mifosplatform.accounting.provisioning.domain.ProvisioningEntry;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -23,5 +25,8 @@ public interface JournalEntryWritePlatformService {
void createJournalEntriesForClientTransactions(Map<String, Object> accountingBridgeData);
CommandProcessingResult defineOpeningBalance(JsonCommand command);
public String revertProvisioningJournalEntries(final Date reversalTransactionDate, final Long entityId, final Integer entityType) ;
public String createProvisioningJournalEntries(ProvisioningEntry entry) ;
}
@@ -7,10 +7,13 @@ package org.mifosplatform.accounting.journalentry.service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
@@ -37,6 +40,8 @@ import org.mifosplatform.accounting.journalentry.exception.JournalEntriesNotFoun
import org.mifosplatform.accounting.journalentry.exception.JournalEntryInvalidException;
import org.mifosplatform.accounting.journalentry.exception.JournalEntryInvalidException.GL_JOURNAL_ENTRY_INVALID_REASON;
import org.mifosplatform.accounting.journalentry.serialization.JournalEntryCommandFromApiJsonDeserializer;
import org.mifosplatform.accounting.provisioning.domain.LoanProductProvisioningEntry;
import org.mifosplatform.accounting.provisioning.domain.ProvisioningEntry;
import org.mifosplatform.accounting.rule.domain.AccountingRule;
import org.mifosplatform.accounting.rule.domain.AccountingRuleRepository;
import org.mifosplatform.accounting.rule.exception.AccountingRuleNotFoundException;
@@ -253,21 +258,18 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
}
private void checkDebitAndCreditAmounts(final SingleDebitOrCreditEntryCommand[] credits,
final SingleDebitOrCreditEntryCommand[] debits) {
private void checkDebitAndCreditAmounts(final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits) {
// sum of all debits must be = sum of all credits
BigDecimal creditsSum = BigDecimal.ZERO;
BigDecimal debitsSum = BigDecimal.ZERO;
for (final SingleDebitOrCreditEntryCommand creditEntryCommand : credits) {
if (creditEntryCommand.getAmount() == null
|| creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
creditsSum = creditsSum.add(creditEntryCommand.getAmount());
}
for (final SingleDebitOrCreditEntryCommand debitEntryCommand : debits) {
if (debitEntryCommand.getAmount() == null
|| debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
debitsSum = debitsSum.add(debitEntryCommand.getAmount());
}
if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException(
@@ -291,8 +293,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
@Override
public CommandProcessingResult revertJournalEntry(final JsonCommand command) {
// is the transaction Id valid
final List<JournalEntry> journalEntries = this.glJournalEntryRepository
.findUnReversedManualJournalEntriesByTransactionId(command.getTransactionId());
final List<JournalEntry> journalEntries = this.glJournalEntryRepository.findUnReversedManualJournalEntriesByTransactionId(command
.getTransactionId());
String reversalComment = command.stringValueOfParameterNamed("comments");
if (journalEntries.size() <= 1) { throw new JournalEntriesNotFoundException(command.getTransactionId()); }
@@ -338,6 +340,101 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
return reversalTransactionId;
}
@Override
public String revertProvisioningJournalEntries(final Date reversalTransactionDate, final Long entityId, final Integer entityType) {
List<JournalEntry> journalEntries = this.glJournalEntryRepository.findProvisioningJournalEntriesByEntityId(entityId, entityType);
final String reversalTransactionId = journalEntries.get(0).getTransactionId();
for (final JournalEntry journalEntry : journalEntries) {
JournalEntry reversalJournalEntry;
String reversalComment = "Reversal entry for Journal Entry with Entry Id :" + journalEntry.getId() + " and transaction Id "
+ journalEntry.getTransactionId();
if (journalEntry.isDebitEntry()) {
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), journalEntry.getTransactionId(), Boolean.FALSE,
reversalTransactionDate, JournalEntryType.CREDIT, journalEntry.getAmount(), reversalComment,
journalEntry.getEntityType(), journalEntry.getEntityId(), journalEntry.getReferenceNumber(),
journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(), journalEntry.getClientTransaction());
} else {
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), journalEntry.getTransactionId(), Boolean.FALSE,
reversalTransactionDate, JournalEntryType.DEBIT, journalEntry.getAmount(), reversalComment,
journalEntry.getEntityType(), journalEntry.getEntityId(), journalEntry.getReferenceNumber(),
journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(), journalEntry.getClientTransaction());
}
// save the reversal entry
this.glJournalEntryRepository.save(reversalJournalEntry);
journalEntry.setReversalJournalEntry(reversalJournalEntry);
// save the updated journal entry
this.glJournalEntryRepository.save(journalEntry);
}
return reversalTransactionId;
}
@Override
public String createProvisioningJournalEntries(ProvisioningEntry provisioningEntry) {
Collection<LoanProductProvisioningEntry> provisioningEntries = provisioningEntry.getLoanProductProvisioningEntries();
Map<OfficeCurrencyKey, List<LoanProductProvisioningEntry>> officeMap = new HashMap<>();
for (LoanProductProvisioningEntry entry : provisioningEntries) {
OfficeCurrencyKey key = new OfficeCurrencyKey(entry.getOffice(), entry.getCurrencyCode());
if (officeMap.containsKey(key)) {
List<LoanProductProvisioningEntry> list = officeMap.get(key);
list.add(entry);
} else {
List<LoanProductProvisioningEntry> list = new ArrayList<>();
list.add(entry);
officeMap.put(key, list);
}
}
Set<OfficeCurrencyKey> officeSet = officeMap.keySet();
Map<GLAccount, BigDecimal> liabilityMap = new HashMap<>();
Map<GLAccount, BigDecimal> expenseMap = new HashMap<>();
for (OfficeCurrencyKey key : officeSet) {
liabilityMap.clear();
expenseMap.clear();
List<LoanProductProvisioningEntry> entries = officeMap.get(key);
for (LoanProductProvisioningEntry entry : entries) {
if (liabilityMap.containsKey(entry.getLiabilityAccount())) {
BigDecimal amount = liabilityMap.get(entry.getLiabilityAccount());
amount = amount.add(entry.getReservedAmount());
liabilityMap.put(entry.getLiabilityAccount(), amount);
} else {
BigDecimal amount = BigDecimal.ZERO.add(entry.getReservedAmount());
liabilityMap.put(entry.getLiabilityAccount(), amount);
}
if (expenseMap.containsKey(entry.getExpenseAccount())) {
BigDecimal amount = expenseMap.get(entry.getExpenseAccount());
amount = amount.add(entry.getReservedAmount());
expenseMap.put(entry.getExpenseAccount(), amount);
} else {
BigDecimal amount = BigDecimal.ZERO.add(entry.getReservedAmount());
expenseMap.put(entry.getExpenseAccount(), amount);
}
}
createJournalEnry(provisioningEntry.getCreatedDate(), provisioningEntry.getId(), key.office, key.currency, liabilityMap, expenseMap);
}
return "P"+provisioningEntry.getId() ;
}
private void createJournalEnry(Date transactionDate, Long entryId, Office office, String currencyCode, Map<GLAccount, BigDecimal> liabilityMap,
Map<GLAccount, BigDecimal> expenseMap) {
Set<GLAccount> liabilityAccounts = liabilityMap.keySet();
for (GLAccount account : liabilityAccounts) {
this.helper.createProvisioningCreditJournalEntry(transactionDate,entryId, office, currencyCode, account,
liabilityMap.get(account));
}
Set<GLAccount> expenseAccounts = expenseMap.keySet();
for (GLAccount account : expenseAccounts) {
this.helper.createProvisioningDebitJournalEntry(transactionDate,entryId, office, currencyCode, account,
expenseMap.get(account));
}
}
private void validateCommentForReversal(final String reversalComment) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
@@ -394,9 +491,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
// shouldn't be before an accounting closure
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(command.getOfficeId());
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(transactionDate)
|| latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
if (latestGLClosure.getClosingDate().after(transactionDate) || latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
}
/*** check if credits and debits are valid **/
@@ -404,16 +500,16 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final SingleDebitOrCreditEntryCommand[] debits = command.getDebits();
// atleast one debit or credit must be present
if (credits == null || credits.length <= 0 || debits == null
|| debits.length <= 0) { throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null,
null, null); }
if (credits == null || credits.length <= 0 || debits == null || debits.length <= 0) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null, null, null); }
checkDebitAndCreditAmounts(credits, debits);
}
private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, final Office office, final PaymentDetail paymentDetail,
final String currencyCode, final Date transactionDate, final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands,
final String transactionId, final JournalEntryType type, final String referenceNumber) {
final String currencyCode, final Date transactionDate,
final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands, final String transactionId,
final JournalEntryType type, final String referenceNumber) {
final boolean manualEntry = true;
for (final SingleDebitOrCreditEntryCommand singleDebitOrCreditEntryCommand : singleDebitOrCreditEntryCommands) {
final GLAccount glAccount = this.glAccountRepository.findOne(singleDebitOrCreditEntryCommand.getGlAccountId());
@@ -511,8 +607,9 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
private void saveAllDebitOrCreditOpeningBalanceEntries(final JournalEntryCommand command, final Office office,
final String currencyCode, final Date transactionDate, final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands,
final String transactionId, final JournalEntryType type, final Long contraAccountId) {
final String currencyCode, final Date transactionDate,
final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands, final String transactionId,
final JournalEntryType type, final Long contraAccountId) {
final boolean manualEntry = true;
final GLAccount contraAccount = this.glAccountRepository.findOne(contraAccountId);
@@ -572,5 +669,28 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final ClientTransactionDTO clientTransactionDTO = this.helper.populateClientTransactionDtoFromMap(accountingBridgeData);
accountingProcessorForClientTransactions.createJournalEntriesForClientTransaction(clientTransactionDTO);
}
private class OfficeCurrencyKey {
Office office;
String currency;
OfficeCurrencyKey(Office office, String currency) {
this.office = office;
this.currency = currency;
}
@Override
public boolean equals(Object obj) {
if (!obj.getClass().equals(this.getClass())) return false;
OfficeCurrencyKey copy = (OfficeCurrencyKey) obj;
return this.office.getId() == copy.office.getId() && this.currency.equals(copy.currency);
}
@Override
public int hashCode() {
return this.office.hashCode() + this.currency.hashCode();
}
}
}
@@ -9,7 +9,7 @@ import java.util.HashMap;
import java.util.Map;
public enum PortfolioProductType {
LOAN(1, "productType.loan"), SAVING(2, "productType.saving"), CLIENT(2, "productType.client");
LOAN(1, "productType.loan"), SAVING(2, "productType.saving"), CLIENT(2, "productType.client"), PROVISIONING(3, "productType.provisioning");
private final Integer value;
private final String code;
@@ -0,0 +1,112 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.api;
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.accounting.provisioning.constant.ProvisioningEntriesApiConstants;
import org.mifosplatform.accounting.provisioning.data.ProvisioningEntryData;
import org.mifosplatform.accounting.provisioning.service.ProvisioningEntriesReadPlatformService;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/provisioningentries")
@Component
@Scope("singleton")
public class ProvisioningEntriesApiResource {
private final PlatformSecurityContext platformSecurityContext;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<ProvisioningEntryData> toApiJsonSerializer;
private final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService ;
private final ApiRequestParameterHelper apiRequestParameterHelper;
@Autowired
public ProvisioningEntriesApiResource(final PlatformSecurityContext platformSecurityContext,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final DefaultToApiJsonSerializer<ProvisioningEntryData> toApiJsonSerializer,
final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService,
final ApiRequestParameterHelper apiRequestParameterHelper) {
this.platformSecurityContext = platformSecurityContext;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.provisioningEntriesReadPlatformService = provisioningEntriesReadPlatformService ;
this.apiRequestParameterHelper = apiRequestParameterHelper ;
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createProvisioningEntries(final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = null;
this.platformSecurityContext.authenticatedUser();
commandWrapper = new CommandWrapperBuilder().createProvisioningEntries().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
@POST
@Path("{entryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String modifyProvisioningEntry(@PathParam("entryId") final Long entryId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = null;
this.platformSecurityContext.authenticatedUser();
if("createjournalentry".equals(commandParam)) {
commandWrapper = new CommandWrapperBuilder().createProvisioningJournalEntries(entryId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}else if("recreateprovisioningentry".equals(commandParam)) {
commandWrapper = new CommandWrapperBuilder().reCreateProvisioningEntries(entryId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
throw new UnrecognizedQueryParamException("command", commandParam);
}
@GET
@Path("{entryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveProvisioningEntry(@PathParam("entryId") final Long entryId, @Context final UriInfo uriInfo) {
platformSecurityContext.authenticatedUser() ;
ProvisioningEntryData data = this.provisioningEntriesReadPlatformService.retrieveProvisioningEntryData(entryId) ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, data, ProvisioningEntriesApiConstants.PROVISIONING_ENTRY_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllProvisioningEntries(@Context final UriInfo uriInfo) {
platformSecurityContext.authenticatedUser() ;
Collection<ProvisioningEntryData> data = this.provisioningEntriesReadPlatformService.retrieveAllProvisioningEntries() ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, data, ProvisioningEntriesApiConstants.ALL_PROVISIONING_ENTRIES);
}
}
@@ -0,0 +1,29 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.constant;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public interface ProvisioningEntriesApiConstants {
public final static String JSON_DATE_PARAM = "date" ;
public final static String JSON_DATEFORMAT_PARAM = "dateFormat" ;
public final static String JSON_LOCALE_PARAM = "locale" ;
public final static String JSON_CREATEJOURNALENTRIES_PARAM = "createjournalentries" ;
Set<String> supportedParameters = new HashSet<>(Arrays.asList(JSON_DATE_PARAM, JSON_DATEFORMAT_PARAM,JSON_LOCALE_PARAM,
JSON_CREATEJOURNALENTRIES_PARAM));
Set<String> PROVISIONING_ENTRY_PARAMETERS = new HashSet<>(Arrays.asList("provisioningentry", "entries"));
Set<String> ALL_PROVISIONING_ENTRIES = new HashSet<>(Arrays.asList("provisioningentry"));
}
@@ -0,0 +1,134 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.data;
import java.math.BigDecimal;
import java.math.BigInteger;
public class LoanProductProvisioningEntryData {
private final Long historyId;
private final Long officeId;
private final String officeName ;
private final String currencyCode;
private final Long productId;
private final String productName ;
private final Long categoryId;
private final String categoryName ;
private final Long overdueInDays;
private final BigDecimal percentage;
private final BigDecimal balance;
private final BigDecimal amountreserved ;
private final Long liablityAccount;
private final String liabilityAccountCode ;
private final Long expenseAccount;
private final String expenseAccountCode ;
private final Long criteriaId ;
public LoanProductProvisioningEntryData(final Long historyId, final Long officeId, final String currencyCode, final Long productId,
final Long categoryId, final Long overdueInDays, final BigDecimal percentage, final BigDecimal balance, Long liablityAccount,
Long expenseAccount, final Long criteriaId) {
this.historyId = historyId;
this.officeId = officeId;
this.currencyCode = currencyCode;
this.productId = productId;
this.categoryId = categoryId;
this.overdueInDays = overdueInDays;
this.percentage = percentage;
this.balance = balance;
this.liablityAccount = liablityAccount;
this.expenseAccount = expenseAccount;
this.amountreserved = null ;
this.officeName = null ;
this.productName = null ;
this.categoryName = null ;
this.liabilityAccountCode = null ;
this.expenseAccountCode = null ;
this.criteriaId = criteriaId ;
}
public LoanProductProvisioningEntryData(final Long historyId, final Long officeId, final String officeName, final String currencyCode, final Long productId,
final String productName, final Long categoryId, final String categoryName, final Long overdueInDays, final BigDecimal amountReserved,
Long liablityAccount, String liabilityAccountName, Long expenseAccount, String expenseAccountName, final Long criteriaId) {
this.historyId = historyId;
this.officeId = officeId;
this.currencyCode = currencyCode;
this.productId = productId;
this.categoryId = categoryId;
this.categoryName = categoryName ;
this.overdueInDays = overdueInDays;
this.percentage = null;
this.balance = null;
this.liablityAccount = liablityAccount;
this.expenseAccount = expenseAccount;
this.officeName = officeName ;
this.productName = productName ;
this.amountreserved = amountReserved ;
this.liabilityAccountCode = liabilityAccountName ;
this.expenseAccountCode = expenseAccountName ;
this.criteriaId = criteriaId ;
}
public Long getHistoryId() {
return this.historyId;
}
public Long getOfficeId() {
return this.officeId;
}
public Long getProductId() {
return this.productId;
}
public Long getCategoryId() {
return this.categoryId;
}
public Long getOverdueInDays() {
return this.overdueInDays;
}
public BigDecimal getOutstandingBalance() {
return balance;
}
public BigDecimal getPercentage() {
return percentage;
}
public Long getLiablityAccount() {
return this.liablityAccount;
}
public Long getExpenseAccount() {
return this.expenseAccount;
}
public String getCurrencyCode() {
return this.currencyCode;
}
public Long getCriteriaId() {
return this.criteriaId ;
}
}
@@ -0,0 +1,57 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.data;
import java.util.Collection;
import java.util.Date;
@SuppressWarnings("unused")
public class ProvisioningEntryData {
private Long id ;
private Boolean journalEntry ;
private Long createdById ;
private String createdUser ;
Date createdDate ;
Long modifiedById ;
private String modifiedUser ;
private Collection<LoanProductProvisioningEntryData> provisioningEntries ;
public ProvisioningEntryData(final Long id, final Collection<LoanProductProvisioningEntryData> provisioningEntries) {
this.provisioningEntries = provisioningEntries ;
this.id = id ;
}
public ProvisioningEntryData(Long id, Boolean journalEntry, Long createdById,
String createdUser, Date createdDate, Long modifiedById, String modifiedUser) {
this.id = id ;
this.journalEntry = journalEntry ;
this.createdById = createdById ;
this.createdUser = createdUser ;
this.modifiedById = modifiedById ;
this.modifiedUser = modifiedUser ;
this.createdDate = createdDate ;
}
public void setEntries(Collection<LoanProductProvisioningEntryData> provisioningEntries) {
this.provisioningEntries = provisioningEntries ;
}
public Long getId() {
return this.id ;
}
public Date getCreatedDate() {
return this.createdDate ;
}
}
@@ -0,0 +1,115 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.domain;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCategory;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_loanproduct_provisioning_entry")
public class LoanProductProvisioningEntry extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "history_id", referencedColumnName = "id", nullable = false)
private ProvisioningEntry entry;
@Column(name = "criteria_id", nullable = false)
private Long criteriaId;
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@Column(name = "currency_code", length = 3)
private String currencyCode;
@ManyToOne
@JoinColumn(name = "product_id", nullable = false)
private LoanProduct loanProduct;
@ManyToOne
@JoinColumn(name = "category_id", nullable = false)
private ProvisioningCategory provisioningCategory;
@Column(name = "overdue_in_days", nullable = false)
private Long overdueInDays;
@Column(name = "reseve_amount", nullable = false)
private BigDecimal reservedAmount;
@ManyToOne
@JoinColumn(name = "liability_account", nullable = false)
private GLAccount liabilityAccount;
@ManyToOne
@JoinColumn(name = "expense_account", nullable = false)
private GLAccount expenseAccount;
protected LoanProductProvisioningEntry() {
}
public LoanProductProvisioningEntry(final LoanProduct loanProduct, final Office office, final String currencyCode,
final ProvisioningCategory provisioningCategory, final Long overdueInDays, final BigDecimal reservedAmount,
final GLAccount liabilityAccount, final GLAccount expenseAccount, Long criteriaId) {
this.loanProduct = loanProduct;
this.office = office;
this.currencyCode = currencyCode;
this.provisioningCategory = provisioningCategory;
this.overdueInDays = overdueInDays;
this.reservedAmount = reservedAmount;
this.liabilityAccount = liabilityAccount;
this.expenseAccount = expenseAccount;
this.criteriaId = criteriaId ;
}
public void setProvisioningEntry(ProvisioningEntry provisioningEntry) {
this.entry = provisioningEntry;
}
public BigDecimal getReservedAmount() {
return this.reservedAmount ;
}
public void addReservedAmount(BigDecimal value) {
this.reservedAmount = this.reservedAmount.add(value) ;
}
public Office getOffice() {
return this.office ;
}
public GLAccount getLiabilityAccount() {
return this.liabilityAccount ;
}
public String getCurrencyCode() {
return this.currencyCode ;
}
public GLAccount getExpenseAccount() {
return this.expenseAccount ;
}
@Override
public boolean equals(Object obj) {
if (!obj.getClass().equals(getClass())) return false;
LoanProductProvisioningEntry entry = (LoanProductProvisioningEntry) obj;
return entry.loanProduct.getId().equals(this.loanProduct.getId())
&& entry.provisioningCategory.getId().equals(this.provisioningCategory.getId())
&& entry.office.getId().equals(this.office.getId())
&& entry.getCurrencyCode().equals(this.getCurrencyCode());
}
}
@@ -0,0 +1,83 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.domain;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_provisioning_history")
public class ProvisioningEntry extends AbstractPersistable<Long> {
@Column(name = "journal_entry_created")
private Boolean isJournalEntryCreated;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "entry", orphanRemoval = true)
Collection<LoanProductProvisioningEntry> provisioningEntries = new HashSet<>();
@OneToOne
@JoinColumn(name = "createdby_id")
private AppUser createdBy;
@Column(name = "created_date")
@Temporal(TemporalType.DATE)
private Date createdDate;
@OneToOne
@JoinColumn(name = "lastmodifiedby_id")
private AppUser lastModifiedBy;
@Column(name = "lastmodified_date")
@Temporal(TemporalType.DATE)
private Date lastModifiedDate;
protected ProvisioningEntry() {
}
public ProvisioningEntry(AppUser createdBy, Date createdDate, AppUser lastModifiedBy, Date lastModifiedDate, Collection<LoanProductProvisioningEntry> provisioningEntries ) {
this.provisioningEntries = provisioningEntries ;
this.createdBy = createdBy ;
this.createdDate = createdDate ;
this.lastModifiedBy = lastModifiedBy ;
this.lastModifiedDate = lastModifiedDate ;
}
public void setProvisioningEntries(Collection<LoanProductProvisioningEntry> provisioningEntries) {
if(this.provisioningEntries == null) this.provisioningEntries = new HashSet<>();
this.provisioningEntries.addAll(provisioningEntries) ;
}
public Collection<LoanProductProvisioningEntry> getLoanProductProvisioningEntries() {
return this.provisioningEntries ;
}
public void setJournalEntryCreated(Boolean bool) {
this.isJournalEntryCreated = bool ;
}
public Date getCreatedDate() {
return this.createdDate ;
}
}
@@ -0,0 +1,22 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.domain;
import java.util.Date;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface ProvisioningEntryRepository extends JpaRepository<ProvisioningEntry, Long>, JpaSpecificationExecutor<ProvisioningEntry> {
@Query("select entry from ProvisioningEntry entry where entry.createdDate = :createdDate")
ProvisioningEntry findByProvisioningEntryDate(@Param("createdDate") Date createdDate);
@Query("select entry from ProvisioningEntry entry where entry.createdDate = (select max(entry1.createdDate) from ProvisioningEntry entry1 where entry1.isJournalEntryCreated='1')")
ProvisioningEntry findExistingProvisioningEntryWithJournalEntries() ;
}
@@ -0,0 +1,18 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.exception;
import java.util.Date;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ProvisioningEntryAlreadyCreatedException extends AbstractPlatformResourceNotFoundException {
public ProvisioningEntryAlreadyCreatedException(Long id, Date date) {
super("error.msg.provisioningentry.already.exists.for.this.date", "ProvisioningEntry with identifier " + id + " exists for given date" + date, id);
}
}
@@ -0,0 +1,17 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ProvisioningEntryNotfoundException extends AbstractPlatformResourceNotFoundException {
public ProvisioningEntryNotfoundException(Long id) {
super("error.msg.provisioningentry.id.invalid", "ProvisioningEntry with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.exception;
import java.util.Date;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ProvisioningJournalEntriesCannotbeCreatedException extends AbstractPlatformResourceNotFoundException {
public ProvisioningJournalEntriesCannotbeCreatedException(Date existingEntriesDate, Date requestedDate) {
super("error.msg.provisioning.journalentries.cannot.be.created", "Provisioning Journal Entries already created on later date "
+ existingEntriesDate + " than requested date " + requestedDate);
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.handler;
import org.mifosplatform.accounting.provisioning.service.ProvisioningEntriesWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONENTRIES", action = "CREATE")
public class CreateProvisioningEntriesRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningEntriesWritePlatformService provisioningEntriesWritePlatformService ;
@Autowired
public CreateProvisioningEntriesRequestCommandHandler(
final ProvisioningEntriesWritePlatformService provisioningEntriesWritePlatformService) {
this.provisioningEntriesWritePlatformService = provisioningEntriesWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningEntriesWritePlatformService.createProvisioningEntries(jsonCommand) ;
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.handler;
import org.mifosplatform.accounting.provisioning.service.ProvisioningEntriesWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONJOURNALENTRIES", action = "CREATE")
public class CreateProvisioningJournalEntriesRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningEntriesWritePlatformService provisioningEntriesWritePlatformService ;
@Autowired
public CreateProvisioningJournalEntriesRequestCommandHandler(
final ProvisioningEntriesWritePlatformService provisioningEntriesWritePlatformService) {
this.provisioningEntriesWritePlatformService = provisioningEntriesWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningEntriesWritePlatformService.createProvisioningJournalEntries(jsonCommand.entityId(), jsonCommand) ;
}
}
@@ -0,0 +1,36 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.handler;
import org.mifosplatform.accounting.provisioning.service.ProvisioningEntriesWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONENTRIES", action = "RECREATE")
public class ReCreateProvisioningEntryRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningEntriesWritePlatformService provisioningEntriesWritePlatformService ;
@Autowired
public ReCreateProvisioningEntryRequestCommandHandler(
final ProvisioningEntriesWritePlatformService provisioningEntriesWritePlatformService) {
this.provisioningEntriesWritePlatformService = provisioningEntriesWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningEntriesWritePlatformService.reCreateProvisioningEntries(jsonCommand.entityId(), jsonCommand) ;
}
}
@@ -0,0 +1,63 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.serialization;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.provisioning.constant.ProvisioningEntriesApiConstants;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.provisioning.exception.ProvisioningCriteriaCannotBeCreatedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public class ProvisioningEntriesDefinitionJsonDeserializer implements ProvisioningEntriesApiConstants {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public ProvisioningEntriesDefinitionJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new ProvisioningCriteriaCannotBeCreatedException(
"error.msg.provisioningentry.cannot.be.created",
"locale, dateformat, date, createjournalentries params are missing in the request");
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("provisioningcriteria");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
baseDataValidator.reset().parameter(JSON_DATEFORMAT_PARAM).value(locale).notNull();
final String dateformat = this.fromApiJsonHelper.extractDateFormatParameter(element.getAsJsonObject());
baseDataValidator.reset().parameter(JSON_DATEFORMAT_PARAM).value(dateformat).notBlank();
LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(JSON_DATE_PARAM, element) ;
baseDataValidator.reset().parameter(JSON_DATE_PARAM).value(localDate).notBlank();
baseDataValidator.reset().parameter(JSON_DATE_PARAM).value(localDate).validateDateBeforeOrEqual(DateUtils.getLocalDateOfTenant()) ;
if(this.fromApiJsonHelper.parameterExists(JSON_CREATEJOURNALENTRIES_PARAM, element)) {
Boolean bool = this.fromApiJsonHelper.extractBooleanNamed(JSON_CREATEJOURNALENTRIES_PARAM, element) ;
baseDataValidator.reset().parameter(JSON_CREATEJOURNALENTRIES_PARAM).value(bool).validateForBooleanValue() ;
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -0,0 +1,28 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.service;
import java.util.Collection;
import java.util.Date;
import org.mifosplatform.accounting.provisioning.data.LoanProductProvisioningEntryData;
import org.mifosplatform.accounting.provisioning.data.ProvisioningEntryData;
public interface ProvisioningEntriesReadPlatformService {
public Collection<LoanProductProvisioningEntryData> retrieveLoanProductsProvisioningData(Date date) ;
public ProvisioningEntryData retrieveProvisioningEntryData(Long entryId) ;
public Collection<ProvisioningEntryData> retrieveAllProvisioningEntries() ;
public ProvisioningEntryData retrieveProvisioningEntryData(String date) ;
public ProvisioningEntryData retrieveProvisioningEntryDataByCriteriaId(Long criteriaId) ;
public ProvisioningEntryData retrieveExistingProvisioningIdDateWithJournals() ;
}
@@ -0,0 +1,241 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.mifosplatform.accounting.provisioning.data.LoanProductProvisioningEntryData;
import org.mifosplatform.accounting.provisioning.data.ProvisioningEntryData;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningEntriesReadPlatformService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public ProvisioningEntriesReadPlatformServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Collection<LoanProductProvisioningEntryData> retrieveLoanProductsProvisioningData(Date date) {
String formattedDate = new SimpleDateFormat("yyyy-MM-dd").format(date);
formattedDate = "'" + formattedDate + "'";
LoanProductProvisioningEntryMapper mapper = new LoanProductProvisioningEntryMapper(formattedDate);
final String sql = mapper.schema();
return this.jdbcTemplate.query(sql, mapper, new Object[] {});
}
private static final class LoanProductProvisioningEntryMapper implements RowMapper<LoanProductProvisioningEntryData> {
private final StringBuilder sqlQuery;
protected LoanProductProvisioningEntryMapper(String formattedDate) {
sqlQuery = new StringBuilder()
.append("select mclient.office_id, pcd.criteria_id as criteriaid, loan.product_id,loan.currency_code,")
.append("GREATEST(datediff(")
.append(formattedDate)
.append(",sch.duedate),0) as numberofdaysoverdue,sch.duedate, pcd.category_id, pcd.provision_percentage,")
.append("loan.total_outstanding_derived as outstandingbalance, pcd.liability_account, pcd.expense_account from m_loan_repayment_schedule sch")
.append(" LEFT JOIN m_loan loan on sch.loan_id = loan.id")
.append(" LEFT JOIN m_loanproduct_provisioning_mapping lpm on lpm.product_id = loan.product_id")
.append(" LEFT JOIN m_provisioning_criteria_definition pcd on pcd.criteria_id = lpm.criteria_id and ")
.append("(pcd.min_age <= GREATEST(datediff(").append(formattedDate).append(",sch.duedate),0) and ")
.append("GREATEST(datediff(").append(formattedDate).append(",sch.duedate),0) <= pcd.max_age) ")
.append("LEFT JOIN m_client mclient ON mclient.id = loan.client_id ")
.append("where loan.loan_status_id=300 and sch.completed_derived=false and provision_percentage is not null ")
.append("GROUP BY loan.id order by loan.product_id");
}
@Override
@SuppressWarnings("unused")
public LoanProductProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long officeId = rs.getLong("office_id");
Long productId = rs.getLong("product_id");
String currentcyCode = rs.getString("currency_code");
Long overdueDays = rs.getLong("numberofdaysoverdue");
Long categoryId = rs.getLong("category_id");
BigDecimal percentage = rs.getBigDecimal("provision_percentage");
BigDecimal outstandingBalance = rs.getBigDecimal("outstandingbalance");
Long liabilityAccountCode = rs.getLong("liability_account");
Long expenseAccountCode = rs.getLong("expense_account");
Long criteriaId = rs.getLong("criteriaid") ;
Long historyId = null;
return new LoanProductProvisioningEntryData(historyId, officeId, currentcyCode, productId, categoryId, overdueDays, percentage,
outstandingBalance, liabilityAccountCode, expenseAccountCode, criteriaId);
}
public String schema() {
return sqlQuery.toString();
}
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryData(Long entryId) {
LoanProductProvisioningEntryRowMapper mapper = new LoanProductProvisioningEntryRowMapper();
final String sql = mapper.getSchema() + " where entry.history_id = ?";
Collection<LoanProductProvisioningEntryData> entries = this.jdbcTemplate.query(sql, mapper, new Object[] { entryId });
ProvisioningEntryDataMapper mapper1 = new ProvisioningEntryDataMapper() ;
final String sql1 = mapper1.getSchema() + " where entry.id = ?";
ProvisioningEntryData data= this.jdbcTemplate.queryForObject(sql1, mapper1, new Object[] { entryId });
data.setEntries(entries) ;
return data ;
}
private static final class ProvisioningEntryDataMapper implements RowMapper<ProvisioningEntryData> {
private final StringBuilder sqlQuery = new StringBuilder()
.append("select entry.id, journal_entry_created, createdby_id, created_date, created.username as createduser,")
.append("lastmodifiedby_id, modified.username as modifieduser, lastmodified_date ")
.append("from m_provisioning_history entry ")
.append("left JOIN m_appuser created ON created.id = entry.createdby_id ")
.append("left JOIN m_appuser modified ON modified.id = entry.lastmodifiedby_id ") ;
@Override
@SuppressWarnings("unused")
public ProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id") ;
Boolean journalEntry = rs.getBoolean("journal_entry_created") ;
Long createdById = rs.getLong("createdby_id") ;
String createdUser = rs.getString("createduser") ;
Date createdDate = rs.getDate("created_date") ;
Long modifiedById = rs.getLong("lastmodifiedby_id") ;
String modifieUser = rs.getString("modifieduser") ;
return new ProvisioningEntryData(id, journalEntry, createdById, createdUser, createdDate, modifiedById, modifieUser);
}
public String getSchema() {
return sqlQuery.toString() ;
}
}
private static final class LoanProductProvisioningEntryRowMapper implements RowMapper<LoanProductProvisioningEntryData> {
private final StringBuilder sqlQuery = new StringBuilder()
.append("select entry.id, entry.history_id as historyId, office_id, entry.criteria_id as criteriaid, office.name as officename, product.name as productname, entry.product_id, ")
.append("category_id, category.category_name, liability.id as liabilityid, liability.gl_code as liabilitycode, ")
.append("expense.id as expenseid, expense.gl_code as expensecode, entry.currency_code, entry.overdue_in_days, entry.reseve_amount from m_loanproduct_provisioning_entry entry ")
.append("left join m_office office ON office.id = entry.office_id ")
.append("left join m_product_loan product ON product.id = entry.product_id ")
.append("left join m_provision_category category ON category.id = entry.category_id ")
.append("left join acc_gl_account liability ON liability.id = entry.liability_account ")
.append("left join acc_gl_account expense ON expense.id = entry.expense_account ") ;
@Override
@SuppressWarnings("unused")
public LoanProductProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long historyId = rs.getLong("historyId") ;
Long officeId = rs.getLong("office_id");
String officeName = rs.getString("officename") ;
Long productId = rs.getLong("product_id");
String productName = rs.getString("productname") ;
String currentcyCode = rs.getString("currency_code");
Long overdueDays = rs.getLong("overdue_in_days");
Long categoryId = rs.getLong("category_id");
String categoryName = rs.getString("category_name") ;
BigDecimal amountreserved = rs.getBigDecimal("reseve_amount");
Long liabilityAccountCode = rs.getLong("liabilityid");
String liabilityAccountName = rs.getString("liabilitycode") ;
String expenseAccountName = rs.getString("expensecode") ;
Long expenseAccountCode = rs.getLong("expenseid");
Long criteriaId = rs.getLong("criteriaid") ;
return new LoanProductProvisioningEntryData(historyId, officeId, officeName, currentcyCode, productId, productName, categoryId, categoryName, overdueDays, amountreserved,
liabilityAccountCode, liabilityAccountName, expenseAccountCode, expenseAccountName, criteriaId);
}
public String getSchema() {
return sqlQuery.toString() ;
}
}
@Override
public Collection<ProvisioningEntryData> retrieveAllProvisioningEntries() {
ProvisioningEntryDataMapper mapper = new ProvisioningEntryDataMapper() ;
final String sql1 = mapper.getSchema() + " order by entry.created_date ";
return this.jdbcTemplate.query(sql1, mapper, new Object[] {});
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryData(String date) {
ProvisioningEntryDataMapper mapper1 = new ProvisioningEntryDataMapper() ;
final String sql1 = mapper1.getSchema() + " where entry.created_date like " + "'"+date+"%'" ;
ProvisioningEntryData data = null ;
try {
data= this.jdbcTemplate.queryForObject(sql1, mapper1, new Object[] {});
}catch(EmptyResultDataAccessException e) {
}
return data ;
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryDataByCriteriaId(Long criteriaId) {
ProvisioningEntryData data = null ;
LoanProductProvisioningEntryRowMapper mapper = new LoanProductProvisioningEntryRowMapper();
final String sql = mapper.getSchema() + " where entry.criteria_id = ?";
Collection<LoanProductProvisioningEntryData> entries = this.jdbcTemplate.query(sql, mapper, new Object[] { criteriaId });
if(entries != null && entries.size() > 0) {
Long entryId = ((LoanProductProvisioningEntryData)entries.toArray()[0]).getHistoryId() ;
ProvisioningEntryDataMapper mapper1 = new ProvisioningEntryDataMapper() ;
final String sql1 = mapper1.getSchema() + " where entry.id = ?";
data= this.jdbcTemplate.queryForObject(sql1, mapper1, new Object[] { entryId });
data.setEntries(entries) ;
}
return data ;
}
@Override
public ProvisioningEntryData retrieveExistingProvisioningIdDateWithJournals() {
ProvisioningEntryData data = null ;
ProvisioningEntryIdDateRowMapper mapper = new ProvisioningEntryIdDateRowMapper() ;
try {
data = this.jdbcTemplate.queryForObject(mapper.schema(), mapper, new Object[] {});
}catch(EmptyResultDataAccessException e) {
data = null ;
}
return data ;
}
private static final class ProvisioningEntryIdDateRowMapper implements RowMapper<ProvisioningEntryData> {
StringBuffer buff = new StringBuffer()
.append("select history1.id, history1.created_date from m_provisioning_history history1 ")
.append("where history1.created_date = (select max(history2.created_date) from m_provisioning_history history2 ")
.append("where history2.journal_entry_created='1')");
@Override
@SuppressWarnings("unused")
public ProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Map<String, Object> map = new HashMap<>() ;
Long id = rs.getLong("id") ;
Date createdDate = rs.getDate("created_date") ;
Long createdBy = null ;
String createdName = null ;
Long modifiedBy = null ;
String modifiedName = null ;
return new ProvisioningEntryData(id, Boolean.TRUE, createdBy, createdName, createdDate, modifiedBy, modifiedName) ;
}
public String schema() {
return buff.toString() ;
}
}
}
@@ -0,0 +1,21 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface ProvisioningEntriesWritePlatformService {
CommandProcessingResult createProvisioningEntries(JsonCommand command);
CommandProcessingResult reCreateProvisioningEntries(Long provisioningEntryId, JsonCommand command) ;
CommandProcessingResult createProvisioningJournalEntries(Long provisioningEntryId, JsonCommand command);
void generateLoanLossProvisioningAmount() ;
}
@@ -0,0 +1,213 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.provisioning.service;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.mifosplatform.accounting.provisioning.data.LoanProductProvisioningEntryData;
import org.mifosplatform.accounting.provisioning.data.ProvisioningEntryData;
import org.mifosplatform.accounting.provisioning.domain.LoanProductProvisioningEntry;
import org.mifosplatform.accounting.provisioning.domain.ProvisioningEntry;
import org.mifosplatform.accounting.provisioning.domain.ProvisioningEntryRepository;
import org.mifosplatform.accounting.provisioning.exception.ProvisioningEntryAlreadyCreatedException;
import org.mifosplatform.accounting.provisioning.exception.ProvisioningEntryNotfoundException;
import org.mifosplatform.accounting.provisioning.exception.ProvisioningJournalEntriesCannotbeCreatedException;
import org.mifosplatform.accounting.provisioning.serialization.ProvisioningEntriesDefinitionJsonDeserializer;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.organisation.monetary.domain.MoneyHelper;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCategory;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCategoryRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.useradministration.domain.AppUser;
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 com.google.gson.JsonObject;
@Service
public class ProvisioningEntriesWritePlatformServiceJpaRepositoryImpl implements ProvisioningEntriesWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(ProvisioningEntriesWritePlatformServiceJpaRepositoryImpl.class);
private final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService;
private final LoanProductRepository loanProductRepository;
private final GLAccountRepository glAccountRepository;
private final OfficeRepository officeRepository;
private final ProvisioningCategoryRepository provisioningCategoryRepository;
private final PlatformSecurityContext platformSecurityContext;
private final ProvisioningEntryRepository provisioningEntryRepository;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final ProvisioningEntriesDefinitionJsonDeserializer fromApiJsonDeserializer;
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public ProvisioningEntriesWritePlatformServiceJpaRepositoryImpl(
final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService,
final LoanProductRepository loanProductRepository, final GLAccountRepository glAccountRepository,
final OfficeRepository officeRepository, final ProvisioningCategoryRepository provisioningCategoryRepository,
final PlatformSecurityContext platformSecurityContext, final ProvisioningEntryRepository provisioningEntryRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService,
final ProvisioningEntriesDefinitionJsonDeserializer fromApiJsonDeserializer, final FromJsonHelper fromApiJsonHelper) {
this.provisioningEntriesReadPlatformService = provisioningEntriesReadPlatformService;
this.loanProductRepository = loanProductRepository;
this.glAccountRepository = glAccountRepository;
this.officeRepository = officeRepository;
this.provisioningCategoryRepository = provisioningCategoryRepository;
this.platformSecurityContext = platformSecurityContext;
this.provisioningEntryRepository = provisioningEntryRepository;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.fromApiJsonHelper = fromApiJsonHelper;
}
@Override
public CommandProcessingResult createProvisioningJournalEntries(Long provisioningEntryId, JsonCommand command) {
ProvisioningEntry requestedEntry = this.provisioningEntryRepository.findOne(provisioningEntryId);
if (requestedEntry == null) { throw new ProvisioningEntryNotfoundException(provisioningEntryId); }
ProvisioningEntryData exisProvisioningEntryData = this.provisioningEntriesReadPlatformService
.retrieveExistingProvisioningIdDateWithJournals();
revertAndAddJournalEntries(exisProvisioningEntryData, requestedEntry);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(requestedEntry.getId()).build();
}
private void revertAndAddJournalEntries(ProvisioningEntryData existingEntryData, ProvisioningEntry requestedEntry) {
if (existingEntryData != null) {
validateForCreateJournalEntry(existingEntryData, requestedEntry);
this.journalEntryWritePlatformService.revertProvisioningJournalEntries(requestedEntry.getCreatedDate(),
existingEntryData.getId(), PortfolioProductType.PROVISIONING.getValue());
}
requestedEntry.setJournalEntryCreated(Boolean.TRUE);
this.provisioningEntryRepository.save(requestedEntry);
this.journalEntryWritePlatformService.createProvisioningJournalEntries(requestedEntry);
}
private void validateForCreateJournalEntry(ProvisioningEntryData existingEntry, ProvisioningEntry requested) {
Date existingDate = existingEntry.getCreatedDate();
Date requestedDate = requested.getCreatedDate();
if (existingDate.after(requestedDate) || existingDate.equals(requestedDate)) { throw new ProvisioningJournalEntriesCannotbeCreatedException(
existingEntry.getCreatedDate(), requestedDate); }
}
private boolean isJournalEntriesRequired(JsonCommand command) {
boolean bool = false;
if (this.fromApiJsonHelper.parameterExists("createjournalentries", command.parsedJson())) {
JsonObject jsonObject = command.parsedJson().getAsJsonObject();
bool = jsonObject.get("createjournalentries").getAsBoolean();
}
return bool;
}
private Date parseDate(JsonCommand command) {
LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed("date", command.parsedJson());
return localDate.toDate();
}
@Override
@CronTarget(jobName = JobName.GENERATE_LOANLOSS_PROVISIONING)
public void generateLoanLossProvisioningAmount() {
Date currentDate = new Date();
boolean addJournalEntries = true;
try {
createProvsioningEntry(currentDate, addJournalEntries);
} catch (ProvisioningEntryAlreadyCreatedException peace) {} catch (DataIntegrityViolationException dive) {}
}
@Override
public CommandProcessingResult createProvisioningEntries(JsonCommand command) {
this.fromApiJsonDeserializer.validateForCreate(command.json());
Date createdDate = parseDate(command);
boolean addJournalEntries = isJournalEntriesRequired(command);
try {
ProvisioningEntry requestedEntry = createProvsioningEntry(createdDate, addJournalEntries);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(requestedEntry.getId()).build();
} catch (DataIntegrityViolationException dve) {
return CommandProcessingResult.empty();
}
}
private ProvisioningEntry createProvsioningEntry(Date date, boolean addJournalEntries) {
ProvisioningEntry existingEntry = this.provisioningEntryRepository.findByProvisioningEntryDate(date);
if (existingEntry != null) { throw new ProvisioningEntryAlreadyCreatedException(existingEntry.getId(),
existingEntry.getCreatedDate()); }
AppUser currentUser = this.platformSecurityContext.authenticatedUser();
AppUser lastModifiedBy = null;
Date lastModifiedDate = null;
Collection<LoanProductProvisioningEntry> nullEntries = null;
ProvisioningEntry requestedEntry = new ProvisioningEntry(currentUser, date, lastModifiedBy, lastModifiedDate, nullEntries);
Collection<LoanProductProvisioningEntry> entries = generateLoanProvisioningEntry(requestedEntry, date);
requestedEntry.setProvisioningEntries(entries);
if (addJournalEntries) {
ProvisioningEntryData exisProvisioningEntryData = this.provisioningEntriesReadPlatformService
.retrieveExistingProvisioningIdDateWithJournals();
revertAndAddJournalEntries(exisProvisioningEntryData, requestedEntry);
} else {
this.provisioningEntryRepository.save(requestedEntry);
}
return requestedEntry;
}
@Override
public CommandProcessingResult reCreateProvisioningEntries(Long provisioningEntryId, JsonCommand command) {
ProvisioningEntry requestedEntry = this.provisioningEntryRepository.findOne(provisioningEntryId);
if (requestedEntry == null) { throw new ProvisioningEntryNotfoundException(provisioningEntryId); }
requestedEntry.getLoanProductProvisioningEntries().clear();
this.provisioningEntryRepository.save(requestedEntry);
Collection<LoanProductProvisioningEntry> entries = generateLoanProvisioningEntry(requestedEntry, requestedEntry.getCreatedDate());
requestedEntry.setProvisioningEntries(entries);
this.provisioningEntryRepository.save(requestedEntry);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(requestedEntry.getId()).build();
}
private Collection<LoanProductProvisioningEntry> generateLoanProvisioningEntry(ProvisioningEntry parent, Date date) {
Collection<LoanProductProvisioningEntryData> entries = this.provisioningEntriesReadPlatformService
.retrieveLoanProductsProvisioningData(date);
Map<LoanProductProvisioningEntry, LoanProductProvisioningEntry> provisioningEntries = new HashMap<>();
for (LoanProductProvisioningEntryData data : entries) {
LoanProduct loanProduct = this.loanProductRepository.findOne(data.getProductId());
Office office = this.officeRepository.findOne(data.getOfficeId());
ProvisioningCategory provisioningCategory = provisioningCategoryRepository.findOne(data.getCategoryId());
GLAccount liabilityAccount = glAccountRepository.findOne(data.getLiablityAccount());
GLAccount expenseAccount = glAccountRepository.findOne(data.getExpenseAccount());
MonetaryCurrency currency = loanProduct.getPrincipalAmount().getCurrency();
Money money = Money.of(currency, data.getOutstandingBalance());
Money amountToReserve = money.percentageOf(data.getPercentage(), MoneyHelper.getRoundingMode());
Long criteraId = data.getCriteriaId();
LoanProductProvisioningEntry entry = new LoanProductProvisioningEntry(loanProduct, office, data.getCurrencyCode(),
provisioningCategory, data.getOverdueInDays(), amountToReserve.getAmount(), liabilityAccount, expenseAccount, criteraId);
entry.setProvisioningEntry(parent);
if (!provisioningEntries.containsKey(entry)) {
provisioningEntries.put(entry, entry);
} else {
LoanProductProvisioningEntry entry1 = provisioningEntries.get(entry);
entry1.addReservedAmount(entry.getReservedAmount());
}
}
return provisioningEntries.values();
}
}
@@ -2457,4 +2457,76 @@ public class CommandWrapperBuilder {
this.href = "/clients/" + clientId + "/transactions/" + transactionId + "?command=undo";
return this;
}
public CommandWrapperBuilder createProvisioningCategory() {
this.actionName = "CREATE";
this.entityName = "PROVISIONCATEGORY";
this.entityId = null;
this.href = "/provisioningcategory";
return this;
}
public CommandWrapperBuilder updateProvisioningCategory(final Long cateoryId) {
this.actionName = "UPDATE";
this.entityName = "PROVISIONCATEGORY";
this.entityId = cateoryId;
this.href = "/provisioningcategory/" + cateoryId;
return this;
}
public CommandWrapperBuilder deleteProvisioningCategory(final Long categoryId) {
this.actionName = "DELETE";
this.entityName = "PROVISIONCATEGORY";
this.entityId = categoryId;
this.href = "/provisioningcategory/" + categoryId;
return this;
}
public CommandWrapperBuilder createProvisioningCriteria() {
this.actionName = "CREATE";
this.entityName = "PROVISIONCRITERIA";
this.entityId = null;
this.href = "/provisioningcriteria";
return this;
}
public CommandWrapperBuilder updateProvisioningCriteria(final Long criteriaId) {
this.actionName = "UPDATE";
this.entityName = "PROVISIONCRITERIA";
this.entityId = criteriaId;
this.href = "/provisioningcriteria/" + criteriaId;
return this;
}
public CommandWrapperBuilder deleteProvisioningCriteria(final Long criteriaId) {
this.actionName = "DELETE";
this.entityName = "PROVISIONCRITERIA";
this.entityId = criteriaId;
this.href = "/provisioningcriteria/" + criteriaId;
return this;
}
public CommandWrapperBuilder createProvisioningEntries() {
this.actionName = "CREATE";
this.entityName = "PROVISIONENTRIES";
this.entityId = null;
this.href = "/provisioningentries";
return this;
}
public CommandWrapperBuilder createProvisioningJournalEntries(final Long entryId) {
this.actionName = "CREATE";
this.entityName = "PROVISIONJOURNALENTRIES";
this.entityId = entryId;
this.href = "/provisioningentries/"+entryId;
return this;
}
public CommandWrapperBuilder reCreateProvisioningEntries(final Long entryId) {
this.actionName = "RECREATE";
this.entityName = "PROVISIONENTRIES";
this.entityId = entryId;
this.href = "/provisioningentries/"+entryId;
return this;
}
}
@@ -462,6 +462,45 @@ public class DataValidatorBuilder {
return this;
}
/*
* should be used with .notNull() before it
*/
public DataValidatorBuilder longZeroOrGreater() {
if (this.value == null && this.ignoreNullValue) { return this; }
if (this.value != null) {
final Long number = Long.valueOf(this.value.toString());
if (number < 0) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".not.equal.or.greater.than.zero");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter).append(
" must be equal or greater than 0.");
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), this.parameter, number, 0);
this.dataValidationErrors.add(error);
}
}
return this;
}
public DataValidatorBuilder longGreaterThanNumber(Long number) {
if (this.value == null && this.ignoreNullValue) { return this; }
if (this.value != null) {
final Long longValue = Long.valueOf(this.value.toString());
if (longValue < number + 1) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".not.greater.than.specified.number");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter)
.append(" must be greater than ").append(number);
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), this.parameter, longValue, number);
this.dataValidationErrors.add(error);
}
}
return this;
}
public DataValidatorBuilder arrayNotEmpty() {
if (this.value == null && this.ignoreNullValue) { return this; }
@@ -24,8 +24,9 @@ public enum JobName {
TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings"),
ADD_PERIODIC_ACCRUAL_ENTRIES("Add Periodic Accrual Transactions"),
RECALCULATE_INTEREST_FOR_LOAN("Recalculate Interest For Loans"),
GENERATE_RD_SCEHDULE("Generate Mandatory Savings Schedule");
GENERATE_RD_SCEHDULE("Generate Mandatory Savings Schedule"),
GENERATE_LOANLOSS_PROVISIONING("Generate Loan Loss Provisioning");
private final String name;
private JobName(final String name) {
@@ -10,12 +10,18 @@ import java.util.Date;
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetail;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobRunHistory;
import org.mifosplatform.useradministration.domain.AppUser;
import org.mifosplatform.useradministration.domain.AppUserRepositoryWrapper;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.JobListener;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
/**
@@ -33,9 +39,15 @@ public class SchedulerJobListener implements JobListener {
private final SchedularWritePlatformService schedularService;
private final AppUserRepositoryWrapper userRepository ;
private final GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
@Autowired
public SchedulerJobListener(final SchedularWritePlatformService schedularService) {
public SchedulerJobListener(final SchedularWritePlatformService schedularService,
final AppUserRepositoryWrapper userRepository) {
this.schedularService = schedularService;
this.userRepository = userRepository ;
}
@Override
@@ -45,7 +57,10 @@ public class SchedulerJobListener implements JobListener {
@Override
public void jobToBeExecuted(@SuppressWarnings("unused") final JobExecutionContext context) {
AppUser user = this.userRepository.fetchSystemUser();
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, user.getPassword(),
authoritiesMapper.mapAuthorities(user.getAuthorities()));
SecurityContextHolder.getContext().setAuthentication(auth);
}
@Override
@@ -224,6 +224,10 @@ public class Money implements Comparable<Money> {
return this.multiplyRetainScale(BigDecimal.valueOf(valueToMultiplyBy), roundingMode);
}
public Money percentageOf(BigDecimal percentage, final RoundingMode roundingMode) {
final BigDecimal newAmount = (this.amount.multiply(percentage)).divide(BigDecimal.valueOf(100), roundingMode);
return Money.of(monetaryCurrency(), newAmount);
}
@Override
public int compareTo(final Money other) {
final Money otherMoney = other;
@@ -0,0 +1,105 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.api;
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCategoryData;
import org.mifosplatform.organisation.provisioning.service.ProvisioningCategoryReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/provisioningcategory")
@Component
@Scope("singleton")
public class ProvisioningCategoryApiResource {
private final PlatformSecurityContext platformSecurityContext;
private final ProvisioningCategoryReadPlatformService provisioningCategoryReadPlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final DefaultToApiJsonSerializer<ProvisioningCategoryData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public ProvisioningCategoryApiResource(final PlatformSecurityContext platformSecurityContext,
final ProvisioningCategoryReadPlatformService provisioningCategoryReadPlatformService,
final ApiRequestParameterHelper apiRequestParameterHelper,
final DefaultToApiJsonSerializer<ProvisioningCategoryData> toApiJsonSerializer,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.platformSecurityContext = platformSecurityContext;
this.provisioningCategoryReadPlatformService = provisioningCategoryReadPlatformService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.toApiJsonSerializer = toApiJsonSerializer;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAll(@Context final UriInfo uriInfo) {
this.platformSecurityContext.authenticatedUser();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
Collection<ProvisioningCategoryData> provisionCategoriesSet = null;
provisionCategoriesSet = this.provisioningCategoryReadPlatformService.retrieveAllProvisionCategories();
return this.toApiJsonSerializer.serialize(settings, provisionCategoriesSet);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createProvisioningCategory(final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = null;
this.platformSecurityContext.authenticatedUser();
commandWrapper = new CommandWrapperBuilder().createProvisioningCategory().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
@PUT
@Path("{categoryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateProvisioningCategory(@PathParam("categoryId") final Long categoryId, final String apiRequestBodyAsJson) {
this.platformSecurityContext.authenticatedUser();
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateProvisioningCategory(categoryId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@DELETE
@Path("{categoryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String deleteProvisioningCategory(@PathParam("categoryId") final Long categoryId) {
this.platformSecurityContext.authenticatedUser();
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteProvisioningCategory(categoryId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
}
@@ -0,0 +1,128 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.api;
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.provisioning.constants.ProvisioningCriteriaConstants;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCriteriaData;
import org.mifosplatform.organisation.provisioning.service.ProvisioningCriteriaReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/provisioningcriteria")
@Component
@Scope("singleton")
public class ProvisioningCriteriaApiResource {
private final PlatformSecurityContext platformSecurityContext;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final ProvisioningCriteriaReadPlatformService provisioningCriteriaReadPlatformService;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<ProvisioningCriteriaData> toApiJsonSerializer;
@Autowired
public ProvisioningCriteriaApiResource(final PlatformSecurityContext platformSecurityContext,
final ApiRequestParameterHelper apiRequestParameterHelper,
final ProvisioningCriteriaReadPlatformService provisioningCriteriaReadPlatformService,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final DefaultToApiJsonSerializer<ProvisioningCriteriaData> toApiJsonSerializer) {
this.platformSecurityContext = platformSecurityContext;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.provisioningCriteriaReadPlatformService = provisioningCriteriaReadPlatformService;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService ;
this.toApiJsonSerializer = toApiJsonSerializer ;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@Context final UriInfo uriInfo) {
this.platformSecurityContext.authenticatedUser();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
ProvisioningCriteriaData data = this.provisioningCriteriaReadPlatformService.retrievePrivisiongCriteriaTemplate();
return this.toApiJsonSerializer.serialize(settings, data, ProvisioningCriteriaConstants.PROVISIONING_CRITERIA_TEMPLATE_PARAMETER);
}
@GET
@Path("{criteriaId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveProvisioningCriteria(@PathParam("criteriaId") final Long criteriaId, @Context final UriInfo uriInfo) {
platformSecurityContext.authenticatedUser() ;
ProvisioningCriteriaData criteria = this.provisioningCriteriaReadPlatformService.retrieveProvisioningCriteria(criteriaId) ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if(settings.isTemplate()) {
criteria = this.provisioningCriteriaReadPlatformService.retrievePrivisiongCriteriaTemplate(criteria);
}
return this.toApiJsonSerializer.serialize(settings, criteria, ProvisioningCriteriaConstants.PROVISIONING_CRITERIA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllProvisioningCriterias(@Context final UriInfo uriInfo) {
platformSecurityContext.authenticatedUser() ;
Collection<ProvisioningCriteriaData> data = this.provisioningCriteriaReadPlatformService.retrieveAllProvisioningCriterias() ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, data, ProvisioningCriteriaConstants.ALL_PROVISIONING_CRITERIA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createProvisioningCriteria(final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = null;
this.platformSecurityContext.authenticatedUser();
commandWrapper = new CommandWrapperBuilder().createProvisioningCriteria().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
@PUT
@Path("{criteriaId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateProvisioningCriteria(@PathParam("criteriaId") final Long criteriaId, final String apiRequestBodyAsJson) {
this.platformSecurityContext.authenticatedUser();
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateProvisioningCriteria(criteriaId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@DELETE
@Path("{criteriaId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String deleteProvisioningCriteria(@PathParam("criteriaId") final Long criteriaId) {
this.platformSecurityContext.authenticatedUser();
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteProvisioningCriteria(criteriaId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
}
@@ -0,0 +1,66 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.constants;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public interface ProvisioningCriteriaConstants {
public final static String JSON_LOCALE_PARAM = "locale" ;
public final static String JSON_CRITERIAID_PARAM = "criteriaId" ;
public final static String JSON_CRITERIANAME_PARAM = "criteriaName";
public final static String JSON_LOANPRODUCTS_PARAM = "loanProducts";
public final static String JSON_LOAN_PRODUCT_ID_PARAM = "id" ;
public final static String JSON_LOAN_PRODUCTNAME_PARAM = "name" ;
public final static String JSON_LOAN_PRODUCT_BORROWERCYCLE_PARAM = "includeInBorrowerCycle" ;
public final static String JSON_PROVISIONING_DEFINITIONS_PARAM = "provisioningcriteria";
public final static String JSON_CATEOGRYID_PARAM = "categoryId";
public final static String JSON_CATEOGRYNAME_PARAM = "categoryName";
public final static String JSON_MINIMUM_AGE_PARAM = "minAge";
public final static String JSON_MAXIMUM_AGE_PARAM = "maxAge";
public final static String JSON_PROVISIONING_PERCENTAGE_PARAM = "provisioningPercentage";
public final static String JSON_LIABILITY_ACCOUNT_PARAM = "liabilityAccount";
public final static String JSON_EXPENSE_ACCOUNT_PARAM = "expenseAccount";
Set<String> supportedParametersForCreate = new HashSet<>(Arrays.asList(JSON_LOCALE_PARAM, JSON_CRITERIANAME_PARAM,
JSON_LOANPRODUCTS_PARAM, JSON_PROVISIONING_DEFINITIONS_PARAM));
Set<String> supportedParametersForUpdate = new HashSet<>(Arrays.asList(JSON_CRITERIAID_PARAM, JSON_LOCALE_PARAM, JSON_CRITERIANAME_PARAM,
JSON_LOANPRODUCTS_PARAM, JSON_PROVISIONING_DEFINITIONS_PARAM));
Set<String> loanProductSupportedParams = new HashSet<>(Arrays.asList(JSON_LOAN_PRODUCT_ID_PARAM,
JSON_LOAN_PRODUCTNAME_PARAM, JSON_LOAN_PRODUCT_BORROWERCYCLE_PARAM)) ;
Set<String> provisioningcriteriaSupportedParams = new HashSet<>(Arrays.asList(JSON_CATEOGRYID_PARAM,
JSON_CATEOGRYNAME_PARAM, JSON_MINIMUM_AGE_PARAM, JSON_MAXIMUM_AGE_PARAM, JSON_MINIMUM_AGE_PARAM, JSON_PROVISIONING_PERCENTAGE_PARAM, JSON_EXPENSE_ACCOUNT_PARAM, JSON_LIABILITY_ACCOUNT_PARAM)) ;
Set<String> PROVISIONING_CRITERIA_TEMPLATE_PARAMETER = new HashSet<>(Arrays.asList("provisioninCriteria", "loanProducts",
"glAccounts"));
Set<String> PROVISIONING_CRITERIA_PARAMETERS = new HashSet<>(Arrays.asList("criteriaName", "loanProducts",
"definitions"));
Set<String> ALL_PROVISIONING_CRITERIA_PARAMETERS = new HashSet<>(Arrays.asList("criteriaId", "criterianame",
"createdby"));
}
@@ -0,0 +1,54 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.data;
import java.io.Serializable;
/**
* Immutable object representing organization's provision category data
*/
public class ProvisioningCategoryData implements Comparable<ProvisioningCategoryData>, Serializable {
private final Long id;
private final String categoryName;
private final String categoryDescription;
public ProvisioningCategoryData(final Long id, final String categoryName, final String categoryDescription) {
this.id = id;
this.categoryName = categoryName;
this.categoryDescription = categoryDescription;
}
public Long getId() {
return this.id;
}
public String getCategoryName() {
return this.categoryName;
}
public String getCategoryDescription() {
return this.categoryDescription;
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof ProvisioningCategoryData)) return false;
final ProvisioningCategoryData provisionCategoryData = (ProvisioningCategoryData) obj;
return provisionCategoryData.id.equals(this.id);
}
@Override
public int hashCode() {
return this.id.hashCode();
}
@Override
public int compareTo(ProvisioningCategoryData obj) {
if (obj == null) { return -1; }
return obj.id.compareTo(this.id);
}
}
@@ -0,0 +1,79 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.data;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
@SuppressWarnings("unused")
public class ProvisioningCriteriaData implements Comparable<ProvisioningCriteriaData>, Serializable {
private final Long criteriaId;
private final String criteriaName;
private final String createdBy;
private final Collection<LoanProductData> loanProducts;
private Collection<LoanProductData> selectedLoanProducts ;
private final Collection<ProvisioningCriteriaDefinitionData> definitions;
private final Collection<GLAccountData> glAccounts;
private ProvisioningCriteriaData(final Long criteriaId, final String criteriaName, final Collection<LoanProductData> loanProducts,
Collection<ProvisioningCriteriaDefinitionData> definitions, Collection<GLAccountData> glAccounts, final String createdBy) {
this.criteriaId = criteriaId;
this.criteriaName = criteriaName;
this.loanProducts = loanProducts;
this.definitions = definitions;
this.glAccounts = glAccounts;
this.createdBy = createdBy;
}
private ProvisioningCriteriaData(ProvisioningCriteriaData data, final Collection<LoanProductData> loanProducts,
Collection<GLAccountData> glAccounts) {
this.criteriaId = data.criteriaId;
this.criteriaName = data.criteriaName;
this.selectedLoanProducts = data.loanProducts ;
this.loanProducts = loanProducts;
this.loanProducts.removeAll(selectedLoanProducts) ;
this.definitions = data.definitions;
this.glAccounts = glAccounts;
this.createdBy = data.createdBy;
}
public static ProvisioningCriteriaData toLookup(final Long criteriaId, final String criteriaName, final Collection<LoanProductData> loanProducts,
final List<ProvisioningCriteriaDefinitionData> definitions) {
Collection<GLAccountData> glAccounts = null;
String createdBy = null;
return new ProvisioningCriteriaData(criteriaId, criteriaName, loanProducts, definitions, glAccounts, createdBy);
}
public static ProvisioningCriteriaData toLookup(final Long criteriaId, final String criteriaName, String createdBy) {
Collection<GLAccountData> glAccounts = null;
Collection<LoanProductData> loanProducts = null;
List<ProvisioningCriteriaDefinitionData> definitions = null;
return new ProvisioningCriteriaData(criteriaId, criteriaName, loanProducts, definitions, glAccounts, createdBy);
}
public static ProvisioningCriteriaData toTemplate(final Collection<ProvisioningCriteriaDefinitionData> definitions,
final Collection<LoanProductData> loanProducts, final Collection<GLAccountData> glAccounts) {
Long criteriaId = null;
String criteriaName = null;
String createdBy = null;
return new ProvisioningCriteriaData(criteriaId, criteriaName, loanProducts, definitions, glAccounts, createdBy);
}
public static ProvisioningCriteriaData toTemplate(final ProvisioningCriteriaData data, final Collection<ProvisioningCriteriaDefinitionData> definitions,
final Collection<LoanProductData> loanProducts, final Collection<GLAccountData> glAccounts) {
return new ProvisioningCriteriaData(data, loanProducts, glAccounts);
}
@Override
public int compareTo(ProvisioningCriteriaData obj) {
if (obj == null ) { return -1; }
return obj.criteriaId.compareTo(this.criteriaId);
}
}
@@ -0,0 +1,90 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.data;
import java.io.Serializable;
import java.math.BigDecimal;
public final class ProvisioningCriteriaDefinitionData implements Comparable<ProvisioningCriteriaDefinitionData>, Serializable {
private final Long id;
private final Long categoryId;
private final String categoryName;
private final Long minAge;
private final Long maxAge;
private final BigDecimal provisioningPercentage;
private final Long liabilityAccount;
private final Long expenseAccount;
private final String liabilityCode;
private final String expenseCode;
public ProvisioningCriteriaDefinitionData(Long id, Long categoryId, String categoryName, Long minAge, Long maxAge,
BigDecimal provisioningPercentage, Long liabilityAccount, Long expenseAccount, final String liabilityCode,
final String expenseCode) {
this.id = id;
this.categoryId = categoryId;
this.minAge = minAge;
this.maxAge = maxAge;
this.provisioningPercentage = provisioningPercentage;
this.liabilityAccount = liabilityAccount;
this.expenseAccount = expenseAccount;
this.categoryName = categoryName;
this.liabilityCode = liabilityCode;
this.expenseCode = expenseCode;
}
public static ProvisioningCriteriaDefinitionData template(Long categoryId, String categoryName) {
return new ProvisioningCriteriaDefinitionData(null, categoryId, categoryName, null, null, null, null, null, null, null);
}
public Long getId() {
return this.id;
}
public Long getCategoryId() {
return this.categoryId;
}
public String getCategoryName() {
return this.categoryName;
}
public Long getMinAge() {
return this.minAge;
}
public Long getMaxAge() {
return this.maxAge;
}
public BigDecimal getProvisioningPercentage() {
return this.provisioningPercentage;
}
public Long getLiabilityAccount() {
return this.liabilityAccount;
}
public Long getExpenseAccount() {
return this.expenseAccount;
}
public String getLiabilityCode() {
return this.liabilityCode;
}
public String getExpenseCode() {
return this.expenseCode;
}
@Override
public int compareTo(ProvisioningCriteriaDefinitionData obj) {
if (obj == null) { return -1; }
return obj.id.compareTo(this.id);
}
}
@@ -0,0 +1,41 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.domain;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_loanproduct_provisioning_mapping", uniqueConstraints = { @UniqueConstraint(columnNames = { "product_id" }, name = "product_id") })
public class LoanProductProvisionCriteria extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "criteria_id", referencedColumnName = "id", nullable = false)
private ProvisioningCriteria criteria;
@ManyToOne(optional = false)
@JoinColumn(name = "product_id", referencedColumnName = "id", nullable = false)
private LoanProduct loanProduct;
protected LoanProductProvisionCriteria() {
}
public LoanProductProvisionCriteria(ProvisioningCriteria criteria, LoanProduct loanProduct) {
this.criteria = criteria ;
this.loanProduct = loanProduct ;
}
public LoanProduct getLoanProduct() {
return this.loanProduct ;
}
}
@@ -0,0 +1,83 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.domain;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_provision_category", uniqueConstraints = { @UniqueConstraint(columnNames = { "category_name" }, name = "category_name") })
public class ProvisioningCategory extends AbstractPersistable<Long> {
@Column(name = "category_name", nullable = false, unique = true)
private String categoryName;
@Column(name = "description", nullable = true)
private String categoryDescription;
protected ProvisioningCategory() {
}
private ProvisioningCategory(String categoryName, String categoryDescription) {
this.categoryName = categoryName;
this.categoryDescription = categoryDescription;
}
public static ProvisioningCategory fromJson(JsonCommand jsonCommand) {
final String categoryName = jsonCommand.stringValueOfParameterNamed("categoryname");
final String description = jsonCommand.stringValueOfParameterNamed("description");
return new ProvisioningCategory(categoryName, description);
}
public Map<String, Object> update(JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(2);
final String nameParamName = "categoryname";
if (command.isChangeInStringParameterNamed(nameParamName, this.categoryName)) {
final String newValue = command.stringValueOfParameterNamed(nameParamName);
actualChanges.put(nameParamName, newValue);
this.categoryName = newValue;
}
final String descriptionParamName = "categorydescription";
if (command.isChangeInStringParameterNamed(descriptionParamName, this.categoryDescription)) {
final String newValue = command.stringValueOfParameterNamed(descriptionParamName);
actualChanges.put(descriptionParamName, newValue);
this.categoryDescription = newValue;
}
return actualChanges;
}
public String getCategoryName() {
return this.categoryName;
}
public String getCategoryDescription() {
return this.categoryDescription;
}
@Override
public boolean equals(Object obj) {
if (obj == null) { return false; }
if (obj == this) { return true; }
if (obj.getClass() != getClass()) { return false; }
ProvisioningCategory pc = (ProvisioningCategory) obj;
return pc.getCategoryName().equals(this.categoryName);
}
@Override
public int hashCode() {
return categoryName.hashCode() ^ getId().hashCode();
}
}
@@ -0,0 +1,14 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ProvisioningCategoryRepository extends JpaRepository<ProvisioningCategory, Long>, JpaSpecificationExecutor<ProvisioningCategory>{
}
@@ -0,0 +1,115 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.domain;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.DateTime;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.organisation.provisioning.constants.ProvisioningCriteriaConstants;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCriteriaDefinitionData;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.useradministration.domain.AppUser;
@Entity
@Table(name = "m_provisioning_criteria", uniqueConstraints = { @UniqueConstraint(columnNames = { "criteria_name" }, name = "criteria_name") })
public class ProvisioningCriteria extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "criteria_name", nullable = false)
private String criteriaName;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "criteria", orphanRemoval = true)
Set<ProvisioningCriteriaDefinition> provisioningCriteriaDefinition = new HashSet<>();
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "criteria", orphanRemoval = true)
Set<LoanProductProvisionCriteria> loanProductMapping = new HashSet<>();
public String getCriteriaName() {
return this.criteriaName;
}
public void setCriteriaName(String criteriaName) {
this.criteriaName = criteriaName;
}
protected ProvisioningCriteria() {
}
public ProvisioningCriteria(String criteriaName, AppUser createdBy, DateTime createdDate, AppUser lastModifiedBy, DateTime lastModifiedDate) {
this.criteriaName = criteriaName;
setCreatedBy(createdBy) ;
setCreatedDate(createdDate) ;
setLastModifiedBy(lastModifiedBy) ;
setLastModifiedDate(lastModifiedDate) ;
}
public void setProvisioningCriteriaDefinitions(Set<ProvisioningCriteriaDefinition> provisioningCriteriaDefinition) {
this.provisioningCriteriaDefinition.clear();
this.provisioningCriteriaDefinition.addAll(provisioningCriteriaDefinition);
}
public void setLoanProductProvisioningCriteria(Set<LoanProductProvisionCriteria> loanProductMapping) {
this.loanProductMapping.clear();
this.loanProductMapping.addAll(loanProductMapping);
}
public Map<String, Object> update(JsonCommand command, List<LoanProduct> loanProducts) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(7);
if(command.isChangeInStringParameterNamed(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM, criteriaName)) {
final String valueAsInput = command.stringValueOfParameterNamed(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM);
actualChanges.put(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM, valueAsInput);
this.criteriaName = valueAsInput ;
}
Set<LoanProductProvisionCriteria> temp = new HashSet<>() ;
Set<LoanProduct> productsTemp = new HashSet<>() ;
for(LoanProductProvisionCriteria mapping: loanProductMapping) {
if(!loanProducts.contains(mapping.getLoanProduct())) {
temp.add(mapping) ;
}else {
productsTemp.add(mapping.getLoanProduct()) ;
}
}
loanProductMapping.removeAll(temp) ;
for(LoanProduct loanProduct: loanProducts) {
if(!productsTemp.contains(loanProduct)) {
this.loanProductMapping.add( new LoanProductProvisionCriteria(this, loanProduct)) ;
}
}
actualChanges.put(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM, loanProductMapping);
return actualChanges ;
}
public void update(ProvisioningCriteriaDefinitionData data, GLAccount liability, GLAccount expense) {
for(ProvisioningCriteriaDefinition def: provisioningCriteriaDefinition) {
if(data.getId() == def.getId()) {
def.update(data.getMinAge(), data.getMaxAge(), data.getProvisioningPercentage(), liability, expense) ;
break ;
}
}
}
}
@@ -0,0 +1,78 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.domain;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_provisioning_criteria_definition")
public class ProvisioningCriteriaDefinition extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "criteria_id", referencedColumnName = "id", nullable = false)
private ProvisioningCriteria criteria;
@ManyToOne
@JoinColumn(name = "category_id", nullable = false)
private ProvisioningCategory provisioningCategory;
@Column(name = "min_age", nullable = false)
private Long minimumAge;
@Column(name = "max_age", nullable = false)
private Long maximumAge;
@Column(name = "provision_percentage", nullable = false)
private BigDecimal provisioningPercentage;
@ManyToOne
@JoinColumn(name = "liability_account", nullable = false)
private GLAccount liabilityAccount;
@ManyToOne
@JoinColumn(name = "expense_account", nullable = false)
private GLAccount expenseAccount;
protected ProvisioningCriteriaDefinition() {
}
private ProvisioningCriteriaDefinition(ProvisioningCriteria criteria, ProvisioningCategory provisioningCategory, Long minimumAge,
Long maximumAge, BigDecimal provisioningPercentage, GLAccount liabilityAccount, GLAccount expenseAccount) {
this.criteria = criteria;
this.provisioningCategory = provisioningCategory;
this.minimumAge = minimumAge;
this.maximumAge = maximumAge;
this.provisioningPercentage = provisioningPercentage;
this.liabilityAccount = liabilityAccount;
this.expenseAccount = expenseAccount;
}
public static ProvisioningCriteriaDefinition newPrivisioningCriteria(ProvisioningCriteria criteria,
ProvisioningCategory provisioningCategory, Long minimumAge, Long maximumAge, BigDecimal provisioningPercentage,
GLAccount liabilityAccount, GLAccount expenseAccount) {
return new ProvisioningCriteriaDefinition(criteria, provisioningCategory, minimumAge, maximumAge, provisioningPercentage,
liabilityAccount, expenseAccount);
}
public void update(Long minAge, Long maxAge, BigDecimal percentage, GLAccount lia, GLAccount exp) {
this.minimumAge = minAge ;
this.maximumAge = maxAge ;
this.provisioningPercentage = percentage ;
this.liabilityAccount = lia ;
this.expenseAccount = exp ;
}
}
@@ -0,0 +1,14 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ProvisioningCriteriaDefinitionRepository extends JpaRepository<ProvisioningCriteriaDefinition, Long>, JpaSpecificationExecutor<ProvisioningCriteriaDefinition> {
}
@@ -0,0 +1,14 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ProvisioningCriteriaRepository extends JpaRepository<ProvisioningCriteria, Long>, JpaSpecificationExecutor<ProvisioningCriteria>{
}
@@ -0,0 +1,18 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class ProvisioningCategoryCannotBeDeletedException extends AbstractPlatformDomainRuleException {
public ProvisioningCategoryCannotBeDeletedException(final String globalisationMessageCode, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
super(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -0,0 +1,15 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ProvisioningCategoryNotFoundException extends AbstractPlatformResourceNotFoundException {
public ProvisioningCategoryNotFoundException(final Long id) {
super("error.msg.provisioning.id.invalid", "Provisioning Category with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,17 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class ProvisioningCriteriaCannotBeCreatedException extends AbstractPlatformDomainRuleException {
public ProvisioningCriteriaCannotBeCreatedException(String globalisationMessageCode, String defaultUserMessage,
final Object... defaultUserMessageArgs) {
super(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -0,0 +1,16 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class ProvisioningCriteriaCannotBeDeletedException extends AbstractPlatformDomainRuleException {
public ProvisioningCriteriaCannotBeDeletedException(Long criteriaId) {
super("error.msg.provisioningcriteria.id.usedin.provisioning.entries", "Provisioningcriteria with identifier " + criteriaId + "associated in journal entries");
}
}
@@ -0,0 +1,15 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ProvisioningCriteriaNotFoundException extends AbstractPlatformResourceNotFoundException {
public ProvisioningCriteriaNotFoundException(final Long id) {
super("error.msg.provisioning.criteria.id.invalid", "Provisioning Criteria with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,35 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.handler;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.organisation.provisioning.service.ProvisioningCategoryWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONCATEGORY", action = "CREATE")
public class CreateProvisioningCategoryRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningCategoryWritePlatformService provisioningCategoryWritePlatformService;
@Autowired
public CreateProvisioningCategoryRequestCommandHandler(
ProvisioningCategoryWritePlatformService provisioningCategoryWritePlatformService) {
this.provisioningCategoryWritePlatformService = provisioningCategoryWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningCategoryWritePlatformService.createProvisioningCateogry(jsonCommand);
}
}
@@ -0,0 +1,35 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.handler;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.organisation.provisioning.service.ProvisioningCriteriaWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONCRITERIA", action = "CREATE")
public class CreateProvisioningCriteriaRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningCriteriaWritePlatformService provisioningCriteriaWritePlatformService;
@Autowired
public CreateProvisioningCriteriaRequestCommandHandler(
final ProvisioningCriteriaWritePlatformService provisioningCriteriaWritePlatformService) {
this.provisioningCriteriaWritePlatformService = provisioningCriteriaWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningCriteriaWritePlatformService.createProvisioningCriteria(jsonCommand);
}
}
@@ -0,0 +1,35 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.handler;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.organisation.provisioning.service.ProvisioningCategoryWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONCATEGORY", action = "DELETE")
public class DeleteProvisioningCategoryRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningCategoryWritePlatformService provisioningCategoryWritePlatformService;
@Autowired
public DeleteProvisioningCategoryRequestCommandHandler(
ProvisioningCategoryWritePlatformService provisioningCategoryWritePlatformService) {
this.provisioningCategoryWritePlatformService = provisioningCategoryWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningCategoryWritePlatformService.deleteProvisioningCateogry(jsonCommand);
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.handler;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.organisation.provisioning.service.ProvisioningCriteriaWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONCRITERIA", action = "DELETE")
public class DeleteProvisioningCriteriaRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningCriteriaWritePlatformService provisioningCriteriaWritePlatformService;
@Autowired
public DeleteProvisioningCriteriaRequestCommandHandler(final ProvisioningCriteriaWritePlatformService provisioningCriteriaWritePlatformService) {
this.provisioningCriteriaWritePlatformService = provisioningCriteriaWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningCriteriaWritePlatformService.deleteProvisioningCriteria(jsonCommand.entityId()) ;
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.handler;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.organisation.provisioning.service.ProvisioningCategoryWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONCATEGORY", action = "UPDATE")
public class UpdateProvisioningCategoryRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningCategoryWritePlatformService provisioningCategoryWritePlatformService;
@Autowired
public UpdateProvisioningCategoryRequestCommandHandler(ProvisioningCategoryWritePlatformService provisioningCategoryWritePlatformService) {
this.provisioningCategoryWritePlatformService = provisioningCategoryWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningCategoryWritePlatformService.updateProvisioningCategory(jsonCommand.entityId(), jsonCommand);
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.handler;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.organisation.provisioning.service.ProvisioningCriteriaWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PROVISIONCRITERIA", action = "UPDATE")
public class UpdateProvisioningCriteriaRequestCommandHandler implements NewCommandSourceHandler {
private final ProvisioningCriteriaWritePlatformService provisioningCriteriaWritePlatformService;
@Autowired
public UpdateProvisioningCriteriaRequestCommandHandler(final ProvisioningCriteriaWritePlatformService provisioningCriteriaWritePlatformService) {
this.provisioningCriteriaWritePlatformService = provisioningCriteriaWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.provisioningCriteriaWritePlatformService.updateProvisioningCriteria(jsonCommand.entityId(), jsonCommand);
}
}
@@ -0,0 +1,74 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.serialization;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public class ProvisioningCategoryDefinitionJsonDeserializer {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("categoryname", "categorydescription"));
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public ProvisioningCategoryDefinitionJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("provisioningcategories");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String name = this.fromApiJsonHelper.extractStringNamed("categoryname", element);
baseDataValidator.reset().parameter("categoryname").value(name).notBlank().notExceedingLengthOf(100);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("provisioningcategories");
final JsonElement element = this.fromApiJsonHelper.parse(json);
if (this.fromApiJsonHelper.parameterExists("categoryname", element)) {
final String categoryName = this.fromApiJsonHelper.extractStringNamed("categoryname", element);
baseDataValidator.reset().parameter("categoryname").value(categoryName).notBlank().notExceedingLengthOf(100);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -0,0 +1,210 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.serialization;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.organisation.provisioning.constants.ProvisioningCriteriaConstants;
import org.mifosplatform.organisation.provisioning.exception.ProvisioningCriteriaCannotBeCreatedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
@Component
public class ProvisioningCriteriaDefinitionJsonDeserializer implements ProvisioningCriteriaConstants{
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public ProvisioningCriteriaDefinitionJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new ProvisioningCriteriaCannotBeCreatedException(
"error.msg.provisioningcriteria.cannot.be.created",
"criterianame, loanproducts[], provisioningcriteria[] params are missing in the request");
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersForCreate);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("provisioningcriteria");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
final String name = this.fromApiJsonHelper.extractStringNamed(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM, element);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM).value(name).notBlank()
.notExceedingLengthOf(200);
// if the param present, then we should have the loan product ids. If
// not we will load all loan products
if (this.fromApiJsonHelper.parameterExists(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM, element)) {
JsonArray jsonloanProducts = this.fromApiJsonHelper.extractJsonArrayNamed(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM,
element);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM).value(jsonloanProducts)
.jsonArrayNotEmpty();
// check for unsupported params
int i = 0 ;
for (JsonElement obj : jsonloanProducts) {
this.fromApiJsonHelper.checkForUnsupportedParameters(obj.getAsJsonObject() , loanProductSupportedParams);
Long productId = this.fromApiJsonHelper.extractLongNamed("id", obj.getAsJsonObject());
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_LOAN_PRODUCT_ID_PARAM, i + 1).value(productId).notNull()
.longGreaterThanZero();
i++ ;
}
}
if (this.fromApiJsonHelper.parameterExists(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM, element)) {
JsonArray jsonProvisioningCriteria = this.fromApiJsonHelper.extractJsonArrayNamed(
ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM, element);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.value(jsonProvisioningCriteria).jsonArrayNotEmpty();
for (int i = 0; i < jsonProvisioningCriteria.size(); i++) {
JsonObject jsonObject = jsonProvisioningCriteria.get(i).getAsJsonObject();
this.fromApiJsonHelper.checkForUnsupportedParameters(jsonObject , provisioningcriteriaSupportedParams);
final Long categoryId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_CATEOGRYID_PARAM, jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_CATEOGRYID_PARAM, i + 1).value(categoryId).notNull()
.longGreaterThanZero();
Long minimumAge = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_MINIMUM_AGE_PARAM, jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_MINIMUM_AGE_PARAM, i + 1).value(minimumAge).notNull()
.longZeroOrGreater() ;
Long maximumAge = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_MAXIMUM_AGE_PARAM, jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_MAXIMUM_AGE_PARAM, i + 1).value(maximumAge).notNull()
.longZeroOrGreater();
BigDecimal provisioningpercentage = this.fromApiJsonHelper.extractBigDecimalNamed(
ProvisioningCriteriaConstants.JSON_PROVISIONING_PERCENTAGE_PARAM, jsonObject, locale);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_PROVISIONING_PERCENTAGE_PARAM, i + 1).value(provisioningpercentage)
.notNull().zeroOrPositiveAmount();
Long liabilityAccountId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_LIABILITY_ACCOUNT_PARAM,
jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_LIABILITY_ACCOUNT_PARAM, i + 1).value(liabilityAccountId)
.notNull().longGreaterThanZero() ;
Long expenseAccountId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_EXPENSE_ACCOUNT_PARAM,
jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_EXPENSE_ACCOUNT_PARAM, i + 1).value(expenseAccountId).notNull()
.longGreaterThanZero() ;
}
}else {
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM).jsonArrayNotEmpty() ;
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new ProvisioningCriteriaCannotBeCreatedException(
"error.msg.provisioningcriteria.cannot.be.updated",
"update params are missing in the request");
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersForUpdate);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("provisioningcriteria");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
if(this.fromApiJsonHelper.parameterExists(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM, element);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM).value(name).notBlank()
.notExceedingLengthOf(200);
}
// if the param present, then we should have the loan product ids. If
// not we will load all loan products
if (this.fromApiJsonHelper.parameterExists(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM, element)) {
JsonArray jsonloanProducts = this.fromApiJsonHelper.extractJsonArrayNamed(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM,
element);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM).value(jsonloanProducts)
.jsonArrayNotEmpty();
// check for unsupported params
int i = 0 ;
for (JsonElement obj : jsonloanProducts) {
Long productId = this.fromApiJsonHelper.extractLongNamed("id", obj.getAsJsonObject());
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_LOAN_PRODUCT_ID_PARAM, i + 1).value(productId).notNull()
.longGreaterThanZero();
i++ ;
}
}
if (this.fromApiJsonHelper.parameterExists(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM, element)) {
JsonArray jsonProvisioningCriteria = this.fromApiJsonHelper.extractJsonArrayNamed(
ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM, element);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.value(jsonProvisioningCriteria).jsonArrayNotEmpty();
for (int i = 0; i < jsonProvisioningCriteria.size(); i++) {
// check for unsupported params
JsonObject jsonObject = jsonProvisioningCriteria.get(i).getAsJsonObject();
final Long categoryId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_CATEOGRYID_PARAM, jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_CATEOGRYID_PARAM, i + 1).value(categoryId).notNull()
.longGreaterThanZero();
Long minimumAge = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_MINIMUM_AGE_PARAM, jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_MINIMUM_AGE_PARAM, i + 1).value(minimumAge).notNull()
.longZeroOrGreater() ;
Long maximumAge = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_MAXIMUM_AGE_PARAM, jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_MAXIMUM_AGE_PARAM, i + 1).value(maximumAge).notNull()
.longZeroOrGreater();
BigDecimal provisioningpercentage = this.fromApiJsonHelper.extractBigDecimalNamed(
ProvisioningCriteriaConstants.JSON_PROVISIONING_PERCENTAGE_PARAM, jsonObject, locale);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_PROVISIONING_PERCENTAGE_PARAM, i + 1).value(provisioningpercentage)
.notNull().zeroOrPositiveAmount();
Long liabilityAccountId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_LIABILITY_ACCOUNT_PARAM,
jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_LIABILITY_ACCOUNT_PARAM, i + 1).value(liabilityAccountId)
.notNull().longGreaterThanZero() ;
Long expenseAccountId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_EXPENSE_ACCOUNT_PARAM,
jsonObject);
baseDataValidator.reset().parameter(ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM)
.parameterAtIndexArray(ProvisioningCriteriaConstants.JSON_EXPENSE_ACCOUNT_PARAM, i + 1).value(expenseAccountId).notNull()
.longGreaterThanZero() ;
}
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -0,0 +1,16 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import java.util.Collection;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCategoryData;
public interface ProvisioningCategoryReadPlatformService {
public Collection<ProvisioningCategoryData> retrieveAllProvisionCategories() ;
}
@@ -0,0 +1,52 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCategoryData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class ProvisioningCategoryReadPlatformServiceImpl implements ProvisioningCategoryReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final ProvisioningCategoryRowMapper provisionCategoryRowMapper;
@Autowired
public ProvisioningCategoryReadPlatformServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.provisionCategoryRowMapper = new ProvisioningCategoryRowMapper();
}
@Override
public Collection<ProvisioningCategoryData> retrieveAllProvisionCategories() {
//User is already authenticated by API. So we no need to check again here
final String sql = "select " + this.provisionCategoryRowMapper.schema() + " from m_provision_category pc order by pc.id";
return this.jdbcTemplate.query(sql, this.provisionCategoryRowMapper, new Object[] {});
}
private static final class ProvisioningCategoryRowMapper implements RowMapper<ProvisioningCategoryData> {
@Override
public ProvisioningCategoryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = JdbcSupport.getLong(rs, "id");
final String categoryName = rs.getString("category_name");
final String description = rs.getString("description");
return new ProvisioningCategoryData(id, categoryName, description);
}
public String schema() {
return " pc.id as id, pc.category_name as category_name, pc.description as description";
}
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface ProvisioningCategoryWritePlatformService {
CommandProcessingResult createProvisioningCateogry(JsonCommand command);
CommandProcessingResult updateProvisioningCategory(final Long categoryId, JsonCommand command) ;
CommandProcessingResult deleteProvisioningCateogry(JsonCommand command) ;
}
@@ -0,0 +1,113 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import java.util.Map;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCategory;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCategoryRepository;
import org.mifosplatform.organisation.provisioning.exception.ProvisioningCategoryCannotBeDeletedException;
import org.mifosplatform.organisation.provisioning.exception.ProvisioningCategoryNotFoundException;
import org.mifosplatform.organisation.provisioning.serialization.ProvisioningCategoryDefinitionJsonDeserializer;
import org.mifosplatform.portfolio.charge.service.ChargeWritePlatformServiceJpaRepositoryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class ProvisioningCategoryWritePlatformServiceJpaRepositoryImpl implements ProvisioningCategoryWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(ChargeWritePlatformServiceJpaRepositoryImpl.class);
private final ProvisioningCategoryRepository provisioningCategoryRepository;
private final ProvisioningCategoryDefinitionJsonDeserializer fromApiJsonDeserializer;
private final JdbcTemplate jdbcTemplate;
@Autowired
public ProvisioningCategoryWritePlatformServiceJpaRepositoryImpl(final ProvisioningCategoryRepository provisioningCategoryRepository,
final ProvisioningCategoryDefinitionJsonDeserializer fromApiJsonDeserializer, final RoutingDataSource dataSource) {
this.provisioningCategoryRepository = provisioningCategoryRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public CommandProcessingResult createProvisioningCateogry(JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForCreate(command.json());
final ProvisioningCategory provisioningCategory = ProvisioningCategory.fromJson(command);
this.provisioningCategoryRepository.save(provisioningCategory);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(provisioningCategory.getId())
.build();
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Override
public CommandProcessingResult deleteProvisioningCateogry(JsonCommand command) {
this.fromApiJsonDeserializer.validateForCreate(command.json());
final ProvisioningCategory provisioningCategory = ProvisioningCategory.fromJson(command);
boolean isProvisioningCategoryInUse = isAnyLoanProductsAssociateWithThisProvisioningCategory(provisioningCategory.getId()) ;
if(isProvisioningCategoryInUse) {
throw new ProvisioningCategoryCannotBeDeletedException(
"error.msg.provisioningcategory.cannot.be.deleted.it.is.already.used.in.loanproduct",
"This provisioning category cannot be deleted, it is already used in loan product");
}
this.provisioningCategoryRepository.delete(provisioningCategory);
return new CommandProcessingResultBuilder().withEntityId(provisioningCategory.getId()).build();
}
@Override
public CommandProcessingResult updateProvisioningCategory(final Long categoryId, JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForUpdate(command.json());
final ProvisioningCategory provisioningCategoryForUpdate = this.provisioningCategoryRepository.findOne(categoryId);
if (provisioningCategoryForUpdate == null) { throw new ProvisioningCategoryNotFoundException(categoryId); }
final Map<String, Object> changes = provisioningCategoryForUpdate.update(command);
if (!changes.isEmpty()) {
this.provisioningCategoryRepository.save(provisioningCategoryForUpdate);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(categoryId).with(changes).build();
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
private boolean isAnyLoanProductsAssociateWithThisProvisioningCategory(final Long categoryID) {
final String sql = "select if((exists (select 1 from m_loanproduct_provisioning_details lpd where lpd.category_id = ?)) = 1, 'true', 'false')";
final String isLoansUsingCharge = this.jdbcTemplate.queryForObject(sql, String.class, new Object[] { categoryID });
return new Boolean(isLoansUsingCharge);
}
/*
* Guaranteed to throw an exception no matter what the data integrity issue
* is.
*/
private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("category_name")) {
final String name = command.stringValueOfParameterNamed("category_name");
throw new PlatformDataIntegrityException("error.msg.provisioning.duplicate.categoryname", "Provisioning Cateory with name `"
+ name + "` already exists", "category name", name);
}
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.charge.unknown.data.integrity.issue",
"Unknown data integrity issue with resource: " + realCause.getMessage());
}
}
@@ -0,0 +1,120 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.joda.time.DateTime;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.provisioning.constants.ProvisioningCriteriaConstants;
import org.mifosplatform.organisation.provisioning.domain.LoanProductProvisionCriteria;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCategory;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCategoryRepository;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCriteria;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCriteriaDefinition;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@Service
public class ProvisioningCriteriaAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final ProvisioningCategoryRepository provisioningCategoryRepository;
private final LoanProductRepository loanProductRepository;
private final GLAccountRepository glAccountRepository;
private final PlatformSecurityContext platformSecurityContext;
@Autowired
public ProvisioningCriteriaAssembler(final FromJsonHelper fromApiJsonHelper,
final ProvisioningCategoryRepository provisioningCategoryRepository, final LoanProductRepository loanProductRepository,
final GLAccountRepository glAccountRepository, final PlatformSecurityContext platformSecurityContext) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.provisioningCategoryRepository = provisioningCategoryRepository;
this.loanProductRepository = loanProductRepository;
this.glAccountRepository = glAccountRepository;
this.platformSecurityContext = platformSecurityContext;
}
public List<LoanProduct> parseLoanProducts(final JsonElement jsonElement) {
List<LoanProduct> loanProducts = new ArrayList<>();
if (fromApiJsonHelper.parameterExists(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM, jsonElement)) {
JsonArray jsonloanProducts = this.fromApiJsonHelper.extractJsonArrayNamed(ProvisioningCriteriaConstants.JSON_LOANPRODUCTS_PARAM,
jsonElement);
for (JsonElement element : jsonloanProducts) {
Long productId = this.fromApiJsonHelper.extractLongNamed("id", element.getAsJsonObject());
loanProducts.add(loanProductRepository.findOne(productId));
}
} else {
loanProducts = loanProductRepository.findAll();
}
return loanProducts ;
}
public ProvisioningCriteria fromParsedJson(final JsonElement jsonElement) {
ProvisioningCriteria provisioningCriteria = createCriteria(jsonElement);
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(jsonElement.getAsJsonObject());
List<LoanProduct> loanProducts = parseLoanProducts(jsonElement) ;
Set<ProvisioningCriteriaDefinition> criteriaDefinitions = new HashSet<>();
JsonArray jsonProvisioningCriteria = this.fromApiJsonHelper.extractJsonArrayNamed(
ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM, jsonElement);
for (JsonElement element : jsonProvisioningCriteria) {
JsonObject jsonObject = element.getAsJsonObject();
ProvisioningCriteriaDefinition provisioningCriteriaData = createProvisioningCriteriaDefinitions(jsonObject, locale,
provisioningCriteria);
criteriaDefinitions.add(provisioningCriteriaData);
}
Set<LoanProductProvisionCriteria> mapping = new HashSet<>();
for (LoanProduct loanProduct : loanProducts) {
mapping.add(new LoanProductProvisionCriteria(provisioningCriteria, loanProduct));
}
provisioningCriteria.setProvisioningCriteriaDefinitions(criteriaDefinitions);
provisioningCriteria.setLoanProductProvisioningCriteria(mapping);
return provisioningCriteria;
}
private ProvisioningCriteria createCriteria(final JsonElement jsonElement) {
final String criteriaName = this.fromApiJsonHelper.extractStringNamed(ProvisioningCriteriaConstants.JSON_CRITERIANAME_PARAM, jsonElement);
AppUser modifiedBy = null;
DateTime modifiedOn = null;
ProvisioningCriteria criteria = new ProvisioningCriteria(criteriaName, platformSecurityContext.authenticatedUser(), new DateTime(),
modifiedBy, modifiedOn);
return criteria;
}
private ProvisioningCriteriaDefinition createProvisioningCriteriaDefinitions(JsonObject jsonObject, Locale locale,
ProvisioningCriteria criteria) {
Long categoryId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_CATEOGRYID_PARAM, jsonObject);
Long minimumAge = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_MINIMUM_AGE_PARAM, jsonObject);
Long maximumAge = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_MAXIMUM_AGE_PARAM, jsonObject);
BigDecimal provisioningpercentage = this.fromApiJsonHelper.extractBigDecimalNamed(ProvisioningCriteriaConstants.JSON_PROVISIONING_PERCENTAGE_PARAM,
jsonObject, locale);
Long liabilityAccountId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_LIABILITY_ACCOUNT_PARAM, jsonObject);
Long expenseAccountId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_EXPENSE_ACCOUNT_PARAM, jsonObject);
ProvisioningCategory provisioningCategory = provisioningCategoryRepository.findOne(categoryId);
GLAccount liabilityAccount = glAccountRepository.findOne(liabilityAccountId);
GLAccount expenseAccount = glAccountRepository.findOne(expenseAccountId);
return ProvisioningCriteriaDefinition.newPrivisioningCriteria(criteria, provisioningCategory, minimumAge, maximumAge,
provisioningpercentage, liabilityAccount, expenseAccount);
}
}
@@ -0,0 +1,22 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import java.util.Collection;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCriteriaData;
public interface ProvisioningCriteriaReadPlatformService {
public ProvisioningCriteriaData retrievePrivisiongCriteriaTemplate() ;
public ProvisioningCriteriaData retrieveProvisioningCriteria(Long criteriaId) ;
public Collection<ProvisioningCriteriaData> retrieveAllProvisioningCriterias() ;
public ProvisioningCriteriaData retrievePrivisiongCriteriaTemplate(ProvisioningCriteriaData data) ;
}
@@ -0,0 +1,167 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCategoryData;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCriteriaData;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCriteriaDefinitionData;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class ProvisioningCriteriaReadPlatformServiceImpl implements ProvisioningCriteriaReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final ProvisioningCategoryReadPlatformService provisioningCategoryReadPlatformService;
private final LoanProductReadPlatformService loanProductReadPlatformService;
private final GLAccountReadPlatformService glAccountReadPlatformService;
private final LoanProductReadPlatformService loanProductReaPlatformService;
@Autowired
public ProvisioningCriteriaReadPlatformServiceImpl(final RoutingDataSource dataSource,
final ProvisioningCategoryReadPlatformService provisioningCategoryReadPlatformService,
final LoanProductReadPlatformService loanProductReadPlatformService,
final GLAccountReadPlatformService glAccountReadPlatformService,
final LoanProductReadPlatformService loanProductReaPlatformService) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.provisioningCategoryReadPlatformService = provisioningCategoryReadPlatformService;
this.loanProductReadPlatformService = loanProductReadPlatformService;
this.glAccountReadPlatformService = glAccountReadPlatformService;
this.loanProductReaPlatformService = loanProductReaPlatformService;
}
@Override
public ProvisioningCriteriaData retrievePrivisiongCriteriaTemplate() {
boolean onlyActive = true;
final Collection<ProvisioningCategoryData> categories = this.provisioningCategoryReadPlatformService
.retrieveAllProvisionCategories();
final Collection<LoanProductData> allLoanProducts = this.loanProductReadPlatformService
.retrieveAllLoanProductsForLookup(onlyActive);
final Collection<GLAccountData> glAccounts = this.glAccountReadPlatformService.retrieveAllEnabledDetailGLAccounts();
return ProvisioningCriteriaData.toTemplate(constructCriteriaTemplate(categories), allLoanProducts, glAccounts);
}
@Override
public ProvisioningCriteriaData retrievePrivisiongCriteriaTemplate(ProvisioningCriteriaData data) {
boolean onlyActive = true;
final Collection<ProvisioningCategoryData> categories = this.provisioningCategoryReadPlatformService
.retrieveAllProvisionCategories();
final Collection<LoanProductData> allLoanProducts = this.loanProductReadPlatformService
.retrieveAllLoanProductsForLookup(onlyActive);
final Collection<GLAccountData> glAccounts = this.glAccountReadPlatformService.retrieveAllEnabledDetailGLAccounts();
return ProvisioningCriteriaData.toTemplate(data, constructCriteriaTemplate(categories), allLoanProducts, glAccounts);
}
private Collection<ProvisioningCriteriaDefinitionData> constructCriteriaTemplate(Collection<ProvisioningCategoryData> categories) {
List<ProvisioningCriteriaDefinitionData> definitions = new ArrayList<>();
for (ProvisioningCategoryData data : categories) {
definitions.add(ProvisioningCriteriaDefinitionData.template(data.getId(), data.getCategoryName()));
}
return definitions;
}
@Override
public Collection<ProvisioningCriteriaData> retrieveAllProvisioningCriterias() {
ProvisioningCriteriaRowMapper mapper = new ProvisioningCriteriaRowMapper() ;
final String sql = "select " + mapper.schema() ;
return this.jdbcTemplate.query(sql, mapper, new Object[] {});
}
private static final class ProvisioningCriteriaRowMapper implements RowMapper<ProvisioningCriteriaData> {
@Override
public ProvisioningCriteriaData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
Long criteriaId = rs.getLong("id") ;
String criteriaName = rs.getString("criteriaName");
String createdBy = rs.getString("username") ;
return ProvisioningCriteriaData.toLookup(criteriaId, criteriaName, createdBy) ;
}
public String schema() {
return "mpc.id as id, mpc.criteria_name as criteriaName, appu.username as username from m_provisioning_criteria as mpc LEFT JOIN m_appuser appu on mpc.createdby_id=appu.id";
}
}
@Override
public ProvisioningCriteriaData retrieveProvisioningCriteria(Long criteriaId) {
String criteriaName = retrieveCriteriaName(criteriaId);
Collection<LoanProductData> loanProducts = loanProductReaPlatformService
.retrieveAllLoanProductsForLookup("select product_id from m_loanproduct_provisioning_mapping where m_loanproduct_provisioning_mapping.criteria_id="
+ criteriaId);
List<ProvisioningCriteriaDefinitionData> definitions = retrieveProvisioningDefinitions(criteriaId);
return ProvisioningCriteriaData.toLookup(criteriaId, criteriaName, loanProducts, definitions);
}
private List<ProvisioningCriteriaDefinitionData> retrieveProvisioningDefinitions(Long criteriaId) {
ProvisioningCriteriaDefinitionRowMapper rowMapper = new ProvisioningCriteriaDefinitionRowMapper();
final String sql = "select " + rowMapper.schema() + " where pc.criteria_id = ?";
return this.jdbcTemplate.query(sql, rowMapper, new Object[] { criteriaId });
}
private static final class ProvisioningCriteriaDefinitionRowMapper implements RowMapper<ProvisioningCriteriaDefinitionData> {
private final StringBuilder sqlQuery = new StringBuilder()
.append("pc.id, pc.criteria_id, pc.category_id, mpc.category_name, pc.min_age, pc.max_age, ")
.append("pc.provision_percentage, pc.liability_account, pc.expense_account, lia.gl_code as liabilitycode, expe.gl_code as expensecode ")
.append("from m_provisioning_criteria_definition as pc ")
.append("LEFT JOIN acc_gl_account lia ON lia.id = pc.liability_account ")
.append("LEFT JOIN acc_gl_account expe ON expe.id = pc.expense_account ")
.append("LEFT JOIN m_provision_category mpc ON mpc.id = pc.category_id");
@Override
public ProvisioningCriteriaDefinitionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum)
throws SQLException {
Long id = rs.getLong("id");
// Long criteriaId = rs.getLong("criteria_id");
Long categoryId = rs.getLong("category_id");
String categoryName = rs.getString("category_name");
Long minAge = rs.getLong("min_age");
Long maxAge = rs.getLong("max_age");
BigDecimal provisioningPercentage = rs.getBigDecimal("provision_percentage");
Long liabilityAccount = rs.getLong("liability_account");
Long expenseAccount = rs.getLong("expense_account");
String liabilityAccountCode = rs.getString("liabilitycode");
String expenseAccountCode = rs.getString("expensecode");
return new ProvisioningCriteriaDefinitionData(id, categoryId, categoryName, minAge, maxAge, provisioningPercentage,
liabilityAccount, expenseAccount, liabilityAccountCode, expenseAccountCode);
}
public String schema() {
return sqlQuery.toString();
}
}
private String retrieveCriteriaName(Long criteriaId) {
ProvisioningCriteriaNameRowMapper rowMapper = new ProvisioningCriteriaNameRowMapper();
final String sql = "select " + rowMapper.schema() + " from m_provisioning_criteria pc where pc.id = ?";
return this.jdbcTemplate.queryForObject(sql, rowMapper, new Object[] { criteriaId });
}
private static final class ProvisioningCriteriaNameRowMapper implements RowMapper<String> {
@Override
public String mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
return rs.getString("criteriaName");
}
public String schema() {
return " pc.criteria_name as criteriaName";
}
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface ProvisioningCriteriaWritePlatformService {
CommandProcessingResult createProvisioningCriteria(JsonCommand command);
CommandProcessingResult updateProvisioningCriteria(final Long categoryId, JsonCommand command) ;
CommandProcessingResult deleteProvisioningCriteria(Long entryId) ;
}
@@ -0,0 +1,149 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.organisation.provisioning.service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
import org.mifosplatform.accounting.provisioning.service.ProvisioningEntriesReadPlatformService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.organisation.provisioning.constants.ProvisioningCriteriaConstants;
import org.mifosplatform.organisation.provisioning.data.ProvisioningCriteriaDefinitionData;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCriteria;
import org.mifosplatform.organisation.provisioning.domain.ProvisioningCriteriaRepository;
import org.mifosplatform.organisation.provisioning.exception.ProvisioningCategoryNotFoundException;
import org.mifosplatform.organisation.provisioning.exception.ProvisioningCriteriaCannotBeDeletedException;
import org.mifosplatform.organisation.provisioning.exception.ProvisioningCriteriaNotFoundException;
import org.mifosplatform.organisation.provisioning.serialization.ProvisioningCriteriaDefinitionJsonDeserializer;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
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 com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@Service
public class ProvisioningCriteriaWritePlatformServiceJpaRepositoryImpl implements ProvisioningCriteriaWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(ProvisioningCriteriaWritePlatformServiceJpaRepositoryImpl.class);
private final ProvisioningCriteriaDefinitionJsonDeserializer fromApiJsonDeserializer;
private final ProvisioningCriteriaAssembler provisioningCriteriaAssembler;
private final ProvisioningCriteriaRepository provisioningCriteriaRepository;
private final FromJsonHelper fromApiJsonHelper;
private final GLAccountRepository glAccountRepository;
private final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService ;
@Autowired
public ProvisioningCriteriaWritePlatformServiceJpaRepositoryImpl(final ProvisioningCriteriaDefinitionJsonDeserializer fromApiJsonDeserializer,
final ProvisioningCriteriaAssembler provisioningCriteriaAssembler, final ProvisioningCriteriaRepository provisioningCriteriaRepository,
final FromJsonHelper fromApiJsonHelper,
final GLAccountRepository glAccountRepository,
final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService) {
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.provisioningCriteriaAssembler = provisioningCriteriaAssembler;
this.provisioningCriteriaRepository = provisioningCriteriaRepository;
this.fromApiJsonHelper = fromApiJsonHelper ;
this.glAccountRepository = glAccountRepository ;
this.provisioningEntriesReadPlatformService = provisioningEntriesReadPlatformService ;
}
@Override
public CommandProcessingResult createProvisioningCriteria(JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForCreate(command.json());
ProvisioningCriteria provisioningCriteria = provisioningCriteriaAssembler.fromParsedJson(command.parsedJson());
this.provisioningCriteriaRepository.save(provisioningCriteria);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(provisioningCriteria.getId()).build();
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Override
public CommandProcessingResult deleteProvisioningCriteria(Long criteriaId) {
ProvisioningCriteria criteria = this.provisioningCriteriaRepository.findOne(criteriaId) ;
if(criteria == null) {
throw new ProvisioningCriteriaNotFoundException(criteriaId) ;
}
if(this.provisioningEntriesReadPlatformService.retrieveProvisioningEntryDataByCriteriaId(criteriaId) != null) {
throw new ProvisioningCriteriaCannotBeDeletedException(criteriaId) ;
}
this.provisioningCriteriaRepository.delete(criteriaId) ;
return new CommandProcessingResultBuilder().withEntityId(criteriaId).build();
}
@Override
public CommandProcessingResult updateProvisioningCriteria(final Long criteriaId, JsonCommand command) {
this.fromApiJsonDeserializer.validateForUpdate(command.json());
ProvisioningCriteria provisioningCriteria = provisioningCriteriaRepository.findOne(criteriaId) ;
if(provisioningCriteria == null) {
throw new ProvisioningCategoryNotFoundException(criteriaId) ;
}
List<LoanProduct> products = this.provisioningCriteriaAssembler.parseLoanProducts(command.parsedJson()) ;
final Map<String, Object> changes = provisioningCriteria.update(command, products) ;
if(!changes.isEmpty()) {
updateProvisioningCriteriaDefinitions(provisioningCriteria, command) ;
provisioningCriteriaRepository.save(provisioningCriteria) ;
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(provisioningCriteria.getId()).build();
}
private void updateProvisioningCriteriaDefinitions(ProvisioningCriteria provisioningCriteria, JsonCommand command) {
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(command.parsedJson().getAsJsonObject());
JsonArray jsonProvisioningCriteria = this.fromApiJsonHelper.extractJsonArrayNamed(
ProvisioningCriteriaConstants.JSON_PROVISIONING_DEFINITIONS_PARAM, command.parsedJson());
for (JsonElement element : jsonProvisioningCriteria) {
JsonObject jsonObject = element.getAsJsonObject();
Long id = this.fromApiJsonHelper.extractLongNamed("id", jsonObject) ;
Long categoryId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_CATEOGRYID_PARAM, jsonObject);
Long minimumAge = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_MINIMUM_AGE_PARAM, jsonObject);
Long maximumAge = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_MAXIMUM_AGE_PARAM, jsonObject);
BigDecimal provisioningpercentage = this.fromApiJsonHelper.extractBigDecimalNamed(ProvisioningCriteriaConstants.JSON_PROVISIONING_PERCENTAGE_PARAM,
jsonObject, locale);
Long liabilityAccountId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_LIABILITY_ACCOUNT_PARAM, jsonObject);
Long expenseAccountId = this.fromApiJsonHelper.extractLongNamed(ProvisioningCriteriaConstants.JSON_EXPENSE_ACCOUNT_PARAM, jsonObject);
GLAccount liabilityAccount = glAccountRepository.findOne(liabilityAccountId);
GLAccount expenseAccount = glAccountRepository.findOne(expenseAccountId);
String categoryName = null ;
ProvisioningCriteriaDefinitionData data = new ProvisioningCriteriaDefinitionData(id, categoryId,
categoryName, minimumAge, maximumAge, provisioningpercentage,
liabilityAccount.getId(), expenseAccount.getId(), liabilityAccount.getGlCode(), expenseAccount.getGlCode()) ;
provisioningCriteria.update(data, liabilityAccount, expenseAccount) ;
}
}
/*
* Guaranteed to throw an exception no matter what the data integrity issue
* is.
*/
private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("criteria_name")) {
final String name = command.stringValueOfParameterNamed("criteria_name");
throw new PlatformDataIntegrityException("error.msg.provisioning.duplicate.criterianame", "Provisioning Criteria with name `"
+ name + "` already exists", "category name", name);
}
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.charge.unknown.data.integrity.issue",
"Unknown data integrity issue with resource: " + realCause.getMessage());
}
}
@@ -14,6 +14,8 @@ public interface LoanProductReadPlatformService {
Collection<LoanProductData> retrieveAllLoanProducts();
Collection<LoanProductData> retrieveAllLoanProductsForLookup(String inClass);
Collection<LoanProductData> retrieveAllLoanProductsForLookup();
Collection<LoanProductData> retrieveAllLoanProductsForLookup(boolean activeOnly);
@@ -98,6 +98,22 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
return this.jdbcTemplate.query(sql, rm, new Object[] {});
}
@Override
public Collection<LoanProductData> retrieveAllLoanProductsForLookup(String inClause) {
this.context.authenticatedUser();
final LoanProductLookupMapper rm = new LoanProductLookupMapper();
String sql = "select " + rm.schema();
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
sql += " where lp.id in ( " + inClause + " ) ";
}
return this.jdbcTemplate.query(sql, rm, new Object[] {});
}
@Override
public Collection<LoanProductData> retrieveAllLoanProductsForLookup() {
return retrieveAllLoanProductsForLookup(false);
@@ -8,7 +8,11 @@ package org.mifosplatform.useradministration.domain;
import org.mifosplatform.infrastructure.security.domain.PlatformUserRepository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface AppUserRepository extends JpaRepository<AppUser, Long>, JpaSpecificationExecutor<AppUser>, PlatformUserRepository {
@Query("Select appUser from AppUser appUser where appUser.username = :username")
AppUser findAppUserByName(@Param("username") String username);
}
@@ -0,0 +1,30 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.useradministration.domain;
import org.mifosplatform.useradministration.exception.UserNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AppUserRepositoryWrapper {
private final AppUserRepository appUserRepository ;
@Autowired
public AppUserRepositoryWrapper(final AppUserRepository appUserRepository) {
this.appUserRepository = appUserRepository ;
}
public AppUser fetchSystemUser() {
String userName = "system" ;
AppUser user = this.appUserRepository.findAppUserByName(userName);
if(user == null) {
throw new UserNotFoundException(userName) ;
}
return user ;
}
}
@@ -15,4 +15,8 @@ public class UserNotFoundException extends AbstractPlatformResourceNotFoundExcep
public UserNotFoundException(final Long id) {
super("error.msg.user.id.invalid", "User with identifier " + id + " does not exist", id);
}
public UserNotFoundException(final String userName) {
super("error.msg.user.name.not.found", "User with identifier " + userName + " does not exist");
}
}
@@ -0,0 +1,106 @@
CREATE TABLE `m_provision_category` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`category_name` VARCHAR(100) NOT NULL,
`description` VARCHAR(300) NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `category_name` (`category_name`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1
;
INSERT INTO `m_provision_category` (`category_name`, `description`) VALUES ('STANDARD', 'Punctual Payment without any dues');
INSERT INTO `m_provision_category` (`category_name`, `description`) VALUES ('SUB-STANDARD', 'Principal and/or Interest overdue by x days');
INSERT INTO `m_provision_category` (`category_name`, `description`) VALUES ('DOUBTFUL', 'Principal and/or Interest overdue by x days and less than y');
INSERT INTO `m_provision_category` (`category_name`, `description`) VALUES ('LOSS', 'Principal and/or Interest overdue by y days');
CREATE TABLE `m_provisioning_criteria` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`criteria_name` VARCHAR(200) NOT NULL,
`createdby_id` BIGINT(20) NULL DEFAULT NULL,
`created_date` DATETIME NULL DEFAULT NULL,
`lastmodifiedby_id` BIGINT(20) NULL DEFAULT NULL,
`lastmodified_date` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `criteria_name` (`criteria_name`),
FOREIGN KEY (`createdby_id`) REFERENCES `m_appuser` (`id`),
FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `m_appuser` (`id`)
);
CREATE TABLE `m_provisioning_criteria_definition` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`criteria_id` BIGINT(20) NOT NULL,
`category_id` BIGINT(20) NOT NULL,
`min_age` BIGINT(20) NOT NULL,
`max_age` BIGINT(20) NOT NULL,
`provision_percentage` DECIMAL(5,2) NOT NULL,
`liability_account` BIGINT(20),
`expense_account` BIGINT(20),
PRIMARY KEY (`id`),
FOREIGN KEY (`criteria_id`) REFERENCES `m_provisioning_criteria` (`id`),
FOREIGN KEY (`category_id`) REFERENCES `m_provision_category` (`id`),
FOREIGN KEY (`liability_account`) REFERENCES `acc_gl_account` (`id`),
FOREIGN KEY (`expense_account`) REFERENCES `acc_gl_account` (`id`)
);
CREATE TABLE `m_loanproduct_provisioning_mapping` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`product_id` BIGINT(20) NOT NULL,
`criteria_id` BIGINT(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `product_id` (`product_id`),
FOREIGN KEY (`product_id`) REFERENCES `m_product_loan` (`id`),
FOREIGN KEY (`criteria_id`) REFERENCES `m_provisioning_criteria` (`id`)
);
CREATE TABLE `m_provisioning_history` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`journal_entry_created` BIT(1) DEFAULT 0,
`createdby_id` BIGINT(20) NULL DEFAULT NULL,
`created_date` DATE NULL DEFAULT NULL,
`lastmodifiedby_id` BIGINT(20) NULL DEFAULT NULL,
`lastmodified_date` DATE NULL DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`createdby_id`) REFERENCES `m_appuser` (`id`),
FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `m_appuser` (`id`)
);
CREATE TABLE `m_loanproduct_provisioning_entry` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`history_id` BIGINT(20) NOT NULL,
`criteria_id` BIGINT(20) NOT NULL,
`currency_code` VARCHAR(3) NOT NULL,
`office_id` BIGINT(20) NOT NULL,
`product_id` BIGINT(20) NOT NULL,
`category_id` BIGINT(20) NOT NULL,
`overdue_in_days` BIGINT(20) DEFAULT 0,
`reseve_amount` DECIMAL(20,6) DEFAULT 0,
`liability_account` BIGINT(20) NULL DEFAULT NULL,
`expense_account` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`history_id`) REFERENCES `m_provisioning_history` (`id`),
FOREIGN KEY (`criteria_id`) REFERENCES `m_provisioning_criteria` (`id`),
FOREIGN KEY (`currency_code`) REFERENCES `m_currency` (`code`),
FOREIGN KEY (`office_id`) REFERENCES `m_office` (`id`),
FOREIGN KEY (`product_id`) REFERENCES `m_product_loan` (`id`),
FOREIGN KEY (`category_id`) REFERENCES `m_provision_category` (`id`),
FOREIGN KEY (`liability_account`) REFERENCES `acc_gl_account` (`id`),
FOREIGN KEY (`expense_account`) REFERENCES `acc_gl_account` (`id`)
);
INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`, `is_misfired`) VALUES ('Generate Loan Loss Provisioning', 'Generate Loan Loss Provisioning', '0 0 0 1/1 * ? *', now(), 5, NULL, NULL, NULL, NULL, NULL, 1, 0, 1, 0, 0);
INSERT INTO `m_permission`(`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES
('LOAN_PROVISIONING', 'CREATE_PROVISIONCATEGORY', 'PROVISIONCATEGORY', 'CREATE', 0),
('LOAN_PROVISIONING', 'DELETE_PROVISIONCATEGORY', 'PROVISIONCATEGORY', 'DELETE', 0),
('LOAN_PROVISIONING', 'CREATE_PROVISIONCRITERIA', 'PROVISIONINGCRITERIA', 'CREATE', 0),
('LOAN_PROVISIONING', 'UPDATE_PROVISIONCRITERIA', 'PROVISIONINGCRITERIA', 'UPDATE', 0),
('LOAN_PROVISIONING', 'DELETE_PROVISIONCRITERIA', 'PROVISIONINGCRITERIA', 'DELETE', 0),
('LOAN_PROVISIONING', 'CREATE_PROVISIONENTRIES', 'PROVISIONINGENTRIES', 'CREATE', 0),
('LOAN_PROVISIONING', 'CREATE_PROVISIONJOURNALENTRIES', 'PROVISIONINGENTRIES', 'CREATE', 0),
('LOAN_PROVISIONING', 'RECREATE_PROVISIONENTRIES', 'PROVISIONINGENTRIES', 'RECREATE', 0);
INSERT INTO `m_appuser` ( `is_deleted`, `office_id`, `staff_id`, `username`, `firstname`, `lastname`, `password`, `email`, `firsttime_login_remaining`, `nonexpired`, `nonlocked`, `nonexpired_credentials`, `enabled`, `last_time_password_updated`, `password_never_expires`) VALUES
(0, 1, NULL, 'system', 'system', 'system', '5787039480429368bf94732aacc771cd0a3ea02bcf504ffe1185ab94213bc63a', 'demomfi@mifos.org', b'0', b'1', b'1', b'1', b'1', '2014-03-07', 0);