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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); + final List 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); } + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformService.java new file mode 100644 index 000000000..71af21efe --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformService.java @@ -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 retrieveLoanProductsProvisioningData(Date date) ; + + public ProvisioningEntryData retrieveProvisioningEntryData(Long entryId) ; + + public Collection retrieveAllProvisioningEntries() ; + + public ProvisioningEntryData retrieveProvisioningEntryData(String date) ; + + public ProvisioningEntryData retrieveProvisioningEntryDataByCriteriaId(Long criteriaId) ; + + public ProvisioningEntryData retrieveExistingProvisioningIdDateWithJournals() ; +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java new file mode 100644 index 000000000..b7d131a84 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java @@ -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 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 { + + 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 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 { + 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 { + + 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 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 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 { + + 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 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() ; + } + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesWritePlatformService.java new file mode 100644 index 000000000..52574b976 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesWritePlatformService.java @@ -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() ; +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..8c185751e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesWritePlatformServiceJpaRepositoryImpl.java @@ -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 nullEntries = null; + ProvisioningEntry requestedEntry = new ProvisioningEntry(currentUser, date, lastModifiedBy, lastModifiedDate, nullEntries); + Collection 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 entries = generateLoanProvisioningEntry(requestedEntry, requestedEntry.getCreatedDate()); + requestedEntry.setProvisioningEntries(entries); + this.provisioningEntryRepository.save(requestedEntry); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(requestedEntry.getId()).build(); + } + + private Collection generateLoanProvisioningEntry(ProvisioningEntry parent, Date date) { + Collection entries = this.provisioningEntriesReadPlatformService + .retrieveLoanProductsProvisioningData(date); + Map 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(); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 2f62e7c2d..cceb6f9de 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -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; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java index 91a90f830..2acd8b7a9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java @@ -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; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java index e07bb85ee..64265978e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java @@ -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) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java index a87339c90..6e5c31d97 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java @@ -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 diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java index af8dc4b41..e58fbd870 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java @@ -224,6 +224,10 @@ public class Money implements Comparable { 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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/api/ProvisioningCategoryApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/api/ProvisioningCategoryApiResource.java new file mode 100644 index 000000000..92be4dabc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/api/ProvisioningCategoryApiResource.java @@ -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 toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + @Autowired + public ProvisioningCategoryApiResource(final PlatformSecurityContext platformSecurityContext, + final ProvisioningCategoryReadPlatformService provisioningCategoryReadPlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final DefaultToApiJsonSerializer 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 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); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/api/ProvisioningCriteriaApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/api/ProvisioningCriteriaApiResource.java new file mode 100644 index 000000000..3ae50a5af --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/api/ProvisioningCriteriaApiResource.java @@ -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 toApiJsonSerializer; + @Autowired + public ProvisioningCriteriaApiResource(final PlatformSecurityContext platformSecurityContext, + final ApiRequestParameterHelper apiRequestParameterHelper, + final ProvisioningCriteriaReadPlatformService provisioningCriteriaReadPlatformService, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final DefaultToApiJsonSerializer 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 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); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/constants/ProvisioningCriteriaConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/constants/ProvisioningCriteriaConstants.java new file mode 100644 index 000000000..a43bf19b1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/constants/ProvisioningCriteriaConstants.java @@ -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 supportedParametersForCreate = new HashSet<>(Arrays.asList(JSON_LOCALE_PARAM, JSON_CRITERIANAME_PARAM, + JSON_LOANPRODUCTS_PARAM, JSON_PROVISIONING_DEFINITIONS_PARAM)); + + Set supportedParametersForUpdate = new HashSet<>(Arrays.asList(JSON_CRITERIAID_PARAM, JSON_LOCALE_PARAM, JSON_CRITERIANAME_PARAM, + JSON_LOANPRODUCTS_PARAM, JSON_PROVISIONING_DEFINITIONS_PARAM)); + + Set loanProductSupportedParams = new HashSet<>(Arrays.asList(JSON_LOAN_PRODUCT_ID_PARAM, + JSON_LOAN_PRODUCTNAME_PARAM, JSON_LOAN_PRODUCT_BORROWERCYCLE_PARAM)) ; + + Set 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 PROVISIONING_CRITERIA_TEMPLATE_PARAMETER = new HashSet<>(Arrays.asList("provisioninCriteria", "loanProducts", + "glAccounts")); + + Set PROVISIONING_CRITERIA_PARAMETERS = new HashSet<>(Arrays.asList("criteriaName", "loanProducts", + "definitions")); + + Set ALL_PROVISIONING_CRITERIA_PARAMETERS = new HashSet<>(Arrays.asList("criteriaId", "criterianame", + "createdby")); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCategoryData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCategoryData.java new file mode 100644 index 000000000..11b75b2bb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCategoryData.java @@ -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, 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); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCriteriaData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCriteriaData.java new file mode 100644 index 000000000..f3dec1cd8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCriteriaData.java @@ -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, Serializable { + + private final Long criteriaId; + private final String criteriaName; + private final String createdBy; + private final Collection loanProducts; + private Collection selectedLoanProducts ; + private final Collection definitions; + private final Collection glAccounts; + + private ProvisioningCriteriaData(final Long criteriaId, final String criteriaName, final Collection loanProducts, + Collection definitions, Collection 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 loanProducts, + Collection 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 loanProducts, + final List definitions) { + Collection 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 glAccounts = null; + Collection loanProducts = null; + List definitions = null; + return new ProvisioningCriteriaData(criteriaId, criteriaName, loanProducts, definitions, glAccounts, createdBy); + } + + public static ProvisioningCriteriaData toTemplate(final Collection definitions, + final Collection loanProducts, final Collection 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 definitions, + final Collection loanProducts, final Collection glAccounts) { + return new ProvisioningCriteriaData(data, loanProducts, glAccounts); + } + + @Override + public int compareTo(ProvisioningCriteriaData obj) { + if (obj == null ) { return -1; } + return obj.criteriaId.compareTo(this.criteriaId); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCriteriaDefinitionData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCriteriaDefinitionData.java new file mode 100644 index 000000000..fe2f39dbe --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/data/ProvisioningCriteriaDefinitionData.java @@ -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, 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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/LoanProductProvisionCriteria.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/LoanProductProvisionCriteria.java new file mode 100644 index 000000000..c4b62830d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/LoanProductProvisionCriteria.java @@ -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 { + + @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 ; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCategory.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCategory.java new file mode 100644 index 000000000..0338aaa1b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCategory.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.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 { + + @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 update(JsonCommand command) { + final Map 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(); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCategoryRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCategoryRepository.java new file mode 100644 index 000000000..e3805b0ee --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCategoryRepository.java @@ -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, JpaSpecificationExecutor{ + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteria.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteria.java new file mode 100644 index 000000000..d97f8f3df --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteria.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.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 { + + @Column(name = "criteria_name", nullable = false) + private String criteriaName; + + @LazyCollection(LazyCollectionOption.FALSE) + @OneToMany(cascade = CascadeType.ALL, mappedBy = "criteria", orphanRemoval = true) + Set provisioningCriteriaDefinition = new HashSet<>(); + + @LazyCollection(LazyCollectionOption.FALSE) + @OneToMany(cascade = CascadeType.ALL, mappedBy = "criteria", orphanRemoval = true) + Set 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) { + this.provisioningCriteriaDefinition.clear(); + this.provisioningCriteriaDefinition.addAll(provisioningCriteriaDefinition); + } + + public void setLoanProductProvisioningCriteria(Set loanProductMapping) { + this.loanProductMapping.clear(); + this.loanProductMapping.addAll(loanProductMapping); + } + + public Map update(JsonCommand command, List loanProducts) { + final Map 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 temp = new HashSet<>() ; + Set 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 ; + } + } + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaDefinition.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaDefinition.java new file mode 100644 index 000000000..25d0485b7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaDefinition.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.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 { + + @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 ; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaDefinitionRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaDefinitionRepository.java new file mode 100644 index 000000000..731c0030d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaDefinitionRepository.java @@ -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, JpaSpecificationExecutor { + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaRepository.java new file mode 100644 index 000000000..11b76edcb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/domain/ProvisioningCriteriaRepository.java @@ -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, JpaSpecificationExecutor{ + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCategoryCannotBeDeletedException.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCategoryCannotBeDeletedException.java new file mode 100644 index 000000000..3026019a5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCategoryCannotBeDeletedException.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.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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCategoryNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCategoryNotFoundException.java new file mode 100644 index 000000000..a5a169d29 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCategoryNotFoundException.java @@ -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); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaCannotBeCreatedException.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaCannotBeCreatedException.java new file mode 100644 index 000000000..f9a4d0499 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaCannotBeCreatedException.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.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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaCannotBeDeletedException.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaCannotBeDeletedException.java new file mode 100644 index 000000000..7417a8043 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaCannotBeDeletedException.java @@ -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"); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaNotFoundException.java new file mode 100644 index 000000000..c28fa01a5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/exception/ProvisioningCriteriaNotFoundException.java @@ -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); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/CreateProvisioningCategoryRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/CreateProvisioningCategoryRequestCommandHandler.java new file mode 100644 index 000000000..54d454329 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/CreateProvisioningCategoryRequestCommandHandler.java @@ -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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/CreateProvisioningCriteriaRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/CreateProvisioningCriteriaRequestCommandHandler.java new file mode 100644 index 000000000..f42e61c99 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/CreateProvisioningCriteriaRequestCommandHandler.java @@ -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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/DeleteProvisioningCategoryRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/DeleteProvisioningCategoryRequestCommandHandler.java new file mode 100644 index 000000000..f250f72c4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/DeleteProvisioningCategoryRequestCommandHandler.java @@ -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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/DeleteProvisioningCriteriaRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/DeleteProvisioningCriteriaRequestCommandHandler.java new file mode 100644 index 000000000..5b3c87e7e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/DeleteProvisioningCriteriaRequestCommandHandler.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.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()) ; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/UpdateProvisioningCategoryRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/UpdateProvisioningCategoryRequestCommandHandler.java new file mode 100644 index 000000000..01584ab15 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/UpdateProvisioningCategoryRequestCommandHandler.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.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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/UpdateProvisioningCriteriaRequestCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/UpdateProvisioningCriteriaRequestCommandHandler.java new file mode 100644 index 000000000..d96f2971f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/handler/UpdateProvisioningCriteriaRequestCommandHandler.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.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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/serialization/ProvisioningCategoryDefinitionJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/serialization/ProvisioningCategoryDefinitionJsonDeserializer.java new file mode 100644 index 000000000..c2c408858 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/serialization/ProvisioningCategoryDefinitionJsonDeserializer.java @@ -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 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); + + final List 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); + + final List 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); } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/serialization/ProvisioningCriteriaDefinitionJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/serialization/ProvisioningCriteriaDefinitionJsonDeserializer.java new file mode 100644 index 000000000..7f4fb2f19 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/serialization/ProvisioningCriteriaDefinitionJsonDeserializer.java @@ -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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersForCreate); + + final List 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersForUpdate); + + final List 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); } + + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryReadPlatformService.java new file mode 100644 index 000000000..9f9f606c7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryReadPlatformService.java @@ -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 retrieveAllProvisionCategories() ; +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryReadPlatformServiceImpl.java new file mode 100644 index 000000000..0ce8ceb70 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryReadPlatformServiceImpl.java @@ -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 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 { + @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"; + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryWritePlatformService.java new file mode 100644 index 000000000..fafd4a1a1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryWritePlatformService.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.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) ; +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..2b5a12512 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCategoryWritePlatformServiceJpaRepositoryImpl.java @@ -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 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()); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaAssembler.java new file mode 100644 index 000000000..3e798e98e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaAssembler.java @@ -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 parseLoanProducts(final JsonElement jsonElement) { + List 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 loanProducts = parseLoanProducts(jsonElement) ; + + Set 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 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); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaReadPlatformService.java new file mode 100644 index 000000000..71f3e66b2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaReadPlatformService.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.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 retrieveAllProvisioningCriterias() ; + + public ProvisioningCriteriaData retrievePrivisiongCriteriaTemplate(ProvisioningCriteriaData data) ; +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaReadPlatformServiceImpl.java new file mode 100644 index 000000000..724f95828 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaReadPlatformServiceImpl.java @@ -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 categories = this.provisioningCategoryReadPlatformService + .retrieveAllProvisionCategories(); + final Collection allLoanProducts = this.loanProductReadPlatformService + .retrieveAllLoanProductsForLookup(onlyActive); + final Collection glAccounts = this.glAccountReadPlatformService.retrieveAllEnabledDetailGLAccounts(); + return ProvisioningCriteriaData.toTemplate(constructCriteriaTemplate(categories), allLoanProducts, glAccounts); + } + + @Override + public ProvisioningCriteriaData retrievePrivisiongCriteriaTemplate(ProvisioningCriteriaData data) { + boolean onlyActive = true; + final Collection categories = this.provisioningCategoryReadPlatformService + .retrieveAllProvisionCategories(); + final Collection allLoanProducts = this.loanProductReadPlatformService + .retrieveAllLoanProductsForLookup(onlyActive); + final Collection glAccounts = this.glAccountReadPlatformService.retrieveAllEnabledDetailGLAccounts(); + return ProvisioningCriteriaData.toTemplate(data, constructCriteriaTemplate(categories), allLoanProducts, glAccounts); + } + + private Collection constructCriteriaTemplate(Collection categories) { + List definitions = new ArrayList<>(); + for (ProvisioningCategoryData data : categories) { + definitions.add(ProvisioningCriteriaDefinitionData.template(data.getId(), data.getCategoryName())); + } + return definitions; + } + @Override + public Collection 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 { + + @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 loanProducts = loanProductReaPlatformService + .retrieveAllLoanProductsForLookup("select product_id from m_loanproduct_provisioning_mapping where m_loanproduct_provisioning_mapping.criteria_id=" + + criteriaId); + List definitions = retrieveProvisioningDefinitions(criteriaId); + return ProvisioningCriteriaData.toLookup(criteriaId, criteriaName, loanProducts, definitions); + } + + private List 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 { + + 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 { + + @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"; + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaWritePlatformService.java new file mode 100644 index 000000000..9ea897d10 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaWritePlatformService.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.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) ; +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..de0e7ca3f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/provisioning/service/ProvisioningCriteriaWritePlatformServiceJpaRepositoryImpl.java @@ -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 products = this.provisioningCriteriaAssembler.parseLoanProducts(command.parsedJson()) ; + + final Map 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()); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java index 1cbbdf65a..cc035dd56 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java @@ -14,6 +14,8 @@ public interface LoanProductReadPlatformService { Collection retrieveAllLoanProducts(); + Collection retrieveAllLoanProductsForLookup(String inClass); + Collection retrieveAllLoanProductsForLookup(); Collection retrieveAllLoanProductsForLookup(boolean activeOnly); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index 451f020f9..9042868de 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -98,6 +98,22 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo return this.jdbcTemplate.query(sql, rm, new Object[] {}); } + @Override + public Collection 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 retrieveAllLoanProductsForLookup() { return retrieveAllLoanProductsForLookup(false); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUserRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUserRepository.java index b27bfc9f4..590dcc523 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUserRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUserRepository.java @@ -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, JpaSpecificationExecutor, PlatformUserRepository { + @Query("Select appUser from AppUser appUser where appUser.username = :username") + AppUser findAppUserByName(@Param("username") String username); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUserRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUserRepositoryWrapper.java new file mode 100644 index 000000000..bff5bfa4e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUserRepositoryWrapper.java @@ -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 ; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/exception/UserNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/exception/UserNotFoundException.java index 27b565d7d..2c03e42c6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/exception/UserNotFoundException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/exception/UserNotFoundException.java @@ -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"); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V277__Loan_Product_Provisioning.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V277__Loan_Product_Provisioning.sql new file mode 100644 index 000000000..88257a724 --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V277__Loan_Product_Provisioning.sql @@ -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); +