From 3a67d013056c79694a81c6184e9580e85403bbdf Mon Sep 17 00:00:00 2001 From: ShruthiRajaram Date: Thu, 7 Feb 2019 17:54:32 +0530 Subject: [PATCH] FINERACT-646 Pockets API --- api-docs/apiLive.htm | 123 ++++++++++++++- .../service/CommandWrapperBuilder.java | 15 ++ .../service/LoanReadPlatformService.java | 3 + .../service/LoanReadPlatformServiceImpl.java | 8 + .../SavingsAccountReadPlatformService.java | 2 + ...SavingsAccountReadPlatformServiceImpl.java | 10 ++ .../self/pockets/api/PocketApiConstants.java | 49 ++++++ .../self/pockets/api/PocketApiResource.java | 99 +++++++++++++ .../data/PocketAccountMappingData.java | 45 ++++++ .../pockets/data/PocketDataValidator.java | 140 ++++++++++++++++++ .../portfolio/self/pockets/domain/Pocket.java | 59 ++++++++ .../pockets/domain/PocketAccountMapping.java | 86 +++++++++++ .../PocketAccountMappingRepository.java | 38 +++++ ...PocketAccountMappingRepositoryWrapper.java | 66 +++++++++ .../self/pockets/domain/PocketRepository.java | 32 ++++ .../domain/PocketRepositoryWrapper.java | 51 +++++++ .../MappingIdNotLinkedToPocketException.java | 31 ++++ .../exception/PocketNotFoundException.java | 32 ++++ ...elinkAccountsFromPocketCommandHandler.java | 48 ++++++ .../LinkAccountsToPocketCommandHandler.java | 50 +++++++ .../pockets/service/AccountEntityService.java | 29 ++++ .../service/AccountEntityServiceFactory.java | 48 ++++++ .../AccountEntityServiceForLoanImpl.java | 67 +++++++++ .../AccountEntityServiceForSavingsImpl.java | 70 +++++++++ ...ountEntityServiceForShareAccountsImpl.java | 69 +++++++++ ...cketAccountMappingReadPlatformService.java | 29 ++++ ...AccountMappingReadPlatformServiceImpl.java | 91 ++++++++++++ .../service/PocketWritePlatformService.java | 32 ++++ .../PocketWritePlatformServiceImpl.java | 138 +++++++++++++++++ .../ShareAccountReadPlatformService.java | 3 +- .../ShareAccountReadPlatformServiceImpl.java | 12 ++ .../core_db/V351__pocket_mapping.sql | 40 +++++ 32 files changed, 1613 insertions(+), 2 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/api/PocketApiConstants.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/api/PocketApiResource.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/data/PocketAccountMappingData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/data/PocketDataValidator.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/Pocket.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMapping.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMappingRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMappingRepositoryWrapper.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketRepositoryWrapper.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/exception/MappingIdNotLinkedToPocketException.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/exception/PocketNotFoundException.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/handler/DelinkAccountsFromPocketCommandHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/handler/LinkAccountsToPocketCommandHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceFactory.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForLoanImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForSavingsImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForShareAccountsImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketAccountMappingReadPlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketAccountMappingReadPlatformServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketWritePlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketWritePlatformServiceImpl.java create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V351__pocket_mapping.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index a02cae75c..1a98be002 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -3864,7 +3864,7 @@ self/loans/{loanId}/guarantors Guarantors list - + @@ -3875,6 +3875,7 @@ + Run Report self/runreports/{reportName} @@ -3904,6 +3905,30 @@ self/surveys/scorecards/clients/{clientId} Survey Scorecards + + + + + Link/Delink accounts to pocket + self/pockets?command=linkAccounts + Link accounts to pocket + + + + + + + self/pockets?command=delinkAccounts + Delink accounts from pocket + + + + + + + self/pockets + + Retrieve accounts linked to pocket @@ -50691,6 +50716,102 @@ GET https://DomainName/api/v1/self/runreports/{reportName} + +   +
+
+

Link/delink accounts to/from pocket

+

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.

+

Link accounts to pocket

+

Example Requests:

+
self/pockets?command=linkAccounts
+
+
+ POST https://DomainName/api/v1/self/pockets?command=linkAccounts + POST self/pockets?command=linkAccounts +Content-Type: application/json +Request Body: + +{ + "accountsDetail":[ + { + "accountId":"11", + "accountType":"LOAN" + }, + { + "accountId":"2", + "accountType":"SAVINGS" + } + ] +} + + +{ + "resourceId": 6 +} + +
+
+ +   +
+
+

Delink accounts from pocket

+

Example Requests:

+
self/pockets?command=delinkAccounts
+
+
+ POST https://DomainName/api/v1/self/pockets?command=delinkAccounts + POST self/pockets?command=delinkAccounts +Content-Type: application/json +Request Body: + +{ "pocketAccountMappingIds" :[8,9] +} + + +{ + "resourceId":10 +} + +
+
+ +   +
+
+

Retrieve accounts linked to pocket

+

All linked loan

+

Example Requests:

+
self/pockets
+
+
+ POST https://DomainName/api/v1/self/pockets + +{ + "loanAccounts": [ + { + "pocketId": 6, + "accountId": 11, + "accountType": 2, + "accountNumber": "000000011", + "id": 10 + } + ], + "savingsAccounts": [ + { + "pocketId": 6, + "accountId": 2, + "accountType": 3, + "accountNumber": "000000002", + "id": 11 + } + ], + "shareAccounts": [] +} + +
+
diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index ae53bb78c..eb3fe6dce 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -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; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java index e205fbf19..2fd6c62ea 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java @@ -134,4 +134,7 @@ public interface LoanReadPlatformService { LoanAccountData retrieveLoanByLoanAccount(String loanAccountNumber); Long retrieveLoanIdByAccountNumber(String loanAccountNumber); + + String retrieveAccountNumberByAccountId(Long accountId); + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index e2464d83b..20230d6a4 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -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); + } + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java index adac1919b..2593297b5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java @@ -60,4 +60,6 @@ public interface SavingsAccountReadPlatformService { List retrieveSavingsIdsPendingEscheat(LocalDate tenantLocalDate); boolean isAccountBelongsToClient(final Long clientId, final Long accountId, final DepositAccountType depositAccountType, final String currencyCode) ; + String retrieveAccountNumberByAccountId(Long accountId); + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 6bb4fd1ce..b0ba652c7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -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); + } + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/api/PocketApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/api/PocketApiConstants.java new file mode 100644 index 000000000..11df8cc21 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/api/PocketApiConstants.java @@ -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."; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/api/PocketApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/api/PocketApiResource.java new file mode 100644 index 000000000..1d39cc2b4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/api/PocketApiResource.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/data/PocketAccountMappingData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/data/PocketAccountMappingData.java new file mode 100644 index 000000000..34b984f63 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/data/PocketAccountMappingData.java @@ -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 loanAccounts; + private final Collection savingsAccounts; + private final Collection shareAccounts; + + private PocketAccountMappingData(final Collection loanAccounts, Collection savingsAccounts, final Collection shareAccounts ) { + this.loanAccounts = loanAccounts; + this.savingsAccounts = savingsAccounts; + this.shareAccounts = shareAccounts; + } + + public static PocketAccountMappingData instance(final Collection loanAccounts, final Collection savingsAccounts, final Collection shareAccounts) { + + return new PocketAccountMappingData(loanAccounts, savingsAccounts, shareAccounts); + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/data/PocketDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/data/PocketDataValidator.java new file mode 100644 index 000000000..29d762420 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/data/PocketDataValidator.java @@ -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 linkingAccountsSupportedParameters = new HashSet<>( + Arrays.asList(PocketApiConstants.accountIdParamName, PocketApiConstants.accountTypeParamName, + PocketApiConstants.accountsDetail)); + + private final Set 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>() { + }.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.linkingAccountsSupportedParameters); + + final List 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 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>() { + }.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + this.delinkingAccountsSupportedParameters); + + final List 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 dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException(PocketApiConstants.dataValidationMessage, + PocketApiConstants.validationErrorMessage, dataValidationErrors); + } + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/Pocket.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/Pocket.java new file mode 100644 index 000000000..fe52535d9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/Pocket.java @@ -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 { + + @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); + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMapping.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMapping.java new file mode 100644 index 000000000..4ee65b9eb --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMapping.java @@ -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 { + + @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; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMappingRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMappingRepository.java new file mode 100644 index 000000000..bf58742c0 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMappingRepository.java @@ -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, JpaSpecificationExecutor { + + @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 findByPocketId(@Param("pocketId") Long pocketId); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMappingRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMappingRepositoryWrapper.java new file mode 100644 index 000000000..fe32e7ad5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketAccountMappingRepositoryWrapper.java @@ -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 save(final List pocketAccountsList) { + return this.pocketAccountMappingRepository.save(pocketAccountsList); + } + + public void delete(final List 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 findByPocketId(final Long pocketId) { + return this.pocketAccountMappingRepository.findByPocketId(pocketId); + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketRepository.java new file mode 100644 index 000000000..7987a2e75 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketRepository.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.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 , JpaSpecificationExecutor{ + + @Query("select pocket.id from Pocket pocket where pocket.appUserId= :appUserId") + Long findByAppUserId(@Param("appUserId") Long appUserId); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketRepositoryWrapper.java new file mode 100644 index 000000000..a72b2fde1 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/domain/PocketRepositoryWrapper.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.portfolio.self.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; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/exception/MappingIdNotLinkedToPocketException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/exception/MappingIdNotLinkedToPocketException.java new file mode 100644 index 000000000..4a3316231 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/exception/MappingIdNotLinkedToPocketException.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.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); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/exception/PocketNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/exception/PocketNotFoundException.java new file mode 100644 index 000000000..7ae00b414 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/exception/PocketNotFoundException.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.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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/handler/DelinkAccountsFromPocketCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/handler/DelinkAccountsFromPocketCommandHandler.java new file mode 100644 index 000000000..51d769021 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/handler/DelinkAccountsFromPocketCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/handler/LinkAccountsToPocketCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/handler/LinkAccountsToPocketCommandHandler.java new file mode 100644 index 000000000..98ef91b41 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/handler/LinkAccountsToPocketCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityService.java new file mode 100644 index 000000000..18dd1b4f1 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityService.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.pockets.service; + +public interface AccountEntityService { + + String getKey(); + + void validateSelfUserAccountMapping(Long accountId); + + String retrieveAccountNumberByAccountId(Long accountId); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceFactory.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceFactory.java new file mode 100644 index 000000000..5edb17a98 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceFactory.java @@ -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 accountEntityServiceHashMap = new HashMap<>(); + + @Autowired + public AccountEntityServiceFactory(final Set accountEntityServices) { + for (AccountEntityService service : accountEntityServices) { + this.accountEntityServiceHashMap.put(service.getKey(), service); + } + } + + public AccountEntityService getAccountEntityService(final String key) { + return this.accountEntityServiceHashMap.get(key); + } + + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForLoanImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForLoanImpl.java new file mode 100644 index 000000000..c4408167c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForLoanImpl.java @@ -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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForSavingsImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForSavingsImpl.java new file mode 100644 index 000000000..5afbf3abe --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForSavingsImpl.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForShareAccountsImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForShareAccountsImpl.java new file mode 100644 index 000000000..6f0112e96 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/AccountEntityServiceForShareAccountsImpl.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.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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketAccountMappingReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketAccountMappingReadPlatformService.java new file mode 100644 index 000000000..7b496aae7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketAccountMappingReadPlatformService.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.pockets.service; + +import org.apache.fineract.portfolio.self.pockets.data.PocketAccountMappingData; + +public interface PocketAccountMappingReadPlatformService { + + PocketAccountMappingData retrieveAll(); + + boolean validatePocketAndAccountMapping(Long pocketId, Long accountId, Integer accountType); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketAccountMappingReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketAccountMappingReadPlatformServiceImpl.java new file mode 100644 index 000000000..b293a29b5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketAccountMappingReadPlatformServiceImpl.java @@ -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 pocketAccountMappingList = this.pocketAccountMappingRepositoryWrapper + .findByPocketId(pocketId); + if (pocketAccountMappingList != null && !pocketAccountMappingList.isEmpty()) { + Collection loanAccounts = new ArrayList<>(); + Collection savingsAccounts = new ArrayList<>(); + Collection 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; + } + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketWritePlatformService.java new file mode 100644 index 000000000..9a6649ddd --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketWritePlatformService.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.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); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketWritePlatformServiceImpl.java new file mode 100644 index 000000000..ecb8b397a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/pockets/service/PocketWritePlatformServiceImpl.java @@ -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 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 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(); + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformService.java index 06cdf62f5..a6fa93f6b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformService.java @@ -37,5 +37,6 @@ public interface ShareAccountReadPlatformService extends AccountReadPlatformServ public Set getResponseDataParams(); Collection retrieveAllShareAccountDataForDividends(Long productId, boolean fetchInActiveAccounts, LocalDate startDate); - + + String retrieveAccountNumberByAccountId(Long accountId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java index 8d9cbee0e..1df67559a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java @@ -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); + } + } } diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V351__pocket_mapping.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V351__pocket_mapping.sql new file mode 100644 index 000000000..b97164bfc --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V351__pocket_mapping.sql @@ -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);