FINERACT-646 Pockets API

This commit is contained in:
ShruthiRajaram
2019-02-25 11:00:22 +05:30
parent 8b14bb88a7
commit 3a67d01305
32 changed files with 1613 additions and 2 deletions
+122 -1
View File
@@ -3864,7 +3864,7 @@
<td>self/loans/{loanId}/guarantors</td>
<td></td>
<td><a href="#self_guarantors_list">Guarantors list</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
@@ -3875,6 +3875,7 @@
</td>
</td>
</tr>
<tr>
<td><a href="#self_service_user_reports">Run Report</a></td>
<td>self/runreports/{reportName}</td>
@@ -3904,6 +3905,30 @@
<td>self/surveys/scorecards/clients/{clientId}</td>
<td></td>
<td><a href="#survey_score">Survey Scorecards</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="#linkaccountstopocket">Link/Delink accounts to pocket</a></td>
<td>self/pockets?command=linkAccounts</td>
<td><a href="#linkaccountstopocket">Link accounts to pocket</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>self/pockets?command=delinkAccounts</td>
<td><a href="#delinkaccountstopocket">Delink accounts from pocket</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>self/pockets</td>
<td></td>
<td><a href="#retrievepocketaccounts">Retrieve accounts linked to pocket</a></td>
<td></td>
<td></td>
</tr>
@@ -50691,6 +50716,102 @@ GET https://DomainName/api/v1/self/runreports/{reportName}
</code>
</div>
</div>
<a id="linkaccountstopocket" name="linkaccountstopocket" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Link/delink accounts to/from pocket</h2>
<p>Pockets behave as favourites. An user can link his/her Loan, Savings and Share accounts to pocket for faster access. In a similar way linked accounts can be delinked from the pocket.</p>
<h2>Link accounts to pocket</h2>
<p>Example Requests:</p>
<div class=apiClick>self/pockets?command=linkAccounts</div>
</div>
<div class="method-example">
<code class="method-declaration">POST https://DomainName/api/v1/self/pockets?command=linkAccounts</code>
<code class="method-request">POST self/pockets?command=linkAccounts
Content-Type: application/json
Request Body:
{
"accountsDetail":[
{
"accountId":"11",
"accountType":"LOAN"
},
{
"accountId":"2",
"accountType":"SAVINGS"
}
]
}
</code>
<code class="method-response">
{
"resourceId": 6
}
</code>
</div>
</div>
<a id="delinkaccountstopocket" name="delinkaccountstopocket" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Delink accounts from pocket</h2>
<p>Example Requests:</p>
<div class=apiClick>self/pockets?command=delinkAccounts</div>
</div>
<div class="method-example">
<code class="method-declaration">POST https://DomainName/api/v1/self/pockets?command=delinkAccounts</code>
<code class="method-request">POST self/pockets?command=delinkAccounts
Content-Type: application/json
Request Body:
{ "pocketAccountMappingIds" :[8,9]
}
</code>
<code class="method-response">
{
"resourceId":10
}
</code>
</div>
</div>
<a id="retrievepocketaccounts" name="retrievepocketaccounts" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Retrieve accounts linked to pocket</h2>
<p>All linked loan </p>
<p>Example Requests:</p>
<div class=apiClick>self/pockets</div>
</div>
<div class="method-example">
<code class="method-declaration">POST https://DomainName/api/v1/self/pockets</code>
<code class="method-response">
{
"loanAccounts": [
{
"pocketId": 6,
"accountId": 11,
"accountType": 2,
"accountNumber": "000000011",
"id": 10
}
],
"savingsAccounts": [
{
"pocketId": 6,
"accountId": 2,
"accountType": 3,
"accountNumber": "000000002",
"id": 11
}
],
"shareAccounts": []
}
</code>
</div>
</div>
<!-- end of Customer Self Service APIs-->
</div>
<!-- main-content-wrapper -->
@@ -23,6 +23,7 @@ import org.apache.fineract.infrastructure.accountnumberformat.service.AccountNum
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
import org.apache.fineract.portfolio.paymenttype.api.PaymentTypeApiResourceConstants;
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.self.pockets.api.PocketApiConstants;
import org.apache.fineract.useradministration.api.PasswordPreferencesApiConstants;
public class CommandWrapperBuilder {
@@ -3132,4 +3133,18 @@ public class CommandWrapperBuilder {
this.href = "/twofactor/configure";
return this;
}
public CommandWrapperBuilder linkAccountsToPocket() {
this.actionName = PocketApiConstants.linkAccountsActionName;
this.entityName = PocketApiConstants.pocketEntityName;
this.href = "/self/pocket?command="+PocketApiConstants.linkAccountsToPocketCommandParam;
return this;
}
public CommandWrapperBuilder delinkAccountsFromPocket() {
this.actionName = PocketApiConstants.delinkAccountsActionName;
this.entityName = PocketApiConstants.pocketEntityName;
this.href = "/self/pocket?command="+PocketApiConstants.delinkAccountsFromPocketCommandParam;
return this;
}
}
@@ -134,4 +134,7 @@ public interface LoanReadPlatformService {
LoanAccountData retrieveLoanByLoanAccount(String loanAccountNumber);
Long retrieveLoanIdByAccountNumber(String loanAccountNumber);
String retrieveAccountNumberByAccountId(Long accountId);
}
@@ -2209,4 +2209,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
}
public String retrieveAccountNumberByAccountId(Long accountId) {
try {
final String sql = "select loan.account_no from m_loan loan where loan.id = ?";
return this.jdbcTemplate.queryForObject(sql, new Object[] { accountId }, String.class);
} catch (final EmptyResultDataAccessException e) {
throw new LoanNotFoundException(accountId);
}
}
}
@@ -60,4 +60,6 @@ public interface SavingsAccountReadPlatformService {
List<Long> retrieveSavingsIdsPendingEscheat(LocalDate tenantLocalDate);
boolean isAccountBelongsToClient(final Long clientId, final Long accountId, final DepositAccountType depositAccountType, final String currencyCode) ;
String retrieveAccountNumberByAccountId(Long accountId);
}
@@ -1248,4 +1248,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
* return SavingsAccountAnnualFeeData.instance(id, accountNo,
* annualFeeNextDueDate); } }
*/
@Override
public String retrieveAccountNumberByAccountId(Long accountId) {
try {
final String sql = "select s.account_no from m_savings_account s where s.id = ?";
return this.jdbcTemplate.queryForObject(sql, new Object[] { accountId }, String.class);
} catch (final EmptyResultDataAccessException e) {
throw new SavingsAccountNotFoundException(accountId);
}
}
}
@@ -0,0 +1,49 @@
/**
* 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.pockets.api;
public interface PocketApiConstants {
String pocketsResourceName = "pockets";
String linkAccountsToPocketCommandParam = "linkAccounts";
String delinkAccountsFromPocketCommandParam = "delinkAccounts";
String linkAccountsActionName = "LINK_ACCOUNT_TO";
String pocketEntityName = "POCKET";
String delinkAccountsActionName = "DELINK_ACCOUNT_FROM";
String accountIdParamName = "accountId";
String accountTypeParamName = "accountType";
String accountsDetail = "accountsDetail";
String pocketAccountMappingList = "pocketAccountMappingIds";
String pocketAccountMappingId = "pocketAccountMappingId";
String dataValidationMessage = "validation.msg.validation.errors.exist";
String validationErrorMessage = "Validation errors exist.";
String pocketNotFoundException = "error.msg.pocket.not.found";
String pocketNotFoundErrorMessage = "Pocket not found.";
String mappingIdNotLinkedToPocketException = "mapping.id.not.linked.to.pocket.exception";
String mappingIdNotLinkedToPocketErrorMessage = "Mapping Id is not linked to Pocket.";
String uniqueConstraintName = "m_pocket_account_unique_mapping";
String duplicateMappingException = "error.msg.one.or.more.accounts.are.already.mapped.to.pocket.";
String duplicateMappingExceptionMessage = "One or more accounts are already mapped to pocket.";
String unknownDataIntegrityException = "error.msg.unknown.data.integrity.issue";
String unknownDataIntegrityExceptionMessage = "Unknown data integrity issue with resource.";
}
@@ -0,0 +1,99 @@
/**
* 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.pockets.api;
import javax.ws.rs.Consumes;
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.commons.lang.StringUtils;
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.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.portfolio.self.pockets.service.PocketAccountMappingReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/self/pockets")
@Component
@Scope("singleton")
public class PocketApiResource {
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@SuppressWarnings("rawtypes")
private final DefaultToApiJsonSerializer toApiJsonSerializer;
private final PocketAccountMappingReadPlatformService pocketAccountMappingReadPlatformService;
@SuppressWarnings("rawtypes")
@Autowired
public PocketApiResource(PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final DefaultToApiJsonSerializer toApiJsonSerializer,
final PocketAccountMappingReadPlatformService pocketAccountMappingReadPlatformService) {
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.pocketAccountMappingReadPlatformService = pocketAccountMappingReadPlatformService;
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String handleCommands(@QueryParam("command") final String commandParam, @Context final UriInfo uriInfo,
final String apiRequestBodyAsJson) {
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandProcessingResult result = null;
if (is(commandParam, PocketApiConstants.linkAccountsToPocketCommandParam)) {
final CommandWrapper commandRequest = builder.linkAccountsToPocket().build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, PocketApiConstants.delinkAccountsFromPocketCommandParam)) {
final CommandWrapper commandRequest = builder.delinkAccountsFromPocket().build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
if (result == null) {
throw new UnrecognizedQueryParamException("command", commandParam);
}
return this.toApiJsonSerializer.serialize(result);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAll() {
return this.toApiJsonSerializer.serialize(this.pocketAccountMappingReadPlatformService.retrieveAll());
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
}
@@ -0,0 +1,45 @@
/**
* 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.pockets.data;
import java.util.Collection;
import org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMapping;
@SuppressWarnings("unused")
public class PocketAccountMappingData {
private final Collection<PocketAccountMapping> loanAccounts;
private final Collection<PocketAccountMapping> savingsAccounts;
private final Collection<PocketAccountMapping> shareAccounts;
private PocketAccountMappingData(final Collection<PocketAccountMapping> loanAccounts, Collection<PocketAccountMapping> savingsAccounts, final Collection<PocketAccountMapping> shareAccounts ) {
this.loanAccounts = loanAccounts;
this.savingsAccounts = savingsAccounts;
this.shareAccounts = shareAccounts;
}
public static PocketAccountMappingData instance(final Collection<PocketAccountMapping> loanAccounts, final Collection<PocketAccountMapping> savingsAccounts, final Collection<PocketAccountMapping> shareAccounts) {
return new PocketAccountMappingData(loanAccounts, savingsAccounts, shareAccounts);
}
}
@@ -0,0 +1,140 @@
/**
* 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.pockets.data;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
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.self.pockets.api.PocketApiConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Service
public class PocketDataValidator {
private final Set<String> linkingAccountsSupportedParameters = new HashSet<>(
Arrays.asList(PocketApiConstants.accountIdParamName, PocketApiConstants.accountTypeParamName,
PocketApiConstants.accountsDetail));
private final Set<String> delinkingAccountsSupportedParameters = new HashSet<>(
Arrays.asList(PocketApiConstants.pocketAccountMappingList));
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public PocketDataValidator(FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForLinkingAccounts(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.linkingAccountsSupportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(PocketApiConstants.pocketsResourceName);
final JsonElement element = this.fromApiJsonHelper.parse(json);
JsonArray accountsDetail = this.fromApiJsonHelper.extractJsonArrayNamed(PocketApiConstants.accountsDetail,
element);
baseDataValidator.reset().parameter(PocketApiConstants.accountsDetail).value(accountsDetail).notNull()
.jsonArrayNotEmpty();
final List<String> valueList = Arrays.asList(EntityAccountType.LOAN.name().toLowerCase(),
EntityAccountType.SAVINGS.name().toLowerCase(), EntityAccountType.SHARES.name().toLowerCase());
for (JsonElement accountDetails : accountsDetail) {
final Long accountId = this.fromApiJsonHelper.extractLongNamed(PocketApiConstants.accountIdParamName,
accountDetails);
baseDataValidator.reset().parameter(PocketApiConstants.accountIdParamName).value(accountId).notBlank();
final String accountType = this.fromApiJsonHelper
.extractStringNamed(PocketApiConstants.accountTypeParamName, accountDetails);
baseDataValidator.reset().parameter(PocketApiConstants.accountTypeParamName).value(accountType).notBlank()
.isOneOfTheseStringValues(valueList);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForDeLinkingAccounts(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
this.delinkingAccountsSupportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(PocketApiConstants.pocketsResourceName);
final JsonElement element = this.fromApiJsonHelper.parse(json);
JsonArray pocketAccountMappingList = this.fromApiJsonHelper
.extractJsonArrayNamed(PocketApiConstants.pocketAccountMappingList, element);
baseDataValidator.reset().parameter(PocketApiConstants.pocketAccountMappingList).value(pocketAccountMappingList)
.notNull().jsonArrayNotEmpty();
for (JsonElement pocketAccountMapping : pocketAccountMappingList) {
final Long mappingId = pocketAccountMapping.getAsLong();
baseDataValidator.reset().parameter(PocketApiConstants.pocketAccountMappingId).value(mappingId).notBlank();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(PocketApiConstants.dataValidationMessage,
PocketApiConstants.validationErrorMessage, dataValidationErrors);
}
}
}
@@ -0,0 +1,59 @@
/**
* 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.pockets.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@SuppressWarnings("serial")
@Entity
@Table(name = "m_pocket", uniqueConstraints = {
@UniqueConstraint(columnNames = { "app_user_id" }, name = "unique_app_user") })
public class Pocket extends AbstractPersistableCustom<Long> {
@Column(name = "app_user_id", length = 20, nullable = false)
private Long appUserId;
public Long getAppUserId() {
return appUserId;
}
public void setAppUserId(Long appUserId) {
this.appUserId = appUserId;
}
protected Pocket() {
}
private Pocket(final Long appUserId) {
this.appUserId = appUserId;
}
public static Pocket instance(final Long appUserId) {
return new Pocket(appUserId);
}
}
@@ -0,0 +1,86 @@
/**
* 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.pockets.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@SuppressWarnings("serial")
@Entity
@Table(name = "m_pocket_accounts_mapping")
public class PocketAccountMapping extends AbstractPersistableCustom<Long> {
@Column(name = "pocket_id", length = 20, nullable = false)
private Long pocketId;
@Column(name = "account_id", length = 20, nullable = false)
private Long accountId;
@Column(name = "account_type", nullable = false)
private Integer accountType;
@Column(name = "account_number", nullable = false)
private String accountNumber;
protected PocketAccountMapping() {
}
private PocketAccountMapping(final Long pocketId, final Long accountId, final Integer accountType,
final String accountNumber) {
this.pocketId = pocketId;
this.accountId = accountId;
this.accountType = accountType;
this.accountNumber = accountNumber;
}
public static PocketAccountMapping instance(final Long pocketId, final Long accountId, final Integer accountType,
final String accountNumber) {
return new PocketAccountMapping(pocketId, accountId, accountType, accountNumber);
}
public Long getPocketId() {
return pocketId;
}
public void setPocketId(Long pocketId) {
this.pocketId = pocketId;
}
public Long getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public Integer getAccountType() {
return accountType;
}
public void setAccountType(Integer accountType) {
this.accountType = accountType;
}
}
@@ -0,0 +1,38 @@
/**
* 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.pockets.domain;
import java.util.Collection;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface PocketAccountMappingRepository
extends JpaRepository<PocketAccountMapping, Long>, JpaSpecificationExecutor<PocketAccountMapping> {
@Query("select pam from PocketAccountMapping pam where pam.id = :id and pam.pocketId =:pocketId")
PocketAccountMapping findByIdAndPocketId(@Param("id") Long id, @Param("pocketId") Long pocketId);
@Query("select pam from PocketAccountMapping pam where pam.pocketId =:pocketId")
Collection<PocketAccountMapping> findByPocketId(@Param("pocketId") Long pocketId);
}
@@ -0,0 +1,66 @@
/**
* 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.pockets.domain;
import java.util.Collection;
import java.util.List;
import org.apache.fineract.portfolio.self.pockets.exception.MappingIdNotLinkedToPocketException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PocketAccountMappingRepositoryWrapper {
private final PocketAccountMappingRepository pocketAccountMappingRepository;
@Autowired
public PocketAccountMappingRepositoryWrapper(final PocketAccountMappingRepository pocketAccountMappingRepository) {
this.pocketAccountMappingRepository = pocketAccountMappingRepository;
}
public void save(final PocketAccountMapping pocketAccountMapping) {
this.pocketAccountMappingRepository.save(pocketAccountMapping);
}
public List<PocketAccountMapping> save(final List<PocketAccountMapping> pocketAccountsList) {
return this.pocketAccountMappingRepository.save(pocketAccountsList);
}
public void delete(final List<PocketAccountMapping> pocketAccountsList) {
this.pocketAccountMappingRepository.delete(pocketAccountsList);
}
public PocketAccountMapping findByIdAndPocketIdWithNotFoundException(final Long id, final Long pocketId) {
PocketAccountMapping pocketAccountMapping = this.pocketAccountMappingRepository.findByIdAndPocketId(id,
pocketId);
if (pocketAccountMapping == null) {
throw new MappingIdNotLinkedToPocketException(id);
}
return pocketAccountMapping;
}
public Collection<PocketAccountMapping> findByPocketId(final Long pocketId) {
return this.pocketAccountMappingRepository.findByPocketId(pocketId);
}
}
@@ -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.pockets.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface PocketRepository extends JpaRepository<Pocket, Long> , JpaSpecificationExecutor<Pocket>{
@Query("select pocket.id from Pocket pocket where pocket.appUserId= :appUserId")
Long findByAppUserId(@Param("appUserId") Long appUserId);
}
@@ -0,0 +1,51 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.self.pockets.domain;
import org.apache.fineract.portfolio.self.pockets.exception.PocketNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PocketRepositoryWrapper {
private final PocketRepository pocketRepository;
@Autowired
public PocketRepositoryWrapper(final PocketRepository pocketRepository) {
this.pocketRepository = pocketRepository;
}
public void saveAndFlush(final Pocket pocket) {
this.pocketRepository.saveAndFlush(pocket);
}
public Long findByAppUserId(final Long appUserId) {
return this.pocketRepository.findByAppUserId(appUserId);
}
public Long findByAppUserIdWithNotFoundDetection(final Long appUserId) {
final Long pocketId = this.pocketRepository.findByAppUserId(appUserId);
if (pocketId == null) {
throw new PocketNotFoundException();
}
return pocketId;
}
}
@@ -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.pockets.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
import org.apache.fineract.portfolio.self.pockets.api.PocketApiConstants;
@SuppressWarnings("serial")
public class MappingIdNotLinkedToPocketException extends AbstractPlatformDomainRuleException {
public MappingIdNotLinkedToPocketException(final Long id) {
super(PocketApiConstants.mappingIdNotLinkedToPocketException,
PocketApiConstants.mappingIdNotLinkedToPocketErrorMessage, id);
}
}
@@ -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.pockets.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
import org.apache.fineract.portfolio.self.pockets.api.PocketApiConstants;
@SuppressWarnings("serial")
public class PocketNotFoundException extends AbstractPlatformDomainRuleException {
public PocketNotFoundException() {
super(PocketApiConstants.pocketNotFoundException, PocketApiConstants.pocketNotFoundErrorMessage);
}
}
@@ -0,0 +1,48 @@
/**
* 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.pockets.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.pockets.api.PocketApiConstants;
import org.apache.fineract.portfolio.self.pockets.service.PocketWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = PocketApiConstants.pocketEntityName, action = PocketApiConstants.delinkAccountsActionName)
public class DelinkAccountsFromPocketCommandHandler implements NewCommandSourceHandler {
private final PocketWritePlatformService pocketWritePlatformService;
@Autowired
public DelinkAccountsFromPocketCommandHandler(final PocketWritePlatformService pocketWritePlatformService) {
this.pocketWritePlatformService = pocketWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.pocketWritePlatformService.delinkAccounts(command);
}
}
@@ -0,0 +1,50 @@
/**
* 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.pockets.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.pockets.api.PocketApiConstants;
import org.apache.fineract.portfolio.self.pockets.service.PocketWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = PocketApiConstants.pocketEntityName, action = PocketApiConstants.linkAccountsActionName)
public class LinkAccountsToPocketCommandHandler implements NewCommandSourceHandler {
private final PocketWritePlatformService pocketWritePlatformService;
@Autowired
public LinkAccountsToPocketCommandHandler(final PocketWritePlatformService pocketWritePlatformService) {
this.pocketWritePlatformService = pocketWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.pocketWritePlatformService.linkAccounts(command);
}
}
@@ -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.pockets.service;
public interface AccountEntityService {
String getKey();
void validateSelfUserAccountMapping(Long accountId);
String retrieveAccountNumberByAccountId(Long accountId);
}
@@ -0,0 +1,48 @@
/**
* 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.pockets.service;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
public class AccountEntityServiceFactory {
private Map<String, AccountEntityService> accountEntityServiceHashMap = new HashMap<>();
@Autowired
public AccountEntityServiceFactory(final Set<AccountEntityService> accountEntityServices) {
for (AccountEntityService service : accountEntityServices) {
this.accountEntityServiceHashMap.put(service.getKey(), service);
}
}
public AccountEntityService getAccountEntityService(final String key) {
return this.accountEntityServiceHashMap.get(key);
}
}
@@ -0,0 +1,67 @@
/**
* 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.pockets.service;
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
import org.apache.fineract.portfolio.self.loanaccount.service.AppuserLoansMapperReadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountEntityServiceForLoanImpl implements AccountEntityService {
private final String KEY = EntityAccountType.LOAN.name();
private final PlatformSecurityContext context;
private final AppuserLoansMapperReadService appuserLoansMapperReadService;
private final LoanReadPlatformService loanReadPlatformService;
@Autowired
public AccountEntityServiceForLoanImpl(final PlatformSecurityContext context,
final AppuserLoansMapperReadService appuserLoansMapperReadService,
final LoanReadPlatformService loanReadPlatformService) {
this.context = context;
this.appuserLoansMapperReadService = appuserLoansMapperReadService;
this.loanReadPlatformService = loanReadPlatformService;
}
@Override
public String getKey() {
return KEY;
}
@Override
public void validateSelfUserAccountMapping(Long accountId) {
if (!this.appuserLoansMapperReadService.isLoanMappedToUser(accountId,
this.context.authenticatedUser().getId())) {
throw new LoanNotFoundException(accountId);
}
}
@Override
public String retrieveAccountNumberByAccountId(Long accountId) {
return this.loanReadPlatformService.retrieveAccountNumberByAccountId(accountId);
}
}
@@ -0,0 +1,70 @@
/**
* 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.pockets.service;
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.apache.fineract.portfolio.self.savings.service.AppuserSavingsMapperReadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountEntityServiceForSavingsImpl implements AccountEntityService {
private final String KEY = EntityAccountType.SAVINGS.name();
private final PlatformSecurityContext context;
private final AppuserSavingsMapperReadService appuserSavingsMapperReadService;
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
@Autowired
public AccountEntityServiceForSavingsImpl(final PlatformSecurityContext context,
final AppuserSavingsMapperReadService appuserSavingsMapperReadService,
final SavingsAccountReadPlatformService savingsAccountReadPlatformService) {
this.context = context;
this.appuserSavingsMapperReadService = appuserSavingsMapperReadService;
this.savingsAccountReadPlatformService = savingsAccountReadPlatformService;
}
@Override
public String getKey() {
return KEY;
}
@Override
public void validateSelfUserAccountMapping(Long accountId) {
if (!this.appuserSavingsMapperReadService.isSavingsMappedToUser(accountId,
this.context.getAuthenticatedUserIfPresent().getId())) {
throw new SavingsAccountNotFoundException(accountId);
}
}
@Override
public String retrieveAccountNumberByAccountId(Long accountId) {
return this.savingsAccountReadPlatformService.retrieveAccountNumberByAccountId(accountId);
}
}
@@ -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.pockets.service;
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.accounts.exceptions.ShareAccountNotFoundException;
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
import org.apache.fineract.portfolio.self.shareaccounts.service.AppUserShareAccountsMapperReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountEntityServiceForShareAccountsImpl implements AccountEntityService {
private final String KEY = EntityAccountType.SHARES.name();
private final PlatformSecurityContext context;
private final AppUserShareAccountsMapperReadPlatformService appUserShareAccountsMapperReadPlatformService;
private final ShareAccountReadPlatformService shareAccountReadPlatformService;
@Autowired
public AccountEntityServiceForShareAccountsImpl(final PlatformSecurityContext context,
final AppUserShareAccountsMapperReadPlatformService appUserShareAccountsMapperReadPlatformService,
final ShareAccountReadPlatformService shareAccountReadPlatformService) {
this.context = context;
this.appUserShareAccountsMapperReadPlatformService = appUserShareAccountsMapperReadPlatformService;
this.shareAccountReadPlatformService = shareAccountReadPlatformService;
}
@Override
public String getKey() {
return KEY;
}
@Override
public void validateSelfUserAccountMapping(Long accountId) {
if (!this.appUserShareAccountsMapperReadPlatformService.isShareAccountsMappedToUser(accountId,
this.context.getAuthenticatedUserIfPresent().getId())) {
throw new ShareAccountNotFoundException(accountId);
}
}
@Override
public String retrieveAccountNumberByAccountId(Long accountId) {
return this.shareAccountReadPlatformService.retrieveAccountNumberByAccountId(accountId);
}
}
@@ -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.pockets.service;
import org.apache.fineract.portfolio.self.pockets.data.PocketAccountMappingData;
public interface PocketAccountMappingReadPlatformService {
PocketAccountMappingData retrieveAll();
boolean validatePocketAndAccountMapping(Long pocketId, Long accountId, Integer accountType);
}
@@ -0,0 +1,91 @@
/**
* 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.pockets.service;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.self.pockets.data.PocketAccountMappingData;
import org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMapping;
import org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMappingRepositoryWrapper;
import org.apache.fineract.portfolio.self.pockets.domain.PocketRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class PocketAccountMappingReadPlatformServiceImpl implements PocketAccountMappingReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
private final PocketRepositoryWrapper pocketRepositoryWrapper;
private final PocketAccountMappingRepositoryWrapper pocketAccountMappingRepositoryWrapper;
@Autowired
public PocketAccountMappingReadPlatformServiceImpl(final RoutingDataSource dataSource,
final PlatformSecurityContext context, final PocketRepositoryWrapper pocketRepositoryWrapper,
final PocketAccountMappingRepositoryWrapper pocketAccountMappingRepositoryWrapper) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.context = context;
this.pocketRepositoryWrapper = pocketRepositoryWrapper;
this.pocketAccountMappingRepositoryWrapper = pocketAccountMappingRepositoryWrapper;
}
@Override
public PocketAccountMappingData retrieveAll() {
final Long pocketId = this.pocketRepositoryWrapper.findByAppUserId(this.context.authenticatedUser().getId());
if (pocketId != null) {
Collection<PocketAccountMapping> pocketAccountMappingList = this.pocketAccountMappingRepositoryWrapper
.findByPocketId(pocketId);
if (pocketAccountMappingList != null && !pocketAccountMappingList.isEmpty()) {
Collection<PocketAccountMapping> loanAccounts = new ArrayList<>();
Collection<PocketAccountMapping> savingsAccounts = new ArrayList<>();
Collection<PocketAccountMapping> shareAccounts = new ArrayList<>();
for (PocketAccountMapping pocketMapping : pocketAccountMappingList) {
if (pocketMapping.getAccountType().equals(EntityAccountType.LOAN.getValue())) {
loanAccounts.add(pocketMapping);
} else if (pocketMapping.getAccountType().equals(EntityAccountType.SAVINGS.getValue())) {
savingsAccounts.add(pocketMapping);
} else {
shareAccounts.add(pocketMapping);
}
}
return PocketAccountMappingData.instance(loanAccounts, savingsAccounts, shareAccounts);
}
}
return null;
}
@Override
public boolean validatePocketAndAccountMapping(Long pocketId, Long accountId, Integer accountType) {
final String sql = "select count(id) from m_pocket_accounts_mapping mapping where pocket_id = ? and account_id = ? and account_type = ?";
try {
return this.jdbcTemplate.queryForObject(sql, new Object[] { pocketId, accountId, accountType },
Boolean.class);
} catch (EmptyResultDataAccessException e) {
return false;
}
}
}
@@ -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.pockets.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface PocketWritePlatformService {
CommandProcessingResult linkAccounts(JsonCommand command);
CommandProcessingResult delinkAccounts(JsonCommand command);
}
@@ -0,0 +1,138 @@
/**
* 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.pockets.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
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.self.pockets.api.PocketApiConstants;
import org.apache.fineract.portfolio.self.pockets.data.PocketDataValidator;
import org.apache.fineract.portfolio.self.pockets.domain.Pocket;
import org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMapping;
import org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMappingRepositoryWrapper;
import org.apache.fineract.portfolio.self.pockets.domain.PocketRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@Service
public class PocketWritePlatformServiceImpl implements PocketWritePlatformService {
private final PlatformSecurityContext context;
private final PocketDataValidator pocketDataValidator;
private final AccountEntityServiceFactory accountEntityServiceFactory;
private final PocketRepositoryWrapper pocketRepositoryWrapper;
private final PocketAccountMappingRepositoryWrapper pocketAccountMappingRepositoryWrapper;
private final PocketAccountMappingReadPlatformService pocketAccountMappingReadPlatformService;
@Autowired
public PocketWritePlatformServiceImpl(final PlatformSecurityContext context,
PocketDataValidator pocketDataValidator, final AccountEntityServiceFactory accountEntityServiceFactory,
final PocketRepositoryWrapper pocketRepositoryWrapper,
final PocketAccountMappingRepositoryWrapper pocketAccountMappingRepositoryWrapper,
final PocketAccountMappingReadPlatformService pocketAccountMappingReadPlatformService) {
this.context = context;
this.pocketDataValidator = pocketDataValidator;
this.accountEntityServiceFactory = accountEntityServiceFactory;
this.pocketRepositoryWrapper = pocketRepositoryWrapper;
this.pocketAccountMappingRepositoryWrapper = pocketAccountMappingRepositoryWrapper;
this.pocketAccountMappingReadPlatformService = pocketAccountMappingReadPlatformService;
}
@Transactional
@Override
public CommandProcessingResult linkAccounts(JsonCommand command) {
this.pocketDataValidator.validateForLinkingAccounts(command.json());
JsonArray accountsDetail = command.arrayOfParameterNamed(PocketApiConstants.accountsDetail);
Long pocketId = this.pocketRepositoryWrapper.findByAppUserId(this.context.authenticatedUser().getId());
if (pocketId == null) {
final Pocket pocket = Pocket.instance(this.context.authenticatedUser().getId());
this.pocketRepositoryWrapper.saveAndFlush(pocket);
pocketId = pocket.getId();
}
final List<PocketAccountMapping> pocketAccounts = new ArrayList<>();
for (int i = 0; i < accountsDetail.size(); i++) {
final JsonObject element = accountsDetail.get(i).getAsJsonObject();
final Long accountId = element.get(PocketApiConstants.accountIdParamName).getAsLong();
final String accountType = element.get(PocketApiConstants.accountTypeParamName).getAsString();
final AccountEntityService accountEntityService = this.accountEntityServiceFactory
.getAccountEntityService(accountType);
accountEntityService.validateSelfUserAccountMapping(accountId);
Integer accountTypeValue = EntityAccountType.valueOf(accountType).getValue();
if (this.pocketAccountMappingReadPlatformService.validatePocketAndAccountMapping(pocketId, accountId,
accountTypeValue)) {
throw new PlatformDataIntegrityException(PocketApiConstants.duplicateMappingException,
PocketApiConstants.duplicateMappingExceptionMessage, accountId, accountType);
}
final String accountNumber = accountEntityService.retrieveAccountNumberByAccountId(accountId);
pocketAccounts.add(PocketAccountMapping.instance(pocketId, accountId, accountTypeValue, accountNumber));
}
this.pocketAccountMappingRepositoryWrapper.save(pocketAccounts);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(pocketId).build();
}
@Override
public CommandProcessingResult delinkAccounts(JsonCommand command) {
this.pocketDataValidator.validateForDeLinkingAccounts(command.json());
JsonArray pocketAccountMappingList = command.arrayOfParameterNamed(PocketApiConstants.pocketAccountMappingList);
Long pocketId = this.pocketRepositoryWrapper
.findByAppUserIdWithNotFoundDetection(this.context.authenticatedUser().getId());
final List<PocketAccountMapping> pocketAccounts = new ArrayList<>();
for (JsonElement mapping : pocketAccountMappingList) {
final Long mappingId = mapping.getAsLong();
PocketAccountMapping pocketAccountMapping = this.pocketAccountMappingRepositoryWrapper
.findByIdAndPocketIdWithNotFoundException(mappingId, pocketId);
if (pocketAccountMapping != null) {
pocketAccounts.add(pocketAccountMapping);
}
}
this.pocketAccountMappingRepositoryWrapper.delete(pocketAccounts);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(pocketId).build();
}
}
@@ -37,5 +37,6 @@ public interface ShareAccountReadPlatformService extends AccountReadPlatformServ
public Set<String> getResponseDataParams();
Collection<ShareAccountData> retrieveAllShareAccountDataForDividends(Long productId, boolean fetchInActiveAccounts, LocalDate startDate);
String retrieveAccountNumberByAccountId(Long accountId);
}
@@ -38,6 +38,7 @@ import org.apache.fineract.portfolio.accountdetails.data.ShareAccountSummaryData
import org.apache.fineract.portfolio.accounts.constants.AccountsApiConstants;
import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants;
import org.apache.fineract.portfolio.accounts.data.AccountData;
import org.apache.fineract.portfolio.accounts.exceptions.ShareAccountNotFoundException;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.client.data.ClientData;
@@ -64,6 +65,7 @@ import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@@ -502,4 +504,14 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
return this.schema;
}
}
@Override
public String retrieveAccountNumberByAccountId(Long accountId) {
try {
final String sql = "select s.account_no from m_share_account s where s.id = ?";
return this.jdbcTemplate.queryForObject(sql, new Object[] { accountId }, String.class);
} catch (final EmptyResultDataAccessException e) {
throw new ShareAccountNotFoundException(accountId);
}
}
}
@@ -0,0 +1,40 @@
--
-- 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.
--
CREATE TABLE `m_pocket` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`app_user_id` BIGINT(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `app_user_id` (`app_user_id`),
CONSTRAINT `FK__m_appuser__pocket` FOREIGN KEY (`app_user_id`) REFERENCES `m_appuser` (`id`)
);
CREATE TABLE `m_pocket_accounts_mapping` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`pocket_id` BIGINT(20) NOT NULL,
`account_id` BIGINT(20) NOT NULL,
`account_type` INT(5) NOT NULL,
`account_number` VARCHAR(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `unique_pocket_mapping` (`pocket_id`, `account_id`, `account_type`),
CONSTRAINT `FK__m_pocket` FOREIGN KEY (`pocket_id`) REFERENCES `m_pocket` (`id`)
);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'LINK_ACCOUNT_TO_POCKET', 'POCKET', 'LINK_ACCOUNT_TO', 0),
('portfolio', 'DELINK_ACCOUNT_FROM_POCKET', 'POCKET', 'DELINK_ACCOUNT_FROM', 0);