Customer Self Service : APIs for Third Party Beneficiary management and Third Party Account Transfers

This commit is contained in:
Adi Narayana Raju
2016-05-04 13:01:25 +05:30
parent 9d4d90121a
commit 2bd3b0625a
32 changed files with 2620 additions and 504 deletions
+378 -1
View File
@@ -3312,6 +3312,38 @@
<td></td>
<td></td>
</tr>
<tr>
<td><a href="#selfbentemplate">Beneficiary for Third Party Transfer</a></td>
<td>/self/beneficiaries/tpt/template</td>
<td></td>
<td><a href="#selfbentemplate">Beneficiary TPT Template</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>/self/beneficiaries/tpt</td>
<td><a href="#selfaddtpt">Add TPT Beneficiary</a></td>
<td><a href="#selfgettpt">Get TPT Beneficiary List</a></td>
<td><a href="#selfputtpt">Update TPT Beneficiary</a></td>
<td><a href="#selfdeletetpt">Delete TPT Beneficiary</a></td>
</tr>
<tr>
<td><a href="#selftpttransfertemplate">Third Party Account Transfers</a></td>
<td>self/accounttransfers/template?type="tpt"</td>
<td></td>
<td><a href="#selftpttransfertemplate">Third Party Account Transfer Template</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>self/accounttransfers?type="tpt"</td>
<td><a href="#selftpttransfer">Third Party Account Transfer</a></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
</div>
@@ -42193,7 +42225,35 @@ GET https://DomainName/api/v1/self/savingsaccounts/{accountId}/charges/{savingsA
</code>
<code class="method-response">
{
"accountOptions": [
"fromAccountOptions": [
{
"accountId": 1,
"accountNo": "00000001",
"accountType": {
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
"clientId": 1,
"clientName": "ABC",
"officeId": 1,
"officeName": "HEAD OFFICE"
},
{
"accountId": 5,
"accountNo": "00000005",
"accountType": {
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
},
"clientId": 2,
"clientName": "XYZ",
"officeId": 3,
"officeName": "REGIONAL OFFICE"
}
],
"toAccountOptions": [
{
"accountId": 1,
"accountNo": "00000001",
@@ -42221,6 +42281,7 @@ GET https://DomainName/api/v1/self/savingsaccounts/{accountId}/charges/{savingsA
"officeName": "REGIONAL OFFICE"
}
]
}
</code>
</div>
@@ -42262,6 +42323,322 @@ No Request Body:
</div>
</div>
<a id="selfbentemplate" name="selfbentemplate" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Beneficiary Third Party Transfer Template</h2>
<p>Returns Account Type enumerations. Self User is expected to know office name and account number to be able to add beneficiary.</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt/template</div>
</div>
<div class="method-example">
<code class="method-declaration">GET https://DomainName/api/v1/self/beneficiaries/tpt/template
</code>
<code class="method-response">
{
"accountTypeOptions":
[
{
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
{
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
}
]
}
</code>
</div>
</div>
<a id="selfaddtpt" name="selfaddtpt" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Add TPT Beneficiary</h2>
<p>Api to add third party beneficiary linked to current user.</p>
<p>Parameter Definitions</p>
<p>name : Nick name for beneficiary, should be unique for an self service user</p>
<p>officeName : Office Name of beneficiary(not id)</p>
<p>accountNumber : Account Number of beneficiary(not id)</p>
<p>transferLimit : Each transfer initiated to this account will not exceed this amount</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt</div>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="fineractHeading2">Mandatory Fields</div></td>
</tr>
<tr class=alt>
<td>name, officeName, accountNumber, accountType</td>
</tr>
</table>
<br/>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="fineractHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>transferLimit
</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">POST https://DomainName/api/v1/self/beneficiaries/tpt
</code>
<code class="method-request">
POST self/beneficiaries/tpt
Content-Type: application/json
Request Body:
{
"locale": "en_GB",
"name": "beneficiary nick name",
"officeName": "HEAD OFFICE",
"accountNumber": "0000001",
"accountType": 1,
"transferLimit": 1000
}
</code>
<code class="method-response">
{
"resourceId": 5
}
</code>
</div>
</div>
<a id="selfgettpt" name="selfgettpt" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Get All TPT Beneficiary</h2>
<p>Api to get all third party beneficiary linked to current user.</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt</div>
</div>
<div class="method-example">
<code class="method-declaration">GET https://DomainName/api/v1/self/beneficiaries/tpt
</code>
<code class="method-request">
GET self/beneficiaries/tpt
Content-Type: application/json
</code>
<code class="method-response">
{
[
{
"id": 1,
"name": "Client2Savings",
"officeName": "Test Office",
"clientName": "FN2 LN2",
"accountType": {
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
"accountNumber": "000000002",
"transferLimit": 0
},
{
"id": 4,
"name": "Client2Loan",
"officeName": "Test Office",
"clientName": "FN2 LN2",
"accountType": {
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
},
"accountNumber": "000000002",
"transferLimit": 1000
}]
}
</code>
</div>
</div>
<a id="selfputtpt" name="selfputtpt" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Update TPT Beneficiary</h2>
<p>Api to update third party beneficiary linked to current user.</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt/{beneficiaryId}</div>
<br/>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="fineractHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>name, transferLimit
</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">PUT https://DomainName/api/v1/self/beneficiaries/tpt/{beneficiaryId}
</code>
<code class="method-request">
PUT self/beneficiaries/tpt/5
Content-Type: application/json
Request Body:
{
"name": "beneficiary nick name",
"transferLimit": 1000
}
</code>
<code class="method-response">
{
"resourceId": 5,
"changes": {
"transferLimit": 1000,
"name": "Client22"
}
}
</code>
</div>
</div>
<a id="selfdeletetpt" name="selfdeletetpt" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Delete TPT Beneficiary</h2>
<p>Api to delete third party beneficiary linked to current user.</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt/{beneficiaryId}</div>
</div>
<div class="method-example">
<code class="method-declaration">DELETE https://DomainName/api/v1/self/beneficiaries/tpt/{beneficiaryId}
</code>
<code class="method-request">
DELETE self/beneficiaries/tpt/5
Content-Type: application/json
</code>
<code class="method-response">
{
"resourceId": 5
}
</code>
</div>
</div>
<a id="selftpttransfertemplate" name="selftpttransfertemplate" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Third Party Account Transfer Template</h2>
<p>Returns list of loan/savings accounts that can be used for third party account transfer</p>
<p>Example Requests:</p>
<div class=apiClick>self/accounttransfers/template?type="tpt"</div>
</div>
<div class="method-example">
<code class="method-declaration">GET https://DomainName/api/v1/self/accounttransfers/template?type="tpt"
</code>
<code class="method-response">
{
"fromAccountOptions": [
{
"accountId": 1,
"accountNo": "00000001",
"accountType": {
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
"clientId": 1,
"clientName": "ABC",
"officeId": 1,
"officeName": "HEAD OFFICE"
},
{
"accountId": 5,
"accountNo": "00000005",
"accountType": {
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
},
"clientId": 2,
"clientName": "XYZ",
"officeId": 3,
"officeName": "REGIONAL OFFICE"
}
],
"toAccountOptions": [
{
"accountId": 2,
"accountNo": "00000002",
"accountType": {
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
"clientId": 2,
"clientName": "ABC",
"officeId": 1,
"officeName": "HEAD OFFICE"
},
{
"accountId": 6,
"accountNo": "00000006",
"accountType": {
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
},
"clientId": 3,
"clientName": "XYZ",
"officeId": 4,
"officeName": "REGIONAL OFFICE"
}
]
}
</code>
</div>
</div>
<a id="selftpttransfer" name="selftpttransfer" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Third Party Account Transfer</h2>
<p>Ability to create new third party transfer of monetary funds from one account to another.</p>
</div>
<div class="method-example">
<code class="method-declaration">POST https://DomainName/api/v1/self/accounttransfers?type="tpt"</code>
<code class="method-request">POST self/accounttransfers?type="tpt"
Content-Type: application/json
No Request Body:
{
"fromOfficeId": 1,
"fromClientId": 1,
"fromAccountType": 2,
"fromAccountId": 1,
"toOfficeId": 1,
"toClientId": 2,
"toAccountType": 2,
"toAccountId": 2,
"dateFormat": "dd MMMM yyyy",
"locale": "en",
"transferDate": "01 August 2011",
"transferAmount": "112.45",
"transferDescription": "A description of the transfer"
}
</code>
<code class="method-response">
{
"savingsId": 1,
"resourceId": 1
}
</code>
</div>
</div>
<!-- end of Customer Self Service APIs-->
</div>
<!-- main-content-wrapper -->
@@ -2702,4 +2702,29 @@ public class CommandWrapperBuilder {
this.href = "/savingsaccounts/" + accountId + "?commad=updateTaxWithHoldTax";
return this;
}
public CommandWrapperBuilder addSelfServiceBeneficiaryTPT() {
this.actionName = "CREATE";
this.entityName = "SSBENEFICIARYTPT";
this.entityId = null;
this.href = "/self/beneficiaries/tpt";
return this;
}
public CommandWrapperBuilder updateSelfServiceBeneficiaryTPT(final Long beneficiaryId) {
this.actionName = "UPDATE";
this.entityName = "SSBENEFICIARYTPT";
this.entityId = beneficiaryId;
this.href = "/self/beneficiaries/tpt/"+beneficiaryId;
return this;
}
public CommandWrapperBuilder deleteSelfServiceBeneficiaryTPT(final Long beneficiaryId) {
this.actionName = "DELETE";
this.entityName = "SSBENEFICIARYTPT";
this.entityId = beneficiaryId;
this.href = "/self/beneficiaries/tpt/"+beneficiaryId;
return this;
}
}
@@ -79,4 +79,8 @@ public interface ConfigurationDomainService {
boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
boolean isDailyTPTLimitEnabled();
Long getDailyTPTLimit();
}
@@ -19,6 +19,7 @@
package org.apache.fineract.infrastructure.configuration.domain;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.cache.domain.CacheType;
import org.apache.fineract.infrastructure.cache.domain.PlatformCache;
@@ -209,6 +210,7 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
return defaultValue;
}
@Override
public boolean isBackdatePenaltiesEnabled() {
final String propertyName = "backdate-penalties-enabled";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
@@ -263,5 +265,19 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
return property.isEnabled();
}
@Override
public boolean isDailyTPTLimitEnabled() {
final String propertyName = "daily-tpt-limit";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
return property.isEnabled();
}
@Override
public Long getDailyTPTLimit() {
final String propertyName = "daily-tpt-limit";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
return property.getValue();
}
}
@@ -74,7 +74,7 @@ public class TenantDatabaseUpgradeService {
flyway.migrate();
} catch (FlywayException e) {
String betterMessage = e.getMessage() + "; for Tenant DB URL: " + connectionProtocol + ", username: "
+ connection.getSchemaPassword();
+ connection.getSchemaUsername();
throw new FlywayException(betterMessage, e.getCause());
}
}
@@ -136,11 +136,11 @@ public class SchedularWritePlatformServiceJpaRepositoryImpl implements Schedular
boolean isStopExecution = false;
final ScheduledJobDetail scheduledJobDetail = this.scheduledJobDetailsRepository.findByJobKeyWithLock(jobKey);
if (scheduledJobDetail.isCurrentlyRunning()
|| (triggerType == SchedulerServiceConstants.TRIGGER_TYPE_CRON && (scheduledJobDetail.getNextRunTime().after(new Date())))) {
|| (triggerType.equals(SchedulerServiceConstants.TRIGGER_TYPE_CRON) && (scheduledJobDetail.getNextRunTime().after(new Date())))) {
isStopExecution = true;
}
final SchedulerDetail schedulerDetail = retriveSchedulerDetail();
if (triggerType == SchedulerServiceConstants.TRIGGER_TYPE_CRON && schedulerDetail.isSuspended()) {
if (triggerType.equals(SchedulerServiceConstants.TRIGGER_TYPE_CRON) && schedulerDetail.isSuspended()) {
scheduledJobDetail.updateTriggerMisfired(true);
isStopExecution = true;
} else if (!isStopExecution) {
@@ -18,12 +18,14 @@
*/
package org.apache.fineract.portfolio.account.service;
import java.math.BigDecimal;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.data.AccountTransferData;
import org.joda.time.LocalDate;
public interface AccountTransfersReadPlatformService {
@@ -42,4 +44,7 @@ public interface AccountTransfersReadPlatformService {
AccountTransferData retrieveRefundByTransferTemplate(Long fromOfficeId, Long fromClientId, Long fromAccountId, Integer fromAccountType,
Long toOfficeId, Long toClientId, Long toAccountId, Integer toAccountType);
BigDecimal getTotalTransactionAmount(Long accountId, Integer accountType,
LocalDate transactionDate);
}
@@ -69,6 +69,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
public static final String DOES_PRODUCT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then true else false end from Loan loan where loan.loanProduct.id = :productId and loan.loanStatus in (100,200,300,303,304,700)";
public static final String FIND_BY_ACCOUNT_NUMBER = "from Loan loan where loan.accountNumber = :accountNumber and loan.loanStatus in (100,200,300,303,304)";
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType);
@@ -144,5 +146,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query(DOES_PRODUCT_HAVE_NON_CLOSED_LOANS)
boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId);
@Query(FIND_BY_ACCOUNT_NUMBER)
Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber);
}
@@ -45,4 +45,7 @@ public interface SavingsAccountRepository extends JpaRepository<SavingsAccount,
@Query("from SavingsAccount sa where sa.id = :accountId and sa.depositType = :depositAccountTypeId")
SavingsAccount findByIdAndDepositAccountType(@Param("accountId") Long accountId,
@Param("depositAccountTypeId") Integer depositAccountTypeId);
@Query("from SavingsAccount sa where sa.accountNumber = :accountNumber and sa.status in (100, 200, 300, 303, 304) ")
SavingsAccount findNonClosedAccountByAccountNumber(@Param("accountNumber") String accountNumber);
}
@@ -18,27 +18,37 @@
*/
package org.apache.fineract.portfolio.self.account.api;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
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.configuration.domain.ConfigurationDomainService;
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.account.api.AccountTransfersApiResource;
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTemplateData;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTransferData;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTransferDataValidator;
import org.apache.fineract.portfolio.self.account.exception.BeneficiaryTransferLimitExceededException;
import org.apache.fineract.portfolio.self.account.exception.DailyTPTTransactionAmountLimitExceededException;
import org.apache.fineract.portfolio.self.account.service.SelfAccountTransferReadService;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTReadPlatformService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -54,6 +64,9 @@ public class SelfAccountTransferApiResource {
private final SelfAccountTransferReadService selfAccountTransferReadService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final SelfAccountTransferDataValidator dataValidator;
private final SelfBeneficiariesTPTReadPlatformService tptBeneficiaryReadPlatformService;
private final ConfigurationDomainService configurationDomainService;
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
@Autowired
public SelfAccountTransferApiResource(
@@ -62,37 +75,101 @@ public class SelfAccountTransferApiResource {
final AccountTransfersApiResource accountTransfersApiResource,
final SelfAccountTransferReadService selfAccountTransferReadService,
final ApiRequestParameterHelper apiRequestParameterHelper,
final SelfAccountTransferDataValidator dataValidator) {
final SelfAccountTransferDataValidator dataValidator,
final SelfBeneficiariesTPTReadPlatformService tptBeneficiaryReadPlatformService,
final ConfigurationDomainService configurationDomainService,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
this.context = context;
this.toApiJsonSerializer = toApiJsonSerializer;
this.accountTransfersApiResource = accountTransfersApiResource;
this.selfAccountTransferReadService = selfAccountTransferReadService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.dataValidator = dataValidator;
this.tptBeneficiaryReadPlatformService = tptBeneficiaryReadPlatformService;
this.configurationDomainService = configurationDomainService;
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String template(@Context final UriInfo uriInfo) {
public String template(
@DefaultValue("") @QueryParam("type") final String type,
@Context final UriInfo uriInfo) {
AppUser user = this.context.authenticatedUser();
Collection<SelfAccountTemplateData> templateData = this.selfAccountTransferReadService
.retrieveSelfAccountTemplateData(user);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings,
new SelfAccountTransferData(templateData));
Collection<SelfAccountTemplateData> selfTemplateData = this.selfAccountTransferReadService
.retrieveSelfAccountTemplateData(user);
if (type.equals("tpt")) {
Collection<SelfAccountTemplateData> tptTemplateData = this.tptBeneficiaryReadPlatformService
.retrieveTPTSelfAccountTemplateData(user);
return this.toApiJsonSerializer.serialize(settings,
new SelfAccountTransferData(selfTemplateData,
tptTemplateData));
}
return this.toApiJsonSerializer
.serialize(settings, new SelfAccountTransferData(
selfTemplateData, selfTemplateData));
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String create(final String apiRequestBodyAsJson) {
this.dataValidator.validateCreate(apiRequestBodyAsJson);
public String create(
@DefaultValue("") @QueryParam("type") final String type,
final String apiRequestBodyAsJson) {
Map<String, Object> params = this.dataValidator.validateCreate(type,
apiRequestBodyAsJson);
if (type.equals("tpt")) {
checkForLimits(params);
}
return this.accountTransfersApiResource.create(apiRequestBodyAsJson);
}
private void checkForLimits(Map<String, Object> params) {
SelfAccountTemplateData fromAccount = (SelfAccountTemplateData) params
.get("fromAccount");
SelfAccountTemplateData toAccount = (SelfAccountTemplateData) params
.get("toAccount");
LocalDate transactionDate = (LocalDate) params.get("transactionDate");
BigDecimal transactionAmount = (BigDecimal) params
.get("transactionAmount");
AppUser user = this.context.authenticatedUser();
Long transferLimit = this.tptBeneficiaryReadPlatformService
.getTransferLimit(user.getId(), toAccount.getAccountId(),
toAccount.getAccountType());
if (transferLimit != null && transferLimit > 0) {
if (transactionAmount.compareTo(new BigDecimal(transferLimit)) > 0) {
throw new BeneficiaryTransferLimitExceededException();
}
}
if (this.configurationDomainService.isDailyTPTLimitEnabled()) {
Long dailyTPTLimit = this.configurationDomainService
.getDailyTPTLimit();
if (dailyTPTLimit != null && dailyTPTLimit > 0) {
BigDecimal dailyTPTLimitBD = new BigDecimal(dailyTPTLimit);
BigDecimal totTransactionAmount = this.accountTransfersReadPlatformService
.getTotalTransactionAmount(fromAccount.getAccountId(),
fromAccount.getAccountType(), transactionDate);
if (totTransactionAmount != null
&& totTransactionAmount.compareTo(BigDecimal.ZERO) > 0) {
if (dailyTPTLimitBD.compareTo(totTransactionAmount) <= 0
|| dailyTPTLimitBD.compareTo(totTransactionAmount
.add(transactionAmount)) < 0) {
throw new DailyTPTTransactionAmountLimitExceededException(
fromAccount.getAccountId(),
fromAccount.getAccountType());
}
}
}
}
}
}
@@ -0,0 +1,53 @@
/**
* 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.account.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public interface SelfBeneficiariesTPTApiConstants {
public static final String BENEFICIARY_ENTITY_NAME = "SSBENEFICIARYTPT";
public static final String RESOURCE_NAME = "beneficiary";
public static final String LOCALE = "locale";
public static final String NAME_PARAM_NAME = "name";
public static final String OFFICE_NAME_PARAM_NAME = "officeName";
public static final String ACCOUNT_TYPE_PARAM_NAME = "accountType";
public static final String ACCOUNT_NUMBER_PARAM_NAME = "accountNumber";
public static final String TRANSFER_LIMIT_PARAM_NAME = "transferLimit";
public static final String ID_PARAM_NAME = "id";
public static final String CLIENT_NAME_PARAM_NAME = "clientName";
public static final String ACCOUNT_TYPE_OPTIONS_PARAM_NAME = "accountTypeOptions";
public static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(LOCALE, NAME_PARAM_NAME, OFFICE_NAME_PARAM_NAME,
ACCOUNT_NUMBER_PARAM_NAME, ACCOUNT_TYPE_PARAM_NAME,
TRANSFER_LIMIT_PARAM_NAME));
public static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(NAME_PARAM_NAME, TRANSFER_LIMIT_PARAM_NAME));
public static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(NAME_PARAM_NAME, OFFICE_NAME_PARAM_NAME,
ACCOUNT_NUMBER_PARAM_NAME, ACCOUNT_TYPE_PARAM_NAME,
TRANSFER_LIMIT_PARAM_NAME, ID_PARAM_NAME,
CLIENT_NAME_PARAM_NAME, ACCOUNT_TYPE_OPTIONS_PARAM_NAME));
}
@@ -0,0 +1,161 @@
/**
* 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.account.api;
import java.util.Arrays;
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.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
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.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.service.AccountTransferEnumerations;
import org.apache.fineract.portfolio.self.account.data.SelfBeneficiariesTPTData;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/self/beneficiaries/tpt")
@Component
@Scope("singleton")
public class SelfBeneficiariesTPTApiResource {
private final PlatformSecurityContext context;
private final DefaultToApiJsonSerializer<SelfBeneficiariesTPTData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final SelfBeneficiariesTPTReadPlatformService readPlatformService;
@Autowired
public SelfBeneficiariesTPTApiResource(
final PlatformSecurityContext context,
final DefaultToApiJsonSerializer<SelfBeneficiariesTPTData> toApiJsonSerializer,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final ApiRequestParameterHelper apiRequestParameterHelper,
final SelfBeneficiariesTPTReadPlatformService readPlatformService) {
this.context = context;
this.toApiJsonSerializer = toApiJsonSerializer;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.readPlatformService = readPlatformService;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String template(@Context final UriInfo uriInfo) {
final EnumOptionData loanAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.LOAN);
final EnumOptionData savingsAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.SAVINGS);
final Collection<EnumOptionData> accountTypeOptions = Arrays.asList(
savingsAccountType, loanAccountType);
SelfBeneficiariesTPTData templateData = new SelfBeneficiariesTPTData(
accountTypeOptions);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, templateData,
SelfBeneficiariesTPTApiConstants.RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String add(final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder()
.addSelfServiceBeneficiaryTPT().withJson(apiRequestBodyAsJson)
.build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService
.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@PUT
@Path("{beneficiaryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String update(@PathParam("beneficiaryId") final Long beneficiaryId,
final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder()
.updateSelfServiceBeneficiaryTPT(beneficiaryId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService
.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@DELETE
@Path("{beneficiaryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String delete(@PathParam("beneficiaryId") final Long beneficiaryId,
final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder()
.deleteSelfServiceBeneficiaryTPT(beneficiaryId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService
.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAll(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(
SelfBeneficiariesTPTApiConstants.BENEFICIARY_ENTITY_NAME);
final Collection<SelfBeneficiariesTPTData> beneficiaries = this.readPlatformService
.retrieveAll();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, beneficiaries,
SelfBeneficiariesTPTApiConstants.RESPONSE_DATA_PARAMETERS);
}
}
@@ -103,4 +103,13 @@ public class SelfAccountTemplateData implements
.toHashCode();
}
public Long getAccountId() {
return this.accountId;
}
public Integer getAccountType() {
return this.accountType.getId().intValue();
}
}
@@ -23,11 +23,14 @@ import java.util.Collection;
@SuppressWarnings("unused")
public class SelfAccountTransferData {
private final Collection<SelfAccountTemplateData> accountOptions;
private final Collection<SelfAccountTemplateData> fromAccountOptions;
private final Collection<SelfAccountTemplateData> toAccountOptions;
public SelfAccountTransferData(
final Collection<SelfAccountTemplateData> accountOptions) {
this.accountOptions = accountOptions;
final Collection<SelfAccountTemplateData> fromAccountOptions,
Collection<SelfAccountTemplateData> toAccountOptions) {
this.fromAccountOptions = fromAccountOptions;
this.toAccountOptions = toAccountOptions;
}
}
@@ -27,10 +27,16 @@ import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAcc
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName;
import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME;
import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferAmountParamName;
import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferDateParamName;
import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferDescriptionParamName;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
@@ -40,7 +46,9 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.self.account.service.SelfAccountTransferReadService;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTReadPlatformService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -51,19 +59,22 @@ public class SelfAccountTransferDataValidator {
private final PlatformSecurityContext context;
private final SelfAccountTransferReadService selfAccountTransferReadService;
private final SelfBeneficiariesTPTReadPlatformService tptBeneficiaryReadPlatformService;
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public SelfAccountTransferDataValidator(
final PlatformSecurityContext context,
final SelfAccountTransferReadService selfAccountTransferReadService,
final SelfBeneficiariesTPTReadPlatformService tptBeneficiaryReadPlatformService,
final FromJsonHelper fromApiJsonHelper) {
this.context = context;
this.selfAccountTransferReadService = selfAccountTransferReadService;
this.tptBeneficiaryReadPlatformService = tptBeneficiaryReadPlatformService;
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateCreate(String apiRequestBodyAsJson) {
public Map<String,Object> validateCreate(String type, String apiRequestBodyAsJson) {
if (StringUtils.isBlank(apiRequestBodyAsJson)) {
throw new InvalidJsonException();
}
@@ -126,6 +137,16 @@ public class SelfAccountTransferDataValidator {
"Cannot transfer from Loan account to another Loan account.");
}
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(transferDateParamName, element);
baseDataValidator.reset().parameter(transferDateParamName).value(transactionDate).notNull();
final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(transferAmountParamName, element);
baseDataValidator.reset().parameter(transferAmountParamName).value(transactionAmount).notNull().positiveAmount();
final String transactionDescription = this.fromApiJsonHelper.extractStringNamed(transferDescriptionParamName, element);
baseDataValidator.reset().parameter(transferDescriptionParamName).value(transactionDescription).notBlank()
.notExceedingLengthOf(200);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
SelfAccountTemplateData fromAccount = new SelfAccountTemplateData(
@@ -133,21 +154,35 @@ public class SelfAccountTransferDataValidator {
SelfAccountTemplateData toAccount = new SelfAccountTemplateData(
toAccountId, toAccountType, toClientId, toOfficeId);
validateSelfUserAccounts(fromAccount, toAccount, baseDataValidator);
validateUserAccounts(fromAccount, toAccount, baseDataValidator, type);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
Map<String, Object> ret = new HashMap<>();
ret.put("fromAccount", fromAccount);
ret.put("toAccount", toAccount);
ret.put("transactionDate", transactionDate);
ret.put("transactionAmount", transactionAmount);
return ret;
}
private void validateSelfUserAccounts(
private void validateUserAccounts(
final SelfAccountTemplateData fromAccount,
final SelfAccountTemplateData toAccount,
final DataValidatorBuilder baseDataValidator) {
final DataValidatorBuilder baseDataValidator, final String type) {
AppUser user = this.context.authenticatedUser();
Collection<SelfAccountTemplateData> userValidAccounts = this.selfAccountTransferReadService
Collection<SelfAccountTemplateData> validFromAccounts = this.selfAccountTransferReadService
.retrieveSelfAccountTemplateData(user);
Collection<SelfAccountTemplateData> validToAccounts = validFromAccounts;
if (type.equals("tpt")) {
validToAccounts = this.tptBeneficiaryReadPlatformService
.retrieveTPTSelfAccountTemplateData(user);
}
boolean validFromAccount = false;
for (SelfAccountTemplateData validAccount : userValidAccounts) {
for (SelfAccountTemplateData validAccount : validFromAccounts) {
if (validAccount.equals(fromAccount)) {
validFromAccount = true;
break;
@@ -155,7 +190,7 @@ public class SelfAccountTransferDataValidator {
}
boolean validToAccount = false;
for (SelfAccountTemplateData validAccount : userValidAccounts) {
for (SelfAccountTemplateData validAccount : validToAccounts) {
if (validAccount.equals(toAccount)) {
validToAccount = true;
break;
@@ -0,0 +1,69 @@
/**
* 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.account.data;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public class SelfBeneficiariesTPTData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final String name;
@SuppressWarnings("unused")
private final String officeName;
@SuppressWarnings("unused")
private final String clientName;
@SuppressWarnings("unused")
private final EnumOptionData accountType;
@SuppressWarnings("unused")
private final String accountNumber;
@SuppressWarnings("unused")
private final Long transferLimit;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> accountTypeOptions;
public SelfBeneficiariesTPTData(
final Collection<EnumOptionData> accountTypeOptions) {
this.accountTypeOptions = accountTypeOptions;
this.id = null;
this.name = null;
this.officeName = null;
this.clientName = null;
this.accountType = null;
this.accountNumber = null;
this.transferLimit = null;
}
public SelfBeneficiariesTPTData(final Long id, final String name,
final String officeName, final String clientName,
final EnumOptionData accountType, final String accountNumber,
final Long transferLimit) {
this.accountTypeOptions = null;
this.id = id;
this.name = name;
this.officeName = officeName;
this.clientName = clientName;
this.accountType = accountType;
this.accountNumber = accountNumber;
this.transferLimit = transferLimit;
}
}
@@ -0,0 +1,165 @@
/**
* 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.account.data;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.RESOURCE_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.OFFICE_NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.ACCOUNT_TYPE_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.ACCOUNT_NUMBER_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.TRANSFER_LIMIT_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.CREATE_REQUEST_DATA_PARAMETERS;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.UPDATE_REQUEST_DATA_PARAMETERS;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.account.PortfolioAccountType;
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 SelfBeneficiariesTPTDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public SelfBeneficiariesTPTDataValidator(
final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public HashMap<String, Object> validateForCreate(String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
CREATE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
dataValidationErrors).resource(RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String name = this.fromApiJsonHelper.extractStringNamed(
NAME_PARAM_NAME, element);
baseDataValidator.reset().parameter(NAME_PARAM_NAME).value(name)
.notBlank().notExceedingLengthOf(50);
final String officeName = this.fromApiJsonHelper.extractStringNamed(
OFFICE_NAME_PARAM_NAME, element);
baseDataValidator.reset().parameter(OFFICE_NAME_PARAM_NAME)
.value(officeName).notBlank()
.notExceedingLengthOf(50);
final String accountNo = this.fromApiJsonHelper.extractStringNamed(
ACCOUNT_NUMBER_PARAM_NAME, element);
baseDataValidator.reset().parameter(ACCOUNT_NUMBER_PARAM_NAME)
.value(accountNo).notBlank().notExceedingLengthOf(20);
final Integer accountType = this.fromApiJsonHelper.extractIntegerNamed(
ACCOUNT_TYPE_PARAM_NAME, element, this.fromApiJsonHelper
.extractLocaleParameter(element.getAsJsonObject()));
baseDataValidator
.reset()
.parameter(ACCOUNT_TYPE_PARAM_NAME)
.value(accountType)
.notNull()
.isOneOfTheseValues(PortfolioAccountType.LOAN.getValue(),
PortfolioAccountType.SAVINGS.getValue());
final Long transferLimit = this.fromApiJsonHelper.extractLongNamed(
TRANSFER_LIMIT_PARAM_NAME, element);
baseDataValidator.reset().parameter(TRANSFER_LIMIT_PARAM_NAME)
.value(transferLimit).ignoreIfNull().longGreaterThanZero();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
HashMap<String, Object> ret = new HashMap<>();
ret.put(NAME_PARAM_NAME, name);
ret.put(OFFICE_NAME_PARAM_NAME, officeName);
ret.put(ACCOUNT_NUMBER_PARAM_NAME, accountNo);
ret.put(ACCOUNT_TYPE_PARAM_NAME, accountType);
ret.put(TRANSFER_LIMIT_PARAM_NAME, transferLimit);
return ret;
}
public HashMap<String, Object> validateForUpdate(String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
UPDATE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
dataValidationErrors).resource(RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
HashMap<String, Object> ret = new HashMap<>();
if (this.fromApiJsonHelper.parameterExists(NAME_PARAM_NAME, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(
NAME_PARAM_NAME, element);
baseDataValidator.reset().parameter(NAME_PARAM_NAME).value(name)
.notBlank().notExceedingLengthOf(50);
ret.put(NAME_PARAM_NAME, name);
}
if (this.fromApiJsonHelper.parameterExists(TRANSFER_LIMIT_PARAM_NAME,
element)) {
final Long transferLimit = this.fromApiJsonHelper.extractLongNamed(
TRANSFER_LIMIT_PARAM_NAME, element);
baseDataValidator.reset().parameter(TRANSFER_LIMIT_PARAM_NAME)
.value(transferLimit).ignoreIfNull().longGreaterThanZero();
ret.put(TRANSFER_LIMIT_PARAM_NAME, transferLimit);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
return ret;
}
private void throwExceptionIfValidationWarningsExist(
final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -0,0 +1,136 @@
/**
* 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.account.domain;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.TRANSFER_LIMIT_PARAM_NAME;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_selfservice_beneficiaries_tpt", uniqueConstraints = { @UniqueConstraint(columnNames = {
"name", "app_user_id", "is_active" }, name = "name") })
public class SelfBeneficiariesTPT extends AbstractPersistable<Long> {
@Column(name = "app_user_id", nullable = false)
private Long appUserId;
@Column(name = "name", length = 50, nullable = false)
private String name;
@Column(name = "office_id", nullable = false)
private Long officeId;
@Column(name = "client_id", nullable = false)
private Long clientId;
@Column(name = "account_id", nullable = false)
private Long accountId;
@Column(name = "account_type", nullable = false)
private Integer accountType;
@Column(name = "transfer_limit", nullable = true)
private Long transferLimit;
@Column(name = "is_active", nullable = false)
private boolean isActive = true;
protected SelfBeneficiariesTPT() {
//
}
public SelfBeneficiariesTPT(Long appUserId, String name, Long officeId,
Long clientId, Long accountId, Integer accountType,
Long transferLimit) {
this.appUserId = appUserId;
this.name = name;
this.officeId = officeId;
this.clientId = clientId;
this.accountId = accountId;
this.accountType = accountType;
this.transferLimit = transferLimit;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Long getTransferLimit() {
return this.transferLimit;
}
public void setTransferLimit(Long transferLimit) {
this.transferLimit = transferLimit;
}
public boolean isActive() {
return this.isActive;
}
public void setActive(boolean isActive) {
this.isActive = isActive;
}
public Long getAppUserId() {
return this.appUserId;
}
public Long getOfficeId() {
return this.officeId;
}
public Long getClientId() {
return this.clientId;
}
public Long getAccountId() {
return this.accountId;
}
public Integer getAccountType() {
return this.accountType;
}
public Map<String, Object> update(String newName, Long newTransferLimit) {
Map<String, Object> changes = new HashMap<>();
if (!this.name.equals(newName)) {
this.name = newName;
changes.put(NAME_PARAM_NAME, newName);
}
if ((this.transferLimit !=null && !this.transferLimit.equals(newTransferLimit))
|| (this.transferLimit == null && newTransferLimit != null)) {
this.transferLimit = newTransferLimit;
changes.put(TRANSFER_LIMIT_PARAM_NAME, newTransferLimit);
}
return changes;
}
}
@@ -0,0 +1,28 @@
/**
* 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.account.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface SelfBeneficiariesTPTRepository extends
JpaRepository<SelfBeneficiariesTPT, Long>,
JpaSpecificationExecutor<SelfBeneficiariesTPT> {
}
@@ -0,0 +1,30 @@
/**
* 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.account.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class BeneficiaryTransferLimitExceededException extends AbstractPlatformDomainRuleException {
public BeneficiaryTransferLimitExceededException() {
super("error.msg.beneficiary.transfer.amount.limit.for.beneficiary.exceeded",
"Transfer amount limit for beneficiary exceeded");
}
}
@@ -0,0 +1,31 @@
/**
* 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.account.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class DailyTPTTransactionAmountLimitExceededException extends AbstractPlatformDomainRuleException {
public DailyTPTTransactionAmountLimitExceededException(Long accountId,
Integer accountType) {
super("error.msg.beneficiary.daily.tpt.transfer.limit.for.fromaccountid."+accountId+".fromaccounttype."+accountType+".exceeded",
"Daily third party transfer limit for the source account excceeded");
}
}
@@ -0,0 +1,32 @@
/**
* 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.account.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class InvalidAccountInformationException extends
AbstractPlatformDomainRuleException {
public InvalidAccountInformationException(final String officeName,
final String accountNumber, final String accountType) {
super("error.msg.beneficiary.invalid.account.details.with.officeName."
+ officeName + ".accountNumber." + accountNumber
+ ".accountType." + accountType,
"Invalid Office Name, Account Number, Account Type combination");
}
}
@@ -0,0 +1,29 @@
/**
* 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.account.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class InvalidBeneficiaryException extends
AbstractPlatformDomainRuleException {
public InvalidBeneficiaryException(final Long beneficiaryId) {
super("error.msg.beneficiary.invalid.beneficiary.id." + beneficiaryId,
"Beneficiary ID doesn't belong to the User");
}
}
@@ -0,0 +1,46 @@
/**
* 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.account.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "SSBENEFICIARYTPT", action = "CREATE")
public class AddSelfBeneficiariesTPTCommandHandler implements
NewCommandSourceHandler {
private final SelfBeneficiariesTPTWritePlatformService writePlatformService;
@Autowired
public AddSelfBeneficiariesTPTCommandHandler(
final SelfBeneficiariesTPTWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.add(command);
}
}
@@ -0,0 +1,46 @@
/**
* 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.account.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "SSBENEFICIARYTPT", action = "DELETE")
public class DeleteSelfBeneficiariesTPTCommandHandler implements
NewCommandSourceHandler {
private final SelfBeneficiariesTPTWritePlatformService writePlatformService;
@Autowired
public DeleteSelfBeneficiariesTPTCommandHandler(
final SelfBeneficiariesTPTWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.delete(command);
}
}
@@ -0,0 +1,46 @@
/**
* 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.account.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "SSBENEFICIARYTPT", action = "UPDATE")
public class UpdateSelfBeneficiariesTPTCommandHandler implements
NewCommandSourceHandler {
private final SelfBeneficiariesTPTWritePlatformService writePlatformService;
@Autowired
public UpdateSelfBeneficiariesTPTCommandHandler(
final SelfBeneficiariesTPTWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.update(command);
}
}
@@ -0,0 +1,36 @@
/**
* 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.account.service;
import java.util.Collection;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTemplateData;
import org.apache.fineract.portfolio.self.account.data.SelfBeneficiariesTPTData;
import org.apache.fineract.useradministration.domain.AppUser;
public interface SelfBeneficiariesTPTReadPlatformService {
Collection<SelfBeneficiariesTPTData> retrieveAll();
Collection<SelfAccountTemplateData> retrieveTPTSelfAccountTemplateData(
AppUser user);
Long getTransferLimit(Long id, Long accountId, Integer accountType);
}
@@ -0,0 +1,222 @@
/**
* 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.account.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.service.AccountTransferEnumerations;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTemplateData;
import org.apache.fineract.portfolio.self.account.data.SelfBeneficiariesTPTData;
import org.apache.fineract.useradministration.domain.AppUser;
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 SelfBeneficiariesTPTReadPlatformServiceImpl implements
SelfBeneficiariesTPTReadPlatformService {
private final PlatformSecurityContext context;
private final JdbcTemplate jdbcTemplate;
private final BeneficiaryMapper mapper;
private final AccountTemplateMapper accountTemplateMapper;
@Autowired
public SelfBeneficiariesTPTReadPlatformServiceImpl(
final PlatformSecurityContext context,
final RoutingDataSource dataSource) {
this.context = context;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.mapper = new BeneficiaryMapper();
this.accountTemplateMapper = new AccountTemplateMapper();
}
@Override
public Collection<SelfBeneficiariesTPTData> retrieveAll() {
AppUser user = this.context.authenticatedUser();
return this.jdbcTemplate.query(this.mapper.schema(), this.mapper,
new Object[] { user.getId(), user.getId() });
}
@Override
public Collection<SelfAccountTemplateData> retrieveTPTSelfAccountTemplateData(
AppUser user) {
return this.jdbcTemplate.query(this.accountTemplateMapper.schema(),
this.accountTemplateMapper,
new Object[] { user.getId(), user.getId() });
}
private static final class BeneficiaryMapper implements
RowMapper<SelfBeneficiariesTPTData> {
private final String schemaSql;
public BeneficiaryMapper() {
final StringBuilder sqlBuilder = new StringBuilder(
"(select b.id as id, ");
sqlBuilder.append(" b.name as name, ");
sqlBuilder.append(" o.name as officeName, ");
sqlBuilder.append(" c.display_name as clientName, ");
sqlBuilder.append(" b.account_type as accountType, ");
sqlBuilder.append(" s.account_no as accountNumber, ");
sqlBuilder.append(" b.transfer_limit as transferLimit ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder
.append(" inner join m_office as o on b.office_id = o.id ");
sqlBuilder
.append(" inner join m_client as c on b.client_id = c.id ");
sqlBuilder
.append(" inner join m_savings_account as s on b.account_id = s.id ");
sqlBuilder.append(" where b.is_active = 1 ");
sqlBuilder.append(" and b.account_type = 2 ");
sqlBuilder.append(" and b.app_user_id = ?) ");
sqlBuilder.append(" union all ");
sqlBuilder.append(" (select b.id as id, ");
sqlBuilder.append(" b.name as name, ");
sqlBuilder.append(" o.name as officeName, ");
sqlBuilder.append(" c.display_name as clientName, ");
sqlBuilder.append(" b.account_type as accountType, ");
sqlBuilder.append(" l.account_no as accountNumber, ");
sqlBuilder.append(" b.transfer_limit as transferLimit ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder
.append(" inner join m_office as o on b.office_id = o.id ");
sqlBuilder
.append(" inner join m_client as c on b.client_id = c.id ");
sqlBuilder
.append(" inner join m_loan as l on b.account_id = l.id ");
sqlBuilder.append(" where b.is_active = 1 ");
sqlBuilder.append(" and b.account_type = 1 ");
sqlBuilder.append(" and b.app_user_id = ?) ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public SelfBeneficiariesTPTData mapRow(final ResultSet rs,
@SuppressWarnings("unused") final int rowNum)
throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
final String officeName = rs.getString("officeName");
final String clientName = rs.getString("clientName");
final Integer accountTypeId = rs.getInt("accountType");
final EnumOptionData accountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.fromInt(accountTypeId));
final String accountNumber = rs.getString("accountNumber");
final Long transferLimit = rs.getLong("transferLimit");
return new SelfBeneficiariesTPTData(id, name, officeName,
clientName, accountType, accountNumber, transferLimit);
}
}
private static final class AccountTemplateMapper implements
RowMapper<SelfAccountTemplateData> {
private final String schemaSql;
public AccountTemplateMapper() {
final StringBuilder sqlBuilder = new StringBuilder(
"(select o.name as officeName, ");
sqlBuilder.append(" o.id as officeId, ");
sqlBuilder.append(" c.display_name as clientName, ");
sqlBuilder.append(" c.id as clientId, ");
sqlBuilder.append(" b.account_type as accountType, ");
sqlBuilder.append(" s.account_no as accountNumber, ");
sqlBuilder.append(" s.id as accountId ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder
.append(" inner join m_office as o on b.office_id = o.id ");
sqlBuilder
.append(" inner join m_client as c on b.client_id = c.id ");
sqlBuilder
.append(" inner join m_savings_account as s on b.account_id = s.id ");
sqlBuilder.append(" where b.is_active = 1 ");
sqlBuilder.append(" and b.account_type = 2 ");
sqlBuilder.append(" and b.app_user_id = ?) ");
sqlBuilder.append(" union all ");
sqlBuilder.append(" (select o.name as officeName, ");
sqlBuilder.append(" o.id as officeId, ");
sqlBuilder.append(" c.display_name as clientName, ");
sqlBuilder.append(" c.id as clientId, ");
sqlBuilder.append(" b.account_type as accountType, ");
sqlBuilder.append(" l.account_no as accountNumber, ");
sqlBuilder.append(" l.id as accountId ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder
.append(" inner join m_office as o on b.office_id = o.id ");
sqlBuilder
.append(" inner join m_client as c on b.client_id = c.id ");
sqlBuilder
.append(" inner join m_loan as l on b.account_id = l.id ");
sqlBuilder.append(" where b.is_active = 1 ");
sqlBuilder.append(" and b.account_type = 1 ");
sqlBuilder.append(" and b.app_user_id = ?) ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public SelfAccountTemplateData mapRow(final ResultSet rs,
@SuppressWarnings("unused") final int rowNum)
throws SQLException {
final String officeName = rs.getString("officeName");
final Long officeId = rs.getLong("officeId");
final String clientName = rs.getString("clientName");
final Long clientId = rs.getLong("clientId");
final Integer accountTypeId = rs.getInt("accountType");
final String accountNumber = rs.getString("accountNumber");
final Long accountId = rs.getLong("accountId");
return new SelfAccountTemplateData(accountId, accountNumber,
accountTypeId, clientId, clientName, officeId, officeName);
}
}
@Override
public Long getTransferLimit(Long appUserId, Long accountId, Integer accountType) {
final StringBuilder sqlBuilder = new StringBuilder("select b.transfer_limit ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder.append(" where b.app_user_id = ? ");
sqlBuilder.append(" and b.account_id = ? ");
sqlBuilder.append(" and b.account_type = ? ");
sqlBuilder.append(" and b.is_active = 1; ");
return this.jdbcTemplate.queryForObject(sqlBuilder.toString(),
new Object[]{appUserId, accountId, accountType}, Long.class);
}
}
@@ -0,0 +1,32 @@
/**
* 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.account.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface SelfBeneficiariesTPTWritePlatformService {
CommandProcessingResult add(JsonCommand command);
CommandProcessingResult update(JsonCommand command);
CommandProcessingResult delete(JsonCommand command);
}
@@ -0,0 +1,212 @@
/**
* 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.account.service;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.ACCOUNT_NUMBER_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.ACCOUNT_TYPE_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.OFFICE_NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.TRANSFER_LIMIT_PARAM_NAME;
import java.util.HashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
import org.apache.fineract.portfolio.self.account.data.SelfBeneficiariesTPTDataValidator;
import org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPT;
import org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPTRepository;
import org.apache.fineract.portfolio.self.account.exception.InvalidAccountInformationException;
import org.apache.fineract.portfolio.self.account.exception.InvalidBeneficiaryException;
import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SelfBeneficiariesTPTWritePlatformServiceImpl implements
SelfBeneficiariesTPTWritePlatformService {
private final Logger logger;
private final PlatformSecurityContext context;
private final SelfBeneficiariesTPTRepository repository;
private final SelfBeneficiariesTPTDataValidator validator;
private final LoanRepository loanRepo;
private final SavingsAccountRepository savingRepo;
@Autowired
public SelfBeneficiariesTPTWritePlatformServiceImpl(
final PlatformSecurityContext context,
final SelfBeneficiariesTPTRepository repository,
final SelfBeneficiariesTPTDataValidator validator,
final LoanRepository loanRepo,
final SavingsAccountRepository savingRepo) {
this.context = context;
this.repository = repository;
this.validator = validator;
this.loanRepo = loanRepo;
this.savingRepo = savingRepo;
this.logger = LoggerFactory
.getLogger(SelfBeneficiariesTPTWritePlatformServiceImpl.class);
}
@Transactional
@Override
public CommandProcessingResult add(JsonCommand command) {
HashMap<String, Object> params = this.validator
.validateForCreate(command.json());
String name = (String) params.get(NAME_PARAM_NAME);
Integer accountType = (Integer) params.get(ACCOUNT_TYPE_PARAM_NAME);
String accountNumber = (String) params.get(ACCOUNT_NUMBER_PARAM_NAME);
String officeName = (String) params.get(OFFICE_NAME_PARAM_NAME);
Long transferLimit = (Long) params.get(TRANSFER_LIMIT_PARAM_NAME);
Long accountId = null;
Long clientId = null;
Long officeId = null;
boolean validAccountDetails = true;
if (accountType.equals(PortfolioAccountType.LOAN)) {
Loan loan = this.loanRepo
.findNonClosedLoanByAccountNumber(accountNumber);
if (loan != null && loan.getClientId() != null
&& loan.getOffice().getName().equals(officeName)) {
accountId = loan.getId();
officeId = loan.getOfficeId();
clientId = loan.getClientId();
} else {
validAccountDetails = false;
}
} else {
SavingsAccount savings = this.savingRepo
.findNonClosedAccountByAccountNumber(accountNumber);
if (savings != null
&& savings.getClient() != null
&& savings.getClient().getOffice().getName()
.equals(officeName)) {
accountId = savings.getId();
clientId = savings.getClient().getId();
officeId = savings.getClient().getOffice().getId();
} else {
validAccountDetails = false;
}
}
if (validAccountDetails) {
try {
AppUser user = this.context.authenticatedUser();
SelfBeneficiariesTPT beneficiary = new SelfBeneficiariesTPT(
user.getId(), name, officeId, clientId, accountId,
accountType, transferLimit);
this.repository.save(beneficiary);
return new CommandProcessingResultBuilder().withEntityId(
beneficiary.getId()).build();
} catch (DataAccessException dae) {
handleDataIntegrityIssues(command, dae);
}
}
throw new InvalidAccountInformationException(officeName, accountNumber,
PortfolioAccountType.fromInt(accountType).getCode());
}
@Transactional
@Override
public CommandProcessingResult update(JsonCommand command) {
HashMap<String, Object> params = this.validator
.validateForUpdate(command.json());
AppUser user = this.context.authenticatedUser();
Long beneficiaryId = command.entityId();
SelfBeneficiariesTPT beneficiary = this.repository
.findOne(beneficiaryId);
if (beneficiary != null
&& beneficiary.getAppUserId().equals(user.getId())) {
String name = (String) params.get(NAME_PARAM_NAME);
Long transferLimit = (Long) params.get(TRANSFER_LIMIT_PARAM_NAME);
Map<String, Object> changes = beneficiary.update(name,
transferLimit);
if (!changes.isEmpty()) {
try {
this.repository.save(beneficiary);
return new CommandProcessingResultBuilder() //
.withEntityId(beneficiary.getId()) //
.with(changes).build();
} catch (DataAccessException dae) {
handleDataIntegrityIssues(command, dae);
}
}
}
throw new InvalidBeneficiaryException(beneficiaryId);
}
@Transactional
@Override
public CommandProcessingResult delete(JsonCommand command) {
AppUser user = this.context.authenticatedUser();
Long beneficiaryId = command.entityId();
SelfBeneficiariesTPT beneficiary = this.repository
.findOne(beneficiaryId);
if (beneficiary != null
&& beneficiary.getAppUserId().equals(user.getId())) {
beneficiary.setActive(false);
this.repository.save(beneficiary);
return new CommandProcessingResultBuilder() //
.withEntityId(beneficiary.getId()) //
.build();
}
throw new InvalidBeneficiaryException(beneficiaryId);
}
private void handleDataIntegrityIssues(final JsonCommand command,
final DataAccessException dae) {
final Throwable realCause = dae.getMostSpecificCause();
if (realCause.getMessage().contains("name")) {
final String name = command
.stringValueOfParameterNamed(NAME_PARAM_NAME);
throw new PlatformDataIntegrityException(
"error.msg.beneficiary.duplicate.name",
"Beneficiary with name `" + name + "` already exists",
NAME_PARAM_NAME, name);
}
this.logger.error(dae.getMessage(), dae);
throw new PlatformDataIntegrityException(
"error.msg.beneficiary.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
}
@@ -0,0 +1,24 @@
INSERT INTO `c_configuration` (`name`, `value`, `date_value`, `enabled`, `is_trap_door`, `description`) VALUES ( 'daily-tpt-limit', 0, NULL, 0, 0, 'Daily limit for third party transfers');
CREATE TABLE `m_selfservice_beneficiaries_tpt` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`app_user_id` BIGINT NOT NULL,
`name` VARCHAR(50) NOT NULL,
`office_id` BIGINT NOT NULL,
`client_id` BIGINT NOT NULL,
`account_id` BIGINT NOT NULL,
`account_type` SMALLINT(4) NOT NULL,
`transfer_limit` BIGINT NULL DEFAULT 0,
`is_active` BIT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `name` (`name`, `app_user_id`, `is_active`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
INSERT INTO `m_permission`(`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES
('SSBENEFICIARYTPT', 'READ_SSBENEFICIARYTPT', 'SSBENEFICIARYTPT', 'READ', 0),
('SSBENEFICIARYTPT', 'CREATE_SSBENEFICIARYTPT', 'SSBENEFICIARYTPT', 'CREATE', 0),
('SSBENEFICIARYTPT', 'UPDATE_SSBENEFICIARYTPT', 'SSBENEFICIARYTPT', 'UPDATE', 0),
('SSBENEFICIARYTPT', 'DELETE_SSBENEFICIARYTPT', 'SSBENEFICIARYTPT', 'DELETE', 0);