diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index b983bba2d..3bc61e3b7 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -1679,6 +1679,25 @@
|
|
+
+
+ | Provisioning Entries |
+ provisioningentries |
+ Create Provisioning Entry |
+ List Provisioning Entries |
+ |
+ |
+
+
+ |
+ provisioningentries/{entryId} | P
+ |
+ Retrieve Provisioning Entry |
+ Recreates Provisionin Entry |
+ Add Provisioning Journal Entries |
+
+
+
@@ -2090,7 +2109,23 @@
Update Payment Type |
Delete Payment Type |
-
+
+ | Provisioning Criteria |
+ provisioningcriteria |
+ Create Provisioning Criteria |
+ List Provisioning Criteria |
+ |
+ |
+
+
+ |
+ provisioningcriteria/{criteriaId} |
+ Retrieve Provisioning Criteria |
+ Update Provisioning Criteria |
+ Delete Provisioning Criteria |
+ |
+ |
+
@@ -33626,6 +33661,244 @@ Request Body:
+
+
+
+
+
Provisioning Entries
+
This defines the Provisioning Entries for all active loan products
+
+
+ Field Descriptions |
+
+
+ | date |
+
+
+ | Date on which day provisioning entries should be created
+ |
+
+
+ | createjournalentries |
+
+
+ |
+ Boolean variable whether to add journal entries for generated provisioning entries
+ |
+
+
+
+
+
+
+
+
+
+
Create new Provisioning Entries
+
Creates a new Provisioning Entries
+
+
+ Mandatory Fields |
+
+
+ | date |
+
+
+ | dateFormat |
+
+
+ | locale |
+
+
+
+
+ Optional Fields |
+
+
+ | createjournalentries |
+
+
+
+
+
+ POST https://DomainName/api/v1/provisioningentries
+
+
+ POST provisioningentries
+ Content-Type: application/json
+ Request Body:
+ {
+ "date":"16 October 2015",
+ "locale":"en",
+ "dateFormat":"dd MMMM yyyy",
+ "createjournalentries":true
+ }
+
+
+ {
+ "resourceId": 1
+ }
+
+
+
+
+
+
+
+
List all Provisioning Entries
+
List all Provisioning Entries
+
+
+
+
+ GET https://DomainName/api/v1/provisioningentries
+
+
+ GET provisioningentries
+ Content-Type: application/json
+ Request Body:
+ {
+
+ }
+
+
+ {[
+ {"id":1, "journalEntry":true, "createdUser":"mifos", "createdDate":"16 October 2014"},
+ {"id":2, "journalEntry":true, "createdUser":"mifos", "createdDate":"16 October 2015"}
+ ]
+ }
+
+
+
+
+
+
+
+
Retrieves a Provisioning Entry
+
Returns the details of a generated Provisioning Entry.
+
+
+
+
+ GET https://DomainName/api/v1/provisioningentries/{privisioningEntryId}
+
+
+ GET provisioningentries
+ Content-Type: application/json
+ Request Body:
+ {
+
+ }
+
+
+ {
+ "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"
+ }
+ ]
+
+ }
+
+
+
+
+
+
+
+
+
Recreates Provisioning Entry
+
Recreates Provisioning Entry.
+
+
+
+
+ POST https://DomainName/api/v1/provisioningentries/{privisioningEntryId}?command=recreateprovisioningentry
+
+
+ POST provisioningentries
+ Content-Type: application/json
+ Request Body:
+ {
+ "command":"recreateprovisioningentry"
+ }
+
+
+ {
+ "resourceId": 1
+ }
+
+
+
+
+
+
+
+
Retrieves a Provisioning Entry
+
Returns the details of a generated Provisioning Entry.
+
+
+
+
+ POST https://DomainName/api/v1/provisioningentries/{privisioningEntryId}?command=createjournalentry
+
+
+ POST provisioningentries
+ Content-Type: application/json
+ Request Body:
+ {
+ "command":"createjournalentry"
+ }
+
+
+
+ {
+ "resourceId": 1
+ }
+
+
+
+
+
@@ -35679,6 +35952,320 @@ No Request Body:
+
+
+
+
+
Provisioning Criteria
+
This defines the Provisioning Criteria
+
+
+ Field Descriptions |
+
+
+ | Provisioning Criteria Name |
+
+
+ | Name the Provisioning Criteria
+ |
+
+
+ | Loan Products |
+
+
+ |
+ Select all loan products for which provisioning criteria to be associated
+ |
+
+
+ | Provisioning Categories |
+
+
+ |
+ Define minimum, maximum, percentage, liability account, expense account for all provisioning categories
+ |
+
+
+
+
+
+
+
+
+
+
Create a new Provisioning Criteria
+
Creates a new Provisioning Criteria
+
+
+ Mandatory Fields |
+
+
+ | criteriaName |
+
+
+ | provisioningcriteria |
+
+
+
+
+ Optional Fields |
+
+
+ | loanProducts |
+
+
+
+
+
+ POST https://DomainName/api/v1/provisioningcriteria
+
+
+ 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}]
+ }
+
+
+ {
+ "resourceId": 1
+ }
+
+
+
+
+
+
+
Retrieves all created Provisioning Criterias
+
Retrieves all created Provisioning Criterias
+
+
+
+ GET https://DomainName/api/v1/provisioningcriteria
+
+
+ GET provisioningcriteria
+ Content-Type: application/json
+ Request Body:
+ {
+ }
+
+
+ {
+ [
+ {"criteriaId":1,
+ "criteriaName":"High Risk Products Criteria",
+ "createdBy":"mifos"},
+ {"criteriaId":2,
+ "criteriaName":"Low Risk Products Criteria",
+ "createdBy":"mifos"}
+ ]
+ }
+
+
+
+
+
+
+
Retrieves a Provisioning Criteria
+
Retrieves a Provisioning Criteria
+
+
+
+ GET https://DomainName/api/v1/provisioningcriteria/{criteriaId}
+
+
+ GET provisioningcriteria/{criteriaId}
+ Content-Type: application/json
+ Request Body:
+ {
+ }
+
+
+ {
+ "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}]
+ }
+
+
+
+
+
+
+
Updates a new Provisioning Criteria
+
Updates a new Provisioning Criteria
+
+
+ Optional Fields |
+
+
+ | criteriaName, loanProducts, provisioningcriteria |
+
+
+
+
+
+ PUT https://DomainName/api/v1/provisioningcriteria/{criteriaId}
+
+
+ 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}]
+ }
+
+
+ {
+ "resourceId": 1,
+ "changes": {
+ "criteriaName": "High Risk Products Criteria1"
+ }
+ }
+
+
+
+
+
+
+
Deletes Provisioning Criteria
+
Deletes Provisioning Criteria
+
+
+
+ DELETE https://DomainName/api/v1/provisioningcriteria/{criteriaId}
+
+
+ DELETE provisioningcriteria/{criteriaId}
+ Content-Type: application/json
+ Request Body:
+ {
+ }
+
+
+ {
+ "resourceId": 1,
+ }
+
+
+
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ProvisioningIntegrationTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ProvisioningIntegrationTest.java
new file mode 100644
index 000000000..b6238cce7
--- /dev/null
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ProvisioningIntegrationTest.java
@@ -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 loanProducts = new ArrayList<>(LOANPRODUCTS_SIZE);
+ List 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 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);
+ }
+}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/provisioning/ProvisioningHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/provisioning/ProvisioningHelper.java
new file mode 100644
index 000000000..9ce55683f
--- /dev/null
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/provisioning/ProvisioningHelper.java
@@ -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 loanProducts, ArrayList categories, Account liability,
+ Account expense) {
+ final HashMap 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 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 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;
+ }
+}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/provisioning/ProvisioningTransactionHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/provisioning/ProvisioningTransactionHelper.java
new file mode 100644
index 000000000..1054821a7
--- /dev/null
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/provisioning/ProvisioningTransactionHelper.java
@@ -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");
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/JournalEntriesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/JournalEntriesApiResource.java
index e4bdb6902..e23218b84 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/JournalEntriesApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/JournalEntriesApiResource.java
@@ -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 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 })
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java
index 9edf8103f..bca451961 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java
@@ -192,4 +192,12 @@ public class JournalEntry extends AbstractAuditableCustom {
return this.clientTransaction;
}
+ public Long getEntityId() {
+ return this.entityId ;
+ }
+
+ public Integer getEntityType() {
+ return this.entityType ;
+ }
+
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntryRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntryRepository.java
index c43fea5cb..3e2f4946a 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntryRepository.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntryRepository.java
@@ -23,4 +23,7 @@ public interface JournalEntryRepository extends JpaRepository findNonReversedContraTansactionIds(@Param("contraId") Long contraId, @Param("officeId") Long officeId);
+
+ @Query("from JournalEntry journalEntry where journalEntry.entityId= :entityId and journalEntry.entityType = :entityType")
+ List findProvisioningJournalEntriesByEntityId(@Param("entityId") Long entityId, @Param("entityType") Integer entityType) ;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java
index dfb76c296..86e1cb256 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java
@@ -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;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryReadPlatformService.java
index e26e3a38f..1472f4dc1 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryReadPlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryReadPlatformService.java
@@ -22,4 +22,5 @@ public interface JournalEntryReadPlatformService {
OfficeOpeningBalancesData retrieveOfficeOpeningBalances(Long officeId, String currencyCode);
+ Page retrieveJournalEntriesByEntityId(String transactionId, Long entityId, Integer entityType) ;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryReadPlatformServiceImpl.java
index c04e8369c..96a0a5e83 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryReadPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryReadPlatformServiceImpl.java
@@ -502,4 +502,19 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
associationParametersData);
}
+
+ @Override
+ public Page 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);
+ }
+ }
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformService.java
index 384896984..a3b8dc01b 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformService.java
@@ -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 accountingBridgeData);
CommandProcessingResult defineOpeningBalance(JsonCommand command);
+
+ public String revertProvisioningJournalEntries(final Date reversalTransactionDate, final Long entityId, final Integer entityType) ;
+ public String createProvisioningJournalEntries(ProvisioningEntry entry) ;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
index d6c6f295d..b3ec4933c 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
@@ -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 journalEntries = this.glJournalEntryRepository
- .findUnReversedManualJournalEntriesByTransactionId(command.getTransactionId());
+ final List 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 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 provisioningEntries = provisioningEntry.getLoanProductProvisioningEntries();
+ Map> officeMap = new HashMap<>();
+
+ for (LoanProductProvisioningEntry entry : provisioningEntries) {
+ OfficeCurrencyKey key = new OfficeCurrencyKey(entry.getOffice(), entry.getCurrencyCode());
+ if (officeMap.containsKey(key)) {
+ List list = officeMap.get(key);
+ list.add(entry);
+ } else {
+ List list = new ArrayList<>();
+ list.add(entry);
+ officeMap.put(key, list);
+ }
+ }
+
+ Set officeSet = officeMap.keySet();
+ Map liabilityMap = new HashMap<>();
+ Map expenseMap = new HashMap<>();
+
+ for (OfficeCurrencyKey key : officeSet) {
+ liabilityMap.clear();
+ expenseMap.clear();
+ List 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 liabilityMap,
+ Map expenseMap) {
+ Set liabilityAccounts = liabilityMap.keySet();
+ for (GLAccount account : liabilityAccounts) {
+ this.helper.createProvisioningCreditJournalEntry(transactionDate,entryId, office, currencyCode, account,
+ liabilityMap.get(account));
+ }
+ Set 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 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();
+ }
+ }
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/domain/PortfolioProductType.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/domain/PortfolioProductType.java
index 056b23609..27a086e3d 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/domain/PortfolioProductType.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/domain/PortfolioProductType.java
@@ -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;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/api/ProvisioningEntriesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/api/ProvisioningEntriesApiResource.java
new file mode 100644
index 000000000..e4ddb3599
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/api/ProvisioningEntriesApiResource.java
@@ -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 toApiJsonSerializer;
+ private final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService ;
+ private final ApiRequestParameterHelper apiRequestParameterHelper;
+
+ @Autowired
+ public ProvisioningEntriesApiResource(final PlatformSecurityContext platformSecurityContext,
+ final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
+ final DefaultToApiJsonSerializer 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 data = this.provisioningEntriesReadPlatformService.retrieveAllProvisioningEntries() ;
+ final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings, data, ProvisioningEntriesApiConstants.ALL_PROVISIONING_ENTRIES);
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/constant/ProvisioningEntriesApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/constant/ProvisioningEntriesApiConstants.java
new file mode 100644
index 000000000..f890c82c1
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/constant/ProvisioningEntriesApiConstants.java
@@ -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 supportedParameters = new HashSet<>(Arrays.asList(JSON_DATE_PARAM, JSON_DATEFORMAT_PARAM,JSON_LOCALE_PARAM,
+ JSON_CREATEJOURNALENTRIES_PARAM));
+
+ Set PROVISIONING_ENTRY_PARAMETERS = new HashSet<>(Arrays.asList("provisioningentry", "entries"));
+
+ Set ALL_PROVISIONING_ENTRIES = new HashSet<>(Arrays.asList("provisioningentry"));
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/data/LoanProductProvisioningEntryData.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/data/LoanProductProvisioningEntryData.java
new file mode 100644
index 000000000..3960e5233
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/data/LoanProductProvisioningEntryData.java
@@ -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 ;
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/data/ProvisioningEntryData.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/data/ProvisioningEntryData.java
new file mode 100644
index 000000000..902c0cb96
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/data/ProvisioningEntryData.java
@@ -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 provisioningEntries ;
+
+ public ProvisioningEntryData(final Long id, final Collection 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 provisioningEntries) {
+ this.provisioningEntries = provisioningEntries ;
+ }
+
+ public Long getId() {
+ return this.id ;
+ }
+
+ public Date getCreatedDate() {
+ return this.createdDate ;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/LoanProductProvisioningEntry.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/LoanProductProvisioningEntry.java
new file mode 100644
index 000000000..72507892b
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/LoanProductProvisioningEntry.java
@@ -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 {
+
+ @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());
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/ProvisioningEntry.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/ProvisioningEntry.java
new file mode 100644
index 000000000..51b0a9a7a
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/ProvisioningEntry.java
@@ -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 {
+
+ @Column(name = "journal_entry_created")
+ private Boolean isJournalEntryCreated;
+
+ @LazyCollection(LazyCollectionOption.FALSE)
+ @OneToMany(cascade = CascadeType.ALL, mappedBy = "entry", orphanRemoval = true)
+ Collection 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 provisioningEntries ) {
+ this.provisioningEntries = provisioningEntries ;
+ this.createdBy = createdBy ;
+ this.createdDate = createdDate ;
+ this.lastModifiedBy = lastModifiedBy ;
+ this.lastModifiedDate = lastModifiedDate ;
+ }
+
+ public void setProvisioningEntries(Collection provisioningEntries) {
+ if(this.provisioningEntries == null) this.provisioningEntries = new HashSet<>();
+ this.provisioningEntries.addAll(provisioningEntries) ;
+ }
+
+ public Collection getLoanProductProvisioningEntries() {
+ return this.provisioningEntries ;
+ }
+
+ public void setJournalEntryCreated(Boolean bool) {
+ this.isJournalEntryCreated = bool ;
+ }
+
+ public Date getCreatedDate() {
+ return this.createdDate ;
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/ProvisioningEntryRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/ProvisioningEntryRepository.java
new file mode 100644
index 000000000..b34d00f82
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/domain/ProvisioningEntryRepository.java
@@ -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, JpaSpecificationExecutor {
+
+ @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() ;
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningEntryAlreadyCreatedException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningEntryAlreadyCreatedException.java
new file mode 100644
index 000000000..081685c2c
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningEntryAlreadyCreatedException.java
@@ -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);
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningEntryNotfoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningEntryNotfoundException.java
new file mode 100644
index 000000000..792da3ae0
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningEntryNotfoundException.java
@@ -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);
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningJournalEntriesCannotbeCreatedException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningJournalEntriesCannotbeCreatedException.java
new file mode 100644
index 000000000..ca598574e
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/exception/ProvisioningJournalEntriesCannotbeCreatedException.java
@@ -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);
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/CreateProvisioningEntriesRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/CreateProvisioningEntriesRequestCommandHandler.java
new file mode 100644
index 000000000..f38e383c6
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/CreateProvisioningEntriesRequestCommandHandler.java
@@ -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) ;
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/CreateProvisioningJournalEntriesRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/CreateProvisioningJournalEntriesRequestCommandHandler.java
new file mode 100644
index 000000000..be277cde6
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/CreateProvisioningJournalEntriesRequestCommandHandler.java
@@ -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) ;
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/ReCreateProvisioningEntryRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/ReCreateProvisioningEntryRequestCommandHandler.java
new file mode 100644
index 000000000..89719dae3
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/handler/ReCreateProvisioningEntryRequestCommandHandler.java
@@ -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) ;
+ }
+
+}
+
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/serialization/ProvisioningEntriesDefinitionJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/serialization/ProvisioningEntriesDefinitionJsonDeserializer.java
new file mode 100644
index 000000000..f18dd7624
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/serialization/ProvisioningEntriesDefinitionJsonDeserializer.java
@@ -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