ShruthiRajaram closed pull request #466: FINERACT:628 API support for self share accounts URL: https://github.com/apache/fineract/pull/466
This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 9a87171cc..c6f966ff7 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -3697,6 +3697,30 @@ <h2 class="flybar-button">Self Service</h2> <td></td> <td></td> </tr> + <tr> + <td><a href="#selfshareaccount_template">Share Accounts</a></td> + <td>self/shareaccounts/template?clientId=?productId=?</td> + <td></td> + <td><a href="#selfshareaccount_template">Share Accounts Template</a></td> + <td></td> + <td></td> + </tr> + + <tr> + <td></td> + <td>self/shareaccounts</td> + <td><a href="#selfshareaccount_create">Submit New Share Application</a></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td></td> + <td>self/shareaccounts/{accountId}</td> + <td></td> + <td><a href="#selfshareaccount_retrieve">Retrieve a Share Application/Account</a></td> + <td></td> + </tr> <tr> <td><a href="#selftransfertemplate">Own Account Transfers</a></td> <td>self/accounttransfers/template</td> @@ -47487,6 +47511,889 @@ <h2>Third Party Account Transfer</h2> </code> </div> </div> + + <a id="selfshareaccount_template" name="selfshareaccount_template" class="old-syle-anchor"> </a> + <div class="method-section"> + <div class="method-description"> + <h2>Retrieve Share Account Template:</h2> + <p>This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of: + <ul> + <li class=normalli>Field Defaults</li> + <li class=normalli>Allowed Value Lists</li> + </ul> + </p> + <h4>Arguments</h4> + <dl class="argument-list"> + <dt>clientId</dt> + <dd>Integer <span>mandatory</span></dd> + <dt>productId</dt> + <dd>Integer <span>optional</span></dd> + <dd>If entered, productId, productName and selectedProduct fields are returned.</dd> + </dl> + <p>Example Requests: </p> + <div class=apiClick>self/shareaccounts/template?clientId=14</div> + <br><br> + <div class=apiClick>self/shareaccounts/template?clientId=14&productId=3</div> + </div> + <div class="method-example"> + <code class="method-declaration">GET https://Domain Name/api/v1/self/shareaccounts/template?clientId={clientId}</code> + <code class="method-response"> +{ + "clientId": 14, + "productOptions": [ + { + "id": 3, + "name": "SP", + "shortName": "SP", + "totalShares": 1000 + }, + { + "id": 4, + "name": "Product1", + "shortName": "test", + "totalShares": 1000 + } + ], + "chargeOptions": [ + { + "id": 2, + "name": "Activation fee", + "active": true, + "penalty": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "amount": 2, + "chargeTimeType": { + "id": 14, + "code": "chargeTimeType.sharespurchase", + "value": "Share Purchase" + }, + "chargeAppliesTo": { + "id": 4, + "code": "chargeAppliesTo.shares", + "value": "Shares" + }, + "chargeCalculationType": { + "id": 1, + "code": "chargeCalculationType.flat", + "value": "Flat" + }, + "chargePaymentMode": { + "id": 0, + "code": "chargepaymentmode.regular", + "value": "Regular" + } + } + ] +} + </code> + </div> + + <div class="method-example"> + <code class="method-declaration">GET https://Domain Name/api/v1/self/shareaccounts/template?clientId={clientId}&productId={productId}</code> + <code class="method-response"> +{ + "clientId": 14, + "productOptions": [ + { + "id": 3, + "name": "SP", + "shortName": "SP", + "description": "SP1", + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "inMultiplesOf": 1, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalShares": 1000, + "totalSharesIssued": 900, + "unitPrice": 1, + "shareCapital": 900, + "minimumShares": 8, + "nominalShares": 500, + "maximumShares": 500, + "marketPrice": [], + "charges": [], + "allowDividendCalculationForInactiveClients": false, + "lockinPeriod": 50, + "lockPeriodTypeEnum": { + "id": 0, + "code": "shares.lockin.sharePeriodFrequencyType.days", + "value": "Days" + }, + "minimumActivePeriod": 10, + "minimumActivePeriodForDividendsTypeEnum": { + "id": 0, + "code": "shares.minimumactive.sharePeriodFrequencyType.days", + "value": "Days" + }, + "accountingRule": { + "id": 2, + "code": "accountingRuleType.cash", + "value": "CASH BASED" + }, + "accountingMappings": { + "shareReferenceId": { + "id": 32, + "name": "Cash In Hand", + "glCode": "20301" + }, + "incomeFromFeeAccountId": { + "id": 40, + "name": "Other Operating Income", + "glCode": "30105" + }, + "shareEquityId": { + "id": 56, + "name": "Share Equity", + "glCode": "00098" + }, + "shareSuspenseId": { + "id": 2, + "name": "Overpayment Liability", + "glCode": "10200" + } + }, + "currencyOptions": [ + { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + } + ], + "chargeOptions": [ + { + "id": 2, + "name": "Activation fee", + "active": true, + "penalty": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "amount": 2, + "chargeTimeType": { + "id": 14, + "code": "chargeTimeType.sharespurchase", + "value": "Share Purchase" + }, + "chargeAppliesTo": { + "id": 4, + "code": "chargeAppliesTo.shares", + "value": "Shares" + }, + "chargeCalculationType": { + "id": 1, + "code": "chargeCalculationType.flat", + "value": "Flat" + }, + "chargePaymentMode": { + "id": 0, + "code": "chargepaymentmode.regular", + "value": "Regular" + } + } + ], + "minimumActivePeriodFrequencyTypeOptions": [ + { + "id": 0, + "code": "shares.lockin.sharePeriodFrequencyType.days", + "value": "Days" + } + ], + "lockinPeriodFrequencyTypeOptions": [ + { + "id": 0, + "code": "shares.lockin.sharePeriodFrequencyType.days", + "value": "Days" + }, + { + "id": 1, + "code": "shares.lockin.sharePeriodFrequencyType.weeks", + "value": "Weeks" + }, + { + "id": 2, + "code": "shares.lockin.sharePeriodFrequencyType.months", + "value": "Months" + }, + { + "id": 3, + "code": "shares.lockin.sharePeriodFrequencyType.years", + "value": "Years" + } + ], + "accountingMappingOptions": { + "liabilityAccountOptions": [ + { + "id": 30, + "name": "Recurring Deposits", + "parentId": 1, + "glCode": "10104", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 2, + "code": "accountType.liability", + "value": "LIABILITY" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Recurring Deposits", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 2, + "name": "Overpayment Liability", + "glCode": "10200", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 2, + "code": "accountType.liability", + "value": "LIABILITY" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "Overpayment Liability", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 55, + "name": "Liability Transfer (Temp)", + "glCode": "220004-Temp", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 2, + "code": "accountType.liability", + "value": "LIABILITY" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "description": "Temporary Liability account to track Account Transfers", + "nameDecorated": "Liability Transfer (Temp)", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + } + ], + "assetAccountOptions": [ + { + "id": 31, + "name": "Furniture and Fixtures", + "parentId": 8, + "glCode": "20101", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 1, + "code": "accountType.asset", + "value": "ASSET" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Furniture and Fixtures", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 32, + "name": "Cash In Hand", + "parentId": 10, + "glCode": "20301", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 1, + "code": "accountType.asset", + "value": "ASSET" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Cash In Hand", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 33, + "name": "Petty Cash", + "parentId": 10, + "glCode": "20302", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 1, + "code": "accountType.asset", + "value": "ASSET" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Petty Cash", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 34, + "name": "Loans to Customers", + "parentId": 12, + "glCode": "20501", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 1, + "code": "accountType.asset", + "value": "ASSET" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Loans to Customers", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 35, + "name": "Current Account Overdrafts", + "parentId": 12, + "glCode": "20502", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 1, + "code": "accountType.asset", + "value": "ASSET" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Current Account Overdrafts", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 17, + "name": "Suspense Account", + "glCode": "9999", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 1, + "code": "accountType.asset", + "value": "ASSET" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "Suspense Account", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + } + ], + "incomeAccountOptions": [ + { + "id": 54, + "name": "Loan Recovery (Temp)", + "glCode": "220002-Temp", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 4, + "code": "accountType.income", + "value": "INCOME" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "description": "Temporary account to track income from Loan recovery", + "nameDecorated": "Loan Recovery (Temp)", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 36, + "name": "Fees and Charges", + "parentId": 18, + "glCode": "30101", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 4, + "code": "accountType.income", + "value": "INCOME" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Fees and Charges", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 37, + "name": "Penalties", + "parentId": 18, + "glCode": "30102", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 4, + "code": "accountType.income", + "value": "INCOME" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Penalties", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 38, + "name": "Interest Received from Borrowers", + "parentId": 18, + "glCode": "30103", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 4, + "code": "accountType.income", + "value": "INCOME" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Interest Received from Borrowers", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 39, + "name": "Insurance Charges", + "parentId": 18, + "glCode": "30104", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 4, + "code": "accountType.income", + "value": "INCOME" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Insurance Charges", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + }, + { + "id": 40, + "name": "Other Operating Income", + "parentId": 18, + "glCode": "30105", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 4, + "code": "accountType.income", + "value": "INCOME" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Other Operating Income", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + } + ], + "equityAccountOptions": [ + { + "id": 56, + "name": "Share Equity", + "parentId": 25, + "glCode": "00098", + "disabled": false, + "manualEntriesAllowed": true, + "type": { + "id": 3, + "code": "accountType.equity", + "value": "EQUITY" + }, + "usage": { + "id": 1, + "code": "accountUsage.detail", + "value": "DETAIL" + }, + "nameDecorated": "....Share Equity", + "tagId": { + "id": 0, + "active": false, + "mandatory": false + } + } + ] + } + } + ], + "chargeOptions": [ + { + "id": 2, + "name": "Activation fee", + "active": true, + "penalty": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "amount": 2, + "chargeTimeType": { + "id": 14, + "code": "chargeTimeType.sharespurchase", + "value": "Share Purchase" + }, + "chargeAppliesTo": { + "id": 4, + "code": "chargeAppliesTo.shares", + "value": "Shares" + }, + "chargeCalculationType": { + "id": 1, + "code": "chargeCalculationType.flat", + "value": "Flat" + }, + "chargePaymentMode": { + "id": 0, + "code": "chargepaymentmode.regular", + "value": "Regular" + } + } + ] +} + </code> + </div> +</div> + +<a id="selfshareaccount_create" name="selfshareaccount_create" class="old-syle-anchor"> </a> + <div class="method-section"> + <div class="method-description"> + <h2>Submit new share application</h2> + <br/> + <table class=matrixHeading> + <tr class="matrixHeadingBG"><td><div class="fineractHeading2">Mandatory Fields</div></td></tr> + <tr class=alt><td>clientId, productId, submittedDate, savingsAccountId, requestedShares, applicationDate</td></tr> + </table> + <br/> + <table class=matrixHeading> + <tr class="matrixHeadingBG"><td><div class="fineractHeading2">Optional Fields</div></td></tr> + <tr class=alt><td>accountNo, externalId</td></tr> + </table> + <br/> + <table class=matrixHeading> + <tr class="matrixHeadingBG"><td><div class="fineractHeading2">Inherited from Product (if not provided)</div></td></tr> + <tr class=alt><td> minimumActivePeriod, minimumActivePeriodFrequencyType, lockinPeriodFrequency, lockinPeriodFrequencyType</td> + </tr> + </table> + </div> + <div class="method-example"> + <p>Minimal request: accountNo auto generated, remaining details inherited from Share product.</p> + <code class="method-declaration">POST https://Domain Name/api/v1/self/shareaccounts</code> + <code class="method-request">POST Share Account +Content-Type: application/json +Request Body: +{ + "productId":3, + "unitPrice":1, + "requestedShares":500, + "submittedDate":"31 July 2018", + "savingsAccountId":2, + "applicationDate":"31 July 2018", + "locale":"en", + "dateFormat":"dd MMMM yyyy", + "charges":[ + { + "chargeId":2, + "amount":2 + } + ], + "clientId":"14" +} + </code> + <code class="method-response"> +{ + "resourceId": 12 +} + </code> + </div> +</div> + +<a id="selfshareaccount_retrieve" name="selfshareaccount_retrieve" class="old-syle-anchor"> </a> + <div class="method-section"> + <div class="method-description"> + <h2>Retrieve a share application/account:</h2> + + <p>Example Requests : </p> + <div class=apiClick>self/shareaccounts/12</div> + </div> + <div class="method-example"> + <code class="method-declaration">GET https://DomainName/api/v1/self/shareaccounts/{accountId}</code> + <code class="method-response"> +{ + "id": 12, + "accountNo": "000000012", + "savingsAccountNumber": "000000002", + "clientId": 14, + "clientName": "Bheem", + "productId": 3, + "productName": "SP", + "status": { + "id": 100, + "code": "shareAccountStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "submittedAndPendingApproval": true, + "approved": false, + "rejected": false, + "active": false, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2018, + 7, + 31 + ], + "submittedByUsername": "self", + "submittedByFirstname": "self", + "submittedByLastname": "self" + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "inMultiplesOf": 1, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "summary": { + "id": 12, + "accountNo": "000000012", + "totalApprovedShares": 0, + "totalPendingForApprovalShares": 500, + "productId": 3, + "productName": "SP", + "status": { + "id": 100, + "code": "shareAccountStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "submittedAndPendingApproval": true, + "approved": false, + "rejected": false, + "active": false, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "inMultiplesOf": 1, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "timeline": { + "submittedOnDate": [ + 2018, + 7, + 31 + ], + "submittedByUsername": "self", + "submittedByFirstname": "self", + "submittedByLastname": "self" + } + }, + "purchasedShares": [ + { + "id": 13, + "accountId": 12, + "purchasedDate": [ + 2018, + 7, + 31 + ], + "numberOfShares": 500, + "purchasedPrice": 1, + "status": { + "id": 100, + "code": "purchasedSharesStatusType.applied", + "value": "Pending Approval" + }, + "type": { + "id": 500, + "code": "purchasedSharesType.purchased", + "value": "Purchase" + }, + "amount": 502, + "chargeAmount": 2, + "amountPaid": 500 + } + ], + "savingsAccountId": 2, + "currentMarketPrice": 1, + "lockPeriodTypeEnum": { + "id": 4, + "code": "sharePeriodFrequencyType.invalid", + "value": "Invalid" + }, + "minimumActivePeriodTypeEnum": { + "id": 4, + "code": "sharePeriodFrequencyType.invalid", + "value": "Invalid" + }, + "allowDividendCalculationForInactiveClients": false, + "charges": [ + { + "id": 9, + "chargeId": 2, + "accountId": 12, + "name": "Activation fee", + "chargeTimeType": { + "id": 14, + "code": "chargeTimeType.sharespurchase", + "value": "Share Purchase" + }, + "chargeCalculationType": { + "id": 1, + "code": "chargeCalculationType.flat", + "value": "Flat" + }, + "percentage": 0, + "amountPercentageAppliedTo": 0, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "amount": 2, + "amountPaid": 0, + "amountWaived": 0, + "amountWrittenOff": 0, + "amountOutstanding": 2, + "amountOrPercentage": 2, + "isActive": true + } + ], + "dividends": [] +} + </code> + </div> +</div> + <!-- end of Customer Self Service APIs--> </div> <!-- main-content-wrapper --> diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/constants/ShareAccountApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/constants/ShareAccountApiConstants.java index 7ab98a1ca..04aeb749c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/constants/ShareAccountApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/constants/ShareAccountApiConstants.java @@ -95,7 +95,9 @@ public String additionalshares_paramname = "additionalshares" ; - public String closeddate_paramname = "closedDate" ; + public String closeddate_paramname = "closedDate"; + + public static final String shareEntityType = "share"; Set<String> supportedParameters = new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname, id_paramname,clientid_paramname, productid_paramname, submitteddate_paramname,approveddate_paramname, externalid_paramname, currency_paramname, digitsafterdecimal_paramname, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/api/SelfShareAccountsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/api/SelfShareAccountsApiResource.java new file mode 100644 index 000000000..11adb1121 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/api/SelfShareAccountsApiResource.java @@ -0,0 +1,167 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.portfolio.self.shareaccounts.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; + +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.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; +import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.portfolio.accounts.api.AccountsApiResource; +import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants; +import org.apache.fineract.portfolio.accounts.data.AccountData; +import org.apache.fineract.portfolio.accounts.exceptions.ShareAccountNotFoundException; +import org.apache.fineract.portfolio.charge.data.ChargeData; +import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService; +import org.apache.fineract.portfolio.client.exception.ClientNotFoundException; +import org.apache.fineract.portfolio.products.data.ProductData; +import org.apache.fineract.portfolio.products.service.ProductReadPlatformService; +import org.apache.fineract.portfolio.self.client.service.AppuserClientMapperReadService; +import org.apache.fineract.portfolio.self.shareaccounts.data.SelfShareAccountsDataValidator; +import org.apache.fineract.portfolio.self.shareaccounts.service.AppUserShareAccountsMapperReadPlatformService; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData; +import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService; +import org.apache.fineract.useradministration.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/self/shareaccounts") +@Component +@Scope("singleton") +public class SelfShareAccountsApiResource { + + private final PlatformSecurityContext context; + private final AccountsApiResource accountsApiResource; + private final ShareAccountReadPlatformService readPlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + + private final DefaultToApiJsonSerializer<AccountData> toApiJsonSerializer; + private final AppuserClientMapperReadService appuserClientMapperReadService; + private final SelfShareAccountsDataValidator selfShareAccountsDataValidator; + private final ProductReadPlatformService productReadPlatformService; + private final ChargeReadPlatformService chargeReadPlatformService; + private final AppUserShareAccountsMapperReadPlatformService appUserShareAccountsMapperReadPlatformService; + + @Autowired + public SelfShareAccountsApiResource(final PlatformSecurityContext context, + final AccountsApiResource accountsApiResource, final ShareAccountReadPlatformService readPlatformService, + final DefaultToApiJsonSerializer<AccountData> toApiJsonSerializer, + final ApiRequestParameterHelper apiRequestParameterHelper, + final AppuserClientMapperReadService appuserClientMapperReadService, + final SelfShareAccountsDataValidator selfShareAccountsDataValidator, + final ProductReadPlatformService productReadPlatformService, + final ChargeReadPlatformService chargeReadPlatformService, + final AppUserShareAccountsMapperReadPlatformService appUserShareAccountsMapperReadPlatformService) { + this.context = context; + this.accountsApiResource = accountsApiResource; + this.readPlatformService = readPlatformService; + this.toApiJsonSerializer = toApiJsonSerializer; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.selfShareAccountsDataValidator = selfShareAccountsDataValidator; + this.appuserClientMapperReadService = appuserClientMapperReadService; + this.productReadPlatformService = productReadPlatformService; + this.chargeReadPlatformService = chargeReadPlatformService; + this.appUserShareAccountsMapperReadPlatformService = appUserShareAccountsMapperReadPlatformService; + + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template(@QueryParam("clientId") final Long clientId, @QueryParam("productId") final Long productId, + @Context final UriInfo uriInfo) { + + validateAppuserClientsMapping(clientId); + + Collection<ProductData> productOptions = new ArrayList<ProductData>(); + if (productId != null) { + final boolean includeTemplate = true; + productOptions.add(productReadPlatformService.retrieveOne(productId, includeTemplate)); + } else { + productOptions = productReadPlatformService.retrieveAllForLookup(); + } + + String clientName = null; + + final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges(); + final ShareAccountData accountData = new ShareAccountData(clientId, clientName, productOptions, chargeOptions); + + return this.toApiJsonSerializer.serialize(accountData); + + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String createAccount(final String apiRequestBodyAsJson) { + HashMap<String, Object> attr = this.selfShareAccountsDataValidator + .validateShareAccountApplication(apiRequestBodyAsJson); + final Long clientId = (Long) attr.get(ShareAccountApiConstants.clientid_paramname); + validateAppuserClientsMapping(clientId); + String accountType = ShareAccountApiConstants.shareEntityType; + return this.accountsApiResource.createAccount(accountType, apiRequestBodyAsJson); + } + + @GET + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveShareAccount(@PathParam("accountId") final Long accountId, @Context final UriInfo uriInfo) { + validateAppuserShareAccountMapping(accountId); + final boolean includeTemplate = false; + AccountData accountData = this.readPlatformService.retrieveOne(accountId, includeTemplate); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper + .process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, accountData, readPlatformService.getResponseDataParams()); + } + + private void validateAppuserShareAccountMapping(final Long accountId) { + AppUser user = this.context.authenticatedUser(); + final boolean isMapped = this.appUserShareAccountsMapperReadPlatformService + .isShareAccountsMappedToUser(accountId, user.getId()); + if (!isMapped) { + throw new ShareAccountNotFoundException(accountId); + } + } + + private void validateAppuserClientsMapping(final Long clientId) { + AppUser user = this.context.authenticatedUser(); + final boolean mappedClientId = this.appuserClientMapperReadService.isClientMappedToUser(clientId, user.getId()); + if (!mappedClientId) { + throw new ClientNotFoundException(clientId); + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/data/SelfShareAccountsDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/data/SelfShareAccountsDataValidator.java new file mode 100644 index 000000000..c762ecb75 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/data/SelfShareAccountsDataValidator.java @@ -0,0 +1,75 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.portfolio.self.shareaccounts.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.fineract.infrastructure.core.data.ApiParameterError; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; + +@Component +public class SelfShareAccountsDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public SelfShareAccountsDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public HashMap<String, Object> validateShareAccountApplication(final String json) { + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ShareAccountApiConstants.shareEntityType); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final String clientId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.clientid_paramname, + element); + baseDataValidator.reset().parameter(ShareAccountApiConstants.clientid_paramname).value(clientId).notNull() + .longGreaterThanZero(); + + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException(dataValidationErrors); + } + + HashMap<String, Object> retAttr = new HashMap<>(); + retAttr.put(ShareAccountApiConstants.clientid_paramname, Long.parseLong(clientId)); + + return retAttr; + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/service/AppUserShareAccountsMapperReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/service/AppUserShareAccountsMapperReadPlatformService.java new file mode 100644 index 000000000..3e7edbdfb --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/service/AppUserShareAccountsMapperReadPlatformService.java @@ -0,0 +1,26 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.portfolio.self.shareaccounts.service; + +public interface AppUserShareAccountsMapperReadPlatformService { + + public Boolean isShareAccountsMappedToUser(Long accountId, Long appUserId); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/service/AppUserShareAccountsMapperReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/service/AppUserShareAccountsMapperReadPlatformServiceImpl.java new file mode 100644 index 000000000..eb464f1a2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/service/AppUserShareAccountsMapperReadPlatformServiceImpl.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.portfolio.self.shareaccounts.service; + +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +@Service +public class AppUserShareAccountsMapperReadPlatformServiceImpl + implements AppUserShareAccountsMapperReadPlatformService { + + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public AppUserShareAccountsMapperReadPlatformServiceImpl( + final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Boolean isShareAccountsMappedToUser(Long accountId, Long appUserId) { + return this.jdbcTemplate + .queryForObject( + "select case when (count(*) > 0) then true else false end " + + " from m_selfservice_user_client_mapping as m " + + " left join m_share_account as shares on shares.client_id = m.client_id " + + " where shares.id = ? and m.appuser_id = ? ", + new Object[] { accountId, appUserId }, Boolean.class); + } + +} ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services