diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/account/PortfolioAccountType.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/account/PortfolioAccountType.java index 3a81a147c..7b32373fe 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/account/PortfolioAccountType.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/account/PortfolioAccountType.java @@ -1,71 +1,79 @@ -/** - * 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.account; - -public enum PortfolioAccountType { - - INVALID(0, "accountType.invalid"), // - LOAN(1, "accountType.loan"), // - SAVINGS(2, "accountType.savings"), // - ; - - private final Integer value; - private final String code; - - PortfolioAccountType(final Integer value, final String code) { - this.value = value; - this.code = code; - } - - public Integer getValue() { - return this.value; - } - - public String getCode() { - return this.code; - } - - public static PortfolioAccountType fromInt(final Integer type) { - - PortfolioAccountType enumType = INVALID; - if (type != null) { - switch (type) { - case 1: - enumType = LOAN; - break; - case 2: - enumType = SAVINGS; - break; - default: - enumType = INVALID; - } - } - return enumType; - } - - // TODO: bad practice and unnecessary code! why not just use the enum values themselves!?! - public boolean isSavingsAccount() { - return this.equals(SAVINGS); - } - - // TODO: bad practice and unnecessary code! why not just use the enum values themselves!?! - public boolean isLoanAccount() { - return this.equals(LOAN); - } -} +/** + * 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.account; + +public enum PortfolioAccountType { + + INVALID(0, "accountType.invalid"), // + LOAN(1, "accountType.loan"), // + SAVINGS(2, "accountType.savings"), // + CURRENT(3, "accountType.current"), // + ; + + private final Integer value; + private final String code; + + PortfolioAccountType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static PortfolioAccountType fromInt(final Integer type) { + + PortfolioAccountType enumType = INVALID; + if (type != null) { + switch (type) { + case 1: + enumType = LOAN; + break; + case 2: + enumType = SAVINGS; + break; + case 3: + enumType = CURRENT; + break; + default: + enumType = INVALID; + } + } + return enumType; + } + + // TODO: bad practice and unnecessary code! why not just use the enum values themselves!?! + public boolean isSavingsAccount() { + return this.equals(SAVINGS); + } + + // TODO: bad practice and unnecessary code! why not just use the enum values themselves!?! + public boolean isLoanAccount() { + return this.equals(LOAN); + } + + public boolean isCurrentAccount() { + return this.equals(CURRENT); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiResource.java index b443b343d..68309f923 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiResource.java @@ -1,220 +1,220 @@ -/** - * 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.account.api; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.parameters.RequestBody; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.ws.rs.BeanParam; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Context; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.UriInfo; -import java.time.LocalDate; -import java.util.Arrays; -import java.util.Map; -import java.util.Set; -import lombok.RequiredArgsConstructor; -import org.apache.fineract.batch.command.CommandHandlerRegistry; -import org.apache.fineract.commands.domain.CommandWrapper; -import org.apache.fineract.commands.service.CommandWrapperBuilder; -import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; -import org.apache.fineract.infrastructure.core.api.ApiParameterHelper; -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.infrastructure.core.service.CommandParameterUtil; -import org.apache.fineract.infrastructure.core.service.Page; -import org.apache.fineract.infrastructure.core.service.SearchParameters; -import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; -import org.apache.fineract.infrastructure.security.service.SqlValidator; -import org.apache.fineract.portfolio.account.data.AccountTransferData; -import org.apache.fineract.portfolio.account.data.StandingInstructionDTO; -import org.apache.fineract.portfolio.account.data.StandingInstructionData; -import org.apache.fineract.portfolio.account.data.request.StandingInstructionCreationRequest; -import org.apache.fineract.portfolio.account.data.request.StandingInstructionSearchParam; -import org.apache.fineract.portfolio.account.data.request.StandingInstructionUpdatesRequest; -import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; -import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; -import org.springframework.stereotype.Component; - -@Path("/v1/standinginstructions") -@Component -@Tag(name = "Standing Instructions", description = "Standing instructions (or standing orders) refer to instructions a bank account holder (\"the payer\") gives to his or her bank to pay a set amount at regular intervals to another's (\"the payee's\") account.\n" - + "\n" + "Note: At present only savings account to savings account and savings account to Loan account transfers are permitted.") -@RequiredArgsConstructor -public class StandingInstructionApiResource { - - private final PlatformSecurityContext context; - private final DefaultToApiJsonSerializer toApiJsonSerializer; - private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; - private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; - private final SqlValidator sqlValidator; - - private static final CommandHandlerRegistry COMMAND_HANDLER_REGISTRY = new CommandHandlerRegistry<>( - Map.of(CommandParameterUtil.UPDATE_COMMAND_VALUE, - (id, json) -> new CommandWrapperBuilder().updateStandingInstruction(id).withJson(json).build(), - CommandParameterUtil.DELETE_COMMAND_VALUE, - (id, json) -> new CommandWrapperBuilder().deleteStandingInstruction(id).withJson(json).build())); - - @GET - @Path("template") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - @Operation(summary = "Retrieve Standing Instruction Template", description = "This is a convenience resource. " - + "It can be useful when building maintenance user interface screens for client applications. " - + "The template data returned consists of any or all of:\n" + "\n" + "Field Defaults\n" + "Allowed Value Lists\n" - + "Example Requests:\n" + "\n" + "standinginstructions/template?fromAccountType=2&fromOfficeId=1\n" + "\n" - + "standinginstructions/template?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=1\n" + "\n" - + "standinginstructions/template?fromClientId=1&fromAccountType=2&fromAccountId=1&transferType=1") - @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsTemplateResponse.class))) - public StandingInstructionData template(@BeanParam StandingInstructionSearchParam instructionParam) { - context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); - - return standingInstructionReadPlatformService.retrieveTemplate(instructionParam.getFromOfficeId(), - instructionParam.getFromClientId(), instructionParam.getFromAccountId(), instructionParam.getFromAccountType(), - instructionParam.getToOfficeId(), instructionParam.getToClientId(), instructionParam.getToAccountId(), - instructionParam.getToAccountType(), instructionParam.getTransferType()); - } - - @POST - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - @Operation(summary = "Create new Standing Instruction", description = "Ability to create new instruction for transfer of monetary funds from one account to another") - @RequestBody(required = true, content = @Content(schema = @Schema(implementation = StandingInstructionCreationRequest.class))) - @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PostStandingInstructionsResponse.class))) - public CommandProcessingResult create(@Parameter(hidden = true) StandingInstructionCreationRequest creationRequest) { - final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction() - .withJson(toApiJsonSerializer.serialize(creationRequest)).build(); - - return commandsSourceWritePlatformService.logCommandSource(commandRequest); - } - - @PUT - @Path("{standingInstructionId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - @Operation(summary = "Update Standing Instruction | Delete Standing Instruction", description = "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n" - + "\n" + "PUT https://DomainName/api/v1/standinginstructions/1?command=update\n" + "\n\n" - + "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n" + "\n" - + "PUT https://DomainName/api/v1/standinginstructions/1?command=delete") - @RequestBody(content = @Content(schema = @Schema(implementation = StandingInstructionUpdatesRequest.class))) - @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PutStandingInstructionsStandingInstructionIdResponse.class))) - public CommandProcessingResult update( - @PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId, - @Parameter(hidden = true) StandingInstructionUpdatesRequest updatesRequest, - @QueryParam("command") @Parameter(description = "command") final String commandParam) { - - final String serializedUpdatesRequest = toApiJsonSerializer.serialize(updatesRequest); - final CommandWrapper commandRequest = COMMAND_HANDLER_REGISTRY.execute(commandParam, standingInstructionId, - serializedUpdatesRequest, new UnrecognizedQueryParamException("command", commandParam)); - - return commandsSourceWritePlatformService.logCommandSource(commandRequest); - } - - @GET - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - @Operation(summary = "List Standing Instructions", description = "Example Requests:\n" + "\n" + "standinginstructions") - @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsResponse.class))) - public Page retrieveAll( - @QueryParam("externalId") @Parameter(description = "externalId") final String externalId, - @QueryParam("offset") @Parameter(description = "offset") final Integer offset, - @QueryParam("limit") @Parameter(description = "limit") final Integer limit, - @QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, - @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder, - @QueryParam("transferType") @Parameter(description = "transferType") final Integer transferType, - @QueryParam("clientName") @Parameter(description = "clientName") final String clientName, - @QueryParam("clientId") @Parameter(description = "clientId") final Long clientId, - @QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccount, - @QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType) { - - context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); - - sqlValidator.validate(orderBy); - sqlValidator.validate(sortOrder); - sqlValidator.validate(externalId); - final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset) - .orderBy(orderBy).sortOrder(sortOrder).build(); - - final LocalDate startDateRange = null; - final LocalDate endDateRange = null; - StandingInstructionDTO standingInstructionDTO = new StandingInstructionDTO(searchParameters, transferType, clientName, clientId, - fromAccount, fromAccountType, startDateRange, endDateRange); - - return standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO); - } - - @GET - @Path("{standingInstructionId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - @Operation(summary = "Retrieve Standing Instruction", description = "Example Requests :\n" + "\n" + "standinginstructions/1") - @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsStandingInstructionIdResponse.class))) - public StandingInstructionData retrieveOne( - @PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId, - @Context final UriInfo uriInfo, @QueryParam("externalId") @Parameter(description = "externalId") final String externalId, - @QueryParam("offset") @Parameter(description = "offset") final Integer offset, - @QueryParam("limit") @Parameter(description = "limit") final Integer limit, - @QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, - @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) { - - context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); - - sqlValidator.validate(orderBy); - sqlValidator.validate(sortOrder); - StandingInstructionData standingInstructionData = standingInstructionReadPlatformService.retrieveOne(standingInstructionId); - final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset) - .orderBy(orderBy).sortOrder(sortOrder).build(); - final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); - if (!associationParameters.isEmpty()) { - if (associationParameters.contains("all")) { - associationParameters.addAll(Arrays.asList("transactions", "template")); - } - if (associationParameters.contains("transactions")) { - Page transfers = accountTransfersReadPlatformService - .retrieveByStandingInstruction(standingInstructionId, searchParameters); - standingInstructionData = StandingInstructionData.withTransferData(standingInstructionData, transfers); - } - if (associationParameters.contains("template")) { - final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate( - standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(), - standingInstructionData.getFromAccount().getId(), standingInstructionData.getFromAccountType().getValue(), - standingInstructionData.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(), - standingInstructionData.getToAccount().getId(), standingInstructionData.getToAccountType().getValue(), - standingInstructionData.getTransferType().getValue()); - standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData); - } - } - - return standingInstructionData; - } -} +/** + * 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.account.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriInfo; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.batch.command.CommandHandlerRegistry; +import org.apache.fineract.commands.domain.CommandWrapper; +import org.apache.fineract.commands.service.CommandWrapperBuilder; +import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; +import org.apache.fineract.infrastructure.core.api.ApiParameterHelper; +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.infrastructure.core.service.CommandParameterUtil; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.infrastructure.security.service.SqlValidator; +import org.apache.fineract.portfolio.account.data.AccountTransferData; +import org.apache.fineract.portfolio.account.data.StandingInstructionDTO; +import org.apache.fineract.portfolio.account.data.StandingInstructionData; +import org.apache.fineract.portfolio.account.data.request.StandingInstructionCreationRequest; +import org.apache.fineract.portfolio.account.data.request.StandingInstructionSearchParam; +import org.apache.fineract.portfolio.account.data.request.StandingInstructionUpdatesRequest; +import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; +import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; +import org.springframework.stereotype.Component; + +@Path("/v1/standinginstructions") +@Component +@Tag(name = "Standing Instructions", description = "Standing instructions (or standing orders) refer to instructions a bank account holder (\"the payer\") gives to his or her bank to pay a set amount at regular intervals to another's (\"the payee's\") account.\n" + + "\n" + "Note: At present only savings account to savings account and savings account to Loan account transfers are permitted.") +@RequiredArgsConstructor +public class StandingInstructionApiResource { + + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; + private final SqlValidator sqlValidator; + + private static final CommandHandlerRegistry COMMAND_HANDLER_REGISTRY = new CommandHandlerRegistry<>( + Map.of(CommandParameterUtil.UPDATE_COMMAND_VALUE, + (id, json) -> new CommandWrapperBuilder().updateStandingInstruction(id).withJson(json).build(), + CommandParameterUtil.DELETE_COMMAND_VALUE, + (id, json) -> new CommandWrapperBuilder().deleteStandingInstruction(id).withJson(json).build())); + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Retrieve Standing Instruction Template", description = "This is a convenience resource. " + + "It can be useful when building maintenance user interface screens for client applications. " + + "The template data returned consists of any or all of:\n" + "\n" + "Field Defaults\n" + "Allowed Value Lists\n" + + "Example Requests:\n" + "\n" + "standinginstructions/template?fromAccountType=2&fromOfficeId=1\n" + "\n" + + "standinginstructions/template?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=1\n" + "\n" + + "standinginstructions/template?fromClientId=1&fromAccountType=2&fromAccountId=1&transferType=1") + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsTemplateResponse.class))) + public StandingInstructionData template(@BeanParam StandingInstructionSearchParam instructionParam) { + context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + + return standingInstructionReadPlatformService.retrieveTemplate(instructionParam.getFromOfficeId(), + instructionParam.getFromClientId(), instructionParam.getFromAccountId(), instructionParam.getFromAccountType(), + instructionParam.getToOfficeId(), instructionParam.getToClientId(), instructionParam.getToAccountId(), + instructionParam.getToAccountType(), instructionParam.getTransferType()); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Create new Standing Instruction", description = "Ability to create new instruction for transfer of monetary funds from one account to another") + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = StandingInstructionCreationRequest.class))) + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PostStandingInstructionsResponse.class))) + public CommandProcessingResult create(@Parameter(hidden = true) StandingInstructionCreationRequest creationRequest) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction() + .withJson(toApiJsonSerializer.serialize(creationRequest)).build(); + + return commandsSourceWritePlatformService.logCommandSource(commandRequest); + } + + @PUT + @Path("{standingInstructionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Update Standing Instruction | Delete Standing Instruction", description = "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n" + + "\n" + "PUT https://DomainName/api/v1/standinginstructions/1?command=update\n" + "\n\n" + + "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n" + "\n" + + "PUT https://DomainName/api/v1/standinginstructions/1?command=delete") + @RequestBody(content = @Content(schema = @Schema(implementation = StandingInstructionUpdatesRequest.class))) + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PutStandingInstructionsStandingInstructionIdResponse.class))) + public CommandProcessingResult update( + @PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId, + @Parameter(hidden = true) StandingInstructionUpdatesRequest updatesRequest, + @QueryParam("command") @Parameter(description = "command") final String commandParam) { + + final String serializedUpdatesRequest = toApiJsonSerializer.serialize(updatesRequest); + final CommandWrapper commandRequest = COMMAND_HANDLER_REGISTRY.execute(commandParam, standingInstructionId, + serializedUpdatesRequest, new UnrecognizedQueryParamException("command", commandParam)); + + return commandsSourceWritePlatformService.logCommandSource(commandRequest); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "List Standing Instructions", description = "Example Requests:\n" + "\n" + "standinginstructions") + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsResponse.class))) + public Page retrieveAll( + @QueryParam("externalId") @Parameter(description = "externalId") final String externalId, + @QueryParam("offset") @Parameter(description = "offset") final Integer offset, + @QueryParam("limit") @Parameter(description = "limit") final Integer limit, + @QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, + @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder, + @QueryParam("transferType") @Parameter(description = "transferType") final Integer transferType, + @QueryParam("clientName") @Parameter(description = "clientName") final String clientName, + @QueryParam("clientId") @Parameter(description = "clientId") final Long clientId, + @QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccount, + @QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType) { + + context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + + sqlValidator.validate(orderBy); + sqlValidator.validate(sortOrder); + sqlValidator.validate(externalId); + final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset) + .orderBy(orderBy).sortOrder(sortOrder).build(); + + final LocalDate startDateRange = null; + final LocalDate endDateRange = null; + StandingInstructionDTO standingInstructionDTO = new StandingInstructionDTO(searchParameters, transferType, clientName, clientId, + fromAccount, fromAccountType, startDateRange, endDateRange); + + return standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO); + } + + @GET + @Path("{standingInstructionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Retrieve Standing Instruction", description = "Example Requests :\n" + "\n" + "standinginstructions/1") + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsStandingInstructionIdResponse.class))) + public StandingInstructionData retrieveOne( + @PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId, + @Context final UriInfo uriInfo, @QueryParam("externalId") @Parameter(description = "externalId") final String externalId, + @QueryParam("offset") @Parameter(description = "offset") final Integer offset, + @QueryParam("limit") @Parameter(description = "limit") final Integer limit, + @QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, + @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) { + + context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + + sqlValidator.validate(orderBy); + sqlValidator.validate(sortOrder); + StandingInstructionData standingInstructionData = standingInstructionReadPlatformService.retrieveOne(standingInstructionId); + final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset) + .orderBy(orderBy).sortOrder(sortOrder).build(); + final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); + if (!associationParameters.isEmpty()) { + if (associationParameters.contains("all")) { + associationParameters.addAll(Arrays.asList("transactions", "template")); + } + if (associationParameters.contains("transactions")) { + Page transfers = accountTransfersReadPlatformService + .retrieveByStandingInstruction(standingInstructionId, searchParameters); + standingInstructionData = StandingInstructionData.withTransferData(standingInstructionData, transfers); + } + if (associationParameters.contains("template")) { + final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate( + standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(), + standingInstructionData.getFromAccount().getId(), standingInstructionData.fromAccountTypeEnum().getValue(), + standingInstructionData.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(), + standingInstructionData.getToAccount().getId(), standingInstructionData.toAccountTypeEnum().getValue(), + standingInstructionData.transferTypeEnum().getValue()); + standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData); + } + } + + return standingInstructionData; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/AccountTransfersDetailDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/AccountTransfersDetailDataValidator.java index 8c7f876ba..d6dd44145 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/AccountTransfersDetailDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/AccountTransfersDetailDataValidator.java @@ -1,79 +1,79 @@ -/** - * 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.account.data; - -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName; - -import com.google.gson.JsonElement; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; -import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class AccountTransfersDetailDataValidator { - - private final FromJsonHelper fromApiJsonHelper; - - @Autowired - public AccountTransfersDetailDataValidator(final FromJsonHelper fromApiJsonHelper) { - this.fromApiJsonHelper = fromApiJsonHelper; - } - - public void validate(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { - - final JsonElement element = command.parsedJson(); - - final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); - baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero(); - - final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); - baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero(); - - final Long fromAccountId = this.fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element); - baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero(); - - final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); - baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull() - .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); - - final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); - baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero(); - - final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); - baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero(); - - final Long toAccountId = this.fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element); - baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero(); - - final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); - baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull().isOneOfTheseValues(Integer.valueOf(1), - Integer.valueOf(2)); - - } - -} +/** + * 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.account.data; + +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName; + +import com.google.gson.JsonElement; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class AccountTransfersDetailDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public AccountTransfersDetailDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validate(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero(); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero(); + + final Long fromAccountId = this.fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element); + baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero(); + + final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); + baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull() + .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero(); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero(); + + final Long toAccountId = this.fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element); + baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero(); + + final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); + baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull().isOneOfTheseValues(Integer.valueOf(1), + Integer.valueOf(2), Integer.valueOf(3)); + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java index 09b61c6b9..45e9f27ca 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java @@ -1,329 +1,359 @@ -/** - * 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.account.data; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.MonthDay; -import java.time.temporal.ChronoField; -import java.util.Collection; -import java.util.Optional; -import lombok.Getter; -import org.apache.fineract.infrastructure.core.data.EnumOptionData; -import org.apache.fineract.infrastructure.core.service.Page; -import org.apache.fineract.organisation.office.data.OfficeData; -import org.apache.fineract.portfolio.account.PortfolioAccountType; -import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; -import org.apache.fineract.portfolio.account.domain.AccountTransferType; -import org.apache.fineract.portfolio.account.domain.StandingInstructionType; -import org.apache.fineract.portfolio.client.data.ClientData; -import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; - -/** - * Immutable data object representing a savings account. - */ -@SuppressWarnings("unused") -public final class StandingInstructionData { - - @Getter - private final Long id; - @Getter - private final Long accountDetailId; - @Getter - private final String name; - private final OfficeData fromOffice; - @Getter - private final ClientData fromClient; - private final EnumOptionData fromAccountType; - @Getter - private final PortfolioAccountData fromAccount; - private final OfficeData toOffice; - @Getter - private final ClientData toClient; - private final EnumOptionData toAccountType; - @Getter - private final PortfolioAccountData toAccount; - private final EnumOptionData transferType; - private final EnumOptionData priority; - private final EnumOptionData instructionType; - @Getter - private final EnumOptionData status; - @Getter - private final BigDecimal amount; - @Getter - private final LocalDate validFrom; - private final LocalDate validTill; - private final EnumOptionData recurrenceType; - private final EnumOptionData recurrenceFrequency; - @Getter - private final Integer recurrenceInterval; - private final MonthDay recurrenceOnMonthDay; - private final Page transactions; - - private final Collection fromOfficeOptions; - private final Collection fromClientOptions; - private final Collection fromAccountTypeOptions; - private final Collection fromAccountOptions; - private final Collection toOfficeOptions; - private final Collection toClientOptions; - private final Collection toAccountTypeOptions; - private final Collection toAccountOptions; - private final Collection transferTypeOptions; - private final Collection statusOptions; - private final Collection instructionTypeOptions; - private final Collection priorityOptions; - private final Collection recurrenceTypeOptions; - private final Collection recurrenceFrequencyOptions; - - public static StandingInstructionData template(final Collection fromOfficeOptions, - final Collection fromClientOptions, final Collection fromAccountTypeOptions, - final Collection fromAccountOptions, final Collection toOfficeOptions, - final Collection toClientOptions, final Collection toAccountTypeOptions, - final Collection toAccountOptions, final Collection transferTypeOptions, - final Collection statusOptions, final Collection instructionTypeOptions, - final Collection priorityOptions, final Collection recurrenceTypeOptions, - final Collection recurrenceFrequencyOptions) { - - final Long id = null; - final Long accountDetailId = null; - final String name = null; - final OfficeData fromOffice = null; - final OfficeData toOffice = null; - final ClientData fromClient = null; - final ClientData toClient = null; - final EnumOptionData fromAccountType = null; - final PortfolioAccountData fromAccount = null; - final EnumOptionData toAccountType = null; - final PortfolioAccountData toAccount = null; - final EnumOptionData transferType = null; - final EnumOptionData priority = null; - final EnumOptionData instructionType = null; - final EnumOptionData status = null; - final BigDecimal amount = null; - final LocalDate validFrom = null; - final LocalDate validTill = null; - final EnumOptionData recurrenceType = null; - final EnumOptionData recurrenceFrequency = null; - final Integer recurrenceInterval = null; - final MonthDay recurrenceOnMonthDay = null; - final Page transactions = null; - - return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, - toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, - recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, - fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, - toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, - recurrenceFrequencyOptions); - } - - public static StandingInstructionData instance(final Long id, final Long accountDetailId, final String name, - final OfficeData fromOffice, final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, - final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final EnumOptionData toAccountType, - final PortfolioAccountData toAccount, final EnumOptionData transferType, final EnumOptionData priority, - final EnumOptionData instructionType, final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, - final LocalDate validTill, final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, - final Integer recurrenceInterval, final MonthDay recurrenceOnMonthDay) { - final Page transactions = null; - final Collection fromOfficeOptions = null; - final Collection fromClientOptions = null; - final Collection fromAccountTypeOptions = null; - final Collection fromAccountOptions = null; - final Collection toOfficeOptions = null; - final Collection toClientOptions = null; - final Collection toAccountTypeOptions = null; - final Collection toAccountOptions = null; - final Collection transferTypeOptions = null; - final Collection statusOptions = null; - final Collection instructionTypeOptions = null; - final Collection priorityOptions = null; - final Collection recurrenceTypeOptions = null; - final Collection recurrenceFrequencyOptions = null; - - return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, - toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, - recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, - fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, - toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, - recurrenceFrequencyOptions); - } - - public static StandingInstructionData withTemplateData(StandingInstructionData instructionData, StandingInstructionData templateData) { - return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name, - instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount, - instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount, - instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status, - instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType, - instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, - instructionData.transactions, templateData.fromOfficeOptions, templateData.fromClientOptions, - templateData.fromAccountTypeOptions, templateData.fromAccountOptions, templateData.toOfficeOptions, - templateData.toClientOptions, templateData.toAccountTypeOptions, templateData.toAccountOptions, - templateData.transferTypeOptions, templateData.statusOptions, templateData.instructionTypeOptions, - templateData.priorityOptions, templateData.recurrenceTypeOptions, templateData.recurrenceFrequencyOptions); - } - - private StandingInstructionData(final Long id, final Long accountDetailId, final String name, final OfficeData fromOffice, - final ClientData fromClient, final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, - final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount, - final EnumOptionData transferType, final EnumOptionData priority, final EnumOptionData instructionType, - final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, LocalDate validTill, - final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, final Integer recurrenceInterval, - final MonthDay recurrenceOnMonthDay, final Page transactions, - final Collection fromOfficeOptions, final Collection fromClientOptions, - final Collection fromAccountTypeOptions, final Collection fromAccountOptions, - final Collection toOfficeOptions, final Collection toClientOptions, - final Collection toAccountTypeOptions, final Collection toAccountOptions, - final Collection transferTypeOptions, final Collection statusOptions, - final Collection instructionTypeOptions, final Collection priorityOptions, - final Collection recurrenceTypeOptions, final Collection recurrenceFrequencyOptions) { - this.id = id; - this.accountDetailId = accountDetailId; - this.name = name; - this.fromOffice = fromOffice; - this.fromClient = fromClient; - this.fromAccountType = fromAccountType; - this.fromAccount = fromAccount; - this.toOffice = toOffice; - this.toClient = toClient; - this.toAccountType = toAccountType; - this.toAccount = toAccount; - this.transferType = transferType; - - this.priority = priority; - this.instructionType = instructionType; - this.status = status; - this.amount = amount; - this.validFrom = validFrom; - this.validTill = validTill; - this.recurrenceType = recurrenceType; - this.recurrenceFrequency = recurrenceFrequency; - this.recurrenceInterval = recurrenceInterval; - this.recurrenceOnMonthDay = recurrenceOnMonthDay; - - this.fromOfficeOptions = fromOfficeOptions; - this.fromClientOptions = fromClientOptions; - this.fromAccountTypeOptions = fromAccountTypeOptions; - this.fromAccountOptions = fromAccountOptions; - this.toOfficeOptions = toOfficeOptions; - this.toClientOptions = toClientOptions; - this.toAccountTypeOptions = toAccountTypeOptions; - this.toAccountOptions = toAccountOptions; - this.transferTypeOptions = transferTypeOptions; - this.statusOptions = statusOptions; - this.instructionTypeOptions = instructionTypeOptions; - this.priorityOptions = priorityOptions; - this.recurrenceTypeOptions = recurrenceTypeOptions; - this.recurrenceFrequencyOptions = recurrenceFrequencyOptions; - this.transactions = transactions; - } - - public static StandingInstructionData template(OfficeData fromOffice, ClientData fromClient, EnumOptionData fromAccountType, - PortfolioAccountData fromAccount, LocalDate transferDate, OfficeData toOffice, ClientData toClient, - EnumOptionData toAccountType, PortfolioAccountData toAccount, final Collection fromOfficeOptions, - final Collection fromClientOptions, final Collection fromAccountTypeOptions, - final Collection fromAccountOptions, final Collection toOfficeOptions, - final Collection toClientOptions, final Collection toAccountTypeOptions, - final Collection toAccountOptions, final Collection transferTypeOptions, - final Collection statusOptions, final Collection instructionTypeOptions, - final Collection priorityOptions, final Collection recurrenceTypeOptions, - final Collection recurrenceFrequencyOptions) { - final Long id = null; - final Long accountDetailId = null; - final String name = null; - final EnumOptionData transferType = null; - final EnumOptionData priority = null; - final EnumOptionData instructionType = null; - final EnumOptionData status = null; - final BigDecimal amount = null; - final LocalDate validFrom = null; - final LocalDate validTill = null; - final EnumOptionData recurrenceType = null; - final EnumOptionData recurrenceFrequency = null; - final Integer recurrenceInterval = null; - final MonthDay recurrenceOnMonthDay = null; - final Page transactions = null; - - return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, - toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, - recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, - fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, - toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, - recurrenceFrequencyOptions); - } - - public static StandingInstructionData withTransferData(StandingInstructionData instructionData, - final Page transactions) { - return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name, - instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount, - instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount, - instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status, - instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType, - instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, transactions, - instructionData.fromOfficeOptions, instructionData.fromClientOptions, instructionData.fromAccountTypeOptions, - instructionData.fromAccountOptions, instructionData.toOfficeOptions, instructionData.toClientOptions, - instructionData.toAccountTypeOptions, instructionData.toAccountOptions, instructionData.transferTypeOptions, - instructionData.statusOptions, instructionData.instructionTypeOptions, instructionData.priorityOptions, - instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions); - } - - public StandingInstructionType getInstructionType() { - return Optional.ofNullable(this.instructionType).map(e -> StandingInstructionType.fromInt(e.getId().intValue())).orElse(null); - - } - - public AccountTransferRecurrenceType getRecurrenceType() { - return Optional.ofNullable(this.recurrenceType).map(e -> AccountTransferRecurrenceType.fromInt(e.getId().intValue())).orElse(null); - } - - public PeriodFrequencyType getRecurrenceFrequency() { - return Optional.ofNullable(this.recurrenceFrequency).map(e -> PeriodFrequencyType.fromInt(e.getId().intValue())).orElse(null); - } - - public PortfolioAccountType getFromAccountType() { - return Optional.ofNullable(this.fromAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null); - } - - public PortfolioAccountType getToAccountType() { - return Optional.ofNullable(this.toAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null); - } - - public AccountTransferType getTransferType() { - return Optional.ofNullable(this.transferType).map(e -> AccountTransferType.fromInt(e.getId().intValue())).orElse(null); - } - - public Integer getRecurrenceOnDay() { - return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.DAY_OF_MONTH)).orElse(0); - } - - public Integer getRecurrenceOnMonth() { - return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.MONTH_OF_YEAR)).orElse(0); - } - - public Integer toTransferType() { - Integer transferType = null; - AccountTransferType accountTransferType = getTransferType(); - if (accountTransferType.isChargePayment()) { - transferType = LoanTransactionType.CHARGE_PAYMENT.getValue(); - } else if (accountTransferType.isLoanRepayment()) { - transferType = LoanTransactionType.REPAYMENT.getValue(); - } - return transferType; - } - -} +/** + * 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.account.data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.MonthDay; +import java.time.temporal.ChronoField; +import java.util.Collection; +import java.util.Optional; +import lombok.Getter; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.organisation.office.data.OfficeData; +import org.apache.fineract.portfolio.account.PortfolioAccountType; +import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; +import org.apache.fineract.portfolio.account.domain.AccountTransferType; +import org.apache.fineract.portfolio.account.domain.StandingInstructionType; +import org.apache.fineract.portfolio.client.data.ClientData; +import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; + +/** + * Immutable data object representing a savings account. + */ +@SuppressWarnings("unused") +public final class StandingInstructionData { + + @Getter + private final Long id; + @Getter + private final Long accountDetailId; + @Getter + private final String name; + @Getter + private final OfficeData fromOffice; + @Getter + private final ClientData fromClient; + @Getter + private final EnumOptionData fromAccountType; + @Getter + private final PortfolioAccountData fromAccount; + @Getter + private final OfficeData toOffice; + @Getter + private final ClientData toClient; + @Getter + private final EnumOptionData toAccountType; + @Getter + private final PortfolioAccountData toAccount; + @Getter + private final EnumOptionData transferType; + @Getter + private final EnumOptionData priority; + @Getter + private final EnumOptionData instructionType; + @Getter + private final EnumOptionData status; + @Getter + private final BigDecimal amount; + @Getter + private final LocalDate validFrom; + @Getter + private final LocalDate validTill; + @Getter + private final EnumOptionData recurrenceType; + @Getter + private final EnumOptionData recurrenceFrequency; + @Getter + private final Integer recurrenceInterval; + @Getter + private final MonthDay recurrenceOnMonthDay; + @Getter + private final Page transactions; + + @Getter + private final Collection fromOfficeOptions; + @Getter + private final Collection fromClientOptions; + @Getter + private final Collection fromAccountTypeOptions; + @Getter + private final Collection fromAccountOptions; + @Getter + private final Collection toOfficeOptions; + @Getter + private final Collection toClientOptions; + @Getter + private final Collection toAccountTypeOptions; + @Getter + private final Collection toAccountOptions; + @Getter + private final Collection transferTypeOptions; + @Getter + private final Collection statusOptions; + @Getter + private final Collection instructionTypeOptions; + @Getter + private final Collection priorityOptions; + @Getter + private final Collection recurrenceTypeOptions; + @Getter + private final Collection recurrenceFrequencyOptions; + + public static StandingInstructionData template(final Collection fromOfficeOptions, + final Collection fromClientOptions, final Collection fromAccountTypeOptions, + final Collection fromAccountOptions, final Collection toOfficeOptions, + final Collection toClientOptions, final Collection toAccountTypeOptions, + final Collection toAccountOptions, final Collection transferTypeOptions, + final Collection statusOptions, final Collection instructionTypeOptions, + final Collection priorityOptions, final Collection recurrenceTypeOptions, + final Collection recurrenceFrequencyOptions) { + + final Long id = null; + final Long accountDetailId = null; + final String name = null; + final OfficeData fromOffice = null; + final OfficeData toOffice = null; + final ClientData fromClient = null; + final ClientData toClient = null; + final EnumOptionData fromAccountType = null; + final PortfolioAccountData fromAccount = null; + final EnumOptionData toAccountType = null; + final PortfolioAccountData toAccount = null; + final EnumOptionData transferType = null; + final EnumOptionData priority = null; + final EnumOptionData instructionType = null; + final EnumOptionData status = null; + final BigDecimal amount = null; + final LocalDate validFrom = null; + final LocalDate validTill = null; + final EnumOptionData recurrenceType = null; + final EnumOptionData recurrenceFrequency = null; + final Integer recurrenceInterval = null; + final MonthDay recurrenceOnMonthDay = null; + final Page transactions = null; + + return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, + toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, + recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, + fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, + toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, + recurrenceFrequencyOptions); + } + + public static StandingInstructionData instance(final Long id, final Long accountDetailId, final String name, + final OfficeData fromOffice, final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, + final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final EnumOptionData toAccountType, + final PortfolioAccountData toAccount, final EnumOptionData transferType, final EnumOptionData priority, + final EnumOptionData instructionType, final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, + final LocalDate validTill, final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, + final Integer recurrenceInterval, final MonthDay recurrenceOnMonthDay) { + final Page transactions = null; + final Collection fromOfficeOptions = null; + final Collection fromClientOptions = null; + final Collection fromAccountTypeOptions = null; + final Collection fromAccountOptions = null; + final Collection toOfficeOptions = null; + final Collection toClientOptions = null; + final Collection toAccountTypeOptions = null; + final Collection toAccountOptions = null; + final Collection transferTypeOptions = null; + final Collection statusOptions = null; + final Collection instructionTypeOptions = null; + final Collection priorityOptions = null; + final Collection recurrenceTypeOptions = null; + final Collection recurrenceFrequencyOptions = null; + + return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, + toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, + recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, + fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, + toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, + recurrenceFrequencyOptions); + } + + public static StandingInstructionData withTemplateData(StandingInstructionData instructionData, StandingInstructionData templateData) { + return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name, + instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount, + instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount, + instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status, + instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType, + instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, + instructionData.transactions, templateData.fromOfficeOptions, templateData.fromClientOptions, + templateData.fromAccountTypeOptions, templateData.fromAccountOptions, templateData.toOfficeOptions, + templateData.toClientOptions, templateData.toAccountTypeOptions, templateData.toAccountOptions, + templateData.transferTypeOptions, templateData.statusOptions, templateData.instructionTypeOptions, + templateData.priorityOptions, templateData.recurrenceTypeOptions, templateData.recurrenceFrequencyOptions); + } + + private StandingInstructionData(final Long id, final Long accountDetailId, final String name, final OfficeData fromOffice, + final ClientData fromClient, final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, + final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount, + final EnumOptionData transferType, final EnumOptionData priority, final EnumOptionData instructionType, + final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, LocalDate validTill, + final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, final Integer recurrenceInterval, + final MonthDay recurrenceOnMonthDay, final Page transactions, + final Collection fromOfficeOptions, final Collection fromClientOptions, + final Collection fromAccountTypeOptions, final Collection fromAccountOptions, + final Collection toOfficeOptions, final Collection toClientOptions, + final Collection toAccountTypeOptions, final Collection toAccountOptions, + final Collection transferTypeOptions, final Collection statusOptions, + final Collection instructionTypeOptions, final Collection priorityOptions, + final Collection recurrenceTypeOptions, final Collection recurrenceFrequencyOptions) { + this.id = id; + this.accountDetailId = accountDetailId; + this.name = name; + this.fromOffice = fromOffice; + this.fromClient = fromClient; + this.fromAccountType = fromAccountType; + this.fromAccount = fromAccount; + this.toOffice = toOffice; + this.toClient = toClient; + this.toAccountType = toAccountType; + this.toAccount = toAccount; + this.transferType = transferType; + + this.priority = priority; + this.instructionType = instructionType; + this.status = status; + this.amount = amount; + this.validFrom = validFrom; + this.validTill = validTill; + this.recurrenceType = recurrenceType; + this.recurrenceFrequency = recurrenceFrequency; + this.recurrenceInterval = recurrenceInterval; + this.recurrenceOnMonthDay = recurrenceOnMonthDay; + + this.fromOfficeOptions = fromOfficeOptions; + this.fromClientOptions = fromClientOptions; + this.fromAccountTypeOptions = fromAccountTypeOptions; + this.fromAccountOptions = fromAccountOptions; + this.toOfficeOptions = toOfficeOptions; + this.toClientOptions = toClientOptions; + this.toAccountTypeOptions = toAccountTypeOptions; + this.toAccountOptions = toAccountOptions; + this.transferTypeOptions = transferTypeOptions; + this.statusOptions = statusOptions; + this.instructionTypeOptions = instructionTypeOptions; + this.priorityOptions = priorityOptions; + this.recurrenceTypeOptions = recurrenceTypeOptions; + this.recurrenceFrequencyOptions = recurrenceFrequencyOptions; + this.transactions = transactions; + } + + public static StandingInstructionData template(OfficeData fromOffice, ClientData fromClient, EnumOptionData fromAccountType, + PortfolioAccountData fromAccount, LocalDate transferDate, OfficeData toOffice, ClientData toClient, + EnumOptionData toAccountType, PortfolioAccountData toAccount, final Collection fromOfficeOptions, + final Collection fromClientOptions, final Collection fromAccountTypeOptions, + final Collection fromAccountOptions, final Collection toOfficeOptions, + final Collection toClientOptions, final Collection toAccountTypeOptions, + final Collection toAccountOptions, final Collection transferTypeOptions, + final Collection statusOptions, final Collection instructionTypeOptions, + final Collection priorityOptions, final Collection recurrenceTypeOptions, + final Collection recurrenceFrequencyOptions) { + final Long id = null; + final Long accountDetailId = null; + final String name = null; + final EnumOptionData transferType = null; + final EnumOptionData priority = null; + final EnumOptionData instructionType = null; + final EnumOptionData status = null; + final BigDecimal amount = null; + final LocalDate validFrom = null; + final LocalDate validTill = null; + final EnumOptionData recurrenceType = null; + final EnumOptionData recurrenceFrequency = null; + final Integer recurrenceInterval = null; + final MonthDay recurrenceOnMonthDay = null; + final Page transactions = null; + + return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, + toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, + recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, + fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, + toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, + recurrenceFrequencyOptions); + } + + public static StandingInstructionData withTransferData(StandingInstructionData instructionData, + final Page transactions) { + return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name, + instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount, + instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount, + instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status, + instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType, + instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, transactions, + instructionData.fromOfficeOptions, instructionData.fromClientOptions, instructionData.fromAccountTypeOptions, + instructionData.fromAccountOptions, instructionData.toOfficeOptions, instructionData.toClientOptions, + instructionData.toAccountTypeOptions, instructionData.toAccountOptions, instructionData.transferTypeOptions, + instructionData.statusOptions, instructionData.instructionTypeOptions, instructionData.priorityOptions, + instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions); + } + + /** + * Returns the domain enum for instruction type — used internally by business logic. + * NOTE: renamed from getInstructionType() to avoid overriding Lombok @Getter (which must + * return EnumOptionData so Jackson serializes it as an object, not a plain string). + */ + public StandingInstructionType instructionTypeEnum() { + return Optional.ofNullable(this.instructionType).map(e -> StandingInstructionType.fromInt(e.getId().intValue())).orElse(null); + } + + public AccountTransferRecurrenceType recurrenceTypeEnum() { + return Optional.ofNullable(this.recurrenceType).map(e -> AccountTransferRecurrenceType.fromInt(e.getId().intValue())).orElse(null); + } + + public PeriodFrequencyType recurrenceFrequencyEnum() { + return Optional.ofNullable(this.recurrenceFrequency).map(e -> PeriodFrequencyType.fromInt(e.getId().intValue())).orElse(null); + } + + public PortfolioAccountType fromAccountTypeEnum() { + return Optional.ofNullable(this.fromAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null); + } + + public PortfolioAccountType toAccountTypeEnum() { + return Optional.ofNullable(this.toAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null); + } + + public AccountTransferType transferTypeEnum() { + return Optional.ofNullable(this.transferType).map(e -> AccountTransferType.fromInt(e.getId().intValue())).orElse(null); + } + + public Integer getRecurrenceOnDay() { + return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.DAY_OF_MONTH)).orElse(0); + } + + public Integer getRecurrenceOnMonth() { + return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.MONTH_OF_YEAR)).orElse(0); + } + + public Integer toTransferType() { + Integer transferType = null; + AccountTransferType accountTransferType = transferTypeEnum(); + if (accountTransferType != null && accountTransferType.isChargePayment()) { + transferType = LoanTransactionType.CHARGE_PAYMENT.getValue(); + } else if (accountTransferType != null && accountTransferType.isLoanRepayment()) { + transferType = LoanTransactionType.REPAYMENT.getValue(); + } + return transferType; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionDataValidator.java index 552c2fc59..93fc04129 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionDataValidator.java @@ -1,294 +1,296 @@ -/** - * 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.account.data; - -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; - -import com.google.gson.JsonElement; -import com.google.gson.reflect.TypeToken; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.MonthDay; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import org.apache.commons.lang3.StringUtils; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.data.ApiParameterError; -import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; -import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; -import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; -import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; -import org.apache.fineract.portfolio.account.AccountDetailConstants; -import org.apache.fineract.portfolio.account.PortfolioAccountType; -import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants; -import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; -import org.apache.fineract.portfolio.account.domain.AccountTransferType; -import org.apache.fineract.portfolio.account.domain.StandingInstructionType; -import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class StandingInstructionDataValidator { - - private final FromJsonHelper fromApiJsonHelper; - private final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator; - private static final Set CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName, - AccountDetailConstants.dateFormatParamName, AccountDetailConstants.fromOfficeIdParamName, - AccountDetailConstants.fromClientIdParamName, AccountDetailConstants.fromAccountTypeParamName, - AccountDetailConstants.fromAccountIdParamName, AccountDetailConstants.toOfficeIdParamName, - AccountDetailConstants.toClientIdParamName, AccountDetailConstants.toAccountTypeParamName, - AccountDetailConstants.toAccountIdParamName, AccountDetailConstants.transferTypeParamName, - StandingInstructionApiConstants.priorityParamName, StandingInstructionApiConstants.instructionTypeParamName, - StandingInstructionApiConstants.statusParamName, StandingInstructionApiConstants.amountParamName, - StandingInstructionApiConstants.validFromParamName, StandingInstructionApiConstants.validTillParamName, - StandingInstructionApiConstants.recurrenceTypeParamName, StandingInstructionApiConstants.recurrenceFrequencyParamName, - StandingInstructionApiConstants.recurrenceIntervalParamName, StandingInstructionApiConstants.recurrenceOnMonthDayParamName, - StandingInstructionApiConstants.nameParamName, StandingInstructionApiConstants.monthDayFormatParamName)); - - private static final Set UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName, - AccountDetailConstants.dateFormatParamName, StandingInstructionApiConstants.priorityParamName, - StandingInstructionApiConstants.instructionTypeParamName, StandingInstructionApiConstants.statusParamName, - StandingInstructionApiConstants.amountParamName, StandingInstructionApiConstants.validFromParamName, - StandingInstructionApiConstants.validTillParamName, StandingInstructionApiConstants.recurrenceTypeParamName, - StandingInstructionApiConstants.recurrenceFrequencyParamName, StandingInstructionApiConstants.recurrenceIntervalParamName, - StandingInstructionApiConstants.recurrenceOnMonthDayParamName, StandingInstructionApiConstants.monthDayFormatParamName)); - - @Autowired - public StandingInstructionDataValidator(final FromJsonHelper fromApiJsonHelper, - final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator) { - this.fromApiJsonHelper = fromApiJsonHelper; - this.accountTransfersDetailDataValidator = accountTransfersDetailDataValidator; - } - - public void validateForCreate(final JsonCommand command) { - final String json = command.json(); - - if (StringUtils.isBlank(json)) { - throw new InvalidJsonException(); - } - - final Type typeOfMap = new TypeToken>() {}.getType(); - this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CREATE_REQUEST_DATA_PARAMETERS); - - final List dataValidationErrors = new ArrayList<>(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); - this.accountTransfersDetailDataValidator.validate(command, baseDataValidator); - - final JsonElement element = command.parsedJson(); - - final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element, - Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, 2); - - final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName, - element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull(); - - final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName, - element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill) - .validateDateAfter(validFrom); - - final BigDecimal transferAmount = this.fromApiJsonHelper - .extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount(); - - final Integer transferType = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); - baseDataValidator.reset().parameter(transferTypeParamName).value(transferType).notNull().inMinMaxRange(1, 3); - - final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element, - Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull().inMinMaxRange(1, - 4); - - final Integer standingInstructionType = this.fromApiJsonHelper - .extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) - .notNull().inMinMaxRange(1, 2); - - final Integer recurrenceType = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName, - element, Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() - .inMinMaxRange(1, 2); - boolean isPeriodic = false; - if (recurrenceType != null) { - isPeriodic = AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence(); - } - - final Integer recurrenceFrequency = this.fromApiJsonHelper - .extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) - .inMinMaxRange(0, 3); - - if (recurrenceFrequency != null) { - PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(recurrenceFrequency); - if (frequencyType.isMonthly() || frequencyType.isYearly()) { - final MonthDay monthDay = this.fromApiJsonHelper - .extractMonthDayNamed(StandingInstructionApiConstants.recurrenceOnMonthDayParamName, element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceOnMonthDayParamName).value(monthDay) - .notNull(); - } - } - - final Integer recurrenceInterval = this.fromApiJsonHelper - .extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault()); - if (isPeriodic) { - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) - .notNull(); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) - .notNull(); - } - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) - .integerGreaterThanZero(); - - final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull(); - - final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); - if (toAccountType != null && PortfolioAccountType.fromInt(toAccountType).isSavingsAccount()) { - baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) - .notNull().inMinMaxRange(1, 1); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() - .inMinMaxRange(1, 1); - - } - if (standingInstructionType != null && StandingInstructionType.fromInt(standingInstructionType).isFixedAmoutTransfer()) { - baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).notNull(); - } - - String errorCode = null; - AccountTransferType accountTransferType = AccountTransferType.fromInt(transferType); - final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); - if (fromAccountType != null && toAccountType != null) { - PortfolioAccountType fromPortfolioAccountType = PortfolioAccountType.fromInt(fromAccountType); - PortfolioAccountType toPortfolioAccountType = PortfolioAccountType.fromInt(toAccountType); - if (accountTransferType.isAccountTransfer() - && (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) { - errorCode = "not.account.transfer"; - } else if (accountTransferType.isLoanRepayment() - && (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isSavingsAccount())) { - errorCode = "not.loan.repayment"; - } - if (errorCode != null) { - baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode); - } - } - - throwExceptionIfValidationWarningsExist(dataValidationErrors); - } - - public void validateForUpdate(final JsonCommand command) { - final String json = command.json(); - - if (StringUtils.isBlank(json)) { - throw new InvalidJsonException(); - } - - final Type typeOfMap = new TypeToken>() {}.getType(); - this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, UPDATE_REQUEST_DATA_PARAMETERS); - - final List dataValidationErrors = new ArrayList<>(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); - - final JsonElement element = command.parsedJson(); - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validFromParamName, element)) { - final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName, - element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull(); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validTillParamName, element)) { - final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName, - element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill).notNull(); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.amountParamName, element)) { - final BigDecimal transferAmount = this.fromApiJsonHelper - .extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount(); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.statusParamName, element)) { - final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element, - Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, - 2); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.priorityParamName, element)) { - final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element, - Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull() - .inMinMaxRange(1, 4); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.instructionTypeParamName, element)) { - final Integer standingInstructionType = this.fromApiJsonHelper - .extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) - .notNull().inMinMaxRange(1, 2); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceTypeParamName, element)) { - final Integer recurrenceType = this.fromApiJsonHelper - .extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName, element, Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() - .inMinMaxRange(1, 2); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceFrequencyParamName, element)) { - final Integer recurrenceFrequency = this.fromApiJsonHelper - .extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) - .inMinMaxRange(0, 3); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceIntervalParamName, element)) { - final Integer recurrenceInterval = this.fromApiJsonHelper - .extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault()); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) - .integerGreaterThanZero(); - } - - if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.nameParamName, element)) { - final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element); - baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull(); - } - - throwExceptionIfValidationWarningsExist(dataValidationErrors); - } - - private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { - if (!dataValidationErrors.isEmpty()) { - throw new PlatformApiDataValidationException(dataValidationErrors); - } - } -} +/** + * 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.account.data; + +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.MonthDay; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.ApiParameterError; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.account.AccountDetailConstants; +import org.apache.fineract.portfolio.account.PortfolioAccountType; +import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants; +import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; +import org.apache.fineract.portfolio.account.domain.AccountTransferType; +import org.apache.fineract.portfolio.account.domain.StandingInstructionType; +import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class StandingInstructionDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + private final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator; + private static final Set CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName, + AccountDetailConstants.dateFormatParamName, AccountDetailConstants.fromOfficeIdParamName, + AccountDetailConstants.fromClientIdParamName, AccountDetailConstants.fromAccountTypeParamName, + AccountDetailConstants.fromAccountIdParamName, AccountDetailConstants.toOfficeIdParamName, + AccountDetailConstants.toClientIdParamName, AccountDetailConstants.toAccountTypeParamName, + AccountDetailConstants.toAccountIdParamName, AccountDetailConstants.transferTypeParamName, + StandingInstructionApiConstants.priorityParamName, StandingInstructionApiConstants.instructionTypeParamName, + StandingInstructionApiConstants.statusParamName, StandingInstructionApiConstants.amountParamName, + StandingInstructionApiConstants.validFromParamName, StandingInstructionApiConstants.validTillParamName, + StandingInstructionApiConstants.recurrenceTypeParamName, StandingInstructionApiConstants.recurrenceFrequencyParamName, + StandingInstructionApiConstants.recurrenceIntervalParamName, StandingInstructionApiConstants.recurrenceOnMonthDayParamName, + StandingInstructionApiConstants.nameParamName, StandingInstructionApiConstants.monthDayFormatParamName)); + + private static final Set UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName, + AccountDetailConstants.dateFormatParamName, StandingInstructionApiConstants.priorityParamName, + StandingInstructionApiConstants.instructionTypeParamName, StandingInstructionApiConstants.statusParamName, + StandingInstructionApiConstants.amountParamName, StandingInstructionApiConstants.validFromParamName, + StandingInstructionApiConstants.validTillParamName, StandingInstructionApiConstants.recurrenceTypeParamName, + StandingInstructionApiConstants.recurrenceFrequencyParamName, StandingInstructionApiConstants.recurrenceIntervalParamName, + StandingInstructionApiConstants.recurrenceOnMonthDayParamName, StandingInstructionApiConstants.monthDayFormatParamName)); + + @Autowired + public StandingInstructionDataValidator(final FromJsonHelper fromApiJsonHelper, + final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.accountTransfersDetailDataValidator = accountTransfersDetailDataValidator; + } + + public void validateForCreate(final JsonCommand command) { + final String json = command.json(); + + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CREATE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + this.accountTransfersDetailDataValidator.validate(command, baseDataValidator); + + final JsonElement element = command.parsedJson(); + + final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, 2); + + final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName, + element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull(); + + final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName, + element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill) + .validateDateAfter(validFrom); + + final BigDecimal transferAmount = this.fromApiJsonHelper + .extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount(); + + final Integer transferType = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(transferTypeParamName).value(transferType).notNull().inMinMaxRange(1, 3); + + final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull().inMinMaxRange(1, + 4); + + final Integer standingInstructionType = this.fromApiJsonHelper + .extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) + .notNull().inMinMaxRange(1, 2); + + final Integer recurrenceType = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName, + element, Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() + .inMinMaxRange(1, 2); + boolean isPeriodic = false; + if (recurrenceType != null) { + isPeriodic = AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence(); + } + + final Integer recurrenceFrequency = this.fromApiJsonHelper + .extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) + .inMinMaxRange(0, 3); + + if (recurrenceFrequency != null) { + PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(recurrenceFrequency); + if (frequencyType.isMonthly() || frequencyType.isYearly()) { + final MonthDay monthDay = this.fromApiJsonHelper + .extractMonthDayNamed(StandingInstructionApiConstants.recurrenceOnMonthDayParamName, element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceOnMonthDayParamName).value(monthDay) + .notNull(); + } + } + + final Integer recurrenceInterval = this.fromApiJsonHelper + .extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault()); + if (isPeriodic) { + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) + .notNull(); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) + .notNull(); + } + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) + .integerGreaterThanZero(); + + final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull(); + + final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); + if (toAccountType != null && (PortfolioAccountType.fromInt(toAccountType).isSavingsAccount() + || PortfolioAccountType.fromInt(toAccountType).isCurrentAccount())) { + baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) + .notNull().inMinMaxRange(1, 1); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() + .inMinMaxRange(1, 1); + + } + if (standingInstructionType != null && StandingInstructionType.fromInt(standingInstructionType).isFixedAmoutTransfer()) { + baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).notNull(); + } + + String errorCode = null; + AccountTransferType accountTransferType = AccountTransferType.fromInt(transferType); + final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); + if (fromAccountType != null && toAccountType != null) { + PortfolioAccountType fromPortfolioAccountType = PortfolioAccountType.fromInt(fromAccountType); + PortfolioAccountType toPortfolioAccountType = PortfolioAccountType.fromInt(toAccountType); + if (accountTransferType.isAccountTransfer() + && (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) { + errorCode = "not.account.transfer"; + } else if (accountTransferType.isLoanRepayment() + && (fromPortfolioAccountType.isLoanAccount() + || (toPortfolioAccountType.isSavingsAccount() || toPortfolioAccountType.isCurrentAccount()))) { + errorCode = "not.loan.repayment"; + } + if (errorCode != null) { + baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode); + } + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForUpdate(final JsonCommand command) { + final String json = command.json(); + + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, UPDATE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + + final JsonElement element = command.parsedJson(); + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validFromParamName, element)) { + final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName, + element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull(); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validTillParamName, element)) { + final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName, + element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill).notNull(); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.amountParamName, element)) { + final BigDecimal transferAmount = this.fromApiJsonHelper + .extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.statusParamName, element)) { + final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, + 2); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.priorityParamName, element)) { + final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element, + Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull() + .inMinMaxRange(1, 4); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.instructionTypeParamName, element)) { + final Integer standingInstructionType = this.fromApiJsonHelper + .extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) + .notNull().inMinMaxRange(1, 2); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceTypeParamName, element)) { + final Integer recurrenceType = this.fromApiJsonHelper + .extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() + .inMinMaxRange(1, 2); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceFrequencyParamName, element)) { + final Integer recurrenceFrequency = this.fromApiJsonHelper + .extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) + .inMinMaxRange(0, 3); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceIntervalParamName, element)) { + final Integer recurrenceInterval = this.fromApiJsonHelper + .extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault()); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) + .integerGreaterThanZero(); + } + + if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.nameParamName, element)) { + final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element); + baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull(); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferDetailAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferDetailAssembler.java index a8fe76f2a..985bc0b29 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferDetailAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferDetailAssembler.java @@ -1,214 +1,314 @@ -/** - * 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.account.domain; - -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; - -import com.google.gson.JsonElement; -import java.util.Locale; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; -import org.apache.fineract.organisation.office.domain.Office; -import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper; -import org.apache.fineract.portfolio.client.domain.Client; -import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; -import org.apache.fineract.portfolio.loanaccount.domain.Loan; -import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; -import org.apache.fineract.portfolio.savings.domain.SavingsAccount; -import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class AccountTransferDetailAssembler { - - private final ClientRepositoryWrapper clientRepository; - private final OfficeRepositoryWrapper officeRepositoryWrapper; - private final SavingsAccountAssembler savingsAccountAssembler; - private final FromJsonHelper fromApiJsonHelper; - private final LoanAssembler loanAccountAssembler; - - @Autowired - public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository, - final OfficeRepositoryWrapper officeRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler, - final FromJsonHelper fromApiJsonHelper, final LoanAssembler loanAccountAssembler) { - this.clientRepository = clientRepository; - this.officeRepositoryWrapper = officeRepositoryWrapper; - this.savingsAccountAssembler = savingsAccountAssembler; - this.fromApiJsonHelper = fromApiJsonHelper; - this.loanAccountAssembler = loanAccountAssembler; - } - - public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) { - - final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); - final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false); - - final boolean backdatedTxnsAllowedTill = false; - final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); - final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill); - - return assembleSavingsToSavingsTransfer(command, fromSavingsAccount, toSavingsAccount); - - } - - public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) { - - final Long fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); - final boolean backdatedTxnsAllowedTill = false; - final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, backdatedTxnsAllowedTill); - - final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName); - final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId); - - return assembleSavingsToLoanTransfer(command, fromSavingsAccount, toLoanAccount); - - } - - public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) { - - final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); - final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId); - final boolean backdatedTxnsAllowedTill = false; - final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName); - final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill); - - return assembleLoanToSavingsTransfer(command, fromLoanAccount, toSavingsAccount); - } - - public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, - final SavingsAccount toSavingsAccount) { - - final JsonElement element = command.parsedJson(); - - final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); - final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); - - final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); - final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); - - final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); - final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); - - final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); - final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); - - final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); - - return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, - toSavingsAccount, transfertype); - - } - - public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, - final Loan toLoanAccount) { - - final JsonElement element = command.parsedJson(); - - final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); - final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); - - final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); - final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); - - final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); - final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); - - final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); - final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); - - final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); - - return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, - transfertype); - - } - - public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount, - final SavingsAccount toSavingsAccount) { - - final JsonElement element = command.parsedJson(); - - final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); - final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); - - final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); - final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); - - final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); - final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); - - final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); - final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); - final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); - - return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, - transfertype); - } - - public AccountTransferDetails assembleSavingsToLoanTransfer(final SavingsAccount fromSavingsAccount, final Loan toLoanAccount, - Integer transferType) { - final Office fromOffice = fromSavingsAccount.office(); - final Client fromClient = fromSavingsAccount.getClient(); - final Office toOffice = toLoanAccount.getOffice(); - final Client toClient = toLoanAccount.client(); - - return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, - transferType); - - } - - public AccountTransferDetails assembleSavingsToSavingsTransfer(final SavingsAccount fromSavingsAccount, - final SavingsAccount toSavingsAccount, Integer transferType) { - final Office fromOffice = fromSavingsAccount.office(); - final Client fromClient = fromSavingsAccount.getClient(); - final Office toOffice = toSavingsAccount.office(); - final Client toClient = toSavingsAccount.getClient(); - - return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, - toSavingsAccount, transferType); - } - - public AccountTransferDetails assembleLoanToSavingsTransfer(final Loan fromLoanAccount, final SavingsAccount toSavingsAccount, - Integer transferType) { - final Office fromOffice = fromLoanAccount.getOffice(); - final Client fromClient = fromLoanAccount.client(); - final Office toOffice = toSavingsAccount.office(); - final Client toClient = toSavingsAccount.getClient(); - - return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, - transferType); - } - - public AccountTransferDetails assembleLoanToLoanTransfer(Loan fromLoanAccount, Loan toLoanAccount, Integer transferType) { - final Office fromOffice = fromLoanAccount.getOffice(); - final Client fromClient = fromLoanAccount.client(); - final Office toOffice = toLoanAccount.getOffice(); - final Client toClient = toLoanAccount.client(); - - return AccountTransferDetails.loanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount, - transferType); - } -} +/** + * 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.account.domain; + +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; + +import com.google.gson.JsonElement; +import java.util.Locale; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.organisation.office.domain.Office; +import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper; +import org.apache.fineract.portfolio.client.domain.Client; +import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AccountTransferDetailAssembler { + + private final ClientRepositoryWrapper clientRepository; + private final OfficeRepositoryWrapper officeRepositoryWrapper; + private final SavingsAccountAssembler savingsAccountAssembler; + private final FromJsonHelper fromApiJsonHelper; + private final LoanAssembler loanAccountAssembler; + private final CurrentAccountRepository currentAccountRepository; + + @Autowired + public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository, + final OfficeRepositoryWrapper officeRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler, + final FromJsonHelper fromApiJsonHelper, final LoanAssembler loanAccountAssembler, + final CurrentAccountRepository currentAccountRepository) { + this.clientRepository = clientRepository; + this.officeRepositoryWrapper = officeRepositoryWrapper; + this.savingsAccountAssembler = savingsAccountAssembler; + this.fromApiJsonHelper = fromApiJsonHelper; + this.loanAccountAssembler = loanAccountAssembler; + this.currentAccountRepository = currentAccountRepository; + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) { + + final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false); + + final boolean backdatedTxnsAllowedTill = false; + final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill); + + return assembleSavingsToSavingsTransfer(command, fromSavingsAccount, toSavingsAccount); + + } + + public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) { + + final Long fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final boolean backdatedTxnsAllowedTill = false; + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, backdatedTxnsAllowedTill); + + final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId); + + return assembleSavingsToLoanTransfer(command, fromSavingsAccount, toLoanAccount); + + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) { + + final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId); + final boolean backdatedTxnsAllowedTill = false; + final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill); + + return assembleLoanToSavingsTransfer(command, fromLoanAccount, toSavingsAccount); + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, + final SavingsAccount toSavingsAccount) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, + toSavingsAccount, transfertype); + + } + + public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, + final Loan toLoanAccount) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, + transfertype); + + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount, + final SavingsAccount toSavingsAccount) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, + transfertype); + } + + public AccountTransferDetails assembleSavingsToLoanTransfer(final SavingsAccount fromSavingsAccount, final Loan toLoanAccount, + Integer transferType) { + final Office fromOffice = fromSavingsAccount.office(); + final Client fromClient = fromSavingsAccount.getClient(); + final Office toOffice = toLoanAccount.getOffice(); + final Client toClient = toLoanAccount.client(); + + return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, + transferType); + + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final SavingsAccount fromSavingsAccount, + final SavingsAccount toSavingsAccount, Integer transferType) { + final Office fromOffice = fromSavingsAccount.office(); + final Client fromClient = fromSavingsAccount.getClient(); + final Office toOffice = toSavingsAccount.office(); + final Client toClient = toSavingsAccount.getClient(); + + return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, + toSavingsAccount, transferType); + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final Loan fromLoanAccount, final SavingsAccount toSavingsAccount, + Integer transferType) { + final Office fromOffice = fromLoanAccount.getOffice(); + final Client fromClient = fromLoanAccount.client(); + final Office toOffice = toSavingsAccount.office(); + final Client toClient = toSavingsAccount.getClient(); + + return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, + transferType); + } + + public AccountTransferDetails assembleLoanToLoanTransfer(Loan fromLoanAccount, Loan toLoanAccount, Integer transferType) { + final Office fromOffice = fromLoanAccount.getOffice(); + final Client fromClient = fromLoanAccount.client(); + final Office toOffice = toLoanAccount.getOffice(); + final Client toClient = toLoanAccount.client(); + + return AccountTransferDetails.loanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount, + transferType); + } + + // --- Current account assembly methods --- + + public AccountTransferDetails assembleCurrentToCurrentTransfer(final JsonCommand command) { + final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId) + .orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId)); + + final Long toCurrentAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final CurrentAccount toCurrentAccount = currentAccountRepository.findById(toCurrentAccountId) + .orElseThrow(() -> new IllegalArgumentException("To current account not found: " + toCurrentAccountId)); + + final JsonElement element = command.parsedJson(); + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.currentToCurrentTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient, + toCurrentAccount, transfertype); + } + + public AccountTransferDetails assembleCurrentToSavingsTransfer(final JsonCommand command) { + final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId) + .orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId)); + + final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, false); + + final JsonElement element = command.parsedJson(); + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.currentToSavingsTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient, + toSavingsAccount, transfertype); + } + + public AccountTransferDetails assembleSavingsToCurrentTransfer(final JsonCommand command) { + final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false); + + final Long toCurrentAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final CurrentAccount toCurrentAccount = currentAccountRepository.findById(toCurrentAccountId) + .orElseThrow(() -> new IllegalArgumentException("To current account not found: " + toCurrentAccountId)); + + final JsonElement element = command.parsedJson(); + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.savingsToCurrentTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, + toCurrentAccount, transfertype); + } + + public AccountTransferDetails assembleCurrentToLoanTransfer(final JsonCommand command) { + final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId) + .orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId)); + + final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId); + + final JsonElement element = command.parsedJson(); + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId); + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); + + return AccountTransferDetails.currentToLoanTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient, + toLoanAccount, transfertype); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferDetails.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferDetails.java index ff9b535a2..8a21f84c6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferDetails.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferDetails.java @@ -1,165 +1,216 @@ -/** - * 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.account.domain; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; -import java.util.ArrayList; -import java.util.List; -import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; -import org.apache.fineract.organisation.office.domain.Office; -import org.apache.fineract.portfolio.client.domain.Client; -import org.apache.fineract.portfolio.loanaccount.domain.Loan; -import org.apache.fineract.portfolio.savings.domain.SavingsAccount; - -@Entity -@Table(name = "m_account_transfer_details") -public class AccountTransferDetails extends AbstractPersistableCustom { - - @ManyToOne - @JoinColumn(name = "from_office_id", nullable = false) - private Office fromOffice; - - @ManyToOne - @JoinColumn(name = "from_client_id", nullable = false) - private Client fromClient; - - @ManyToOne - @JoinColumn(name = "from_savings_account_id", nullable = true) - private SavingsAccount fromSavingsAccount; - - @ManyToOne - @JoinColumn(name = "to_office_id", nullable = false) - private Office toOffice; - - @ManyToOne - @JoinColumn(name = "to_client_id", nullable = false) - private Client toClient; - - @ManyToOne - @JoinColumn(name = "to_savings_account_id", nullable = true) - private SavingsAccount toSavingsAccount; - - @ManyToOne - @JoinColumn(name = "to_loan_account_id", nullable = true) - private Loan toLoanAccount; - - @ManyToOne - @JoinColumn(name = "from_loan_account_id", nullable = true) - private Loan fromLoanAccount; - - @Column(name = "transfer_type") - private Integer transferType; - - @OneToMany(cascade = CascadeType.ALL, mappedBy = "accountTransferDetails", orphanRemoval = true, fetch = FetchType.EAGER) - private List accountTransferTransactions = new ArrayList<>(); - - @OneToOne(mappedBy = "accountTransferDetails", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER) - private AccountTransferStandingInstruction accountTransferStandingInstruction; - - public static AccountTransferDetails savingsToSavingsTransfer(final Office fromOffice, final Client fromClient, - final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, - Integer transferType) { - - return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, toSavingsAccount, null, - transferType, null); - } - - public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient, - final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount, - Integer transferType) { - return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, null, toLoanAccount, - transferType, null); - } - - public static AccountTransferDetails loanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount, - final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, Integer transferType) { - return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, toSavingsAccount, null, - transferType, null); - } - - protected AccountTransferDetails() { - // - } - - private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount, - final Loan fromLoanAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, - final Loan toLoanAccount, final Integer transferType, - final AccountTransferStandingInstruction accountTransferStandingInstruction) { - this.fromOffice = fromOffice; - this.fromClient = fromClient; - this.fromSavingsAccount = fromSavingsAccount; - this.fromLoanAccount = fromLoanAccount; - this.toOffice = toOffice; - this.toClient = toClient; - this.toSavingsAccount = toSavingsAccount; - this.toLoanAccount = toLoanAccount; - this.transferType = transferType; - this.accountTransferStandingInstruction = accountTransferStandingInstruction; - } - - public SavingsAccount toSavingsAccount() { - return this.toSavingsAccount; - } - - public SavingsAccount fromSavingsAccount() { - return this.fromSavingsAccount; - } - - public void addAccountTransferTransaction(AccountTransferTransaction accountTransferTransaction) { - this.accountTransferTransactions.add(accountTransferTransaction); - } - - public void updateAccountTransferStandingInstruction(final AccountTransferStandingInstruction accountTransferStandingInstruction) { - this.accountTransferStandingInstruction = accountTransferStandingInstruction; - } - - public Loan toLoanAccount() { - return this.toLoanAccount; - } - - public Loan fromLoanAccount() { - return this.fromLoanAccount; - } - - public AccountTransferStandingInstruction accountTransferStandingInstruction() { - return this.accountTransferStandingInstruction; - } - - public AccountTransferType transferType() { - return AccountTransferType.fromInt(this.transferType); - } - - public static AccountTransferDetails loanToLoanTransfer(Office fromOffice, Client fromClient, Loan fromLoanAccount, Office toOffice, - Client toClient, Loan toLoanAccount, Integer transferType) { - return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, null, toLoanAccount, - transferType, null); - } - - public List getAccountTransferTransactions() { - return accountTransferTransactions; - } -} +/** + * 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.account.domain; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.util.ArrayList; +import java.util.List; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.organisation.office.domain.Office; +import org.apache.fineract.portfolio.client.domain.Client; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; + +@Entity +@Table(name = "m_account_transfer_details") +public class AccountTransferDetails extends AbstractPersistableCustom { + + @ManyToOne + @JoinColumn(name = "from_office_id", nullable = false) + private Office fromOffice; + + @ManyToOne + @JoinColumn(name = "from_client_id", nullable = false) + private Client fromClient; + + @ManyToOne + @JoinColumn(name = "from_savings_account_id", nullable = true) + private SavingsAccount fromSavingsAccount; + + @ManyToOne + @JoinColumn(name = "to_office_id", nullable = false) + private Office toOffice; + + @ManyToOne + @JoinColumn(name = "to_client_id", nullable = false) + private Client toClient; + + @ManyToOne + @JoinColumn(name = "to_savings_account_id", nullable = true) + private SavingsAccount toSavingsAccount; + + @ManyToOne + @JoinColumn(name = "to_loan_account_id", nullable = true) + private Loan toLoanAccount; + + @ManyToOne + @JoinColumn(name = "from_loan_account_id", nullable = true) + private Loan fromLoanAccount; + + @ManyToOne + @JoinColumn(name = "from_current_account_id", nullable = true) + private CurrentAccount fromCurrentAccount; + + @ManyToOne + @JoinColumn(name = "to_current_account_id", nullable = true) + private CurrentAccount toCurrentAccount; + + @Column(name = "transfer_type") + private Integer transferType; + + @OneToMany(cascade = CascadeType.ALL, mappedBy = "accountTransferDetails", orphanRemoval = true, fetch = FetchType.EAGER) + private List accountTransferTransactions = new ArrayList<>(); + + @OneToOne(mappedBy = "accountTransferDetails", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER) + private AccountTransferStandingInstruction accountTransferStandingInstruction; + + public static AccountTransferDetails savingsToSavingsTransfer(final Office fromOffice, final Client fromClient, + final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, + Integer transferType) { + + return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, toSavingsAccount, + null, null, transferType, null); + } + + public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient, + final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount, + Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, null, toLoanAccount, + null, transferType, null); + } + + public static AccountTransferDetails loanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount, + final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, toSavingsAccount, null, + null, transferType, null); + } + + protected AccountTransferDetails() { + // + } + + private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount, + final Loan fromLoanAccount, final CurrentAccount fromCurrentAccount, + final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, + final Loan toLoanAccount, final CurrentAccount toCurrentAccount, + final Integer transferType, + final AccountTransferStandingInstruction accountTransferStandingInstruction) { + this.fromOffice = fromOffice; + this.fromClient = fromClient; + this.fromSavingsAccount = fromSavingsAccount; + this.fromLoanAccount = fromLoanAccount; + this.fromCurrentAccount = fromCurrentAccount; + this.toOffice = toOffice; + this.toClient = toClient; + this.toSavingsAccount = toSavingsAccount; + this.toLoanAccount = toLoanAccount; + this.toCurrentAccount = toCurrentAccount; + this.transferType = transferType; + this.accountTransferStandingInstruction = accountTransferStandingInstruction; + } + + public SavingsAccount toSavingsAccount() { + return this.toSavingsAccount; + } + + public SavingsAccount fromSavingsAccount() { + return this.fromSavingsAccount; + } + + public void addAccountTransferTransaction(AccountTransferTransaction accountTransferTransaction) { + this.accountTransferTransactions.add(accountTransferTransaction); + } + + public void updateAccountTransferStandingInstruction(final AccountTransferStandingInstruction accountTransferStandingInstruction) { + this.accountTransferStandingInstruction = accountTransferStandingInstruction; + } + + public Loan toLoanAccount() { + return this.toLoanAccount; + } + + public Loan fromLoanAccount() { + return this.fromLoanAccount; + } + + public CurrentAccount fromCurrentAccount() { + return this.fromCurrentAccount; + } + + public CurrentAccount toCurrentAccount() { + return this.toCurrentAccount; + } + + public AccountTransferStandingInstruction accountTransferStandingInstruction() { + return this.accountTransferStandingInstruction; + } + + public AccountTransferType transferType() { + return AccountTransferType.fromInt(this.transferType); + } + + public static AccountTransferDetails loanToLoanTransfer(Office fromOffice, Client fromClient, Loan fromLoanAccount, Office toOffice, + Client toClient, Loan toLoanAccount, Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, null, toLoanAccount, + null, transferType, null); + } + + // --- Current account factory methods --- + + public static AccountTransferDetails currentToCurrentTransfer(final Office fromOffice, final Client fromClient, + final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient, + final CurrentAccount toCurrentAccount, Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, null, null, + toCurrentAccount, transferType, null); + } + + public static AccountTransferDetails currentToSavingsTransfer(final Office fromOffice, final Client fromClient, + final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient, + final SavingsAccount toSavingsAccount, Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, toSavingsAccount, + null, null, transferType, null); + } + + public static AccountTransferDetails savingsToCurrentTransfer(final Office fromOffice, final Client fromClient, + final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, + final CurrentAccount toCurrentAccount, Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, null, null, + toCurrentAccount, transferType, null); + } + + public static AccountTransferDetails currentToLoanTransfer(final Office fromOffice, final Client fromClient, + final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient, + final Loan toLoanAccount, Integer transferType) { + return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, null, toLoanAccount, + null, transferType, null); + } + + public List getAccountTransferTransactions() { + return accountTransferTransactions; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferStandingInstruction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferStandingInstruction.java index fb4373087..780264b58 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferStandingInstruction.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/AccountTransferStandingInstruction.java @@ -1,290 +1,302 @@ -/** - * 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.account.domain; - -import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.MonthDay; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.data.ApiParameterError; -import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; -import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; -import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; -import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; - -@Entity -@Table(name = "m_account_transfer_standing_instructions", uniqueConstraints = { - @UniqueConstraint(columnNames = { "name" }, name = "name") }) -public class AccountTransferStandingInstruction extends AbstractPersistableCustom { - - @ManyToOne - @JoinColumn(name = "account_transfer_details_id", nullable = true) - private AccountTransferDetails accountTransferDetails; - - @Column(name = "name") - private String name; - - @Column(name = "priority") - private Integer priority; - - @Column(name = "instruction_type") - private Integer instructionType; - - @Column(name = "status") - private Integer status; - - @Column(name = "amount", scale = 6, precision = 19, nullable = true) - private BigDecimal amount; - - @Column(name = "valid_from") - private LocalDate validFrom; - - @Column(name = "valid_till") - private LocalDate validTill; - - @Column(name = "recurrence_type") - private Integer recurrenceType; - - @Column(name = "recurrence_frequency") - private Integer recurrenceFrequency; - - @Column(name = "recurrence_interval") - private Integer recurrenceInterval; - - @Column(name = "recurrence_on_day") - private Integer recurrenceOnDay; - - @Column(name = "recurrence_on_month") - private Integer recurrenceOnMonth; - - @Column(name = "last_run_date") - private LocalDate latsRunDate; - - protected AccountTransferStandingInstruction() { - - } - - public static AccountTransferStandingInstruction create(final AccountTransferDetails accountTransferDetails, final String name, - final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom, - final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval, - final MonthDay recurrenceOnMonthDay) { - Integer recurrenceOnDay = null; - Integer recurrenceOnMonth = null; - if (recurrenceOnMonthDay != null) { - recurrenceOnDay = recurrenceOnMonthDay.getDayOfMonth(); - recurrenceOnMonth = recurrenceOnMonthDay.getMonthValue(); - } - return new AccountTransferStandingInstruction(accountTransferDetails, name, priority, instructionType, status, amount, validFrom, - validTill, recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnDay, recurrenceOnMonth); - } - - private AccountTransferStandingInstruction(final AccountTransferDetails accountTransferDetails, final String name, - final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom, - final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval, - final Integer recurrenceOnDay, final Integer recurrenceOnMonth) { - this.accountTransferDetails = accountTransferDetails; - this.name = name; - this.priority = priority; - this.instructionType = instructionType; - this.status = status; - this.amount = amount; - this.validFrom = validFrom; - this.validTill = validTill; - this.recurrenceType = recurrenceType; - this.recurrenceFrequency = recurrenceFrequency; - this.recurrenceInterval = recurrenceInterval; - this.recurrenceOnDay = recurrenceOnDay; - this.recurrenceOnMonth = recurrenceOnMonth; - final List dataValidationErrors = new ArrayList<>(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(STANDING_INSTRUCTION_RESOURCE_NAME); - - validateDependencies(baseDataValidator); - if (!dataValidationErrors.isEmpty()) { - throw new PlatformApiDataValidationException(dataValidationErrors); - } - } - - public Map update(JsonCommand command) { - final Map actualChanges = new HashMap<>(); - - final List dataValidationErrors = new ArrayList<>(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(STANDING_INSTRUCTION_RESOURCE_NAME); - - if (StandingInstructionStatus.fromInt(this.status).isDeleted()) { - baseDataValidator.reset().parameter(statusParamName).failWithCode("can.not.modify.once.deleted"); - } - - if (command.isChangeInDateParameterNamed(validFromParamName, this.validFrom)) { - this.validFrom = command.localDateValueOfParameterNamed(validFromParamName); - actualChanges.put(validFromParamName, this.validFrom); - } - - if (command.isChangeInDateParameterNamed(validTillParamName, this.validTill)) { - this.validTill = command.localDateValueOfParameterNamed(validTillParamName); - actualChanges.put(validTillParamName, this.validTill); - } - - if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) { - final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName); - actualChanges.put(amountParamName, newValue); - this.amount = newValue; - } - - if (command.isChangeInIntegerParameterNamed(statusParamName, this.status)) { - final Integer newValue = command.integerValueOfParameterNamed(statusParamName); - actualChanges.put(statusParamName, newValue); - this.status = newValue; - } - - if (command.isChangeInIntegerParameterNamed(priorityParamName, this.priority)) { - final Integer newValue = command.integerValueOfParameterNamed(priorityParamName); - actualChanges.put(priorityParamName, newValue); - this.priority = newValue; - } - - if (command.isChangeInIntegerParameterNamed(instructionTypeParamName, this.instructionType)) { - final Integer newValue = command.integerValueOfParameterNamed(instructionTypeParamName); - actualChanges.put(instructionTypeParamName, newValue); - this.instructionType = newValue; - } - - if (command.isChangeInIntegerParameterNamed(recurrenceTypeParamName, this.recurrenceType)) { - final Integer newValue = command.integerValueOfParameterNamed(recurrenceTypeParamName); - actualChanges.put(recurrenceTypeParamName, newValue); - this.recurrenceType = newValue; - } - - if (command.isChangeInIntegerParameterNamed(recurrenceFrequencyParamName, this.recurrenceFrequency)) { - final Integer newValue = command.integerValueOfParameterNamed(recurrenceFrequencyParamName); - actualChanges.put(recurrenceFrequencyParamName, newValue); - this.recurrenceFrequency = newValue; - } - - if (command.hasParameter(recurrenceOnMonthDayParamName)) { - final MonthDay monthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName); - final String actualValueEntered = command.stringValueOfParameterNamed(recurrenceOnMonthDayParamName); - final Integer dayOfMonthValue = monthDay.getDayOfMonth(); - if (!this.recurrenceOnDay.equals(dayOfMonthValue)) { - actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered); - this.recurrenceOnDay = dayOfMonthValue; - } - - final Integer monthOfYear = monthDay.getMonthValue(); - if (!this.recurrenceOnMonth.equals(monthOfYear)) { - actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered); - this.recurrenceOnMonth = monthOfYear; - } - } - - if (command.isChangeInIntegerParameterNamed(recurrenceIntervalParamName, this.recurrenceInterval)) { - final Integer newValue = command.integerValueOfParameterNamed(recurrenceIntervalParamName); - actualChanges.put(recurrenceIntervalParamName, newValue); - this.recurrenceInterval = newValue; - } - validateDependencies(baseDataValidator); - if (!dataValidationErrors.isEmpty()) { - throw new PlatformApiDataValidationException(dataValidationErrors); - } - return actualChanges; - } - - private void validateDependencies(final DataValidatorBuilder baseDataValidator) { - - if (this.validTill != null && this.validFrom != null) { - baseDataValidator.reset().parameter(validTillParamName).value(this.validTill).validateDateAfter(this.validFrom); - } - - if (AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence()) { - baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(this.recurrenceFrequency).notNull(); - baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(this.recurrenceInterval).notNull(); - if (this.recurrenceFrequency != null) { - PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency); - if (frequencyType.isMonthly()) { - baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull(); - } else if (frequencyType.isYearly()) { - baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull(); - baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnMonth).notNull(); - } - } - } - - if (this.accountTransferDetails.toSavingsAccount() != null) { - baseDataValidator.reset().parameter(instructionTypeParamName).value(this.instructionType).notNull().inMinMaxRange(1, 1); - baseDataValidator.reset().parameter(recurrenceTypeParamName).value(this.recurrenceType).notNull().inMinMaxRange(1, 1); - } - - if (StandingInstructionType.fromInt(this.instructionType).isFixedAmoutTransfer()) { - baseDataValidator.reset().parameter(amountParamName).value(this.amount).notNull(); - } - - String errorCode = null; - if (this.accountTransferDetails.transferType().isAccountTransfer() - && (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toSavingsAccount() == null)) { - errorCode = "not.account.transfer"; - } else if (this.accountTransferDetails.transferType().isLoanRepayment() - && (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toLoanAccount() == null)) { - errorCode = "not.loan.repayment"; - } - if (errorCode != null) { - baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode); - } - - } - - public void updateLatsRunDate(LocalDate latsRunDate) { - this.latsRunDate = latsRunDate; - } - - public void updateStatus(Integer status) { - this.status = status; - } - - /** - * delete the standing instruction by setting the status to 3 and appending "_deleted_" and the id to the name - **/ - public void delete() { - this.status = StandingInstructionStatus.DELETED.getValue(); - this.name = this.name + "_deleted_" + this.getId(); - } -} +/** + * 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.account.domain; + +import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.MonthDay; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.ApiParameterError; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; + +@Entity +@Table(name = "m_account_transfer_standing_instructions", uniqueConstraints = { + @UniqueConstraint(columnNames = { "name" }, name = "name") }) +public class AccountTransferStandingInstruction extends AbstractPersistableCustom { + + @ManyToOne + @JoinColumn(name = "account_transfer_details_id", nullable = true) + private AccountTransferDetails accountTransferDetails; + + @Column(name = "name") + private String name; + + @Column(name = "priority") + private Integer priority; + + @Column(name = "instruction_type") + private Integer instructionType; + + @Column(name = "status") + private Integer status; + + @Column(name = "amount", scale = 6, precision = 19, nullable = true) + private BigDecimal amount; + + @Column(name = "valid_from") + private LocalDate validFrom; + + @Column(name = "valid_till") + private LocalDate validTill; + + @Column(name = "recurrence_type") + private Integer recurrenceType; + + @Column(name = "recurrence_frequency") + private Integer recurrenceFrequency; + + @Column(name = "recurrence_interval") + private Integer recurrenceInterval; + + @Column(name = "recurrence_on_day") + private Integer recurrenceOnDay; + + @Column(name = "recurrence_on_month") + private Integer recurrenceOnMonth; + + @Column(name = "last_run_date") + private LocalDate latsRunDate; + + protected AccountTransferStandingInstruction() { + + } + + public static AccountTransferStandingInstruction create(final AccountTransferDetails accountTransferDetails, final String name, + final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom, + final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval, + final MonthDay recurrenceOnMonthDay) { + Integer recurrenceOnDay = null; + Integer recurrenceOnMonth = null; + if (recurrenceOnMonthDay != null) { + recurrenceOnDay = recurrenceOnMonthDay.getDayOfMonth(); + recurrenceOnMonth = recurrenceOnMonthDay.getMonthValue(); + } + return new AccountTransferStandingInstruction(accountTransferDetails, name, priority, instructionType, status, amount, validFrom, + validTill, recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnDay, recurrenceOnMonth); + } + + private AccountTransferStandingInstruction(final AccountTransferDetails accountTransferDetails, final String name, + final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom, + final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval, + final Integer recurrenceOnDay, final Integer recurrenceOnMonth) { + this.accountTransferDetails = accountTransferDetails; + this.name = name; + this.priority = priority; + this.instructionType = instructionType; + this.status = status; + this.amount = amount; + this.validFrom = validFrom; + this.validTill = validTill; + this.recurrenceType = recurrenceType; + this.recurrenceFrequency = recurrenceFrequency; + this.recurrenceInterval = recurrenceInterval; + this.recurrenceOnDay = recurrenceOnDay; + this.recurrenceOnMonth = recurrenceOnMonth; + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(STANDING_INSTRUCTION_RESOURCE_NAME); + + validateDependencies(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } + + public Map update(JsonCommand command) { + final Map actualChanges = new HashMap<>(); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(STANDING_INSTRUCTION_RESOURCE_NAME); + + if (StandingInstructionStatus.fromInt(this.status).isDeleted()) { + baseDataValidator.reset().parameter(statusParamName).failWithCode("can.not.modify.once.deleted"); + } + + if (command.isChangeInDateParameterNamed(validFromParamName, this.validFrom)) { + this.validFrom = command.localDateValueOfParameterNamed(validFromParamName); + actualChanges.put(validFromParamName, this.validFrom); + } + + if (command.isChangeInDateParameterNamed(validTillParamName, this.validTill)) { + this.validTill = command.localDateValueOfParameterNamed(validTillParamName); + actualChanges.put(validTillParamName, this.validTill); + } + + if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName); + actualChanges.put(amountParamName, newValue); + this.amount = newValue; + } + + if (command.isChangeInIntegerParameterNamed(statusParamName, this.status)) { + final Integer newValue = command.integerValueOfParameterNamed(statusParamName); + actualChanges.put(statusParamName, newValue); + this.status = newValue; + } + + if (command.isChangeInIntegerParameterNamed(priorityParamName, this.priority)) { + final Integer newValue = command.integerValueOfParameterNamed(priorityParamName); + actualChanges.put(priorityParamName, newValue); + this.priority = newValue; + } + + if (command.isChangeInIntegerParameterNamed(instructionTypeParamName, this.instructionType)) { + final Integer newValue = command.integerValueOfParameterNamed(instructionTypeParamName); + actualChanges.put(instructionTypeParamName, newValue); + this.instructionType = newValue; + } + + if (command.isChangeInIntegerParameterNamed(recurrenceTypeParamName, this.recurrenceType)) { + final Integer newValue = command.integerValueOfParameterNamed(recurrenceTypeParamName); + actualChanges.put(recurrenceTypeParamName, newValue); + this.recurrenceType = newValue; + } + + if (command.isChangeInIntegerParameterNamed(recurrenceFrequencyParamName, this.recurrenceFrequency)) { + final Integer newValue = command.integerValueOfParameterNamed(recurrenceFrequencyParamName); + actualChanges.put(recurrenceFrequencyParamName, newValue); + this.recurrenceFrequency = newValue; + } + + if (command.hasParameter(recurrenceOnMonthDayParamName)) { + final MonthDay monthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName); + final String actualValueEntered = command.stringValueOfParameterNamed(recurrenceOnMonthDayParamName); + final Integer dayOfMonthValue = monthDay.getDayOfMonth(); + if (!this.recurrenceOnDay.equals(dayOfMonthValue)) { + actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered); + this.recurrenceOnDay = dayOfMonthValue; + } + + final Integer monthOfYear = monthDay.getMonthValue(); + if (!this.recurrenceOnMonth.equals(monthOfYear)) { + actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered); + this.recurrenceOnMonth = monthOfYear; + } + } + + if (command.isChangeInIntegerParameterNamed(recurrenceIntervalParamName, this.recurrenceInterval)) { + final Integer newValue = command.integerValueOfParameterNamed(recurrenceIntervalParamName); + actualChanges.put(recurrenceIntervalParamName, newValue); + this.recurrenceInterval = newValue; + } + validateDependencies(baseDataValidator); + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException(dataValidationErrors); + } + return actualChanges; + } + + private void validateDependencies(final DataValidatorBuilder baseDataValidator) { + + if (this.validTill != null && this.validFrom != null) { + baseDataValidator.reset().parameter(validTillParamName).value(this.validTill).validateDateAfter(this.validFrom); + } + + if (AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence()) { + baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(this.recurrenceFrequency).notNull(); + baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(this.recurrenceInterval).notNull(); + if (this.recurrenceFrequency != null) { + PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency); + if (frequencyType.isMonthly()) { + baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull(); + } else if (frequencyType.isYearly()) { + baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull(); + baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnMonth).notNull(); + } + } + } + + // For savings-to-savings or current-to-savings: enforce Fixed instruction + Periodic recurrence + if (this.accountTransferDetails.toSavingsAccount() != null + || this.accountTransferDetails.toCurrentAccount() != null) { + baseDataValidator.reset().parameter(instructionTypeParamName).value(this.instructionType).notNull().inMinMaxRange(1, 1); + baseDataValidator.reset().parameter(recurrenceTypeParamName).value(this.recurrenceType).notNull().inMinMaxRange(1, 1); + } + + if (StandingInstructionType.fromInt(this.instructionType).isFixedAmoutTransfer()) { + baseDataValidator.reset().parameter(amountParamName).value(this.amount).notNull(); + } + + // Account Transfer: from must be savings OR current; to must be savings OR current + boolean fromAccountValid = this.accountTransferDetails.fromSavingsAccount() != null + || this.accountTransferDetails.fromCurrentAccount() != null; + boolean toAccountValid = this.accountTransferDetails.toSavingsAccount() != null + || this.accountTransferDetails.toCurrentAccount() != null; + + // Loan Repayment: from must be savings OR current; to must be loan + boolean fromAccountValidForLoan = this.accountTransferDetails.fromSavingsAccount() != null + || this.accountTransferDetails.fromCurrentAccount() != null; + + String errorCode = null; + if (this.accountTransferDetails.transferType().isAccountTransfer() + && !(fromAccountValid && toAccountValid)) { + errorCode = "not.account.transfer"; + } else if (this.accountTransferDetails.transferType().isLoanRepayment() + && !(fromAccountValidForLoan && this.accountTransferDetails.toLoanAccount() != null)) { + errorCode = "not.loan.repayment"; + } + if (errorCode != null) { + baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode); + } + + } + + public void updateLatsRunDate(LocalDate latsRunDate) { + this.latsRunDate = latsRunDate; + } + + public void updateStatus(Integer status) { + this.status = status; + } + + /** + * delete the standing instruction by setting the status to 3 and appending "_deleted_" and the id to the name + **/ + public void delete() { + this.status = StandingInstructionStatus.DELETED.getValue(); + this.name = this.name + "_deleted_" + this.getId(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/StandingInstructionAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/StandingInstructionAssembler.java index 079bda738..f84ca4ee2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/StandingInstructionAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/domain/StandingInstructionAssembler.java @@ -1,93 +1,143 @@ -/** - * 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.account.domain; - -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.nameParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.MonthDay; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.organisation.monetary.domain.Money; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class StandingInstructionAssembler { - - private final AccountTransferDetailAssembler accountTransferDetailAssembler; - - @Autowired - public StandingInstructionAssembler(final AccountTransferDetailAssembler accountTransferDetailAssembler) { - - this.accountTransferDetailAssembler = accountTransferDetailAssembler; - } - - public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) { - final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(command); - assembleStandingInstruction(command, accountTransferDetails); - return accountTransferDetails; - } - - public void assembleStandingInstruction(final JsonCommand command, final AccountTransferDetails accountTransferDetails) { - final LocalDate validFrom = command.localDateValueOfParameterNamed(validFromParamName); - final LocalDate validTill = command.localDateValueOfParameterNamed(validTillParamName); - BigDecimal amount = null; - final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName); - if (transferAmount != null) { - final Money monetaryAmount = Money.of(accountTransferDetails.fromSavingsAccount().getCurrency(), transferAmount); - amount = monetaryAmount.getAmount(); - } - final Integer status = command.integerValueOfParameterNamed(statusParamName); - final Integer priority = command.integerValueOfParameterNamed(priorityParamName); - final Integer standingInstructionType = command.integerValueOfParameterNamed(instructionTypeParamName); - final Integer recurrenceType = command.integerValueOfParameterNamed(recurrenceTypeParamName); - final Integer recurrenceFrequency = command.integerValueOfParameterNamed(recurrenceFrequencyParamName); - final MonthDay recurrenceOnMonthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName); - final Integer recurrenceInterval = command.integerValueOfParameterNamed(recurrenceIntervalParamName); - final String name = command.stringValueOfParameterNamed(nameParamName); - AccountTransferStandingInstruction accountTransferStandingInstruction = AccountTransferStandingInstruction.create( - accountTransferDetails, name, priority, standingInstructionType, status, amount, validFrom, validTill, recurrenceType, - recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay); - accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction); - } - - public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) { - final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command); - assembleStandingInstruction(command, accountTransferDetails); - return accountTransferDetails; - } - - public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) { - final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToSavingsTransfer(command); - assembleStandingInstruction(command, accountTransferDetails); - return accountTransferDetails; - } - -} +/** + * 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.account.domain; + +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.nameParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.MonthDay; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class StandingInstructionAssembler { + + private final AccountTransferDetailAssembler accountTransferDetailAssembler; + + @Autowired + public StandingInstructionAssembler(final AccountTransferDetailAssembler accountTransferDetailAssembler) { + + this.accountTransferDetailAssembler = accountTransferDetailAssembler; + } + + public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + + public void assembleStandingInstruction(final JsonCommand command, final AccountTransferDetails accountTransferDetails) { + final LocalDate validFrom = command.localDateValueOfParameterNamed(validFromParamName); + final LocalDate validTill = command.localDateValueOfParameterNamed(validTillParamName); + BigDecimal amount = null; + final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName); + if (transferAmount != null) { + final MonetaryCurrency currency = resolveCurrency(accountTransferDetails); + if (currency != null) { + final Money monetaryAmount = Money.of(currency, transferAmount); + amount = monetaryAmount.getAmount(); + } else { + amount = transferAmount; + } + } + final Integer status = command.integerValueOfParameterNamed(statusParamName); + final Integer priority = command.integerValueOfParameterNamed(priorityParamName); + final Integer standingInstructionType = command.integerValueOfParameterNamed(instructionTypeParamName); + final Integer recurrenceType = command.integerValueOfParameterNamed(recurrenceTypeParamName); + final Integer recurrenceFrequency = command.integerValueOfParameterNamed(recurrenceFrequencyParamName); + final MonthDay recurrenceOnMonthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName); + final Integer recurrenceInterval = command.integerValueOfParameterNamed(recurrenceIntervalParamName); + final String name = command.stringValueOfParameterNamed(nameParamName); + AccountTransferStandingInstruction accountTransferStandingInstruction = AccountTransferStandingInstruction.create( + accountTransferDetails, name, priority, standingInstructionType, status, amount, validFrom, validTill, recurrenceType, + recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay); + accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction); + } + + /** + * Resolves the currency from the "from" account — savings, loan (null), or current account. + */ + private MonetaryCurrency resolveCurrency(final AccountTransferDetails details) { + final SavingsAccount fromSavings = details.fromSavingsAccount(); + if (fromSavings != null) { + return fromSavings.getCurrency(); + } + final CurrentAccount fromCurrent = details.fromCurrentAccount(); + if (fromCurrent != null) { + return new MonetaryCurrency(fromCurrent.getCurrencyCode(), fromCurrent.getCurrencyDigits(), + fromCurrent.getCurrencyMultiplesOf()); + } + return null; // loan accounts don't have a direct currency on AccountTransferDetails + } + + public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + + public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToSavingsTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + + // --- Current account methods --- + + public AccountTransferDetails assembleCurrentToCurrentTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToCurrentTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + + public AccountTransferDetails assembleCurrentToSavingsTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToSavingsTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + + public AccountTransferDetails assembleSavingsToCurrentTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToCurrentTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + + public AccountTransferDetails assembleCurrentToLoanTransfer(final JsonCommand command) { + final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToLoanTransfer(command); + assembleStandingInstruction(command, accountTransferDetails); + return accountTransferDetails; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/jobs/executestandinginstructions/ExecuteStandingInstructionsTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/jobs/executestandinginstructions/ExecuteStandingInstructionsTasklet.java index 0b4a9e064..352c012e3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/jobs/executestandinginstructions/ExecuteStandingInstructionsTasklet.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/jobs/executestandinginstructions/ExecuteStandingInstructionsTasklet.java @@ -1,172 +1,172 @@ -/** - * 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.account.jobs.executestandinginstructions; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.fineract.infrastructure.core.domain.ExternalId; -import org.apache.fineract.infrastructure.core.exception.AbstractPlatformServiceUnavailableException; -import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; -import org.apache.fineract.infrastructure.core.service.DateUtils; -import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; -import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; -import org.apache.fineract.portfolio.account.data.AccountTransferDTO; -import org.apache.fineract.portfolio.account.data.StandingInstructionData; -import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData; -import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; -import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; -import org.apache.fineract.portfolio.account.domain.StandingInstructionType; -import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService; -import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; -import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; -import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator; -import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator; -import org.apache.fineract.portfolio.savings.domain.SavingsAccount; -import org.apache.fineract.portfolio.savings.exception.InsufficientAccountBalanceException; -import org.springframework.batch.core.StepContribution; -import org.springframework.batch.core.scope.context.ChunkContext; -import org.springframework.batch.core.step.tasklet.Tasklet; -import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.jdbc.core.JdbcTemplate; - -@Slf4j -@RequiredArgsConstructor -public class ExecuteStandingInstructionsTasklet implements Tasklet { - - private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; - private final JdbcTemplate jdbcTemplate; - private final DatabaseSpecificSQLGenerator sqlGenerator; - private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; - - @Override - public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { - Collection instructionData = standingInstructionReadPlatformService - .retrieveAll(StandingInstructionStatus.ACTIVE.getValue()); - List errors = new ArrayList<>(); - for (StandingInstructionData data : instructionData) { - boolean isDueForTransfer = false; - AccountTransferRecurrenceType recurrenceType = data.getRecurrenceType(); - StandingInstructionType instructionType = data.getInstructionType(); - LocalDate transactionDate = DateUtils.getBusinessLocalDate(); - if (recurrenceType.isPeriodicRecurrence()) { - final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); - PeriodFrequencyType frequencyType = data.getRecurrenceFrequency(); - LocalDate startDate = data.getValidFrom(); - if (frequencyType.isMonthly()) { - startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()); - if (DateUtils.isBefore(startDate, data.getValidFrom())) { - startDate = startDate.plusMonths(1); - } - } else if (frequencyType.isYearly()) { - startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()).withMonth(data.getRecurrenceOnMonth()); - if (DateUtils.isBefore(startDate, data.getValidFrom())) { - startDate = startDate.plusYears(1); - } - } - isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.getRecurrenceInterval(), startDate, - transactionDate); - - } - BigDecimal transactionAmount = data.getAmount(); - if (data.getToAccountType().isLoanAccount() - && (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) { - StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService - .retriveLoanDuesData(data.getToAccount().getId()); - if (data.getInstructionType().isDuesAmoutTransfer()) { - transactionAmount = standingInstructionDuesData.totalDueAmount(); - } - if (recurrenceType.isDuesRecurrence()) { - isDueForTransfer = isDueForTransfer(standingInstructionDuesData); - } - } - - if (isDueForTransfer && transactionAmount != null && transactionAmount.compareTo(BigDecimal.ZERO) > 0) { - final SavingsAccount fromSavingsAccount = null; - final boolean isRegularTransaction = true; - final boolean isExceptionForBalanceCheck = false; - AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, - data.getFromAccountType(), data.getToAccountType(), data.getFromAccount().getId(), data.getToAccount().getId(), - data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, - data.getTransferType().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount, - isRegularTransaction, isExceptionForBalanceCheck); - final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId()); - - if (transferCompleted) { - final String updateQuery = "UPDATE m_account_transfer_standing_instructions SET last_run_date = ? where id = ?"; - jdbcTemplate.update(updateQuery, transactionDate, data.getId()); - } - - } - } - if (!errors.isEmpty()) { - throw new JobExecutionException(errors); - } - return RepeatStatus.FINISHED; - } - - private boolean transferAmount(final List errors, final AccountTransferDTO accountTransferDTO, final Long instructionId) { - boolean transferCompleted = true; - StringBuilder errorLog = new StringBuilder(); - StringBuilder updateQuery = new StringBuilder( - "INSERT INTO m_account_transfer_standing_instructions_history (standing_instruction_id, " + sqlGenerator.escape("status") - + ", amount,execution_time, error_log) VALUES ("); - try { - accountTransfersWritePlatformService.transferFunds(accountTransferDTO); - } catch (final PlatformApiDataValidationException e) { - errors.add(new Exception("Validation exception while transfering funds for standing Instruction id" + instructionId + " from " - + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); - errorLog.append("Validation exception while trasfering funds ").append(e.getDefaultUserMessage()); - } catch (final InsufficientAccountBalanceException e) { - errors.add(new Exception("InsufficientAccountBalance Exception while trasfering funds for standing Instruction id" - + instructionId + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); - errorLog.append("InsufficientAccountBalance Exception "); - } catch (final AbstractPlatformServiceUnavailableException e) { - errors.add(new Exception("Platform exception while trasfering funds for standing Instruction id" + instructionId + " from " - + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); - errorLog.append("Platform exception while trasfering funds ").append(e.getDefaultUserMessage()); - } catch (Exception e) { - errors.add(new Exception("Unhandled System Exception while trasfering funds for standing Instruction id" + instructionId - + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); - errorLog.append("Exception while trasfering funds ").append(e.getMessage()); - - } - updateQuery.append(instructionId).append(","); - if (errorLog.length() > 0) { - transferCompleted = false; - updateQuery.append("'failed'").append(","); - } else { - updateQuery.append("'success'").append(","); - } - updateQuery.append(accountTransferDTO.getTransactionAmount().doubleValue()); - updateQuery.append(", now(),"); - updateQuery.append("'").append(errorLog).append("')"); - jdbcTemplate.update(updateQuery.toString()); - return transferCompleted; - } - - public boolean isDueForTransfer(StandingInstructionDuesData standingInstructionDuesData) { - return standingInstructionDuesData.dueDate() != null - && !standingInstructionDuesData.dueDate().isAfter(LocalDate.now(DateUtils.getDateTimeZoneOfTenant())); - } -} +/** + * 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.account.jobs.executestandinginstructions; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.infrastructure.core.domain.ExternalId; +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformServiceUnavailableException; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; +import org.apache.fineract.portfolio.account.data.AccountTransferDTO; +import org.apache.fineract.portfolio.account.data.StandingInstructionData; +import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData; +import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; +import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; +import org.apache.fineract.portfolio.account.domain.StandingInstructionType; +import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService; +import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; +import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; +import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator; +import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.exception.InsufficientAccountBalanceException; +import org.springframework.batch.core.StepContribution; +import org.springframework.batch.core.scope.context.ChunkContext; +import org.springframework.batch.core.step.tasklet.Tasklet; +import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.jdbc.core.JdbcTemplate; + +@Slf4j +@RequiredArgsConstructor +public class ExecuteStandingInstructionsTasklet implements Tasklet { + + private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; + private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; + private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; + + @Override + public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { + Collection instructionData = standingInstructionReadPlatformService + .retrieveAll(StandingInstructionStatus.ACTIVE.getValue()); + List errors = new ArrayList<>(); + for (StandingInstructionData data : instructionData) { + boolean isDueForTransfer = false; + AccountTransferRecurrenceType recurrenceType = data.recurrenceTypeEnum(); + StandingInstructionType instructionType = data.instructionTypeEnum(); + LocalDate transactionDate = DateUtils.getBusinessLocalDate(); + if (recurrenceType.isPeriodicRecurrence()) { + final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); + PeriodFrequencyType frequencyType = data.recurrenceFrequencyEnum(); + LocalDate startDate = data.getValidFrom(); + if (frequencyType.isMonthly()) { + startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()); + if (DateUtils.isBefore(startDate, data.getValidFrom())) { + startDate = startDate.plusMonths(1); + } + } else if (frequencyType.isYearly()) { + startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()).withMonth(data.getRecurrenceOnMonth()); + if (DateUtils.isBefore(startDate, data.getValidFrom())) { + startDate = startDate.plusYears(1); + } + } + isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.getRecurrenceInterval(), startDate, + transactionDate); + + } + BigDecimal transactionAmount = data.getAmount(); + if (data.toAccountTypeEnum() != null && data.toAccountTypeEnum().isLoanAccount() + && (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) { + StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService + .retriveLoanDuesData(data.getToAccount().getId()); + if (data.instructionTypeEnum().isDuesAmoutTransfer()) { + transactionAmount = standingInstructionDuesData.totalDueAmount(); + } + if (recurrenceType.isDuesRecurrence()) { + isDueForTransfer = isDueForTransfer(standingInstructionDuesData); + } + } + + if (isDueForTransfer && transactionAmount != null && transactionAmount.compareTo(BigDecimal.ZERO) > 0) { + final SavingsAccount fromSavingsAccount = null; + final boolean isRegularTransaction = true; + final boolean isExceptionForBalanceCheck = false; + AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, + data.fromAccountTypeEnum(), data.toAccountTypeEnum(), data.getFromAccount().getId(), data.getToAccount().getId(), + data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, + data.transferTypeEnum().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount, + isRegularTransaction, isExceptionForBalanceCheck); + final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId()); + + if (transferCompleted) { + final String updateQuery = "UPDATE m_account_transfer_standing_instructions SET last_run_date = ? where id = ?"; + jdbcTemplate.update(updateQuery, transactionDate, data.getId()); + } + + } + } + if (!errors.isEmpty()) { + throw new JobExecutionException(errors); + } + return RepeatStatus.FINISHED; + } + + private boolean transferAmount(final List errors, final AccountTransferDTO accountTransferDTO, final Long instructionId) { + boolean transferCompleted = true; + StringBuilder errorLog = new StringBuilder(); + StringBuilder updateQuery = new StringBuilder( + "INSERT INTO m_account_transfer_standing_instructions_history (standing_instruction_id, " + sqlGenerator.escape("status") + + ", amount,execution_time, error_log) VALUES ("); + try { + accountTransfersWritePlatformService.transferFunds(accountTransferDTO); + } catch (final PlatformApiDataValidationException e) { + errors.add(new Exception("Validation exception while transfering funds for standing Instruction id" + instructionId + " from " + + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); + errorLog.append("Validation exception while trasfering funds ").append(e.getDefaultUserMessage()); + } catch (final InsufficientAccountBalanceException e) { + errors.add(new Exception("InsufficientAccountBalance Exception while trasfering funds for standing Instruction id" + + instructionId + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); + errorLog.append("InsufficientAccountBalance Exception "); + } catch (final AbstractPlatformServiceUnavailableException e) { + errors.add(new Exception("Platform exception while trasfering funds for standing Instruction id" + instructionId + " from " + + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); + errorLog.append("Platform exception while trasfering funds ").append(e.getDefaultUserMessage()); + } catch (Exception e) { + errors.add(new Exception("Unhandled System Exception while trasfering funds for standing Instruction id" + instructionId + + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); + errorLog.append("Exception while trasfering funds ").append(e.getMessage()); + + } + updateQuery.append(instructionId).append(","); + if (errorLog.length() > 0) { + transferCompleted = false; + updateQuery.append("'failed'").append(","); + } else { + updateQuery.append("'success'").append(","); + } + updateQuery.append(accountTransferDTO.getTransactionAmount().doubleValue()); + updateQuery.append(", now(),"); + updateQuery.append("'").append(errorLog).append("')"); + jdbcTemplate.update(updateQuery.toString()); + return transferCompleted; + } + + public boolean isDueForTransfer(StandingInstructionDuesData standingInstructionDuesData) { + return standingInstructionDuesData.dueDate() != null + && !standingInstructionDuesData.dueDate().isAfter(LocalDate.now(DateUtils.getDateTimeZoneOfTenant())); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransferEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransferEnumerations.java index 991857970..3a6c5c358 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransferEnumerations.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransferEnumerations.java @@ -1,202 +1,206 @@ -/** - * 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.account.service; - -import org.apache.fineract.infrastructure.core.data.EnumOptionData; -import org.apache.fineract.portfolio.account.PortfolioAccountType; -import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; -import org.apache.fineract.portfolio.account.domain.AccountTransferType; -import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority; -import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; -import org.apache.fineract.portfolio.account.domain.StandingInstructionType; - -public final class AccountTransferEnumerations { - - private AccountTransferEnumerations() { - - } - - public static EnumOptionData accountType(final Integer type) { - return accountType(PortfolioAccountType.fromInt(type)); - } - - public static EnumOptionData accountType(final PortfolioAccountType type) { - - EnumOptionData optionData = null; - - if (type != null) { - switch (type) { - case INVALID: - break; - case LOAN: - optionData = new EnumOptionData(PortfolioAccountType.LOAN.getValue().longValue(), PortfolioAccountType.LOAN.getCode(), - "Loan Account"); - break; - case SAVINGS: - optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(), - PortfolioAccountType.SAVINGS.getCode(), "Savings Account"); - break; - } - } - - return optionData; - } - - public static EnumOptionData recurrenceType(final Integer type) { - return recurrenceType(AccountTransferRecurrenceType.fromInt(type)); - } - - public static EnumOptionData recurrenceType(final AccountTransferRecurrenceType type) { - EnumOptionData optionData = null; - if (type != null) { - switch (type) { - case INVALID: - break; - case PERIODIC: - optionData = new EnumOptionData(AccountTransferRecurrenceType.PERIODIC.getValue().longValue(), - AccountTransferRecurrenceType.PERIODIC.getCode(), "Periodic Recurrence"); - break; - case AS_PER_DUES: - optionData = new EnumOptionData(AccountTransferRecurrenceType.AS_PER_DUES.getValue().longValue(), - AccountTransferRecurrenceType.AS_PER_DUES.getCode(), "As Per Dues Recurrence"); - break; - } - } - return optionData; - } - - public static EnumOptionData transferType(final Integer type) { - return transferType(AccountTransferType.fromInt(type)); - } - - public static EnumOptionData transferType(final AccountTransferType type) { - EnumOptionData optionData = null; - if (type != null) { - switch (type) { - case INVALID: - break; - case ACCOUNT_TRANSFER: - optionData = new EnumOptionData(AccountTransferType.ACCOUNT_TRANSFER.getValue().longValue(), - AccountTransferType.ACCOUNT_TRANSFER.getCode(), "Account Transfer"); - break; - case LOAN_REPAYMENT: - optionData = new EnumOptionData(AccountTransferType.LOAN_REPAYMENT.getValue().longValue(), - AccountTransferType.LOAN_REPAYMENT.getCode(), "Loan Repayment"); - break; - case CHARGE_PAYMENT: - optionData = new EnumOptionData(AccountTransferType.CHARGE_PAYMENT.getValue().longValue(), - AccountTransferType.CHARGE_PAYMENT.getCode(), "Charge Payment"); - break; - case INTEREST_TRANSFER: - optionData = new EnumOptionData(AccountTransferType.INTEREST_TRANSFER.getValue().longValue(), - AccountTransferType.INTEREST_TRANSFER.getCode(), "Interest Transfer"); - break; - case LOAN_DOWN_PAYMENT: - optionData = new EnumOptionData(AccountTransferType.LOAN_DOWN_PAYMENT.getValue().longValue(), - AccountTransferType.LOAN_DOWN_PAYMENT.getCode(), "Loan Down Payment"); - break; - - } - } - return optionData; - } - - public static EnumOptionData standingInstructionPriority(final Integer type) { - return standingInstructionPriority(StandingInstructionPriority.fromInt(type)); - } - - public static EnumOptionData standingInstructionPriority(final StandingInstructionPriority type) { - EnumOptionData optionData = null; - if (type != null) { - switch (type) { - case INVALID: - break; - case HIGH: - optionData = new EnumOptionData(StandingInstructionPriority.HIGH.getValue().longValue(), - StandingInstructionPriority.HIGH.getCode(), "High Priority"); - break; - case LOW: - optionData = new EnumOptionData(StandingInstructionPriority.LOW.getValue().longValue(), - StandingInstructionPriority.LOW.getCode(), "Low Priority"); - break; - case MEDIUM: - optionData = new EnumOptionData(StandingInstructionPriority.MEDIUM.getValue().longValue(), - StandingInstructionPriority.MEDIUM.getCode(), "Medium Priority"); - break; - case URGENT: - optionData = new EnumOptionData(StandingInstructionPriority.URGENT.getValue().longValue(), - StandingInstructionPriority.URGENT.getCode(), "Urgent Priority"); - break; - } - } - return optionData; - } - - public static EnumOptionData standingInstructionStatus(final Integer type) { - return standingInstructionStatus(StandingInstructionStatus.fromInt(type)); - } - - public static EnumOptionData standingInstructionStatus(final StandingInstructionStatus type) { - EnumOptionData optionData = null; - if (type != null) { - switch (type) { - case INVALID: - break; - case ACTIVE: - optionData = new EnumOptionData(StandingInstructionStatus.ACTIVE.getValue().longValue(), - StandingInstructionStatus.ACTIVE.getCode(), "Active"); - break; - case DELETED: - optionData = new EnumOptionData(StandingInstructionStatus.DELETED.getValue().longValue(), - StandingInstructionStatus.DELETED.getCode(), "Deleted"); - break; - case DISABLED: - optionData = new EnumOptionData(StandingInstructionStatus.DISABLED.getValue().longValue(), - StandingInstructionStatus.DISABLED.getCode(), "Disabled"); - break; - } - } - return optionData; - } - - public static EnumOptionData standingInstructionType(final Integer type) { - return standingInstructionType(StandingInstructionType.fromInt(type)); - } - - public static EnumOptionData standingInstructionType(final StandingInstructionType type) { - EnumOptionData optionData = null; - if (type != null) { - switch (type) { - case INVALID: - break; - case DUES: - optionData = new EnumOptionData(StandingInstructionType.DUES.getValue().longValue(), - StandingInstructionType.DUES.getCode(), "Dues"); - break; - case FIXED: - optionData = new EnumOptionData(StandingInstructionType.FIXED.getValue().longValue(), - StandingInstructionType.FIXED.getCode(), "Fixed"); - break; - - } - } - return optionData; - } - -} +/** + * 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.account.service; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.portfolio.account.PortfolioAccountType; +import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; +import org.apache.fineract.portfolio.account.domain.AccountTransferType; +import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority; +import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; +import org.apache.fineract.portfolio.account.domain.StandingInstructionType; + +public final class AccountTransferEnumerations { + + private AccountTransferEnumerations() { + + } + + public static EnumOptionData accountType(final Integer type) { + return accountType(PortfolioAccountType.fromInt(type)); + } + + public static EnumOptionData accountType(final PortfolioAccountType type) { + + EnumOptionData optionData = null; + + if (type != null) { + switch (type) { + case INVALID: + break; + case LOAN: + optionData = new EnumOptionData(PortfolioAccountType.LOAN.getValue().longValue(), PortfolioAccountType.LOAN.getCode(), + "Loan Account"); + break; + case SAVINGS: + optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(), + PortfolioAccountType.SAVINGS.getCode(), "Savings Account"); + break; + case CURRENT: + optionData = new EnumOptionData(PortfolioAccountType.CURRENT.getValue().longValue(), + PortfolioAccountType.CURRENT.getCode(), "Current Account"); + break; + } + } + + return optionData; + } + + public static EnumOptionData recurrenceType(final Integer type) { + return recurrenceType(AccountTransferRecurrenceType.fromInt(type)); + } + + public static EnumOptionData recurrenceType(final AccountTransferRecurrenceType type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case PERIODIC: + optionData = new EnumOptionData(AccountTransferRecurrenceType.PERIODIC.getValue().longValue(), + AccountTransferRecurrenceType.PERIODIC.getCode(), "Periodic Recurrence"); + break; + case AS_PER_DUES: + optionData = new EnumOptionData(AccountTransferRecurrenceType.AS_PER_DUES.getValue().longValue(), + AccountTransferRecurrenceType.AS_PER_DUES.getCode(), "As Per Dues Recurrence"); + break; + } + } + return optionData; + } + + public static EnumOptionData transferType(final Integer type) { + return transferType(AccountTransferType.fromInt(type)); + } + + public static EnumOptionData transferType(final AccountTransferType type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case ACCOUNT_TRANSFER: + optionData = new EnumOptionData(AccountTransferType.ACCOUNT_TRANSFER.getValue().longValue(), + AccountTransferType.ACCOUNT_TRANSFER.getCode(), "Account Transfer"); + break; + case LOAN_REPAYMENT: + optionData = new EnumOptionData(AccountTransferType.LOAN_REPAYMENT.getValue().longValue(), + AccountTransferType.LOAN_REPAYMENT.getCode(), "Loan Repayment"); + break; + case CHARGE_PAYMENT: + optionData = new EnumOptionData(AccountTransferType.CHARGE_PAYMENT.getValue().longValue(), + AccountTransferType.CHARGE_PAYMENT.getCode(), "Charge Payment"); + break; + case INTEREST_TRANSFER: + optionData = new EnumOptionData(AccountTransferType.INTEREST_TRANSFER.getValue().longValue(), + AccountTransferType.INTEREST_TRANSFER.getCode(), "Interest Transfer"); + break; + case LOAN_DOWN_PAYMENT: + optionData = new EnumOptionData(AccountTransferType.LOAN_DOWN_PAYMENT.getValue().longValue(), + AccountTransferType.LOAN_DOWN_PAYMENT.getCode(), "Loan Down Payment"); + break; + + } + } + return optionData; + } + + public static EnumOptionData standingInstructionPriority(final Integer type) { + return standingInstructionPriority(StandingInstructionPriority.fromInt(type)); + } + + public static EnumOptionData standingInstructionPriority(final StandingInstructionPriority type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case HIGH: + optionData = new EnumOptionData(StandingInstructionPriority.HIGH.getValue().longValue(), + StandingInstructionPriority.HIGH.getCode(), "High Priority"); + break; + case LOW: + optionData = new EnumOptionData(StandingInstructionPriority.LOW.getValue().longValue(), + StandingInstructionPriority.LOW.getCode(), "Low Priority"); + break; + case MEDIUM: + optionData = new EnumOptionData(StandingInstructionPriority.MEDIUM.getValue().longValue(), + StandingInstructionPriority.MEDIUM.getCode(), "Medium Priority"); + break; + case URGENT: + optionData = new EnumOptionData(StandingInstructionPriority.URGENT.getValue().longValue(), + StandingInstructionPriority.URGENT.getCode(), "Urgent Priority"); + break; + } + } + return optionData; + } + + public static EnumOptionData standingInstructionStatus(final Integer type) { + return standingInstructionStatus(StandingInstructionStatus.fromInt(type)); + } + + public static EnumOptionData standingInstructionStatus(final StandingInstructionStatus type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case ACTIVE: + optionData = new EnumOptionData(StandingInstructionStatus.ACTIVE.getValue().longValue(), + StandingInstructionStatus.ACTIVE.getCode(), "Active"); + break; + case DELETED: + optionData = new EnumOptionData(StandingInstructionStatus.DELETED.getValue().longValue(), + StandingInstructionStatus.DELETED.getCode(), "Deleted"); + break; + case DISABLED: + optionData = new EnumOptionData(StandingInstructionStatus.DISABLED.getValue().longValue(), + StandingInstructionStatus.DISABLED.getCode(), "Disabled"); + break; + } + } + return optionData; + } + + public static EnumOptionData standingInstructionType(final Integer type) { + return standingInstructionType(StandingInstructionType.fromInt(type)); + } + + public static EnumOptionData standingInstructionType(final StandingInstructionType type) { + EnumOptionData optionData = null; + if (type != null) { + switch (type) { + case INVALID: + break; + case DUES: + optionData = new EnumOptionData(StandingInstructionType.DUES.getValue().longValue(), + StandingInstructionType.DUES.getCode(), "Dues"); + break; + case FIXED: + optionData = new EnumOptionData(StandingInstructionType.FIXED.getValue().longValue(), + StandingInstructionType.FIXED.getCode(), "Fixed"); + break; + + } + } + return optionData; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java index 7eea9969e..aaf24bd3d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java @@ -1,561 +1,753 @@ -/** - * 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.account.service; - -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; -import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferAmountParamName; -import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferDateParamName; - -import com.google.common.collect.Lists; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import lombok.RequiredArgsConstructor; -import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.config.FineractProperties; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; -import org.apache.fineract.infrastructure.core.domain.ExternalId; -import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException; -import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; -import org.apache.fineract.portfolio.account.PortfolioAccountType; -import org.apache.fineract.portfolio.account.data.AccountTransferDTO; -import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator; -import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler; -import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; -import org.apache.fineract.portfolio.account.domain.AccountTransferDetails; -import org.apache.fineract.portfolio.account.domain.AccountTransferRepository; -import org.apache.fineract.portfolio.account.domain.AccountTransferTransaction; -import org.apache.fineract.portfolio.account.domain.AccountTransferType; -import org.apache.fineract.portfolio.account.exception.DifferentCurrenciesException; -import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; -import org.apache.fineract.portfolio.loanaccount.domain.Loan; -import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; -import org.apache.fineract.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException; -import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; -import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; -import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; -import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues; -import org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; -import org.apache.fineract.portfolio.savings.domain.GroupSavingsIndividualMonitoring; -import org.apache.fineract.portfolio.savings.domain.SavingsAccount; -import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; -import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction; -import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService; -import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService; -import org.springframework.transaction.annotation.Transactional; - -@RequiredArgsConstructor -public class AccountTransfersWritePlatformServiceImpl implements AccountTransfersWritePlatformService { - - private final AccountTransfersDataValidator accountTransfersDataValidator; - private final AccountTransferAssembler accountTransferAssembler; - private final AccountTransferRepository accountTransferRepository; - private final SavingsAccountAssembler savingsAccountAssembler; - private final SavingsAccountDomainService savingsAccountDomainService; - private final LoanAssembler loanAccountAssembler; - private final LoanAccountDomainService loanAccountDomainService; - private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; - private final AccountTransferDetailRepository accountTransferDetailRepository; - private final LoanReadPlatformService loanReadPlatformService; - private final GSIMRepositoy gsimRepository; - private final ConfigurationDomainService configurationDomainService; - private final ExternalIdFactory externalIdFactory; - private final FineractProperties fineractProperties; - - @Transactional - @Override - public CommandProcessingResult create(final JsonCommand command) { - boolean isRegularTransaction = true; - - this.accountTransfersDataValidator.validate(command); - - final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); - final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); - - final Locale locale = command.extractLocale(); - final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); - - final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName); - final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId); - - final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName); - final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId); - - final PaymentDetail paymentDetail = null; - Long fromSavingsAccountId = null; - Long transferDetailId = null; - boolean isInterestTransfer = false; - boolean isAccountTransfer = true; - Long fromLoanAccountId = null; - boolean isWithdrawBalance = false; - final boolean backdatedTxnsAllowedTill = false; - - if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) { - - fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); - final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, - backdatedTxnsAllowedTill); - - final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, - isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), isInterestTransfer, isWithdrawBalance); - final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, - transactionDate, transactionAmount, paymentDetail, transactionBooleanValues, backdatedTxnsAllowedTill); - - final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); - final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill); - - final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, - transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); - - if (!fromSavingsAccount.getCurrency().getCode().equals(toSavingsAccount.getCurrency().getCode())) { - throw new DifferentCurrenciesException(fromSavingsAccount.getCurrency().getCode(), - toSavingsAccount.getCurrency().getCode()); - } - - final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, - fromSavingsAccount, toSavingsAccount, withdrawal, deposit); - this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); - transferDetailId = accountTransferDetails.getId(); - - } else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) { - // - fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); - final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, - backdatedTxnsAllowedTill); - - final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, - isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), isInterestTransfer, isWithdrawBalance); - final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, - transactionDate, transactionAmount, paymentDetail, transactionBooleanValues, backdatedTxnsAllowedTill); - - final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName); - Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId); - - final Boolean isHolidayValidationDone = false; - final HolidayDetailDTO holidayDetailDto = null; - final boolean isRecoveryRepayment = false; - final String chargeRefundChargeType = null; - - ExternalId externalId = externalIdFactory.create(); - final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, - toLoanAccount, transactionDate, transactionAmount, paymentDetail, null, externalId, isRecoveryRepayment, - chargeRefundChargeType, isAccountTransfer, holidayDetailDto, isHolidayValidationDone); - toLoanAccount = loanRepaymentTransaction.getLoan(); - final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command, - fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction); - this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); - transferDetailId = accountTransferDetails.getId(); - - } else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) { - // FIXME - kw - ADD overpaid loan to savings account transfer - // support. - - fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); - final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId); - ExternalId externalId = externalIdFactory.create(); - final LoanTransaction loanRefundTransaction = this.loanAccountDomainService.makeRefund(fromLoanAccountId, - new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, externalId); - - final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName); - final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill); - - final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, - transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); - - final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(command, - fromLoanAccount, toSavingsAccount, deposit, loanRefundTransaction); - this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); - transferDetailId = accountTransferDetails.getId(); - - } - - final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(transferDetailId); - - if (fromAccountType.isSavingsAccount()) { - builder.withSavingsId(fromSavingsAccountId); - } - if (fromAccountType.isLoanAccount()) { - builder.withLoanId(fromLoanAccountId); - } - - return builder.build(); - } - - @Override - @Transactional - public void reverseTransfersWithFromAccountType(final Long accountNumber, final PortfolioAccountType accountTypeId) { - List accountTransfers = null; - if (accountTypeId.isLoanAccount()) { - accountTransfers = this.accountTransferRepository.findByFromLoanId(accountNumber); - } - if (accountTransfers != null && !accountTransfers.isEmpty()) { - undoTransactions(accountTransfers); - } - - } - - @Override - @Transactional - public void reverseTransfersWithFromAccountTransactions(final Collection fromTransactionIds, - final PortfolioAccountType accountTypeId) { - List accountTransfers = new ArrayList<>(); - if (accountTypeId.isLoanAccount()) { - List> partitions = Lists.partition(fromTransactionIds.stream().toList(), - fineractProperties.getQuery().getInClauseParameterSizeLimit()); - partitions.forEach(partition -> accountTransfers.addAll(this.accountTransferRepository.findByFromLoanTransactions(partition))); - } - if (!accountTransfers.isEmpty()) { - undoTransactions(accountTransfers); - } - } - - @Override - @Transactional - public void reverseAllTransactions(final Long accountId, final PortfolioAccountType accountTypeId) { - List accountTransfers = null; - if (accountTypeId.isLoanAccount()) { - accountTransfers = this.accountTransferRepository.findAllByLoanId(accountId); - } - if (accountTransfers != null && !accountTransfers.isEmpty()) { - undoTransactions(accountTransfers); - } - } - - private void undoTransactions(final List accountTransfers) { - for (final AccountTransferTransaction accountTransfer : accountTransfers) { - if (accountTransfer.getFromLoanTransaction() != null) { - this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction()); - } - if (accountTransfer.getToLoanTransaction() != null) { - this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction()); - } - if (accountTransfer.getFromTransaction() != null) { - this.savingsAccountWritePlatformService.undoTransaction( - accountTransfer.accountTransferDetails().fromSavingsAccount().getId(), accountTransfer.getFromTransaction().getId(), - true); - } - if (accountTransfer.getToSavingsTransaction() != null) { - this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.accountTransferDetails().toSavingsAccount().getId(), - accountTransfer.getToSavingsTransaction().getId(), true); - } - accountTransfer.reverse(); - this.accountTransferRepository.save(accountTransfer); - } - } - - @Override - @Transactional - public Long transferFunds(final AccountTransferDTO accountTransferDTO) { - Long transferTransactionId = null; - final boolean isAccountTransfer = true; - final boolean isRegularTransaction = accountTransferDTO.isRegularTransaction(); - final boolean backdatedTxnsAllowedTill = false; - AccountTransferDetails accountTransferDetails = accountTransferDTO.getAccountTransferDetails(); - if (isSavingsToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { - // - SavingsAccount fromSavingsAccount = null; - Loan toLoanAccount = null; - if (accountTransferDetails == null) { - if (accountTransferDTO.getFromSavingsAccount() == null) { - fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId(), - backdatedTxnsAllowedTill); - } else { - fromSavingsAccount = accountTransferDTO.getFromSavingsAccount(); - this.savingsAccountAssembler.setHelpers(fromSavingsAccount); - } - if (accountTransferDTO.getLoan() == null) { - toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); - } else { - toLoanAccount = accountTransferDTO.getLoan(); - } - - } else { - fromSavingsAccount = accountTransferDetails.fromSavingsAccount(); - this.savingsAccountAssembler.setHelpers(fromSavingsAccount); - toLoanAccount = accountTransferDetails.toLoanAccount(); - } - - final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, - isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), - AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isInterestTransfer(), - accountTransferDTO.isExceptionForBalanceCheck()); - - final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, - accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), transactionBooleanValues, backdatedTxnsAllowedTill); - - LoanTransaction loanTransaction; - - ExternalId txnExternalId = accountTransferDTO.getTxnExternalId(); - // Safety net (it might need to generate new one) - ExternalId externalId = externalIdFactory.create(txnExternalId.getValue()); - - if (AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isChargePayment()) { - loanTransaction = this.loanAccountDomainService.makeChargePayment(toLoanAccount, accountTransferDTO.getChargeId(), - accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), null, externalId, accountTransferDTO.getToTransferType(), - accountTransferDTO.getLoanInstallmentNumber()); - - } else if (AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isLoanDownPayment()) { - final boolean isRecoveryRepayment = false; - final Boolean isHolidayValidationDone = false; - final HolidayDetailDTO holidayDetailDto = null; - final String chargeRefundChargeType = null; - loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.DOWN_PAYMENT, toLoanAccount, - accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), null, externalId, isRecoveryRepayment, chargeRefundChargeType, - isAccountTransfer, holidayDetailDto, isHolidayValidationDone); - toLoanAccount = loanTransaction.getLoan(); - } else { - final boolean isRecoveryRepayment = false; - final Boolean isHolidayValidationDone = false; - final HolidayDetailDTO holidayDetailDto = null; - final String chargeRefundChargeType = null; - loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount, - accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), null, externalId, isRecoveryRepayment, chargeRefundChargeType, - isAccountTransfer, holidayDetailDto, isHolidayValidationDone); - toLoanAccount = loanTransaction.getLoan(); - } - - accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO, fromSavingsAccount, - toLoanAccount, withdrawal, loanTransaction); - this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); - transferTransactionId = accountTransferDetails.getId(); - } else if (isSavingsToSavingsAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { - - SavingsAccount fromSavingsAccount; - SavingsAccount toSavingsAccount; - if (accountTransferDetails == null) { - if (accountTransferDTO.getFromSavingsAccount() == null) { - fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId(), - backdatedTxnsAllowedTill); - } else { - fromSavingsAccount = accountTransferDTO.getFromSavingsAccount(); - this.savingsAccountAssembler.setHelpers(fromSavingsAccount); - } - if (accountTransferDTO.getToSavingsAccount() == null) { - toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId(), false); - } else { - toSavingsAccount = accountTransferDTO.getToSavingsAccount(); - this.savingsAccountAssembler.setHelpers(toSavingsAccount); - } - } else { - fromSavingsAccount = accountTransferDetails.fromSavingsAccount(); - this.savingsAccountAssembler.setHelpers(fromSavingsAccount); - toSavingsAccount = accountTransferDetails.toSavingsAccount(); - this.savingsAccountAssembler.setHelpers(toSavingsAccount); - } - - final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, - isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), - AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isInterestTransfer(), - accountTransferDTO.isExceptionForBalanceCheck()); - - LocalDate transactionDate = accountTransferDTO.getTransactionDate(); - if (configurationDomainService.isSavingsInterestPostingAtCurrentPeriodEnd() - && configurationDomainService.isNextDayFixedDepositInterestTransferEnabledForPeriodEnd() - && AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isInterestTransfer()) { - transactionDate = transactionDate.plusDays(1); - } - - final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, - accountTransferDTO.getFmt(), transactionDate, accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), transactionBooleanValues, backdatedTxnsAllowedTill); - - final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, - accountTransferDTO.getFmt(), transactionDate, accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); - - accountTransferDetails = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(accountTransferDTO, fromSavingsAccount, - toSavingsAccount, withdrawal, deposit); - this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); - transferTransactionId = accountTransferDetails.getId(); - - } else if (isLoanToSavingsAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { - - Loan fromLoanAccount = null; - SavingsAccount toSavingsAccount = null; - if (accountTransferDetails == null) { - if (accountTransferDTO.getLoan() == null) { - fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); - } else { - fromLoanAccount = accountTransferDTO.getLoan(); - } - toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill); - } else { - fromLoanAccount = accountTransferDetails.fromLoanAccount(); - toSavingsAccount = accountTransferDetails.toSavingsAccount(); - this.savingsAccountAssembler.setHelpers(toSavingsAccount); - } - LoanTransaction loanTransaction = null; - - ExternalId txnExternalId = accountTransferDTO.getTxnExternalId(); - // Safety net (it might need to generate new one) - ExternalId externalId = externalIdFactory.create(txnExternalId.getValue()); - - if (LoanTransactionType.DISBURSEMENT.getValue().equals(accountTransferDTO.getFromTransferType())) { - loanTransaction = this.loanAccountDomainService.makeDisburseTransaction(accountTransferDTO.getFromAccountId(), - accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), accountTransferDTO.getNoteText(), externalId); - } else { - loanTransaction = this.loanAccountDomainService.makeRefund(accountTransferDTO.getFromAccountId(), - new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(), - accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), accountTransferDTO.getNoteText(), - externalId); - } - - final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, - accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), - accountTransferDTO.getPaymentDetail(), isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); - accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(accountTransferDTO, fromLoanAccount, - toSavingsAccount, deposit, loanTransaction); - this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); - transferTransactionId = accountTransferDetails.getId(); - - // if the savings account is GSIM, update its parent as well - if (toSavingsAccount.getGsim() != null) { - GroupSavingsIndividualMonitoring gsim = gsimRepository.findById(toSavingsAccount.getGsim().getId()).orElseThrow(); - BigDecimal currentBalance = gsim.getParentDeposit(); - BigDecimal newBalance = currentBalance.add(accountTransferDTO.getTransactionAmount()); - gsim.setParentDeposit(newBalance); - gsimRepository.save(gsim); - } - } else { - throw new GeneralPlatformDomainRuleException("error.msg.accounttransfer.loan.to.loan.not.supported", - "Account transfer from loan to another loan is not supported"); - } - - return transferTransactionId; - } - - @Override - public AccountTransferDetails repayLoanWithTopup(AccountTransferDTO accountTransferDTO) { - final boolean isAccountTransfer = true; - Loan fromLoanAccount = null; - if (accountTransferDTO.getFromLoan() == null) { - fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); - } else { - fromLoanAccount = accountTransferDTO.getFromLoan(); - } - Loan toLoanAccount = null; - if (accountTransferDTO.getToLoan() == null) { - toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); - } else { - toLoanAccount = accountTransferDTO.getToLoan(); - } - - ExternalId externalIdForDisbursement = accountTransferDTO.getTxnExternalId(); - - LoanTransaction disburseTransaction = this.loanAccountDomainService.makeDisburseTransaction(accountTransferDTO.getFromAccountId(), - accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), - accountTransferDTO.getNoteText(), externalIdForDisbursement, true); - final String chargeRefundChargeType = null; - - ExternalId externalIdForRepayment = externalIdFactory.create(); - - LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount, - accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), - null, externalIdForRepayment, false, chargeRefundChargeType, isAccountTransfer, null, false, true); - - AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToLoanTransfer(accountTransferDTO, - fromLoanAccount, toLoanAccount, disburseTransaction, repayTransaction); - this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); - - return accountTransferDetails; - } - - private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { - return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount(); - } - - private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { - return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount(); - } - - private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, - final PortfolioAccountType toAccountType) { - return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount(); - } - - @Override - @Transactional - public CommandProcessingResult refundByTransfer(JsonCommand command) { - // TODO Auto-generated method stub - this.accountTransfersDataValidator.validate(command); - - final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); - final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); - - final Locale locale = command.extractLocale(); - final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); - - final PaymentDetail paymentDetail = null; - Long transferTransactionId = null; - - final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); - final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId); - - BigDecimal overpaid = this.loanReadPlatformService.retrieveTotalPaidInAdvance(fromLoanAccountId).getPaidInAdvance(); - final boolean backdatedTxnsAllowedTill = false; - - if (overpaid == null || overpaid.compareTo(BigDecimal.ZERO) == 0 || transactionAmount.floatValue() > overpaid.floatValue()) { - if (overpaid == null) { - overpaid = BigDecimal.ZERO; - } - throw new InvalidPaidInAdvanceAmountException(overpaid.toPlainString()); - } - - ExternalId externalId = externalIdFactory.create(); - - final LoanTransaction loanRefundTransaction = this.loanAccountDomainService.makeRefundForActiveLoan(fromLoanAccountId, - new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, externalId); - - final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName); - final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill); - - final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, - transactionAmount, paymentDetail, true, true, backdatedTxnsAllowedTill); - - final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(command, - fromLoanAccount, toSavingsAccount, deposit, loanRefundTransaction); - this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); - transferTransactionId = accountTransferDetails.getId(); - - final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(transferTransactionId); - - // if (fromAccountType.isSavingsAccount()) { - - builder.withSavingsId(toSavingsAccountId); - // } - - return builder.build(); - } -} +/** + * 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.account.service; + +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; +import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferAmountParamName; +import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferDateParamName; + +import com.google.common.collect.Lists; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.config.FineractProperties; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.domain.ExternalId; +import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; +import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.portfolio.account.PortfolioAccountType; +import org.apache.fineract.portfolio.account.data.AccountTransferDTO; +import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator; +import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler; +import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; +import org.apache.fineract.portfolio.account.domain.AccountTransferDetails; +import org.apache.fineract.portfolio.account.domain.AccountTransferRepository; +import org.apache.fineract.portfolio.account.domain.AccountTransferTransaction; +import org.apache.fineract.portfolio.account.domain.AccountTransferType; +import org.apache.fineract.portfolio.account.exception.DifferentCurrenciesException; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository; +import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; +import org.apache.fineract.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException; +import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; +import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; +import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; +import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues; +import org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; +import org.apache.fineract.portfolio.savings.domain.GroupSavingsIndividualMonitoring; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction; +import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService; +import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +public class AccountTransfersWritePlatformServiceImpl implements AccountTransfersWritePlatformService { + + private final AccountTransfersDataValidator accountTransfersDataValidator; + private final AccountTransferAssembler accountTransferAssembler; + private final AccountTransferRepository accountTransferRepository; + private final SavingsAccountAssembler savingsAccountAssembler; + private final SavingsAccountDomainService savingsAccountDomainService; + private final LoanAssembler loanAccountAssembler; + private final LoanAccountDomainService loanAccountDomainService; + private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; + private final AccountTransferDetailRepository accountTransferDetailRepository; + private final LoanReadPlatformService loanReadPlatformService; + private final GSIMRepositoy gsimRepository; + private final ConfigurationDomainService configurationDomainService; + private final ExternalIdFactory externalIdFactory; + private final FineractProperties fineractProperties; + private final CurrentAccountRepository currentAccountRepository; + private final CurrentAccountTransactionRepository currentAccountTransactionRepository; + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + boolean isRegularTransaction = true; + + this.accountTransfersDataValidator.validate(command); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); + + final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName); + final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId); + + final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName); + final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId); + + final PaymentDetail paymentDetail = null; + Long fromSavingsAccountId = null; + Long transferDetailId = null; + boolean isInterestTransfer = false; + boolean isAccountTransfer = true; + Long fromLoanAccountId = null; + boolean isWithdrawBalance = false; + final boolean backdatedTxnsAllowedTill = false; + + if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) { + + fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, + backdatedTxnsAllowedTill); + + final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, + isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), isInterestTransfer, isWithdrawBalance); + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, + transactionDate, transactionAmount, paymentDetail, transactionBooleanValues, backdatedTxnsAllowedTill); + + final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, + transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); + + if (!fromSavingsAccount.getCurrency().getCode().equals(toSavingsAccount.getCurrency().getCode())) { + throw new DifferentCurrenciesException(fromSavingsAccount.getCurrency().getCode(), + toSavingsAccount.getCurrency().getCode()); + } + + final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, + fromSavingsAccount, toSavingsAccount, withdrawal, deposit); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferDetailId = accountTransferDetails.getId(); + + } else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) { + // + fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, + backdatedTxnsAllowedTill); + + final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, + isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), isInterestTransfer, isWithdrawBalance); + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, + transactionDate, transactionAmount, paymentDetail, transactionBooleanValues, backdatedTxnsAllowedTill); + + final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId); + + final Boolean isHolidayValidationDone = false; + final HolidayDetailDTO holidayDetailDto = null; + final boolean isRecoveryRepayment = false; + final String chargeRefundChargeType = null; + + ExternalId externalId = externalIdFactory.create(); + final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, + toLoanAccount, transactionDate, transactionAmount, paymentDetail, null, externalId, isRecoveryRepayment, + chargeRefundChargeType, isAccountTransfer, holidayDetailDto, isHolidayValidationDone); + toLoanAccount = loanRepaymentTransaction.getLoan(); + final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command, + fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferDetailId = accountTransferDetails.getId(); + + } else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) { + // FIXME - kw - ADD overpaid loan to savings account transfer + // support. + + fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId); + ExternalId externalId = externalIdFactory.create(); + final LoanTransaction loanRefundTransaction = this.loanAccountDomainService.makeRefund(fromLoanAccountId, + new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, externalId); + + final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, + transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); + + final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(command, + fromLoanAccount, toSavingsAccount, deposit, loanRefundTransaction); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferDetailId = accountTransferDetails.getId(); + + } + + final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(transferDetailId); + + if (fromAccountType.isSavingsAccount()) { + builder.withSavingsId(fromSavingsAccountId); + } + if (fromAccountType.isLoanAccount()) { + builder.withLoanId(fromLoanAccountId); + } + + return builder.build(); + } + + @Override + @Transactional + public void reverseTransfersWithFromAccountType(final Long accountNumber, final PortfolioAccountType accountTypeId) { + List accountTransfers = null; + if (accountTypeId.isLoanAccount()) { + accountTransfers = this.accountTransferRepository.findByFromLoanId(accountNumber); + } + if (accountTransfers != null && !accountTransfers.isEmpty()) { + undoTransactions(accountTransfers); + } + + } + + @Override + @Transactional + public void reverseTransfersWithFromAccountTransactions(final Collection fromTransactionIds, + final PortfolioAccountType accountTypeId) { + List accountTransfers = new ArrayList<>(); + if (accountTypeId.isLoanAccount()) { + List> partitions = Lists.partition(fromTransactionIds.stream().toList(), + fineractProperties.getQuery().getInClauseParameterSizeLimit()); + partitions.forEach(partition -> accountTransfers.addAll(this.accountTransferRepository.findByFromLoanTransactions(partition))); + } + if (!accountTransfers.isEmpty()) { + undoTransactions(accountTransfers); + } + } + + @Override + @Transactional + public void reverseAllTransactions(final Long accountId, final PortfolioAccountType accountTypeId) { + List accountTransfers = null; + if (accountTypeId.isLoanAccount()) { + accountTransfers = this.accountTransferRepository.findAllByLoanId(accountId); + } + if (accountTransfers != null && !accountTransfers.isEmpty()) { + undoTransactions(accountTransfers); + } + } + + private void undoTransactions(final List accountTransfers) { + for (final AccountTransferTransaction accountTransfer : accountTransfers) { + if (accountTransfer.getFromLoanTransaction() != null) { + this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction()); + } + if (accountTransfer.getToLoanTransaction() != null) { + this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction()); + } + if (accountTransfer.getFromTransaction() != null) { + this.savingsAccountWritePlatformService.undoTransaction( + accountTransfer.accountTransferDetails().fromSavingsAccount().getId(), accountTransfer.getFromTransaction().getId(), + true); + } + if (accountTransfer.getToSavingsTransaction() != null) { + this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.accountTransferDetails().toSavingsAccount().getId(), + accountTransfer.getToSavingsTransaction().getId(), true); + } + accountTransfer.reverse(); + this.accountTransferRepository.save(accountTransfer); + } + } + + @Override + @Transactional + public Long transferFunds(final AccountTransferDTO accountTransferDTO) { + Long transferTransactionId = null; + final boolean isAccountTransfer = true; + final boolean isRegularTransaction = accountTransferDTO.isRegularTransaction(); + final boolean backdatedTxnsAllowedTill = false; + AccountTransferDetails accountTransferDetails = accountTransferDTO.getAccountTransferDetails(); + if (isSavingsToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + // + SavingsAccount fromSavingsAccount = null; + Loan toLoanAccount = null; + if (accountTransferDetails == null) { + if (accountTransferDTO.getFromSavingsAccount() == null) { + fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId(), + backdatedTxnsAllowedTill); + } else { + fromSavingsAccount = accountTransferDTO.getFromSavingsAccount(); + this.savingsAccountAssembler.setHelpers(fromSavingsAccount); + } + if (accountTransferDTO.getLoan() == null) { + toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + } else { + toLoanAccount = accountTransferDTO.getLoan(); + } + + } else { + fromSavingsAccount = accountTransferDetails.fromSavingsAccount(); + this.savingsAccountAssembler.setHelpers(fromSavingsAccount); + toLoanAccount = accountTransferDetails.toLoanAccount(); + } + + final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, + isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), + AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isInterestTransfer(), + accountTransferDTO.isExceptionForBalanceCheck()); + + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, + accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), transactionBooleanValues, backdatedTxnsAllowedTill); + + LoanTransaction loanTransaction; + + ExternalId txnExternalId = accountTransferDTO.getTxnExternalId(); + // Safety net (it might need to generate new one) + ExternalId externalId = externalIdFactory.create(txnExternalId.getValue()); + + if (AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isChargePayment()) { + loanTransaction = this.loanAccountDomainService.makeChargePayment(toLoanAccount, accountTransferDTO.getChargeId(), + accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), null, externalId, accountTransferDTO.getToTransferType(), + accountTransferDTO.getLoanInstallmentNumber()); + + } else if (AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isLoanDownPayment()) { + final boolean isRecoveryRepayment = false; + final Boolean isHolidayValidationDone = false; + final HolidayDetailDTO holidayDetailDto = null; + final String chargeRefundChargeType = null; + loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.DOWN_PAYMENT, toLoanAccount, + accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), null, externalId, isRecoveryRepayment, chargeRefundChargeType, + isAccountTransfer, holidayDetailDto, isHolidayValidationDone); + toLoanAccount = loanTransaction.getLoan(); + } else { + final boolean isRecoveryRepayment = false; + final Boolean isHolidayValidationDone = false; + final HolidayDetailDTO holidayDetailDto = null; + final String chargeRefundChargeType = null; + loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount, + accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), null, externalId, isRecoveryRepayment, chargeRefundChargeType, + isAccountTransfer, holidayDetailDto, isHolidayValidationDone); + toLoanAccount = loanTransaction.getLoan(); + } + + accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO, fromSavingsAccount, + toLoanAccount, withdrawal, loanTransaction); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferTransactionId = accountTransferDetails.getId(); + } else if (isSavingsToSavingsAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + + SavingsAccount fromSavingsAccount; + SavingsAccount toSavingsAccount; + if (accountTransferDetails == null) { + if (accountTransferDTO.getFromSavingsAccount() == null) { + fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId(), + backdatedTxnsAllowedTill); + } else { + fromSavingsAccount = accountTransferDTO.getFromSavingsAccount(); + this.savingsAccountAssembler.setHelpers(fromSavingsAccount); + } + if (accountTransferDTO.getToSavingsAccount() == null) { + toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId(), false); + } else { + toSavingsAccount = accountTransferDTO.getToSavingsAccount(); + this.savingsAccountAssembler.setHelpers(toSavingsAccount); + } + } else { + fromSavingsAccount = accountTransferDetails.fromSavingsAccount(); + this.savingsAccountAssembler.setHelpers(fromSavingsAccount); + toSavingsAccount = accountTransferDetails.toSavingsAccount(); + this.savingsAccountAssembler.setHelpers(toSavingsAccount); + } + + final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, + isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), + AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isInterestTransfer(), + accountTransferDTO.isExceptionForBalanceCheck()); + + LocalDate transactionDate = accountTransferDTO.getTransactionDate(); + if (configurationDomainService.isSavingsInterestPostingAtCurrentPeriodEnd() + && configurationDomainService.isNextDayFixedDepositInterestTransferEnabledForPeriodEnd() + && AccountTransferType.fromInt(accountTransferDTO.getTransferType()).isInterestTransfer()) { + transactionDate = transactionDate.plusDays(1); + } + + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, + accountTransferDTO.getFmt(), transactionDate, accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), transactionBooleanValues, backdatedTxnsAllowedTill); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, + accountTransferDTO.getFmt(), transactionDate, accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); + + accountTransferDetails = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(accountTransferDTO, fromSavingsAccount, + toSavingsAccount, withdrawal, deposit); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferTransactionId = accountTransferDetails.getId(); + + } else if (isLoanToSavingsAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + + Loan fromLoanAccount = null; + SavingsAccount toSavingsAccount = null; + if (accountTransferDetails == null) { + if (accountTransferDTO.getLoan() == null) { + fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); + } else { + fromLoanAccount = accountTransferDTO.getLoan(); + } + toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill); + } else { + fromLoanAccount = accountTransferDetails.fromLoanAccount(); + toSavingsAccount = accountTransferDetails.toSavingsAccount(); + this.savingsAccountAssembler.setHelpers(toSavingsAccount); + } + LoanTransaction loanTransaction = null; + + ExternalId txnExternalId = accountTransferDTO.getTxnExternalId(); + // Safety net (it might need to generate new one) + ExternalId externalId = externalIdFactory.create(txnExternalId.getValue()); + + if (LoanTransactionType.DISBURSEMENT.getValue().equals(accountTransferDTO.getFromTransferType())) { + loanTransaction = this.loanAccountDomainService.makeDisburseTransaction(accountTransferDTO.getFromAccountId(), + accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), accountTransferDTO.getNoteText(), externalId); + } else { + loanTransaction = this.loanAccountDomainService.makeRefund(accountTransferDTO.getFromAccountId(), + new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(), + accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), accountTransferDTO.getNoteText(), + externalId); + } + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, + accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); + accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(accountTransferDTO, fromLoanAccount, + toSavingsAccount, deposit, loanTransaction); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferTransactionId = accountTransferDetails.getId(); + + // if the savings account is GSIM, update its parent as well + if (toSavingsAccount.getGsim() != null) { + GroupSavingsIndividualMonitoring gsim = gsimRepository.findById(toSavingsAccount.getGsim().getId()).orElseThrow(); + BigDecimal currentBalance = gsim.getParentDeposit(); + BigDecimal newBalance = currentBalance.add(accountTransferDTO.getTransactionAmount()); + gsim.setParentDeposit(newBalance); + gsimRepository.save(gsim); + } + + } else if (isCurrentToSavingsAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + // --- Current Account → Savings Account --- + final CurrentAccount fromCurrentAccount = currentAccountRepository + .findById(accountTransferDTO.getFromAccountId()) + .orElseThrow(() -> new GeneralPlatformDomainRuleException( + "error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getFromAccountId())); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler + .assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill); + + final CurrentAccountTransaction withdrawal = performCurrentWithdrawal(fromCurrentAccount, + accountTransferDTO.getTransactionAmount(), accountTransferDTO.getTransactionDate()); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, + accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), + accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), + isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill); + + final AccountTransferDetails newDetails = AccountTransferDetails.currentToSavingsTransfer( + fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount, + toSavingsAccount.office(), toSavingsAccount.getClient(), toSavingsAccount, + accountTransferDTO.getTransferType()); + final Money amount = Money.of(toSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount()); + newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer( + newDetails, null, deposit, accountTransferDTO.getTransactionDate(), amount, + accountTransferDTO.getDescription())); + this.accountTransferDetailRepository.saveAndFlush(newDetails); + transferTransactionId = newDetails.getId(); + + } else if (isSavingsToCurrentAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + // --- Savings Account → Current Account --- + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler + .assembleFrom(accountTransferDTO.getFromAccountId(), backdatedTxnsAllowedTill); + final CurrentAccount toCurrentAccount = currentAccountRepository + .findById(accountTransferDTO.getToAccountId()) + .orElseThrow(() -> new GeneralPlatformDomainRuleException( + "error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getToAccountId())); + + final SavingsTransactionBooleanValues txnBooleans = new SavingsTransactionBooleanValues(isAccountTransfer, + isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), false, + accountTransferDTO.isExceptionForBalanceCheck()); + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, + accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), + accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), + txnBooleans, backdatedTxnsAllowedTill); + + performCurrentDeposit(toCurrentAccount, accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getTransactionDate()); + + final AccountTransferDetails newDetails = AccountTransferDetails.savingsToCurrentTransfer( + fromSavingsAccount.office(), fromSavingsAccount.getClient(), fromSavingsAccount, + toCurrentAccount.getOffice(), toCurrentAccount.getClient(), toCurrentAccount, + accountTransferDTO.getTransferType()); + final Money amount = Money.of(fromSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount()); + newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer( + newDetails, withdrawal, null, accountTransferDTO.getTransactionDate(), amount, + accountTransferDTO.getDescription())); + this.accountTransferDetailRepository.saveAndFlush(newDetails); + transferTransactionId = newDetails.getId(); + + } else if (isCurrentToCurrentAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + // --- Current Account → Current Account --- + final CurrentAccount fromCurrentAccount = currentAccountRepository + .findById(accountTransferDTO.getFromAccountId()) + .orElseThrow(() -> new GeneralPlatformDomainRuleException( + "error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getFromAccountId())); + final CurrentAccount toCurrentAccount = currentAccountRepository + .findById(accountTransferDTO.getToAccountId()) + .orElseThrow(() -> new GeneralPlatformDomainRuleException( + "error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getToAccountId())); + + performCurrentWithdrawal(fromCurrentAccount, accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getTransactionDate()); + performCurrentDeposit(toCurrentAccount, accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getTransactionDate()); + + final AccountTransferDetails newDetails = AccountTransferDetails.currentToCurrentTransfer( + fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount, + toCurrentAccount.getOffice(), toCurrentAccount.getClient(), toCurrentAccount, + accountTransferDTO.getTransferType()); + final MonetaryCurrency currency = new MonetaryCurrency(fromCurrentAccount.getCurrencyCode(), + fromCurrentAccount.getCurrencyDigits(), fromCurrentAccount.getCurrencyMultiplesOf()); + final Money amount = Money.of(currency, accountTransferDTO.getTransactionAmount()); + newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer( + newDetails, null, null, accountTransferDTO.getTransactionDate(), amount, + accountTransferDTO.getDescription())); + this.accountTransferDetailRepository.saveAndFlush(newDetails); + transferTransactionId = newDetails.getId(); + + } else if (isCurrentToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { + // --- Current Account → Loan Account --- + final CurrentAccount fromCurrentAccount = currentAccountRepository + .findById(accountTransferDTO.getFromAccountId()) + .orElseThrow(() -> new GeneralPlatformDomainRuleException( + "error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getFromAccountId())); + final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + + performCurrentWithdrawal(fromCurrentAccount, accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getTransactionDate()); + + ExternalId externalId = externalIdFactory.create(accountTransferDTO.getTxnExternalId().getValue()); + final boolean isRecoveryRepayment = false; + final Boolean isHolidayValidationDone = false; + final String chargeRefundChargeType = null; + LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, + toLoanAccount, accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), + accountTransferDTO.getPaymentDetail(), null, externalId, isRecoveryRepayment, + chargeRefundChargeType, isAccountTransfer, null, isHolidayValidationDone); + + final AccountTransferDetails newDetails = AccountTransferDetails.currentToLoanTransfer( + fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount, + toLoanAccount.getOffice(), toLoanAccount.client(), toLoanAccount, + accountTransferDTO.getTransferType()); + final MonetaryCurrency currency = new MonetaryCurrency(fromCurrentAccount.getCurrencyCode(), + fromCurrentAccount.getCurrencyDigits(), fromCurrentAccount.getCurrencyMultiplesOf()); + final Money amount = Money.of(currency, accountTransferDTO.getTransactionAmount()); + newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToLoanTransfer( + newDetails, null, loanTransaction, accountTransferDTO.getTransactionDate(), amount, + accountTransferDTO.getDescription())); + this.accountTransferDetailRepository.saveAndFlush(newDetails); + transferTransactionId = newDetails.getId(); + + } else { + throw new GeneralPlatformDomainRuleException("error.msg.accounttransfer.loan.to.loan.not.supported", + "Account transfer from loan to another loan is not supported"); + } + + return transferTransactionId; + } + + @Override + public AccountTransferDetails repayLoanWithTopup(AccountTransferDTO accountTransferDTO) { + final boolean isAccountTransfer = true; + Loan fromLoanAccount = null; + if (accountTransferDTO.getFromLoan() == null) { + fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); + } else { + fromLoanAccount = accountTransferDTO.getFromLoan(); + } + Loan toLoanAccount = null; + if (accountTransferDTO.getToLoan() == null) { + toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + } else { + toLoanAccount = accountTransferDTO.getToLoan(); + } + + ExternalId externalIdForDisbursement = accountTransferDTO.getTxnExternalId(); + + LoanTransaction disburseTransaction = this.loanAccountDomainService.makeDisburseTransaction(accountTransferDTO.getFromAccountId(), + accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), + accountTransferDTO.getNoteText(), externalIdForDisbursement, true); + final String chargeRefundChargeType = null; + + ExternalId externalIdForRepayment = externalIdFactory.create(); + + LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount, + accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), + null, externalIdForRepayment, false, chargeRefundChargeType, isAccountTransfer, null, false, true); + + AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToLoanTransfer(accountTransferDTO, + fromLoanAccount, toLoanAccount, disburseTransaction, repayTransaction); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + + return accountTransferDetails; + } + + private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount(); + } + + private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount(); + } + + private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount(); + } + + private boolean isCurrentToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isCurrentAccount() && toAccountType.isSavingsAccount(); + } + + private boolean isSavingsToCurrentAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isCurrentAccount(); + } + + private boolean isCurrentToCurrentAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isCurrentAccount() && toAccountType.isCurrentAccount(); + } + + private boolean isCurrentToLoanAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isCurrentAccount() && toAccountType.isLoanAccount(); + } + + /** + * Withdraw amount from a current account and save the transaction. + * Throws {@link GeneralPlatformDomainRuleException} if balance is insufficient. + */ + private CurrentAccountTransaction performCurrentWithdrawal(final CurrentAccount account, final BigDecimal amount, + final LocalDate txnDate) { + final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO; + final BigDecimal onHold = account.getOnHoldFundsDerived() != null ? account.getOnHoldFundsDerived() : BigDecimal.ZERO; + final BigDecimal available = balance.subtract(onHold); + if (!account.isAllowOverdraft() && amount.compareTo(available) > 0) { + throw new GeneralPlatformDomainRuleException("error.msg.current.account.insufficient.balance", + "Insufficient balance in current account " + account.getId() + + ". Available: " + available + ", Requested: " + amount, account.getId()); + } + final CurrentAccountTransaction txn = CurrentAccountTransaction.withdrawal(account, account.getOffice(), amount, txnDate, null); + final BigDecimal newBalance = balance.subtract(amount); + txn.setRunningBalanceDerived(newBalance); + currentAccountTransactionRepository.saveAndFlush(txn); + account.setAccountBalanceDerived(newBalance); + account.setTotalWithdrawalsDerived( + (account.getTotalWithdrawalsDerived() != null ? account.getTotalWithdrawalsDerived() : BigDecimal.ZERO).add(amount)); + currentAccountRepository.save(account); + return txn; + } + + /** + * Deposit amount into a current account and save the transaction. + */ + private CurrentAccountTransaction performCurrentDeposit(final CurrentAccount account, final BigDecimal amount, + final LocalDate txnDate) { + final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO; + final CurrentAccountTransaction txn = CurrentAccountTransaction.deposit(account, account.getOffice(), amount, txnDate, null); + final BigDecimal newBalance = balance.add(amount); + txn.setRunningBalanceDerived(newBalance); + currentAccountTransactionRepository.saveAndFlush(txn); + account.setAccountBalanceDerived(newBalance); + account.setTotalDepositsDerived( + (account.getTotalDepositsDerived() != null ? account.getTotalDepositsDerived() : BigDecimal.ZERO).add(amount)); + currentAccountRepository.save(account); + return txn; + } + + @Override + @Transactional + public CommandProcessingResult refundByTransfer(JsonCommand command) { + // TODO Auto-generated method stub + this.accountTransfersDataValidator.validate(command); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); + + final PaymentDetail paymentDetail = null; + Long transferTransactionId = null; + + final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId); + + BigDecimal overpaid = this.loanReadPlatformService.retrieveTotalPaidInAdvance(fromLoanAccountId).getPaidInAdvance(); + final boolean backdatedTxnsAllowedTill = false; + + if (overpaid == null || overpaid.compareTo(BigDecimal.ZERO) == 0 || transactionAmount.floatValue() > overpaid.floatValue()) { + if (overpaid == null) { + overpaid = BigDecimal.ZERO; + } + throw new InvalidPaidInAdvanceAmountException(overpaid.toPlainString()); + } + + ExternalId externalId = externalIdFactory.create(); + + final LoanTransaction loanRefundTransaction = this.loanAccountDomainService.makeRefundForActiveLoan(fromLoanAccountId, + new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, externalId); + + final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, + transactionAmount, paymentDetail, true, true, backdatedTxnsAllowedTill); + + final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(command, + fromLoanAccount, toSavingsAccount, deposit, loanRefundTransaction); + this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); + transferTransactionId = accountTransferDetails.getId(); + + final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(transferTransactionId); + + // if (fromAccountType.isSavingsAccount()) { + + builder.withSavingsId(toSavingsAccountId); + // } + + return builder.build(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java index 61d5beb0f..0254d4c0c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java @@ -1,396 +1,482 @@ -/** - * 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.account.service; - -import java.math.BigDecimal; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.apache.fineract.infrastructure.core.domain.JdbcSupport; -import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; -import org.apache.fineract.organisation.monetary.data.CurrencyData; -import org.apache.fineract.portfolio.account.PortfolioAccountType; -import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO; -import org.apache.fineract.portfolio.account.data.PortfolioAccountData; -import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; - -public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccountReadPlatformService { - - private final JdbcTemplate jdbcTemplate; - - // mapper - private final PortfolioSavingsAccountMapper savingsAccountMapper; - private final PortfolioLoanAccountMapper loanAccountMapper; - private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper; - - public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) { - this.jdbcTemplate = jdbcTemplate; - this.savingsAccountMapper = new PortfolioSavingsAccountMapper(); - this.loanAccountMapper = new PortfolioLoanAccountMapper(); - this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator); - } - - @Override - public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId) { - return retrieveOne(accountId, accountTypeId, null); - } - - @Override - public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) { - - Object[] sqlParams = new Object[] { accountId }; - PortfolioAccountData accountData = null; - try { - String sql = null; - final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId); - switch (accountType) { - case INVALID: - break; - case LOAN: - - sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?"; - if (currencyCode != null) { - sql += " and la.currency_code = ?"; - sqlParams = new Object[] { accountId, currencyCode }; - } - - accountData = this.jdbcTemplate.queryForObject(sql, this.loanAccountMapper, sqlParams); - break; - case SAVINGS: - sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?"; - if (currencyCode != null) { - sql += " and sa.currency_code = ?"; - sqlParams = new Object[] { accountId, currencyCode }; - } - - accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams); - break; - } - } catch (final EmptyResultDataAccessException e) { - throw new AccountTransferNotFoundException(accountId, e); - } - - return accountData; - } - - @Override - public Collection retrieveAllForLookup(final PortfolioAccountDTO portfolioAccountDTO) { - - final List sqlParams = new ArrayList<>(); - // sqlParams.add(portfolioAccountDTO.getClientId()); - Collection accounts = null; - String sql = null; - long defaultAccountStatus = 300; // Active Status - if (portfolioAccountDTO.getAccountStatus() != null) { - defaultAccountStatus = portfolioAccountDTO.getFirstAccountStatus(); - } - final PortfolioAccountType accountType = PortfolioAccountType.fromInt(portfolioAccountDTO.getAccountTypeId()); - switch (accountType) { - case INVALID: - break; - case LOAN: - sql = "select " + this.loanAccountMapper.schema() + " where "; - if (portfolioAccountDTO.getClientId() != null) { - sql += " la.client_id = ? and la.loan_status_id in (?) "; - sqlParams.add(portfolioAccountDTO.getClientId()); - sqlParams.add(defaultAccountStatus); - } else { - sql += " la.loan_status_id in (?) "; - sqlParams.add(defaultAccountStatus); - } - if (portfolioAccountDTO.getCurrencyCode() != null) { - sql += " and la.currency_code = ?"; - sqlParams.add(portfolioAccountDTO.getCurrencyCode()); - } - - accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams.toArray()); // NOSONAR - break; - case SAVINGS: - sql = "select " + this.savingsAccountMapper.schema() + " where "; - if (portfolioAccountDTO.getClientId() != null) { - sql += " sa.client_id = ? and sa.status_enum in (?) "; - sqlParams.add(portfolioAccountDTO.getClientId()); - sqlParams.add(defaultAccountStatus); - } else { - sql += " sa.status_enum in (?) "; - sqlParams.add(defaultAccountStatus); - } - if (portfolioAccountDTO.getCurrencyCode() != null) { - sql += " and sa.currency_code = ?"; - sqlParams.add(portfolioAccountDTO.getCurrencyCode()); - } - - if (portfolioAccountDTO.getDepositType() != null) { - sql += " and sa.deposit_type_enum = ?"; - sqlParams.add(portfolioAccountDTO.getDepositType().shortValue()); - } - - if (portfolioAccountDTO.isExcludeOverDraftAccounts()) { - sql += " and sa.allow_overdraft = false"; - } - - if (portfolioAccountDTO.getClientId() == null && portfolioAccountDTO.getGroupId() != null) { - sql += " and sa.group_id = ? "; - sqlParams.add(portfolioAccountDTO.getGroupId()); - } - - accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams.toArray()); // NOSONAR - break; - } - - return accounts; - } - - private static final class PortfolioSavingsAccountMapper implements RowMapper { - - private final String schemaSql; - - PortfolioSavingsAccountMapper() { - - final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, "); - sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); - sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); - sqlBuilder.append("sp.id as productId, sp.name as productName, "); - sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); - sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits,"); - sqlBuilder.append("sa.currency_multiplesof as inMultiplesOf, "); - sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); - sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); - sqlBuilder.append("from m_savings_account sa "); - sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id "); - sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); - sqlBuilder.append("left join m_client c ON c.id = sa.client_id "); - sqlBuilder.append("left join m_group g ON g.id = sa.group_id "); - sqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id "); - - this.schemaSql = sqlBuilder.toString(); - } - - public String schema() { - return this.schemaSql; - } - - @Override - public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = rs.getLong("id"); - final String accountNo = rs.getString("accountNo"); - final String externalId = rs.getString("externalId"); - - final Long groupId = JdbcSupport.getLong(rs, "groupId"); - final String groupName = rs.getString("groupName"); - final Long clientId = JdbcSupport.getLong(rs, "clientId"); - final String clientName = rs.getString("clientName"); - - final Long productId = rs.getLong("productId"); - final String productName = rs.getString("productName"); - - final Long fieldOfficerId = rs.getLong("fieldOfficerId"); - final String fieldOfficerName = rs.getString("fieldOfficerName"); - - final String currencyCode = rs.getString("currencyCode"); - final String currencyName = rs.getString("currencyName"); - final String currencyNameCode = rs.getString("currencyNameCode"); - final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); - final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, - currencyNameCode); - - return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, - fieldOfficerId, fieldOfficerName, currency); - } - } - - private static final class PortfolioLoanAccountMapper implements RowMapper { - - private final String schemaSql; - - PortfolioLoanAccountMapper() { - - final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, "); - sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); - sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); - sqlBuilder.append("lp.id as productId, lp.name as productName, "); - sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); - sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,"); - sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, "); - sqlBuilder.append("la.total_overpaid_derived as totalOverpaid, "); - sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); - sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); - sqlBuilder.append("from m_loan la "); - sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id "); - sqlBuilder.append("join m_currency curr on curr.code = la.currency_code "); - sqlBuilder.append("left join m_client c ON c.id = la.client_id "); - sqlBuilder.append("left join m_group g ON g.id = la.group_id "); - sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id "); - - this.schemaSql = sqlBuilder.toString(); - } - - public String schema() { - return this.schemaSql; - } - - @Override - public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = rs.getLong("id"); - final String accountNo = rs.getString("accountNo"); - final String externalId = rs.getString("externalId"); - - final Long groupId = JdbcSupport.getLong(rs, "groupId"); - final String groupName = rs.getString("groupName"); - final Long clientId = JdbcSupport.getLong(rs, "clientId"); - final String clientName = rs.getString("clientName"); - - final Long productId = rs.getLong("productId"); - final String productName = rs.getString("productName"); - - final Long fieldOfficerId = rs.getLong("fieldOfficerId"); - final String fieldOfficerName = rs.getString("fieldOfficerName"); - - final String currencyCode = rs.getString("currencyCode"); - final String currencyName = rs.getString("currencyName"); - final String currencyNameCode = rs.getString("currencyNameCode"); - final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); - final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, - currencyNameCode); - - return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, - fieldOfficerId, fieldOfficerName, currency, amtForTransfer); - } - } - - private static final class PortfolioLoanAccountRefundByTransferMapper implements RowMapper { - - private String schemaSql; - private final DatabaseSpecificSQLGenerator sqlGenerator; - - PortfolioLoanAccountRefundByTransferMapper(DatabaseSpecificSQLGenerator sqlGenerator) { - this.sqlGenerator = sqlGenerator; - } - - public String schema() { - final StringBuilder amountQueryString = new StringBuilder(400); - amountQueryString.append("(select (SUM(COALESCE(mr.principal_completed_derived, 0)) +"); - amountQueryString.append("SUM(COALESCE(mr.interest_completed_derived, 0)) + "); - amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + "); - amountQueryString.append(" SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived"); - amountQueryString.append(" from m_loan ml INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id"); - amountQueryString.append(" where ml.id=? and ml.loan_status_id = 300"); - amountQueryString.append(" and mr.duedate >= " + sqlGenerator.currentBusinessDate() + " group by ml.id having"); - amountQueryString.append(" (SUM(COALESCE(mr.principal_completed_derived, 0)) + "); - amountQueryString.append(" SUM(COALESCE(mr.interest_completed_derived, 0)) + "); - amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + "); - amountQueryString.append("SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) > 0) as totalOverpaid "); - - final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, "); - sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); - sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); - sqlBuilder.append("lp.id as productId, lp.name as productName, "); - sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); - sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,"); - sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, "); - sqlBuilder.append(amountQueryString.toString()); - sqlBuilder.append(", "); - sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); - sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); - sqlBuilder.append("from m_loan la "); - sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id "); - sqlBuilder.append("join m_currency curr on curr.code = la.currency_code "); - sqlBuilder.append("left join m_client c ON c.id = la.client_id "); - sqlBuilder.append("left join m_group g ON g.id = la.group_id "); - sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id "); - - this.schemaSql = sqlBuilder.toString(); - - return this.schemaSql; - } - - @Override - public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = rs.getLong("id"); - final String accountNo = rs.getString("accountNo"); - final String externalId = rs.getString("externalId"); - - final Long groupId = JdbcSupport.getLong(rs, "groupId"); - final String groupName = rs.getString("groupName"); - final Long clientId = JdbcSupport.getLong(rs, "clientId"); - final String clientName = rs.getString("clientName"); - - final Long productId = rs.getLong("productId"); - final String productName = rs.getString("productName"); - - final Long fieldOfficerId = rs.getLong("fieldOfficerId"); - final String fieldOfficerName = rs.getString("fieldOfficerName"); - - final String currencyCode = rs.getString("currencyCode"); - final String currencyName = rs.getString("currencyName"); - final String currencyNameCode = rs.getString("currencyNameCode"); - final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); - final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, - currencyNameCode); - - return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, - fieldOfficerId, fieldOfficerName, currency, amtForTransfer); - } - } - - @Override - public PortfolioAccountData retrieveOneByPaidInAdvance(Long accountId, Integer accountTypeId) { - // TODO Auto-generated method stub - Object[] sqlParams = new Object[] { accountId, accountId }; - PortfolioAccountData accountData = null; - // String currencyCode = null; - try { - String sql = null; - // final PortfolioAccountType accountType = - // PortfolioAccountType.fromInt(accountTypeId); - - sql = "select " + this.accountRefundByTransferMapper.schema() + " where la.id = ?"; - /* - * if (currencyCode != null) { sql += " and la.currency_code = ?"; sqlParams = new Object[] {accountId , - * accountId,currencyCode }; } - */ - - accountData = this.jdbcTemplate.queryForObject(sql, this.accountRefundByTransferMapper, sqlParams); - - } catch (final EmptyResultDataAccessException e) { - throw new AccountTransferNotFoundException(accountId, e); - } - - return accountData; - } -} +/** + * 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.account.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.portfolio.account.PortfolioAccountType; +import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO; +import org.apache.fineract.portfolio.account.data.PortfolioAccountData; +import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; + +public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccountReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + // mapper + private final PortfolioSavingsAccountMapper savingsAccountMapper; + private final PortfolioLoanAccountMapper loanAccountMapper; + private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper; + private final PortfolioCurrentAccountMapper currentAccountMapper; + + public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) { + this.jdbcTemplate = jdbcTemplate; + this.savingsAccountMapper = new PortfolioSavingsAccountMapper(); + this.loanAccountMapper = new PortfolioLoanAccountMapper(); + this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator); + this.currentAccountMapper = new PortfolioCurrentAccountMapper(); + } + + @Override + public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId) { + return retrieveOne(accountId, accountTypeId, null); + } + + @Override + public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) { + + Object[] sqlParams = new Object[] { accountId }; + PortfolioAccountData accountData = null; + try { + String sql = null; + final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId); + switch (accountType) { + case INVALID: + break; + case LOAN: + + sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?"; + if (currencyCode != null) { + sql += " and la.currency_code = ?"; + sqlParams = new Object[] { accountId, currencyCode }; + } + + accountData = this.jdbcTemplate.queryForObject(sql, this.loanAccountMapper, sqlParams); + break; + case SAVINGS: + sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?"; + if (currencyCode != null) { + sql += " and sa.currency_code = ?"; + sqlParams = new Object[] { accountId, currencyCode }; + } + + accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams); + break; + case CURRENT: + sql = "select " + this.currentAccountMapper.schema() + " where ca.id = ?"; + if (currencyCode != null) { + sql += " and ca.currency_code = ?"; + sqlParams = new Object[] { accountId, currencyCode }; + } + + accountData = this.jdbcTemplate.queryForObject(sql, this.currentAccountMapper, sqlParams); + break; + } + } catch (final EmptyResultDataAccessException e) { + throw new AccountTransferNotFoundException(accountId, e); + } + + return accountData; + } + + @Override + public Collection retrieveAllForLookup(final PortfolioAccountDTO portfolioAccountDTO) { + + final List sqlParams = new ArrayList<>(); + // sqlParams.add(portfolioAccountDTO.getClientId()); + Collection accounts = null; + String sql = null; + long defaultAccountStatus = 300; // Active Status + if (portfolioAccountDTO.getAccountStatus() != null) { + defaultAccountStatus = portfolioAccountDTO.getFirstAccountStatus(); + } + final PortfolioAccountType accountType = PortfolioAccountType.fromInt(portfolioAccountDTO.getAccountTypeId()); + switch (accountType) { + case INVALID: + break; + case LOAN: + sql = "select " + this.loanAccountMapper.schema() + " where "; + if (portfolioAccountDTO.getClientId() != null) { + sql += " la.client_id = ? and la.loan_status_id in (?) "; + sqlParams.add(portfolioAccountDTO.getClientId()); + sqlParams.add(defaultAccountStatus); + } else { + sql += " la.loan_status_id in (?) "; + sqlParams.add(defaultAccountStatus); + } + if (portfolioAccountDTO.getCurrencyCode() != null) { + sql += " and la.currency_code = ?"; + sqlParams.add(portfolioAccountDTO.getCurrencyCode()); + } + + accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams.toArray()); // NOSONAR + break; + case SAVINGS: + sql = "select " + this.savingsAccountMapper.schema() + " where "; + if (portfolioAccountDTO.getClientId() != null) { + sql += " sa.client_id = ? and sa.status_enum in (?) "; + sqlParams.add(portfolioAccountDTO.getClientId()); + sqlParams.add(defaultAccountStatus); + } else { + sql += " sa.status_enum in (?) "; + sqlParams.add(defaultAccountStatus); + } + if (portfolioAccountDTO.getCurrencyCode() != null) { + sql += " and sa.currency_code = ?"; + sqlParams.add(portfolioAccountDTO.getCurrencyCode()); + } + + if (portfolioAccountDTO.getDepositType() != null) { + sql += " and sa.deposit_type_enum = ?"; + sqlParams.add(portfolioAccountDTO.getDepositType().shortValue()); + } + + if (portfolioAccountDTO.isExcludeOverDraftAccounts()) { + sql += " and sa.allow_overdraft = false"; + } + + if (portfolioAccountDTO.getClientId() == null && portfolioAccountDTO.getGroupId() != null) { + sql += " and sa.group_id = ? "; + sqlParams.add(portfolioAccountDTO.getGroupId()); + } + + accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams.toArray()); // NOSONAR + break; + case CURRENT: + sql = "select " + this.currentAccountMapper.schema() + " where "; + if (portfolioAccountDTO.getClientId() != null) { + sql += " ca.client_id = ? and ca.status_enum in (?) "; + sqlParams.add(portfolioAccountDTO.getClientId()); + sqlParams.add(defaultAccountStatus); + } else { + sql += " ca.status_enum in (?) "; + sqlParams.add(defaultAccountStatus); + } + if (portfolioAccountDTO.getCurrencyCode() != null) { + sql += " and ca.currency_code = ?"; + sqlParams.add(portfolioAccountDTO.getCurrencyCode()); + } + + accounts = this.jdbcTemplate.query(sql, this.currentAccountMapper, sqlParams.toArray()); // NOSONAR + break; + } + + return accounts; + } + + private static final class PortfolioSavingsAccountMapper implements RowMapper { + + private final String schemaSql; + + PortfolioSavingsAccountMapper() { + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, "); + sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); + sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); + sqlBuilder.append("sp.id as productId, sp.name as productName, "); + sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); + sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits,"); + sqlBuilder.append("sa.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); + sqlBuilder.append("from m_savings_account sa "); + sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id "); + sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); + sqlBuilder.append("left join m_client c ON c.id = sa.client_id "); + sqlBuilder.append("left join m_group g ON g.id = sa.group_id "); + sqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + + final Long groupId = JdbcSupport.getLong(rs, "groupId"); + final String groupName = rs.getString("groupName"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final Long fieldOfficerId = rs.getLong("fieldOfficerId"); + final String fieldOfficerName = rs.getString("fieldOfficerName"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, + currencyNameCode); + + return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, currency); + } + } + + private static final class PortfolioLoanAccountMapper implements RowMapper { + + private final String schemaSql; + + PortfolioLoanAccountMapper() { + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, "); + sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); + sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); + sqlBuilder.append("lp.id as productId, lp.name as productName, "); + sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); + sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,"); + sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("la.total_overpaid_derived as totalOverpaid, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); + sqlBuilder.append("from m_loan la "); + sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id "); + sqlBuilder.append("join m_currency curr on curr.code = la.currency_code "); + sqlBuilder.append("left join m_client c ON c.id = la.client_id "); + sqlBuilder.append("left join m_group g ON g.id = la.group_id "); + sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + + final Long groupId = JdbcSupport.getLong(rs, "groupId"); + final String groupName = rs.getString("groupName"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final Long fieldOfficerId = rs.getLong("fieldOfficerId"); + final String fieldOfficerName = rs.getString("fieldOfficerName"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, + currencyNameCode); + + return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, currency, amtForTransfer); + } + } + + private static final class PortfolioLoanAccountRefundByTransferMapper implements RowMapper { + + private String schemaSql; + private final DatabaseSpecificSQLGenerator sqlGenerator; + + PortfolioLoanAccountRefundByTransferMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + + public String schema() { + final StringBuilder amountQueryString = new StringBuilder(400); + amountQueryString.append("(select (SUM(COALESCE(mr.principal_completed_derived, 0)) +"); + amountQueryString.append("SUM(COALESCE(mr.interest_completed_derived, 0)) + "); + amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + "); + amountQueryString.append(" SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived"); + amountQueryString.append(" from m_loan ml INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id"); + amountQueryString.append(" where ml.id=? and ml.loan_status_id = 300"); + amountQueryString.append(" and mr.duedate >= " + sqlGenerator.currentBusinessDate() + " group by ml.id having"); + amountQueryString.append(" (SUM(COALESCE(mr.principal_completed_derived, 0)) + "); + amountQueryString.append(" SUM(COALESCE(mr.interest_completed_derived, 0)) + "); + amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + "); + amountQueryString.append("SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) > 0) as totalOverpaid "); + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, "); + sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); + sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); + sqlBuilder.append("lp.id as productId, lp.name as productName, "); + sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); + sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,"); + sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append(amountQueryString.toString()); + sqlBuilder.append(", "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); + sqlBuilder.append("from m_loan la "); + sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id "); + sqlBuilder.append("join m_currency curr on curr.code = la.currency_code "); + sqlBuilder.append("left join m_client c ON c.id = la.client_id "); + sqlBuilder.append("left join m_group g ON g.id = la.group_id "); + sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id "); + + this.schemaSql = sqlBuilder.toString(); + + return this.schemaSql; + } + + @Override + public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + + final Long groupId = JdbcSupport.getLong(rs, "groupId"); + final String groupName = rs.getString("groupName"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final Long fieldOfficerId = rs.getLong("fieldOfficerId"); + final String fieldOfficerName = rs.getString("fieldOfficerName"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, + currencyNameCode); + + return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, currency, amtForTransfer); + } + } + + @Override + public PortfolioAccountData retrieveOneByPaidInAdvance(Long accountId, Integer accountTypeId) { + // TODO Auto-generated method stub + Object[] sqlParams = new Object[] { accountId, accountId }; + PortfolioAccountData accountData = null; + // String currencyCode = null; + try { + String sql = null; + // final PortfolioAccountType accountType = + // PortfolioAccountType.fromInt(accountTypeId); + + sql = "select " + this.accountRefundByTransferMapper.schema() + " where la.id = ?"; + /* + * if (currencyCode != null) { sql += " and la.currency_code = ?"; sqlParams = new Object[] {accountId , + * accountId,currencyCode }; } + */ + + accountData = this.jdbcTemplate.queryForObject(sql, this.accountRefundByTransferMapper, sqlParams); + + } catch (final EmptyResultDataAccessException e) { + throw new AccountTransferNotFoundException(accountId, e); + } + + return accountData; + } + + private static final class PortfolioCurrentAccountMapper implements RowMapper { + + private final String schemaSql; + + PortfolioCurrentAccountMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("ca.id as id, ca.account_no as accountNo, ca.external_id as externalId, "); + sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); + sqlBuilder.append("null as groupId, null as groupName, "); + sqlBuilder.append("cp.id as productId, cp.name as productName, "); + sqlBuilder.append("null as fieldOfficerId, null as fieldOfficerName, "); + sqlBuilder.append("ca.currency_code as currencyCode, ca.currency_digits as currencyDigits, "); + sqlBuilder.append("ca.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); + sqlBuilder.append("from m_current_account ca "); + sqlBuilder.append("join m_current_product cp ON ca.product_id = cp.id "); + sqlBuilder.append("join m_currency curr on curr.code = ca.currency_code "); + sqlBuilder.append("left join m_client c ON c.id = ca.client_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + + final Long groupId = JdbcSupport.getLong(rs, "groupId"); + final String groupName = rs.getString("groupName"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final Long fieldOfficerId = JdbcSupport.getLong(rs, "fieldOfficerId"); + final String fieldOfficerName = rs.getString("fieldOfficerName"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, currency); + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java index 7e86160e2..27b2b2627 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java @@ -1,591 +1,625 @@ -/** - * 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.account.service; - -import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.accountType; -import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.recurrenceType; -import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.standingInstructionPriority; -import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.standingInstructionStatus; -import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.standingInstructionType; -import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.transferType; - -import java.math.BigDecimal; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.time.LocalDate; -import java.time.MonthDay; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import org.apache.fineract.infrastructure.core.data.EnumOptionData; -import org.apache.fineract.infrastructure.core.domain.JdbcSupport; -import org.apache.fineract.infrastructure.core.service.DateUtils; -import org.apache.fineract.infrastructure.core.service.Page; -import org.apache.fineract.infrastructure.core.service.PaginationHelper; -import org.apache.fineract.infrastructure.core.service.SearchParameters; -import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; -import org.apache.fineract.infrastructure.security.utils.ColumnValidator; -import org.apache.fineract.organisation.office.data.OfficeData; -import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; -import org.apache.fineract.portfolio.account.PortfolioAccountType; -import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO; -import org.apache.fineract.portfolio.account.data.PortfolioAccountData; -import org.apache.fineract.portfolio.account.data.StandingInstructionDTO; -import org.apache.fineract.portfolio.account.data.StandingInstructionData; -import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData; -import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; -import org.apache.fineract.portfolio.account.domain.AccountTransferType; -import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority; -import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; -import org.apache.fineract.portfolio.account.domain.StandingInstructionType; -import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException; -import org.apache.fineract.portfolio.client.data.ClientData; -import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; -import org.apache.fineract.portfolio.common.service.CommonEnumerations; -import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.util.CollectionUtils; - -public class StandingInstructionReadPlatformServiceImpl implements StandingInstructionReadPlatformService { - - private final JdbcTemplate jdbcTemplate; - private final ColumnValidator columnValidator; - private final ClientReadPlatformService clientReadPlatformService; - private final OfficeReadPlatformService officeReadPlatformService; - private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; - private final DropdownReadPlatformService dropdownReadPlatformService; - private final DatabaseSpecificSQLGenerator sqlGenerator; - - // mapper - private final StandingInstructionMapper standingInstructionMapper; - - // pagination - private final PaginationHelper paginationHelper; - - public StandingInstructionReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, - final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, - final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, - final DropdownReadPlatformService dropdownReadPlatformService, final ColumnValidator columnValidator, - DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { - this.jdbcTemplate = jdbcTemplate; - this.clientReadPlatformService = clientReadPlatformService; - this.officeReadPlatformService = officeReadPlatformService; - this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformService; - this.dropdownReadPlatformService = dropdownReadPlatformService; - this.sqlGenerator = sqlGenerator; - this.standingInstructionMapper = new StandingInstructionMapper(); - this.columnValidator = columnValidator; - this.paginationHelper = paginationHelper; - } - - @Override - public StandingInstructionData retrieveTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId, - final Integer fromAccountType, final Long toOfficeId, final Long toClientId, final Long toAccountId, - final Integer toAccountType, Integer transferType) { - - AccountTransferType accountTransferType = AccountTransferType.INVALID; - if (transferType != null) { - accountTransferType = AccountTransferType.fromInt(transferType); - } - - final EnumOptionData loanAccountType = accountType(PortfolioAccountType.LOAN); - final EnumOptionData savingsAccountType = accountType(PortfolioAccountType.SAVINGS); - - final Integer mostRelevantFromAccountType = fromAccountType; - Collection fromAccountTypeOptions = null; - Collection toAccountTypeOptions = null; - - if (accountTransferType.isAccountTransfer()) { - fromAccountTypeOptions = Arrays.asList(savingsAccountType); - toAccountTypeOptions = Arrays.asList(savingsAccountType); - } else if (accountTransferType.isLoanRepayment()) { - fromAccountTypeOptions = Arrays.asList(savingsAccountType); - toAccountTypeOptions = Arrays.asList(loanAccountType); - } else { - fromAccountTypeOptions = Arrays.asList(savingsAccountType, loanAccountType); - toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType); - } - final Integer mostRelevantToAccountType = toAccountType; - - final EnumOptionData fromAccountTypeData = accountType(mostRelevantFromAccountType); - final EnumOptionData toAccountTypeData = accountType(mostRelevantToAccountType); - - // from settings - OfficeData fromOffice = null; - ClientData fromClient = null; - PortfolioAccountData fromAccount = null; - - OfficeData toOffice = null; - ClientData toClient = null; - PortfolioAccountData toAccount = null; - - // template - Collection fromAccountOptions = null; - Collection toAccountOptions = null; - - Long mostRelevantFromOfficeId = fromOfficeId; - Long mostRelevantFromClientId = fromClientId; - - Long mostRelevantToOfficeId = toOfficeId; - Long mostRelevantToClientId = toClientId; - - if (fromAccountId != null) { - Integer accountType; - if (mostRelevantFromAccountType == 1) { - accountType = PortfolioAccountType.LOAN.getValue(); - } else { - accountType = PortfolioAccountType.SAVINGS.getValue(); - } - fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(fromAccountId, accountType); - - // override provided fromClient with client of account - mostRelevantFromClientId = fromAccount.getClientId(); - } - - if (mostRelevantFromClientId != null) { - fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId); - mostRelevantFromOfficeId = fromClient.getOfficeId(); - long[] loanStatus = null; - if (mostRelevantFromAccountType == 1) { - loanStatus = new long[] { 300, 700 }; - } - PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(mostRelevantFromAccountType, mostRelevantFromClientId, - loanStatus); - fromAccountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(portfolioAccountDTO); - } - - Collection fromOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); - Collection fromClientOptions = null; - - if (mostRelevantFromOfficeId != null) { - fromOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantFromOfficeId); - fromClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId); - } - - // defaults - final LocalDate transferDate = DateUtils.getBusinessLocalDate(); - Collection toOfficeOptions = fromOfficeOptions; - Collection toClientOptions = null; - - if (toAccountId != null && fromAccount != null) { - toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType, - fromAccount.getCurrencyCodeFromCurrency()); - mostRelevantToClientId = toAccount.getClientId(); - } - - if (mostRelevantToClientId != null) { - toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId); - mostRelevantToOfficeId = toClient.getOfficeId(); - - toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); - - toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); - } - - if (mostRelevantToOfficeId != null) { - toOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantToOfficeId); - toOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); - - toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); - if (toClientOptions != null && toClientOptions.size() == 1) { - toClient = new ArrayList<>(toClientOptions).get(0); - - toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); - } - } - - final Collection transferTypeOptions = Arrays.asList(transferType(AccountTransferType.ACCOUNT_TRANSFER), - transferType( - AccountTransferType.LOAN_REPAYMENT)/* - * , transferType( AccountTransferType . CHARGE_PAYMENT ) - */); - final Collection statusOptions = Arrays.asList(standingInstructionStatus(StandingInstructionStatus.ACTIVE), - standingInstructionStatus(StandingInstructionStatus.DISABLED)); - final Collection instructionTypeOptions = Arrays.asList(standingInstructionType(StandingInstructionType.FIXED), - standingInstructionType(StandingInstructionType.DUES)); - final Collection priorityOptions = Arrays.asList(standingInstructionPriority(StandingInstructionPriority.URGENT), - standingInstructionPriority(StandingInstructionPriority.HIGH), - standingInstructionPriority(StandingInstructionPriority.MEDIUM), - standingInstructionPriority(StandingInstructionPriority.LOW)); - final Collection recurrenceTypeOptions = Arrays.asList(recurrenceType(AccountTransferRecurrenceType.PERIODIC), - recurrenceType(AccountTransferRecurrenceType.AS_PER_DUES)); - final Collection recurrenceFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); - - return StandingInstructionData.template(fromOffice, fromClient, fromAccountTypeData, fromAccount, transferDate, toOffice, toClient, - toAccountTypeData, toAccount, fromOfficeOptions, fromClientOptions, fromAccountTypeOptions, fromAccountOptions, - toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions, transferTypeOptions, statusOptions, - instructionTypeOptions, priorityOptions, recurrenceTypeOptions, recurrenceFrequencyOptions); - } - - private Collection retrieveToAccounts(final PortfolioAccountData excludeThisAccountFromOptions, - final Integer toAccountType, final Long toClientId) { - - final String currencyCode = excludeThisAccountFromOptions != null ? excludeThisAccountFromOptions.getCurrencyCodeFromCurrency() - : null; - - PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(toAccountType, toClientId, currencyCode, null, null); - Collection accountOptions = this.portfolioAccountReadPlatformService - .retrieveAllForLookup(portfolioAccountDTO); - if (!CollectionUtils.isEmpty(accountOptions)) { - accountOptions.remove(excludeThisAccountFromOptions); - } else { - accountOptions = null; - } - - return accountOptions; - } - - @Override - public Page retrieveAll(final StandingInstructionDTO standingInstructionDTO) { - - final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); - sqlBuilder.append(this.standingInstructionMapper.schema()); - if (standingInstructionDTO.transferType() != null || standingInstructionDTO.clientId() != null - || standingInstructionDTO.clientName() != null) { - sqlBuilder.append(" where "); - } - boolean addAndCaluse = false; - List paramObj = new ArrayList<>(); - if (standingInstructionDTO.transferType() != null) { - if (addAndCaluse) { - sqlBuilder.append(" and "); - } - sqlBuilder.append(" atd.transfer_type=? "); - paramObj.add(standingInstructionDTO.transferType()); - addAndCaluse = true; - } - if (standingInstructionDTO.clientId() != null) { - if (addAndCaluse) { - sqlBuilder.append(" and "); - } - sqlBuilder.append(" fromclient.id=? "); - paramObj.add(standingInstructionDTO.clientId()); - addAndCaluse = true; - } else if (standingInstructionDTO.clientName() != null) { - if (addAndCaluse) { - sqlBuilder.append(" and "); - } - sqlBuilder.append(" fromclient.display_name=? "); - paramObj.add(standingInstructionDTO.clientName()); - addAndCaluse = true; - } - - if (standingInstructionDTO.fromAccountType() != null && standingInstructionDTO.fromAccount() != null) { - PortfolioAccountType accountType = PortfolioAccountType.fromInt(standingInstructionDTO.fromAccountType()); - if (addAndCaluse) { - sqlBuilder.append(" and "); - } - if (accountType.isSavingsAccount()) { - sqlBuilder.append(" fromsavacc.id=? "); - paramObj.add(standingInstructionDTO.fromAccount()); - } else if (accountType.isLoanAccount()) { - sqlBuilder.append(" fromloanacc.id=? "); - paramObj.add(standingInstructionDTO.fromAccount()); - } - addAndCaluse = true; - } - - final SearchParameters searchParameters = standingInstructionDTO.searchParameters(); - if (searchParameters.hasOrderBy()) { - sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); - this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy()); - if (searchParameters.hasSortOrder()) { - sqlBuilder.append(' ').append(searchParameters.getSortOrder()); - this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder()); - } - } - - if (searchParameters.hasLimit()) { - sqlBuilder.append(" "); - if (searchParameters.hasOffset()) { - sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); - } else { - sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); - } - } - - final Object[] finalObjectArray = paramObj.toArray(); - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.standingInstructionMapper); - } - - @Override - public Collection retrieveAll(final Integer status) { - final StringBuilder sqlBuilder = new StringBuilder(200); - String businessDate = sqlGenerator.currentBusinessDate(); - sqlBuilder.append("select "); - sqlBuilder.append(this.standingInstructionMapper.schema()); - sqlBuilder - .append(" where atsi.status=? and " + businessDate + " >= atsi.valid_from and (atsi.valid_till IS NULL or " + businessDate - + " < atsi.valid_till) ") - .append(" and (atsi.last_run_date <> " + businessDate + " or atsi.last_run_date IS NULL)") - .append(" ORDER BY atsi.priority DESC"); - return this.jdbcTemplate.query(sqlBuilder.toString(), this.standingInstructionMapper, status); - } - - @Override - public StandingInstructionData retrieveOne(final Long instructionId) { - - try { - final String sql = "select " + this.standingInstructionMapper.schema() + " where atsi.id = ?"; - - return this.jdbcTemplate.queryForObject(sql, this.standingInstructionMapper, new Object[] { instructionId }); // NOSONAR - } catch (final EmptyResultDataAccessException e) { - throw new AccountTransferNotFoundException(instructionId, e); - } - } - - @Override - public StandingInstructionDuesData retriveLoanDuesData(final Long loanId) { - final StandingInstructionLoanDuesMapper rm = new StandingInstructionLoanDuesMapper(); - final String sql = "select " + rm.schema() + " where ml.id= ? and ls.duedate <= " + sqlGenerator.currentBusinessDate() - + " and ls.completed_derived <> 1"; - return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId }); // NOSONAR - } - - private static final class StandingInstructionMapper implements RowMapper { - - private final String schemaSql; - - StandingInstructionMapper() { - final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("atsi.id as id,atsi.name as name, atsi.priority as priority,"); - sqlBuilder.append("atsi.status as status, atsi.instruction_type as instructionType,"); - sqlBuilder.append("atsi.amount as amount,"); - sqlBuilder.append("atsi.valid_from as validFrom, atsi.valid_till as validTill,"); - sqlBuilder.append("atsi.recurrence_type as recurrenceType, atsi.recurrence_frequency as recurrenceFrequency,"); - sqlBuilder.append("atsi.recurrence_interval as recurrenceInterval, atsi.recurrence_on_day as recurrenceOnDay,"); - sqlBuilder.append("atsi.recurrence_on_month as recurrenceOnMonth,"); - sqlBuilder.append("atd.id as accountDetailId,atd.transfer_type as transferType,"); - sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); - sqlBuilder.append("tooff.id as toOfficeId, tooff.name as toOfficeName,"); - sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); - sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); - sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); - sqlBuilder.append("fromsp.id as fromProductId, fromsp.name as fromProductName, "); - sqlBuilder.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,"); - sqlBuilder.append("fromlp.id as fromLoanProductId, fromlp.name as fromLoanProductName,"); - sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); - sqlBuilder.append("tosp.id as toProductId, tosp.name as toProductName, "); - sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo, "); - sqlBuilder.append("tolp.id as toLoanProductId, tolp.name as toLoanProductName "); - sqlBuilder.append(" FROM m_account_transfer_standing_instructions atsi "); - sqlBuilder.append("join m_account_transfer_details atd on atd.id = atsi.account_transfer_details_id "); - sqlBuilder.append("join m_office fromoff on fromoff.id = atd.from_office_id "); - sqlBuilder.append("join m_office tooff on tooff.id = atd.to_office_id "); - sqlBuilder.append("join m_client fromclient on fromclient.id = atd.from_client_id "); - sqlBuilder.append("join m_client toclient on toclient.id = atd.to_client_id "); - sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = atd.from_savings_account_id "); - sqlBuilder.append("left join m_savings_product fromsp ON fromsavacc.product_id = fromsp.id "); - sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = atd.from_loan_account_id "); - sqlBuilder.append("left join m_product_loan fromlp ON fromloanacc.product_id = fromlp.id "); - sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = atd.to_savings_account_id "); - sqlBuilder.append("left join m_savings_product tosp ON tosavacc.product_id = tosp.id "); - sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id "); - sqlBuilder.append("left join m_product_loan tolp ON toloanacc.product_id = tolp.id "); - - this.schemaSql = sqlBuilder.toString(); - } - - public String schema() { - return this.schemaSql; - } - - @Override - public StandingInstructionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = rs.getLong("id"); - final Long accountDetailId = rs.getLong("accountDetailId"); - final String name = rs.getString("name"); - final Integer priority = JdbcSupport.getInteger(rs, "priority"); - EnumOptionData priorityEnum = AccountTransferEnumerations.standingInstructionPriority(priority); - - final Integer status = JdbcSupport.getInteger(rs, "status"); - EnumOptionData statusEnum = AccountTransferEnumerations.standingInstructionStatus(status); - final Integer instructionType = JdbcSupport.getInteger(rs, "instructionType"); - EnumOptionData instructionTypeEnum = AccountTransferEnumerations.standingInstructionType(instructionType); - final LocalDate validFrom = JdbcSupport.getLocalDate(rs, "validFrom"); - final LocalDate validTill = JdbcSupport.getLocalDate(rs, "validTill"); - final BigDecimal transferAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount"); - final Integer recurrenceType = JdbcSupport.getInteger(rs, "recurrenceType"); - EnumOptionData recurrenceTypeEnum = AccountTransferEnumerations.recurrenceType(recurrenceType); - final Integer recurrenceFrequency = JdbcSupport.getInteger(rs, "recurrenceFrequency"); - - EnumOptionData recurrenceFrequencyEnum = null; - if (recurrenceFrequency != null) { - recurrenceFrequencyEnum = CommonEnumerations.termFrequencyType(recurrenceFrequency, "recurrence"); - } - final Integer recurrenceInterval = JdbcSupport.getInteger(rs, "recurrenceInterval"); - - MonthDay recurrenceOnMonthDay = null; - final Integer recurrenceOnDay = JdbcSupport.getInteger(rs, "recurrenceOnDay"); - final Integer recurrenceOnMonth = JdbcSupport.getInteger(rs, "recurrenceOnMonth"); - if (recurrenceOnDay != null) { - recurrenceOnMonthDay = MonthDay.now(DateUtils.getDateTimeZoneOfTenant()).withMonth(recurrenceOnMonth) - .withDayOfMonth(recurrenceOnDay); - } - - final Integer transferType = rs.getInt("transferType"); - EnumOptionData transferTypeEnum = AccountTransferEnumerations.transferType(transferType); - - /* - * final String currencyCode = rs.getString("currencyCode"); final String currencyName = - * rs.getString("currencyName"); final String currencyNameCode = rs.getString("currencyNameCode"); final - * String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = - * JdbcSupport.getInteger(rs, "currencyDigits"); final Integer inMultiplesOf = JdbcSupport.getInteger(rs, - * "inMultiplesOf"); final CurrencyData currency = new CurrencyData(currencyCode, currencyName, - * currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); - */ - final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); - final String fromOfficeName = rs.getString("fromOfficeName"); - final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); - - final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); - final String toOfficeName = rs.getString("toOfficeName"); - final OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); - - final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); - final String fromClientName = rs.getString("fromClientName"); - final ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); - - final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); - final String toClientName = rs.getString("toClientName"); - final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); - - final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); - final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); - final Long fromProductId = JdbcSupport.getLong(rs, "fromProductId"); - final String fromProductName = rs.getString("fromProductName"); - final Long fromLoanAccountId = JdbcSupport.getLong(rs, "fromLoanAccountId"); - final String fromLoanAccountNo = rs.getString("fromLoanAccountNo"); - final Long fromLoanProductId = JdbcSupport.getLong(rs, "fromLoanProductId"); - final String fromLoanProductName = rs.getString("fromLoanProductName"); - PortfolioAccountData fromAccount = null; - EnumOptionData fromAccountType = null; - if (fromSavingsAccountId != null) { - fromAccount = new PortfolioAccountData(fromSavingsAccountId, fromSavingsAccountNo, null, null, null, null, null, - fromProductId, fromProductName, null, null, null); - fromAccountType = accountType(PortfolioAccountType.SAVINGS); - } else if (fromLoanAccountId != null) { - fromAccount = new PortfolioAccountData(fromLoanAccountId, fromLoanAccountNo, null, null, null, null, null, - fromLoanProductId, fromLoanProductName, null, null, null); - fromAccountType = accountType(PortfolioAccountType.LOAN); - } - - PortfolioAccountData toAccount = null; - EnumOptionData toAccountType = null; - final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); - final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); - final Long toProductId = JdbcSupport.getLong(rs, "toProductId"); - final String toProductName = rs.getString("toProductName"); - final Long toLoanAccountId = JdbcSupport.getLong(rs, "toLoanAccountId"); - final String toLoanAccountNo = rs.getString("toLoanAccountNo"); - final Long toLoanProductId = JdbcSupport.getLong(rs, "toLoanProductId"); - final String toLoanProductName = rs.getString("toLoanProductName"); - - if (toSavingsAccountId != null) { - toAccount = new PortfolioAccountData(toSavingsAccountId, toSavingsAccountNo, null, null, null, null, null, toProductId, - toProductName, null, null, null); - toAccountType = accountType(PortfolioAccountType.SAVINGS); - } else if (toLoanAccountId != null) { - toAccount = new PortfolioAccountData(toLoanAccountId, toLoanAccountNo, null, null, null, null, null, toLoanProductId, - toLoanProductName, null, null, null); - toAccountType = accountType(PortfolioAccountType.LOAN); - } - - return StandingInstructionData.instance(id, accountDetailId, name, fromOffice, toOffice, fromClient, toClient, fromAccountType, - fromAccount, toAccountType, toAccount, transferTypeEnum, priorityEnum, instructionTypeEnum, statusEnum, transferAmount, - validFrom, validTill, recurrenceTypeEnum, recurrenceFrequencyEnum, recurrenceInterval, recurrenceOnMonthDay); - } - } - - private static final class StandingInstructionLoanDuesMapper implements RowMapper { - - private final String schemaSql; - - StandingInstructionLoanDuesMapper() { - final StringBuilder sqlBuilder = new StringBuilder(400); - - sqlBuilder.append("max(ls.duedate) as dueDate,sum(ls.principal_amount) as principalAmount,"); - sqlBuilder.append("sum(ls.principal_completed_derived) as principalCompleted,"); - sqlBuilder.append("sum(ls.principal_writtenoff_derived) as principalWrittenOff,"); - sqlBuilder.append("sum(ls.interest_amount) as interestAmount,"); - sqlBuilder.append("sum(ls.interest_completed_derived) as interestCompleted,"); - sqlBuilder.append("sum(ls.interest_writtenoff_derived) as interestWrittenOff,"); - sqlBuilder.append("sum(ls.interest_waived_derived) as interestWaived,"); - sqlBuilder.append("sum(ls.penalty_charges_amount) as penalityAmount,"); - sqlBuilder.append("sum(ls.penalty_charges_completed_derived) as penalityCompleted,"); - sqlBuilder.append("sum(ls.penalty_charges_writtenoff_derived)as penaltyWrittenOff,"); - sqlBuilder.append("sum(ls.penalty_charges_waived_derived) as penaltyWaived,"); - sqlBuilder.append("sum(ls.fee_charges_amount) as feeAmount,"); - sqlBuilder.append("sum(ls.fee_charges_completed_derived) as feecompleted,"); - sqlBuilder.append("sum(ls.fee_charges_writtenoff_derived) as feeWrittenOff,"); - sqlBuilder.append("sum(ls.fee_charges_waived_derived) as feeWaived "); - sqlBuilder.append("from m_loan_repayment_schedule ls "); - sqlBuilder.append(" join m_loan ml on ml.id = ls.loan_id "); - - this.schemaSql = sqlBuilder.toString(); - } - - public String schema() { - return this.schemaSql; - } - - @Override - public StandingInstructionDuesData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate"); - final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalAmount"); - final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalCompleted"); - final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff"); - final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff); - - final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestAmount"); - final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestCompleted"); - final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff"); - final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived"); - final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff); - final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid); - - final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalityAmount"); - final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalityCompleted"); - final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyWrittenOff"); - final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyWaived"); - final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived) - .subtract(penaltyChargesWrittenOff); - final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid); - - final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeAmount"); - final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feecompleted"); - final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeWrittenOff"); - final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeWaived"); - final BigDecimal feeChargesActualDue = feeChargesExpectedDue.subtract(feeChargesWaived).subtract(feeChargesWrittenOff); - final BigDecimal feeChargesOutstanding = feeChargesActualDue.subtract(feeChargesPaid); - - final BigDecimal totalOutstanding = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding) - .add(penaltyChargesOutstanding); - - return new StandingInstructionDuesData(dueDate, totalOutstanding); - } - } - -} +/** + * 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.account.service; + +import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.accountType; +import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.recurrenceType; +import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.standingInstructionPriority; +import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.standingInstructionStatus; +import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.standingInstructionType; +import static org.apache.fineract.portfolio.account.service.AccountTransferEnumerations.transferType; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import java.time.MonthDay; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.infrastructure.security.utils.ColumnValidator; +import org.apache.fineract.organisation.office.data.OfficeData; +import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; +import org.apache.fineract.portfolio.account.PortfolioAccountType; +import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO; +import org.apache.fineract.portfolio.account.data.PortfolioAccountData; +import org.apache.fineract.portfolio.account.data.StandingInstructionDTO; +import org.apache.fineract.portfolio.account.data.StandingInstructionData; +import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData; +import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; +import org.apache.fineract.portfolio.account.domain.AccountTransferType; +import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority; +import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; +import org.apache.fineract.portfolio.account.domain.StandingInstructionType; +import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException; +import org.apache.fineract.portfolio.client.data.ClientData; +import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; +import org.apache.fineract.portfolio.common.service.CommonEnumerations; +import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.util.CollectionUtils; + +public class StandingInstructionReadPlatformServiceImpl implements StandingInstructionReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final ColumnValidator columnValidator; + private final ClientReadPlatformService clientReadPlatformService; + private final OfficeReadPlatformService officeReadPlatformService; + private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; + private final DropdownReadPlatformService dropdownReadPlatformService; + private final DatabaseSpecificSQLGenerator sqlGenerator; + + // mapper + private final StandingInstructionMapper standingInstructionMapper; + + // pagination + private final PaginationHelper paginationHelper; + + public StandingInstructionReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, + final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, + final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, + final DropdownReadPlatformService dropdownReadPlatformService, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { + this.jdbcTemplate = jdbcTemplate; + this.clientReadPlatformService = clientReadPlatformService; + this.officeReadPlatformService = officeReadPlatformService; + this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformService; + this.dropdownReadPlatformService = dropdownReadPlatformService; + this.sqlGenerator = sqlGenerator; + this.standingInstructionMapper = new StandingInstructionMapper(); + this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; + } + + @Override + public StandingInstructionData retrieveTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId, + final Integer fromAccountType, final Long toOfficeId, final Long toClientId, final Long toAccountId, + final Integer toAccountType, Integer transferType) { + + AccountTransferType accountTransferType = AccountTransferType.INVALID; + if (transferType != null) { + accountTransferType = AccountTransferType.fromInt(transferType); + } + + final EnumOptionData loanAccountType = accountType(PortfolioAccountType.LOAN); + final EnumOptionData savingsAccountType = accountType(PortfolioAccountType.SAVINGS); + final EnumOptionData currentAccountType = accountType(PortfolioAccountType.CURRENT); + + final Integer mostRelevantFromAccountType = fromAccountType; + Collection fromAccountTypeOptions = null; + Collection toAccountTypeOptions = null; + + if (accountTransferType.isAccountTransfer()) { + fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType); + toAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType); + } else if (accountTransferType.isLoanRepayment()) { + fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType); + toAccountTypeOptions = Arrays.asList(loanAccountType); + } else { + fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType, loanAccountType); + toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType, currentAccountType); + } + final Integer mostRelevantToAccountType = toAccountType; + + final EnumOptionData fromAccountTypeData = accountType(mostRelevantFromAccountType); + final EnumOptionData toAccountTypeData = accountType(mostRelevantToAccountType); + + // from settings + OfficeData fromOffice = null; + ClientData fromClient = null; + PortfolioAccountData fromAccount = null; + + OfficeData toOffice = null; + ClientData toClient = null; + PortfolioAccountData toAccount = null; + + // template + Collection fromAccountOptions = null; + Collection toAccountOptions = null; + + Long mostRelevantFromOfficeId = fromOfficeId; + Long mostRelevantFromClientId = fromClientId; + + Long mostRelevantToOfficeId = toOfficeId; + Long mostRelevantToClientId = toClientId; + + if (fromAccountId != null) { + Integer accountType; + if (mostRelevantFromAccountType == 1) { + accountType = PortfolioAccountType.LOAN.getValue(); + } else if (mostRelevantFromAccountType == 3) { + accountType = PortfolioAccountType.CURRENT.getValue(); + } else { + accountType = PortfolioAccountType.SAVINGS.getValue(); + } + fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(fromAccountId, accountType); + + // override provided fromClient with client of account + mostRelevantFromClientId = fromAccount.getClientId(); + } + + if (mostRelevantFromClientId != null) { + fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId); + mostRelevantFromOfficeId = fromClient.getOfficeId(); + long[] loanStatus = null; + if (mostRelevantFromAccountType == 1) { + loanStatus = new long[] { 300, 700 }; + } + PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(mostRelevantFromAccountType, mostRelevantFromClientId, + loanStatus); + fromAccountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(portfolioAccountDTO); + } + + Collection fromOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); + Collection fromClientOptions = null; + + if (mostRelevantFromOfficeId != null) { + fromOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantFromOfficeId); + fromClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId); + } + + // defaults + final LocalDate transferDate = DateUtils.getBusinessLocalDate(); + Collection toOfficeOptions = fromOfficeOptions; + Collection toClientOptions = null; + + if (toAccountId != null && fromAccount != null) { + toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType, + fromAccount.getCurrencyCodeFromCurrency()); + mostRelevantToClientId = toAccount.getClientId(); + } + + if (mostRelevantToClientId != null) { + toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId); + mostRelevantToOfficeId = toClient.getOfficeId(); + + toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); + + toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); + } + + if (mostRelevantToOfficeId != null) { + toOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantToOfficeId); + toOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); + + toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); + if (toClientOptions != null && toClientOptions.size() == 1) { + toClient = new ArrayList<>(toClientOptions).get(0); + + toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); + } + } + + final Collection transferTypeOptions = Arrays.asList(transferType(AccountTransferType.ACCOUNT_TRANSFER), + transferType( + AccountTransferType.LOAN_REPAYMENT)/* + * , transferType( AccountTransferType . CHARGE_PAYMENT ) + */); + final Collection statusOptions = Arrays.asList(standingInstructionStatus(StandingInstructionStatus.ACTIVE), + standingInstructionStatus(StandingInstructionStatus.DISABLED)); + final Collection instructionTypeOptions = Arrays.asList(standingInstructionType(StandingInstructionType.FIXED), + standingInstructionType(StandingInstructionType.DUES)); + final Collection priorityOptions = Arrays.asList(standingInstructionPriority(StandingInstructionPriority.URGENT), + standingInstructionPriority(StandingInstructionPriority.HIGH), + standingInstructionPriority(StandingInstructionPriority.MEDIUM), + standingInstructionPriority(StandingInstructionPriority.LOW)); + final Collection recurrenceTypeOptions = Arrays.asList(recurrenceType(AccountTransferRecurrenceType.PERIODIC), + recurrenceType(AccountTransferRecurrenceType.AS_PER_DUES)); + final Collection recurrenceFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); + + return StandingInstructionData.template(fromOffice, fromClient, fromAccountTypeData, fromAccount, transferDate, toOffice, toClient, + toAccountTypeData, toAccount, fromOfficeOptions, fromClientOptions, fromAccountTypeOptions, fromAccountOptions, + toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions, transferTypeOptions, statusOptions, + instructionTypeOptions, priorityOptions, recurrenceTypeOptions, recurrenceFrequencyOptions); + } + + private Collection retrieveToAccounts(final PortfolioAccountData excludeThisAccountFromOptions, + final Integer toAccountType, final Long toClientId) { + + final String currencyCode = excludeThisAccountFromOptions != null ? excludeThisAccountFromOptions.getCurrencyCodeFromCurrency() + : null; + + PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(toAccountType, toClientId, currencyCode, null, null); + Collection accountOptions = this.portfolioAccountReadPlatformService + .retrieveAllForLookup(portfolioAccountDTO); + if (!CollectionUtils.isEmpty(accountOptions)) { + accountOptions.remove(excludeThisAccountFromOptions); + } else { + accountOptions = null; + } + + return accountOptions; + } + + @Override + public Page retrieveAll(final StandingInstructionDTO standingInstructionDTO) { + + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); + sqlBuilder.append(this.standingInstructionMapper.schema()); + if (standingInstructionDTO.transferType() != null || standingInstructionDTO.clientId() != null + || standingInstructionDTO.clientName() != null) { + sqlBuilder.append(" where "); + } + boolean addAndCaluse = false; + List paramObj = new ArrayList<>(); + if (standingInstructionDTO.transferType() != null) { + if (addAndCaluse) { + sqlBuilder.append(" and "); + } + sqlBuilder.append(" atd.transfer_type=? "); + paramObj.add(standingInstructionDTO.transferType()); + addAndCaluse = true; + } + if (standingInstructionDTO.clientId() != null) { + if (addAndCaluse) { + sqlBuilder.append(" and "); + } + sqlBuilder.append(" fromclient.id=? "); + paramObj.add(standingInstructionDTO.clientId()); + addAndCaluse = true; + } else if (standingInstructionDTO.clientName() != null) { + if (addAndCaluse) { + sqlBuilder.append(" and "); + } + sqlBuilder.append(" fromclient.display_name=? "); + paramObj.add(standingInstructionDTO.clientName()); + addAndCaluse = true; + } + + if (standingInstructionDTO.fromAccountType() != null && standingInstructionDTO.fromAccount() != null) { + PortfolioAccountType accountType = PortfolioAccountType.fromInt(standingInstructionDTO.fromAccountType()); + if (addAndCaluse) { + sqlBuilder.append(" and "); + } + if (accountType.isSavingsAccount()) { + sqlBuilder.append(" fromsavacc.id=? "); + paramObj.add(standingInstructionDTO.fromAccount()); + } else if (accountType.isLoanAccount()) { + sqlBuilder.append(" fromloanacc.id=? "); + paramObj.add(standingInstructionDTO.fromAccount()); + } else if (accountType.isCurrentAccount()) { + sqlBuilder.append(" fromcuracc.id=? "); + paramObj.add(standingInstructionDTO.fromAccount()); + } + addAndCaluse = true; + } + + final SearchParameters searchParameters = standingInstructionDTO.searchParameters(); + if (searchParameters.hasOrderBy()) { + sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); + this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy()); + if (searchParameters.hasSortOrder()) { + sqlBuilder.append(' ').append(searchParameters.getSortOrder()); + this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder()); + } + } + + if (searchParameters.hasLimit()) { + sqlBuilder.append(" "); + if (searchParameters.hasOffset()) { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); + } + } + + final Object[] finalObjectArray = paramObj.toArray(); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.standingInstructionMapper); + } + + @Override + public Collection retrieveAll(final Integer status) { + final StringBuilder sqlBuilder = new StringBuilder(200); + String businessDate = sqlGenerator.currentBusinessDate(); + sqlBuilder.append("select "); + sqlBuilder.append(this.standingInstructionMapper.schema()); + sqlBuilder + .append(" where atsi.status=? and " + businessDate + " >= atsi.valid_from and (atsi.valid_till IS NULL or " + businessDate + + " < atsi.valid_till) ") + .append(" and (atsi.last_run_date <> " + businessDate + " or atsi.last_run_date IS NULL)") + .append(" ORDER BY atsi.priority DESC"); + return this.jdbcTemplate.query(sqlBuilder.toString(), this.standingInstructionMapper, status); + } + + @Override + public StandingInstructionData retrieveOne(final Long instructionId) { + + try { + final String sql = "select " + this.standingInstructionMapper.schema() + " where atsi.id = ?"; + + return this.jdbcTemplate.queryForObject(sql, this.standingInstructionMapper, new Object[] { instructionId }); // NOSONAR + } catch (final EmptyResultDataAccessException e) { + throw new AccountTransferNotFoundException(instructionId, e); + } + } + + @Override + public StandingInstructionDuesData retriveLoanDuesData(final Long loanId) { + final StandingInstructionLoanDuesMapper rm = new StandingInstructionLoanDuesMapper(); + final String sql = "select " + rm.schema() + " where ml.id= ? and ls.duedate <= " + sqlGenerator.currentBusinessDate() + + " and ls.completed_derived <> 1"; + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId }); // NOSONAR + } + + private static final class StandingInstructionMapper implements RowMapper { + + private final String schemaSql; + + StandingInstructionMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("atsi.id as id,atsi.name as name, atsi.priority as priority,"); + sqlBuilder.append("atsi.status as status, atsi.instruction_type as instructionType,"); + sqlBuilder.append("atsi.amount as amount,"); + sqlBuilder.append("atsi.valid_from as validFrom, atsi.valid_till as validTill,"); + sqlBuilder.append("atsi.recurrence_type as recurrenceType, atsi.recurrence_frequency as recurrenceFrequency,"); + sqlBuilder.append("atsi.recurrence_interval as recurrenceInterval, atsi.recurrence_on_day as recurrenceOnDay,"); + sqlBuilder.append("atsi.recurrence_on_month as recurrenceOnMonth,"); + sqlBuilder.append("atd.id as accountDetailId,atd.transfer_type as transferType,"); + sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); + sqlBuilder.append("tooff.id as toOfficeId, tooff.name as toOfficeName,"); + sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); + sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); + sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); + sqlBuilder.append("fromsp.id as fromProductId, fromsp.name as fromProductName, "); + sqlBuilder.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,"); + sqlBuilder.append("fromlp.id as fromLoanProductId, fromlp.name as fromLoanProductName,"); + sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); + sqlBuilder.append("tosp.id as toProductId, tosp.name as toProductName, "); + sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo, "); + sqlBuilder.append("tolp.id as toLoanProductId, tolp.name as toLoanProductName, "); + sqlBuilder.append("fromcuracc.id as fromCurrentAccountId, fromcuracc.account_no as fromCurrentAccountNo, "); + sqlBuilder.append("fromcp.id as fromCurrentProductId, fromcp.name as fromCurrentProductName, "); + sqlBuilder.append("tocuracc.id as toCurrentAccountId, tocuracc.account_no as toCurrentAccountNo, "); + sqlBuilder.append("tocp.id as toCurrentProductId, tocp.name as toCurrentProductName "); + sqlBuilder.append(" FROM m_account_transfer_standing_instructions atsi "); + sqlBuilder.append("join m_account_transfer_details atd on atd.id = atsi.account_transfer_details_id "); + sqlBuilder.append("join m_office fromoff on fromoff.id = atd.from_office_id "); + sqlBuilder.append("join m_office tooff on tooff.id = atd.to_office_id "); + sqlBuilder.append("join m_client fromclient on fromclient.id = atd.from_client_id "); + sqlBuilder.append("join m_client toclient on toclient.id = atd.to_client_id "); + sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = atd.from_savings_account_id "); + sqlBuilder.append("left join m_savings_product fromsp ON fromsavacc.product_id = fromsp.id "); + sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = atd.from_loan_account_id "); + sqlBuilder.append("left join m_product_loan fromlp ON fromloanacc.product_id = fromlp.id "); + sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = atd.to_savings_account_id "); + sqlBuilder.append("left join m_savings_product tosp ON tosavacc.product_id = tosp.id "); + sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id "); + sqlBuilder.append("left join m_product_loan tolp ON toloanacc.product_id = tolp.id "); + sqlBuilder.append("left join m_current_account fromcuracc on fromcuracc.id = atd.from_current_account_id "); + sqlBuilder.append("left join m_current_product fromcp ON fromcuracc.product_id = fromcp.id "); + sqlBuilder.append("left join m_current_account tocuracc on tocuracc.id = atd.to_current_account_id "); + sqlBuilder.append("left join m_current_product tocp ON tocuracc.product_id = tocp.id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public StandingInstructionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final Long accountDetailId = rs.getLong("accountDetailId"); + final String name = rs.getString("name"); + final Integer priority = JdbcSupport.getInteger(rs, "priority"); + EnumOptionData priorityEnum = AccountTransferEnumerations.standingInstructionPriority(priority); + + final Integer status = JdbcSupport.getInteger(rs, "status"); + EnumOptionData statusEnum = AccountTransferEnumerations.standingInstructionStatus(status); + final Integer instructionType = JdbcSupport.getInteger(rs, "instructionType"); + EnumOptionData instructionTypeEnum = AccountTransferEnumerations.standingInstructionType(instructionType); + final LocalDate validFrom = JdbcSupport.getLocalDate(rs, "validFrom"); + final LocalDate validTill = JdbcSupport.getLocalDate(rs, "validTill"); + final BigDecimal transferAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount"); + final Integer recurrenceType = JdbcSupport.getInteger(rs, "recurrenceType"); + EnumOptionData recurrenceTypeEnum = AccountTransferEnumerations.recurrenceType(recurrenceType); + final Integer recurrenceFrequency = JdbcSupport.getInteger(rs, "recurrenceFrequency"); + + EnumOptionData recurrenceFrequencyEnum = null; + if (recurrenceFrequency != null) { + recurrenceFrequencyEnum = CommonEnumerations.termFrequencyType(recurrenceFrequency, "recurrence"); + } + final Integer recurrenceInterval = JdbcSupport.getInteger(rs, "recurrenceInterval"); + + MonthDay recurrenceOnMonthDay = null; + final Integer recurrenceOnDay = JdbcSupport.getInteger(rs, "recurrenceOnDay"); + final Integer recurrenceOnMonth = JdbcSupport.getInteger(rs, "recurrenceOnMonth"); + if (recurrenceOnDay != null) { + recurrenceOnMonthDay = MonthDay.now(DateUtils.getDateTimeZoneOfTenant()).withMonth(recurrenceOnMonth) + .withDayOfMonth(recurrenceOnDay); + } + + final Integer transferType = rs.getInt("transferType"); + EnumOptionData transferTypeEnum = AccountTransferEnumerations.transferType(transferType); + + /* + * final String currencyCode = rs.getString("currencyCode"); final String currencyName = + * rs.getString("currencyName"); final String currencyNameCode = rs.getString("currencyNameCode"); final + * String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = + * JdbcSupport.getInteger(rs, "currencyDigits"); final Integer inMultiplesOf = JdbcSupport.getInteger(rs, + * "inMultiplesOf"); final CurrencyData currency = new CurrencyData(currencyCode, currencyName, + * currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); + */ + final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); + final String fromOfficeName = rs.getString("fromOfficeName"); + final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); + + final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); + final String toOfficeName = rs.getString("toOfficeName"); + final OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); + + final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); + final String fromClientName = rs.getString("fromClientName"); + final ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); + + final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); + final String toClientName = rs.getString("toClientName"); + final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); + + final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); + final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); + final Long fromProductId = JdbcSupport.getLong(rs, "fromProductId"); + final String fromProductName = rs.getString("fromProductName"); + final Long fromLoanAccountId = JdbcSupport.getLong(rs, "fromLoanAccountId"); + final String fromLoanAccountNo = rs.getString("fromLoanAccountNo"); + final Long fromLoanProductId = JdbcSupport.getLong(rs, "fromLoanProductId"); + final String fromLoanProductName = rs.getString("fromLoanProductName"); + PortfolioAccountData fromAccount = null; + EnumOptionData fromAccountType = null; + if (fromSavingsAccountId != null) { + fromAccount = new PortfolioAccountData(fromSavingsAccountId, fromSavingsAccountNo, null, null, null, null, null, + fromProductId, fromProductName, null, null, null); + fromAccountType = accountType(PortfolioAccountType.SAVINGS); + } else if (fromLoanAccountId != null) { + fromAccount = new PortfolioAccountData(fromLoanAccountId, fromLoanAccountNo, null, null, null, null, null, + fromLoanProductId, fromLoanProductName, null, null, null); + fromAccountType = accountType(PortfolioAccountType.LOAN); + } else { + final Long fromCurrentAccountId = JdbcSupport.getLong(rs, "fromCurrentAccountId"); + if (fromCurrentAccountId != null) { + final String fromCurrentAccountNo = rs.getString("fromCurrentAccountNo"); + final Long fromCurrentProductId = JdbcSupport.getLong(rs, "fromCurrentProductId"); + final String fromCurrentProductName = rs.getString("fromCurrentProductName"); + fromAccount = new PortfolioAccountData(fromCurrentAccountId, fromCurrentAccountNo, null, null, null, null, null, + fromCurrentProductId, fromCurrentProductName, null, null, null); + fromAccountType = accountType(PortfolioAccountType.CURRENT); + } + } + + PortfolioAccountData toAccount = null; + EnumOptionData toAccountType = null; + final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); + final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); + final Long toProductId = JdbcSupport.getLong(rs, "toProductId"); + final String toProductName = rs.getString("toProductName"); + final Long toLoanAccountId = JdbcSupport.getLong(rs, "toLoanAccountId"); + final String toLoanAccountNo = rs.getString("toLoanAccountNo"); + final Long toLoanProductId = JdbcSupport.getLong(rs, "toLoanProductId"); + final String toLoanProductName = rs.getString("toLoanProductName"); + + if (toSavingsAccountId != null) { + toAccount = new PortfolioAccountData(toSavingsAccountId, toSavingsAccountNo, null, null, null, null, null, toProductId, + toProductName, null, null, null); + toAccountType = accountType(PortfolioAccountType.SAVINGS); + } else if (toLoanAccountId != null) { + toAccount = new PortfolioAccountData(toLoanAccountId, toLoanAccountNo, null, null, null, null, null, toLoanProductId, + toLoanProductName, null, null, null); + toAccountType = accountType(PortfolioAccountType.LOAN); + } else { + final Long toCurrentAccountId = JdbcSupport.getLong(rs, "toCurrentAccountId"); + if (toCurrentAccountId != null) { + final String toCurrentAccountNo = rs.getString("toCurrentAccountNo"); + final Long toCurrentProductId = JdbcSupport.getLong(rs, "toCurrentProductId"); + final String toCurrentProductName = rs.getString("toCurrentProductName"); + toAccount = new PortfolioAccountData(toCurrentAccountId, toCurrentAccountNo, null, null, null, null, null, + toCurrentProductId, toCurrentProductName, null, null, null); + toAccountType = accountType(PortfolioAccountType.CURRENT); + } + } + + return StandingInstructionData.instance(id, accountDetailId, name, fromOffice, toOffice, fromClient, toClient, fromAccountType, + fromAccount, toAccountType, toAccount, transferTypeEnum, priorityEnum, instructionTypeEnum, statusEnum, transferAmount, + validFrom, validTill, recurrenceTypeEnum, recurrenceFrequencyEnum, recurrenceInterval, recurrenceOnMonthDay); + } + } + + private static final class StandingInstructionLoanDuesMapper implements RowMapper { + + private final String schemaSql; + + StandingInstructionLoanDuesMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + + sqlBuilder.append("max(ls.duedate) as dueDate,sum(ls.principal_amount) as principalAmount,"); + sqlBuilder.append("sum(ls.principal_completed_derived) as principalCompleted,"); + sqlBuilder.append("sum(ls.principal_writtenoff_derived) as principalWrittenOff,"); + sqlBuilder.append("sum(ls.interest_amount) as interestAmount,"); + sqlBuilder.append("sum(ls.interest_completed_derived) as interestCompleted,"); + sqlBuilder.append("sum(ls.interest_writtenoff_derived) as interestWrittenOff,"); + sqlBuilder.append("sum(ls.interest_waived_derived) as interestWaived,"); + sqlBuilder.append("sum(ls.penalty_charges_amount) as penalityAmount,"); + sqlBuilder.append("sum(ls.penalty_charges_completed_derived) as penalityCompleted,"); + sqlBuilder.append("sum(ls.penalty_charges_writtenoff_derived)as penaltyWrittenOff,"); + sqlBuilder.append("sum(ls.penalty_charges_waived_derived) as penaltyWaived,"); + sqlBuilder.append("sum(ls.fee_charges_amount) as feeAmount,"); + sqlBuilder.append("sum(ls.fee_charges_completed_derived) as feecompleted,"); + sqlBuilder.append("sum(ls.fee_charges_writtenoff_derived) as feeWrittenOff,"); + sqlBuilder.append("sum(ls.fee_charges_waived_derived) as feeWaived "); + sqlBuilder.append("from m_loan_repayment_schedule ls "); + sqlBuilder.append(" join m_loan ml on ml.id = ls.loan_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public StandingInstructionDuesData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate"); + final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalAmount"); + final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalCompleted"); + final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff"); + final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff); + + final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestAmount"); + final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestCompleted"); + final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff"); + final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived"); + final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff); + final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid); + + final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalityAmount"); + final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalityCompleted"); + final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyWrittenOff"); + final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyWaived"); + final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived) + .subtract(penaltyChargesWrittenOff); + final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid); + + final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeAmount"); + final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feecompleted"); + final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeWrittenOff"); + final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeWaived"); + final BigDecimal feeChargesActualDue = feeChargesExpectedDue.subtract(feeChargesWaived).subtract(feeChargesWrittenOff); + final BigDecimal feeChargesOutstanding = feeChargesActualDue.subtract(feeChargesPaid); + + final BigDecimal totalOutstanding = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding) + .add(penaltyChargesOutstanding); + + return new StandingInstructionDuesData(dueDate, totalOutstanding); + } + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java index 5e364315e..4ceb701df 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java @@ -1,145 +1,179 @@ -/** - * 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.account.service; - -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName; -import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; -import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName; - -import java.util.HashMap; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -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.ErrorHandler; -import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; -import org.apache.fineract.portfolio.account.PortfolioAccountType; -import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants; -import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator; -import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; -import org.apache.fineract.portfolio.account.domain.AccountTransferDetails; -import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction; -import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler; -import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; -import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; -import org.apache.fineract.portfolio.account.exception.StandingInstructionNotFoundException; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.NonTransientDataAccessException; -import org.springframework.orm.jpa.JpaSystemException; -import org.springframework.transaction.annotation.Transactional; - -@Slf4j -@RequiredArgsConstructor -public class StandingInstructionWritePlatformServiceImpl implements StandingInstructionWritePlatformService { - - private final StandingInstructionDataValidator standingInstructionDataValidator; - private final StandingInstructionAssembler standingInstructionAssembler; - private final AccountTransferDetailRepository accountTransferDetailRepository; - private final StandingInstructionRepository standingInstructionRepository; - - @Transactional - @Override - public CommandProcessingResult create(final JsonCommand command) { - - this.standingInstructionDataValidator.validateForCreate(command); - - final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName); - final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId); - - final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName); - final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId); - - final Long fromClientId = command.longValueOfParameterNamed(fromClientIdParamName); - - Long standingInstructionId = null; - try { - if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) { - final AccountTransferDetails standingInstruction = this.standingInstructionAssembler - .assembleSavingsToSavingsTransfer(command); - this.accountTransferDetailRepository.saveAndFlush(standingInstruction); - standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); - } else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) { - final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToLoanTransfer(command); - this.accountTransferDetailRepository.saveAndFlush(standingInstruction); - standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); - } else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) { - - final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command); - this.accountTransferDetailRepository.saveAndFlush(standingInstruction); - standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); - - } - } catch (final JpaSystemException | DataIntegrityViolationException dve) { - final Throwable throwable = dve.getMostSpecificCause(); - handleDataIntegrityIssues(command, throwable, dve); - return CommandProcessingResult.empty(); - } - final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(standingInstructionId) - .withClientId(fromClientId); - return builder.build(); - } - - private void handleDataIntegrityIssues(final JsonCommand command, Throwable realCause, final NonTransientDataAccessException dve) { - if (realCause.getMessage().contains("name")) { - final String name = command.stringValueOfParameterNamed(StandingInstructionApiConstants.nameParamName); - throw new PlatformDataIntegrityException("error.msg.standinginstruction.duplicate.name", - "Standinginstruction with name `" + name + "` already exists", "name", name); - } - log.error("Error occured.", dve); - throw ErrorHandler.getMappable(dve, "error.msg.client.unknown.data.integrity.issue", - "Unknown data integrity issue with resource: " + realCause.getMessage()); - } - - private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { - return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount(); - } - - private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { - return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount(); - } - - private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, - final PortfolioAccountType toAccountType) { - return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount(); - } - - @Override - public CommandProcessingResult update(final Long id, final JsonCommand command) { - this.standingInstructionDataValidator.validateForUpdate(command); - AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id) - .orElseThrow(() -> new StandingInstructionNotFoundException(id)); - final Map actualChanges = standingInstructionsForUpdate.update(command); - return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(id).with(actualChanges).build(); - } - - @Override - public CommandProcessingResult delete(final Long id) { - AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id).orElseThrow(); - // update the "deleted" and "name" properties of the standing - // instruction - standingInstructionsForUpdate.delete(); - - final Map actualChanges = new HashMap<>(); - actualChanges.put(statusParamName, StandingInstructionStatus.DELETED.getValue()); - return new CommandProcessingResultBuilder().withEntityId(id).with(actualChanges).build(); - } -} +/** + * 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.account.service; + +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName; +import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; +import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName; + +import java.util.HashMap; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.ErrorHandler; +import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; +import org.apache.fineract.portfolio.account.PortfolioAccountType; +import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants; +import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator; +import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; +import org.apache.fineract.portfolio.account.domain.AccountTransferDetails; +import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction; +import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler; +import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; +import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; +import org.apache.fineract.portfolio.account.exception.StandingInstructionNotFoundException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.NonTransientDataAccessException; +import org.springframework.orm.jpa.JpaSystemException; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@RequiredArgsConstructor +public class StandingInstructionWritePlatformServiceImpl implements StandingInstructionWritePlatformService { + + private final StandingInstructionDataValidator standingInstructionDataValidator; + private final StandingInstructionAssembler standingInstructionAssembler; + private final AccountTransferDetailRepository accountTransferDetailRepository; + private final StandingInstructionRepository standingInstructionRepository; + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + + this.standingInstructionDataValidator.validateForCreate(command); + + final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName); + final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId); + + final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName); + final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId); + + final Long fromClientId = command.longValueOfParameterNamed(fromClientIdParamName); + + Long standingInstructionId = null; + try { + if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler + .assembleSavingsToSavingsTransfer(command); + this.accountTransferDetailRepository.saveAndFlush(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToLoanTransfer(command); + this.accountTransferDetailRepository.saveAndFlush(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command); + this.accountTransferDetailRepository.saveAndFlush(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } else if (isCurrentToCurrentAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToCurrentTransfer(command); + this.accountTransferDetailRepository.saveAndFlush(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } else if (isCurrentToSavingsAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToSavingsTransfer(command); + this.accountTransferDetailRepository.saveAndFlush(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } else if (isSavingsToCurrentAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToCurrentTransfer(command); + this.accountTransferDetailRepository.saveAndFlush(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } else if (isCurrentToLoanAccountTransfer(fromAccountType, toAccountType)) { + final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToLoanTransfer(command); + this.accountTransferDetailRepository.saveAndFlush(standingInstruction); + standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); + } + } catch (final JpaSystemException | DataIntegrityViolationException dve) { + final Throwable throwable = dve.getMostSpecificCause(); + handleDataIntegrityIssues(command, throwable, dve); + return CommandProcessingResult.empty(); + } + final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(standingInstructionId) + .withClientId(fromClientId); + return builder.build(); + } + + private void handleDataIntegrityIssues(final JsonCommand command, Throwable realCause, final NonTransientDataAccessException dve) { + if (realCause.getMessage().contains("name")) { + final String name = command.stringValueOfParameterNamed(StandingInstructionApiConstants.nameParamName); + throw new PlatformDataIntegrityException("error.msg.standinginstruction.duplicate.name", + "Standinginstruction with name `" + name + "` already exists", "name", name); + } + log.error("Error occured.", dve); + throw ErrorHandler.getMappable(dve, "error.msg.client.unknown.data.integrity.issue", + "Unknown data integrity issue with resource: " + realCause.getMessage()); + } + + private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount(); + } + + private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount(); + } + + private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount(); + } + + private boolean isCurrentToCurrentAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isCurrentAccount() && toAccountType.isCurrentAccount(); + } + + private boolean isCurrentToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isCurrentAccount() && toAccountType.isSavingsAccount(); + } + + private boolean isSavingsToCurrentAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isCurrentAccount(); + } + + private boolean isCurrentToLoanAccountTransfer(final PortfolioAccountType fromAccountType, + final PortfolioAccountType toAccountType) { + return fromAccountType.isCurrentAccount() && toAccountType.isLoanAccount(); + } + + @Override + public CommandProcessingResult update(final Long id, final JsonCommand command) { + this.standingInstructionDataValidator.validateForUpdate(command); + AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id) + .orElseThrow(() -> new StandingInstructionNotFoundException(id)); + final Map actualChanges = standingInstructionsForUpdate.update(command); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(id).with(actualChanges).build(); + } + + @Override + public CommandProcessingResult delete(final Long id) { + AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id).orElseThrow(); + // update the "deleted" and "name" properties of the standing + // instruction + standingInstructionsForUpdate.delete(); + + final Map actualChanges = new HashMap<>(); + actualChanges.put(statusParamName, StandingInstructionStatus.DELETED.getValue()); + return new CommandProcessingResultBuilder().withEntityId(id).with(actualChanges).build(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/starter/AccountConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/starter/AccountConfiguration.java index 412b78faa..c2bfdc826 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/starter/AccountConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/starter/AccountConfiguration.java @@ -1,134 +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.account.starter; - -import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; -import org.apache.fineract.infrastructure.core.config.FineractProperties; -import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; -import org.apache.fineract.infrastructure.core.service.PaginationHelper; -import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; -import org.apache.fineract.infrastructure.security.service.SqlValidator; -import org.apache.fineract.infrastructure.security.utils.ColumnValidator; -import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; -import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator; -import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator; -import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler; -import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; -import org.apache.fineract.portfolio.account.domain.AccountTransferRepository; -import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler; -import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; -import org.apache.fineract.portfolio.account.mapper.AccountTransfersMapper; -import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService; -import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformServiceImpl; -import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; -import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformServiceImpl; -import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService; -import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformServiceImpl; -import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformService; -import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformServiceImpl; -import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformService; -import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformServiceImpl; -import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; -import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformServiceImpl; -import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformService; -import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformServiceImpl; -import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; -import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService; -import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService; -import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; -import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; -import org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; -import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; -import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService; -import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.jdbc.core.JdbcTemplate; - -@Configuration -public class AccountConfiguration { - - @Bean - @ConditionalOnMissingBean(AccountAssociationsReadPlatformService.class) - public AccountAssociationsReadPlatformService accountAssociationsReadPlatformService(JdbcTemplate jdbcTemplate) { - return new AccountAssociationsReadPlatformServiceImpl(jdbcTemplate); - } - - @Bean - @ConditionalOnMissingBean(AccountTransfersReadPlatformService.class) - public AccountTransfersReadPlatformService accountTransfersReadPlatformService(JdbcTemplate jdbcTemplate, - ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService, - PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, ColumnValidator columnValidator, - DatabaseSpecificSQLGenerator sqlGenerator, AccountTransfersMapper accountTransfersMapper, PaginationHelper paginationHelper, - SqlValidator sqlValidator) { - return new AccountTransfersReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService, - portfolioAccountReadPlatformService, columnValidator, sqlGenerator, accountTransfersMapper, paginationHelper, sqlValidator); - } - - @Bean - @ConditionalOnMissingBean(AccountTransfersWritePlatformService.class) - public AccountTransfersWritePlatformService accountTransfersWritePlatformService( - AccountTransfersDataValidator accountTransfersDataValidator, AccountTransferAssembler accountTransferAssembler, - AccountTransferRepository accountTransferRepository, SavingsAccountAssembler savingsAccountAssembler, - SavingsAccountDomainService savingsAccountDomainService, LoanAssembler loanAccountAssembler, - LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService, - AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService, - GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory, - FineractProperties fineractProperties) { - return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler, - accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler, - loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService, - gsimRepository, configurationDomainService, externalIdFactory, fineractProperties); - } - - @Bean - @ConditionalOnMissingBean(PortfolioAccountReadPlatformService.class) - public PortfolioAccountReadPlatformService portfolioAccountReadPlatformService(JdbcTemplate jdbcTemplate, - DatabaseSpecificSQLGenerator sqlGenerator) { - return new PortfolioAccountReadPlatformServiceImpl(jdbcTemplate, sqlGenerator); - } - - @Bean - @ConditionalOnMissingBean(StandingInstructionHistoryReadPlatformService.class) - public StandingInstructionHistoryReadPlatformService standingInstructionHistoryReadPlatformService(JdbcTemplate jdbcTemplate, - ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { - return new StandingInstructionHistoryReadPlatformServiceImpl(jdbcTemplate, columnValidator, sqlGenerator, paginationHelper); - } - - @Bean - @ConditionalOnMissingBean(StandingInstructionReadPlatformService.class) - public StandingInstructionReadPlatformService standingInstructionReadPlatformService(JdbcTemplate jdbcTemplate, - ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService, - PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, - DropdownReadPlatformService dropdownReadPlatformService, ColumnValidator columnValidator, - DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { - return new StandingInstructionReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService, - portfolioAccountReadPlatformService, dropdownReadPlatformService, columnValidator, sqlGenerator, paginationHelper); - } - - @Bean - @ConditionalOnMissingBean(StandingInstructionWritePlatformService.class) - public StandingInstructionWritePlatformService standingInstructionWritePlatformService( - StandingInstructionDataValidator standingInstructionDataValidator, StandingInstructionAssembler standingInstructionAssembler, - AccountTransferDetailRepository accountTransferDetailRepository, StandingInstructionRepository standingInstructionRepository) { - return new StandingInstructionWritePlatformServiceImpl(standingInstructionDataValidator, standingInstructionAssembler, - accountTransferDetailRepository, standingInstructionRepository); - } -} +/** + * 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.account.starter; + +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; +import org.apache.fineract.infrastructure.core.config.FineractProperties; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.infrastructure.security.service.SqlValidator; +import org.apache.fineract.infrastructure.security.utils.ColumnValidator; +import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository; +import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository; +import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator; +import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator; +import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler; +import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; +import org.apache.fineract.portfolio.account.domain.AccountTransferRepository; +import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler; +import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; +import org.apache.fineract.portfolio.account.mapper.AccountTransfersMapper; +import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService; +import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformServiceImpl; +import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; +import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformServiceImpl; +import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService; +import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformServiceImpl; +import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformService; +import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformServiceImpl; +import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformService; +import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformServiceImpl; +import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; +import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformServiceImpl; +import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformService; +import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformServiceImpl; +import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; +import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService; +import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService; +import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; +import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; +import org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; +import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService; +import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; + +@Configuration +public class AccountConfiguration { + + @Bean + @ConditionalOnMissingBean(AccountAssociationsReadPlatformService.class) + public AccountAssociationsReadPlatformService accountAssociationsReadPlatformService(JdbcTemplate jdbcTemplate) { + return new AccountAssociationsReadPlatformServiceImpl(jdbcTemplate); + } + + @Bean + @ConditionalOnMissingBean(AccountTransfersReadPlatformService.class) + public AccountTransfersReadPlatformService accountTransfersReadPlatformService(JdbcTemplate jdbcTemplate, + ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService, + PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, AccountTransfersMapper accountTransfersMapper, PaginationHelper paginationHelper, + SqlValidator sqlValidator) { + return new AccountTransfersReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService, + portfolioAccountReadPlatformService, columnValidator, sqlGenerator, accountTransfersMapper, paginationHelper, sqlValidator); + } + + @Bean + @ConditionalOnMissingBean(AccountTransfersWritePlatformService.class) + public AccountTransfersWritePlatformService accountTransfersWritePlatformService( + AccountTransfersDataValidator accountTransfersDataValidator, AccountTransferAssembler accountTransferAssembler, + AccountTransferRepository accountTransferRepository, SavingsAccountAssembler savingsAccountAssembler, + SavingsAccountDomainService savingsAccountDomainService, LoanAssembler loanAccountAssembler, + LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService, + AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService, + GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory, + FineractProperties fineractProperties, CurrentAccountRepository currentAccountRepository, + CurrentAccountTransactionRepository currentAccountTransactionRepository) { + return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler, + accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler, + loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService, + gsimRepository, configurationDomainService, externalIdFactory, fineractProperties, + currentAccountRepository, currentAccountTransactionRepository); + } + + @Bean + @ConditionalOnMissingBean(PortfolioAccountReadPlatformService.class) + public PortfolioAccountReadPlatformService portfolioAccountReadPlatformService(JdbcTemplate jdbcTemplate, + DatabaseSpecificSQLGenerator sqlGenerator) { + return new PortfolioAccountReadPlatformServiceImpl(jdbcTemplate, sqlGenerator); + } + + @Bean + @ConditionalOnMissingBean(StandingInstructionHistoryReadPlatformService.class) + public StandingInstructionHistoryReadPlatformService standingInstructionHistoryReadPlatformService(JdbcTemplate jdbcTemplate, + ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { + return new StandingInstructionHistoryReadPlatformServiceImpl(jdbcTemplate, columnValidator, sqlGenerator, paginationHelper); + } + + @Bean + @ConditionalOnMissingBean(StandingInstructionReadPlatformService.class) + public StandingInstructionReadPlatformService standingInstructionReadPlatformService(JdbcTemplate jdbcTemplate, + ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService, + PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, + DropdownReadPlatformService dropdownReadPlatformService, ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { + return new StandingInstructionReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService, + portfolioAccountReadPlatformService, dropdownReadPlatformService, columnValidator, sqlGenerator, paginationHelper); + } + + @Bean + @ConditionalOnMissingBean(StandingInstructionWritePlatformService.class) + public StandingInstructionWritePlatformService standingInstructionWritePlatformService( + StandingInstructionDataValidator standingInstructionDataValidator, StandingInstructionAssembler standingInstructionAssembler, + AccountTransferDetailRepository accountTransferDetailRepository, StandingInstructionRepository standingInstructionRepository) { + return new StandingInstructionWritePlatformServiceImpl(standingInstructionDataValidator, standingInstructionAssembler, + accountTransferDetailRepository, standingInstructionRepository); + } +} diff --git a/fineract-provider/src/main/resources/application.properties b/fineract-provider/src/main/resources/application.properties index 696393b9e..3432b0ff3 100644 --- a/fineract-provider/src/main/resources/application.properties +++ b/fineract-provider/src/main/resources/application.properties @@ -407,7 +407,7 @@ server.tomcat.mbeanregistry.enabled=${FINERACT_SERVER_TOMCAT_MBEANREGISTRY_ENABL spring.datasource.hikari.driverClassName=${FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME:org.postgresql.Driver} spring.datasource.hikari.jdbcUrl=${FINERACT_HIKARI_JDBC_URL:jdbc:postgresql://localhost:5432/fineract_tenants} spring.datasource.hikari.username=${FINERACT_HIKARI_USERNAME:postgres} -spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:root} +spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:313863} spring.datasource.hikari.minimumIdle=${FINERACT_HIKARI_MINIMUM_IDLE:3} spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10} spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000} diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 592ae3103..e640e4ace 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -317,4 +317,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0301_add_current_account_to_transfer_details.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0301_add_current_account_to_transfer_details.xml new file mode 100644 index 000000000..5a93b8059 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0301_add_current_account_to_transfer_details.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +