Repository: incubator-fineract
Updated Branches:
  refs/heads/develop 9d4d90121 -> 2bd3b0625


http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java
index 60b6e07..dad8da9 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java
@@ -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());
+                                       }
+                               }
+                       }
+               }
+       }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfBeneficiariesTPTApiConstants.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfBeneficiariesTPTApiConstants.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfBeneficiariesTPTApiConstants.java
new file mode 100644
index 0000000..affe03b
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfBeneficiariesTPTApiConstants.java
@@ -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));
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfBeneficiariesTPTApiResource.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfBeneficiariesTPTApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfBeneficiariesTPTApiResource.java
new file mode 100644
index 0000000..344d991
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfBeneficiariesTPTApiResource.java
@@ -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);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTemplateData.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTemplateData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTemplateData.java
index 03e872b..17b2ebe 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTemplateData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTemplateData.java
@@ -103,4 +103,13 @@ public class SelfAccountTemplateData implements
                                .toHashCode();
        }
 
+       public Long getAccountId() {
+               return this.accountId;
+       }
+
+       public Integer getAccountType() {
+               return this.accountType.getId().intValue();
+       }
+
+       
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferData.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferData.java
index b3d6669..5ca23c2 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferData.java
@@ -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;
        }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferDataValidator.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferDataValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferDataValidator.java
index c78852f..f986a67 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferDataValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfAccountTransferDataValidator.java
@@ -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;

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfBeneficiariesTPTData.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfBeneficiariesTPTData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfBeneficiariesTPTData.java
new file mode 100644
index 0000000..ff56606
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfBeneficiariesTPTData.java
@@ -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;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfBeneficiariesTPTDataValidator.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfBeneficiariesTPTDataValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfBeneficiariesTPTDataValidator.java
new file mode 100644
index 0000000..2ba2899
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/data/SelfBeneficiariesTPTDataValidator.java
@@ -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);
+               }
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/domain/SelfBeneficiariesTPT.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/domain/SelfBeneficiariesTPT.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/domain/SelfBeneficiariesTPT.java
new file mode 100644
index 0000000..76d0432
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/domain/SelfBeneficiariesTPT.java
@@ -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;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/domain/SelfBeneficiariesTPTRepository.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/domain/SelfBeneficiariesTPTRepository.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/domain/SelfBeneficiariesTPTRepository.java
new file mode 100644
index 0000000..a74ebea
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/domain/SelfBeneficiariesTPTRepository.java
@@ -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> {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/BeneficiaryTransferLimitExceededException.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/BeneficiaryTransferLimitExceededException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/BeneficiaryTransferLimitExceededException.java
new file mode 100644
index 0000000..3623d33
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/BeneficiaryTransferLimitExceededException.java
@@ -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");
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/DailyTPTTransactionAmountLimitExceededException.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/DailyTPTTransactionAmountLimitExceededException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/DailyTPTTransactionAmountLimitExceededException.java
new file mode 100644
index 0000000..fb365f8
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/DailyTPTTransactionAmountLimitExceededException.java
@@ -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");
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/InvalidAccountInformationException.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/InvalidAccountInformationException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/InvalidAccountInformationException.java
new file mode 100644
index 0000000..7362077
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/InvalidAccountInformationException.java
@@ -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");
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/InvalidBeneficiaryException.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/InvalidBeneficiaryException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/InvalidBeneficiaryException.java
new file mode 100644
index 0000000..daac892
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/exception/InvalidBeneficiaryException.java
@@ -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");
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/AddSelfBeneficiariesTPTCommandHandler.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/AddSelfBeneficiariesTPTCommandHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/AddSelfBeneficiariesTPTCommandHandler.java
new file mode 100644
index 0000000..cd88fa1
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/AddSelfBeneficiariesTPTCommandHandler.java
@@ -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);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/DeleteSelfBeneficiariesTPTCommandHandler.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/DeleteSelfBeneficiariesTPTCommandHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/DeleteSelfBeneficiariesTPTCommandHandler.java
new file mode 100644
index 0000000..46f3780
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/DeleteSelfBeneficiariesTPTCommandHandler.java
@@ -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);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/UpdateSelfBeneficiariesTPTCommandHandler.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/UpdateSelfBeneficiariesTPTCommandHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/UpdateSelfBeneficiariesTPTCommandHandler.java
new file mode 100644
index 0000000..e92123e
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/handler/UpdateSelfBeneficiariesTPTCommandHandler.java
@@ -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);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTReadPlatformService.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTReadPlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTReadPlatformService.java
new file mode 100644
index 0000000..d60b47f
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTReadPlatformService.java
@@ -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);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTReadPlatformServiceImpl.java
new file mode 100644
index 0000000..c209a4d
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTReadPlatformServiceImpl.java
@@ -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);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTWritePlatformService.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTWritePlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTWritePlatformService.java
new file mode 100644
index 0000000..5d4519b
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTWritePlatformService.java
@@ -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);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTWritePlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTWritePlatformServiceImpl.java
new file mode 100644
index 0000000..9a4e8fd
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/service/SelfBeneficiariesTPTWritePlatformServiceImpl.java
@@ -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.");
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2bd3b062/fineract-provider/src/main/resources/sql/migrations/core_db/V304__customer_self_service_third_party_transfers.sql
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/resources/sql/migrations/core_db/V304__customer_self_service_third_party_transfers.sql
 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V304__customer_self_service_third_party_transfers.sql
new file mode 100644
index 0000000..fc6be77
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V304__customer_self_service_third_party_transfers.sql
@@ -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);

Reply via email to