fineract-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nazeer1100...@apache.org
Subject [1/2] incubator-fineract git commit: Customer Self Service : APIs for Third Party Beneficiary management and Third Party Account Transfers
Date Wed, 04 May 2016 07:51:09 GMT
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);


Mime
View raw message