feat: current account standing instructions - full backend support

- StandingInstructionData: rename conflicting Lombok getters (getInstructionType,
  getRecurrenceType, getRecurrenceFrequency, getFromAccountType, getToAccountType,
  getTransferType) to domain enum methods so Jackson serializes EnumOptionData fields
  as {id, code, value} objects instead of plain strings
- AccountTransfersWritePlatformServiceImpl: add current account transfer support for
  all 4 missing cases (Current->Savings, Savings->Current, Current->Current,
  Current->Loan) with performCurrentWithdrawal/performCurrentDeposit helpers
- AccountConfiguration: wire CurrentAccountRepository and CurrentAccountTransactionRepository
  into AccountTransfersWritePlatformService bean
- StandingInstructionApiResource: fix getValue() type mismatch after getter rename
- ExecuteStandingInstructionsTasklet: update to use renamed enum accessor methods
- PortfolioAccountType: add CURRENT(3) type support
- DB migration: 0301_add_current_account_to_transfer_details.xml adds
  from_current_account_id and to_current_account_id columns to transfer details table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-05-26 10:55:35 +05:30
co-authored by Claude Sonnet 4.6
parent 45585ecc10
commit 2251fd2259
19 changed files with 4621 additions and 3957 deletions
@@ -1,71 +1,79 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account; package org.apache.fineract.portfolio.account;
public enum PortfolioAccountType { public enum PortfolioAccountType {
INVALID(0, "accountType.invalid"), // INVALID(0, "accountType.invalid"), //
LOAN(1, "accountType.loan"), // LOAN(1, "accountType.loan"), //
SAVINGS(2, "accountType.savings"), // SAVINGS(2, "accountType.savings"), //
; CURRENT(3, "accountType.current"), //
;
private final Integer value;
private final String code; private final Integer value;
private final String code;
PortfolioAccountType(final Integer value, final String code) {
this.value = value; PortfolioAccountType(final Integer value, final String code) {
this.code = code; this.value = value;
} this.code = code;
}
public Integer getValue() {
return this.value; public Integer getValue() {
} return this.value;
}
public String getCode() {
return this.code; public String getCode() {
} return this.code;
}
public static PortfolioAccountType fromInt(final Integer type) {
public static PortfolioAccountType fromInt(final Integer type) {
PortfolioAccountType enumType = INVALID;
if (type != null) { PortfolioAccountType enumType = INVALID;
switch (type) { if (type != null) {
case 1: switch (type) {
enumType = LOAN; case 1:
break; enumType = LOAN;
case 2: break;
enumType = SAVINGS; case 2:
break; enumType = SAVINGS;
default: break;
enumType = INVALID; case 3:
} enumType = CURRENT;
} break;
return enumType; default:
} enumType = INVALID;
}
// TODO: bad practice and unnecessary code! why not just use the enum values themselves!?! }
public boolean isSavingsAccount() { return enumType;
return this.equals(SAVINGS); }
}
// TODO: bad practice and unnecessary code! why not just use the enum values themselves!?!
// TODO: bad practice and unnecessary code! why not just use the enum values themselves!?! public boolean isSavingsAccount() {
public boolean isLoanAccount() { return this.equals(SAVINGS);
return this.equals(LOAN); }
}
} // 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);
}
}
@@ -1,220 +1,220 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.api; package org.apache.fineract.portfolio.account.api;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT; import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandHandlerRegistry; import org.apache.fineract.batch.command.CommandHandlerRegistry;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper; import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException; import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil; import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.portfolio.account.data.AccountTransferData; import org.apache.fineract.portfolio.account.data.AccountTransferData;
import org.apache.fineract.portfolio.account.data.StandingInstructionDTO; import org.apache.fineract.portfolio.account.data.StandingInstructionDTO;
import org.apache.fineract.portfolio.account.data.StandingInstructionData; 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.StandingInstructionCreationRequest;
import org.apache.fineract.portfolio.account.data.request.StandingInstructionSearchParam; import org.apache.fineract.portfolio.account.data.request.StandingInstructionSearchParam;
import org.apache.fineract.portfolio.account.data.request.StandingInstructionUpdatesRequest; import org.apache.fineract.portfolio.account.data.request.StandingInstructionUpdatesRequest;
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/v1/standinginstructions") @Path("/v1/standinginstructions")
@Component @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" @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.") + "\n" + "Note: At present only savings account to savings account and savings account to Loan account transfers are permitted.")
@RequiredArgsConstructor @RequiredArgsConstructor
public class StandingInstructionApiResource { public class StandingInstructionApiResource {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final DefaultToApiJsonSerializer<StandingInstructionData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<StandingInstructionData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; private final StandingInstructionReadPlatformService standingInstructionReadPlatformService;
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
private final SqlValidator sqlValidator; private final SqlValidator sqlValidator;
private static final CommandHandlerRegistry<String, Long, String, CommandWrapper> COMMAND_HANDLER_REGISTRY = new CommandHandlerRegistry<>( private static final CommandHandlerRegistry<String, Long, String, CommandWrapper> COMMAND_HANDLER_REGISTRY = new CommandHandlerRegistry<>(
Map.of(CommandParameterUtil.UPDATE_COMMAND_VALUE, Map.of(CommandParameterUtil.UPDATE_COMMAND_VALUE,
(id, json) -> new CommandWrapperBuilder().updateStandingInstruction(id).withJson(json).build(), (id, json) -> new CommandWrapperBuilder().updateStandingInstruction(id).withJson(json).build(),
CommandParameterUtil.DELETE_COMMAND_VALUE, CommandParameterUtil.DELETE_COMMAND_VALUE,
(id, json) -> new CommandWrapperBuilder().deleteStandingInstruction(id).withJson(json).build())); (id, json) -> new CommandWrapperBuilder().deleteStandingInstruction(id).withJson(json).build()));
@GET @GET
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Standing Instruction Template", description = "This is a convenience resource. " @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. " + "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" + "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" + "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?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=1\n" + "\n"
+ "standinginstructions/template?fromClientId=1&fromAccountType=2&fromAccountId=1&transferType=1") + "standinginstructions/template?fromClientId=1&fromAccountType=2&fromAccountId=1&transferType=1")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsTemplateResponse.class))) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsTemplateResponse.class)))
public StandingInstructionData template(@BeanParam StandingInstructionSearchParam instructionParam) { public StandingInstructionData template(@BeanParam StandingInstructionSearchParam instructionParam) {
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
return standingInstructionReadPlatformService.retrieveTemplate(instructionParam.getFromOfficeId(), return standingInstructionReadPlatformService.retrieveTemplate(instructionParam.getFromOfficeId(),
instructionParam.getFromClientId(), instructionParam.getFromAccountId(), instructionParam.getFromAccountType(), instructionParam.getFromClientId(), instructionParam.getFromAccountId(), instructionParam.getFromAccountType(),
instructionParam.getToOfficeId(), instructionParam.getToClientId(), instructionParam.getToAccountId(), instructionParam.getToOfficeId(), instructionParam.getToClientId(), instructionParam.getToAccountId(),
instructionParam.getToAccountType(), instructionParam.getTransferType()); instructionParam.getToAccountType(), instructionParam.getTransferType());
} }
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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") @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))) @RequestBody(required = true, content = @Content(schema = @Schema(implementation = StandingInstructionCreationRequest.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PostStandingInstructionsResponse.class))) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PostStandingInstructionsResponse.class)))
public CommandProcessingResult create(@Parameter(hidden = true) StandingInstructionCreationRequest creationRequest) { public CommandProcessingResult create(@Parameter(hidden = true) StandingInstructionCreationRequest creationRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction() final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction()
.withJson(toApiJsonSerializer.serialize(creationRequest)).build(); .withJson(toApiJsonSerializer.serialize(creationRequest)).build();
return commandsSourceWritePlatformService.logCommandSource(commandRequest); return commandsSourceWritePlatformService.logCommandSource(commandRequest);
} }
@PUT @PUT
@Path("{standingInstructionId}") @Path("{standingInstructionId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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" @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" + "\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" + "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") + "PUT https://DomainName/api/v1/standinginstructions/1?command=delete")
@RequestBody(content = @Content(schema = @Schema(implementation = StandingInstructionUpdatesRequest.class))) @RequestBody(content = @Content(schema = @Schema(implementation = StandingInstructionUpdatesRequest.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PutStandingInstructionsStandingInstructionIdResponse.class))) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PutStandingInstructionsStandingInstructionIdResponse.class)))
public CommandProcessingResult update( public CommandProcessingResult update(
@PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId, @PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId,
@Parameter(hidden = true) StandingInstructionUpdatesRequest updatesRequest, @Parameter(hidden = true) StandingInstructionUpdatesRequest updatesRequest,
@QueryParam("command") @Parameter(description = "command") final String commandParam) { @QueryParam("command") @Parameter(description = "command") final String commandParam) {
final String serializedUpdatesRequest = toApiJsonSerializer.serialize(updatesRequest); final String serializedUpdatesRequest = toApiJsonSerializer.serialize(updatesRequest);
final CommandWrapper commandRequest = COMMAND_HANDLER_REGISTRY.execute(commandParam, standingInstructionId, final CommandWrapper commandRequest = COMMAND_HANDLER_REGISTRY.execute(commandParam, standingInstructionId,
serializedUpdatesRequest, new UnrecognizedQueryParamException("command", commandParam)); serializedUpdatesRequest, new UnrecognizedQueryParamException("command", commandParam));
return commandsSourceWritePlatformService.logCommandSource(commandRequest); return commandsSourceWritePlatformService.logCommandSource(commandRequest);
} }
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Standing Instructions", description = "Example Requests:\n" + "\n" + "standinginstructions") @Operation(summary = "List Standing Instructions", description = "Example Requests:\n" + "\n" + "standinginstructions")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsResponse.class))) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsResponse.class)))
public Page<StandingInstructionData> retrieveAll( public Page<StandingInstructionData> retrieveAll(
@QueryParam("externalId") @Parameter(description = "externalId") final String externalId, @QueryParam("externalId") @Parameter(description = "externalId") final String externalId,
@QueryParam("offset") @Parameter(description = "offset") final Integer offset, @QueryParam("offset") @Parameter(description = "offset") final Integer offset,
@QueryParam("limit") @Parameter(description = "limit") final Integer limit, @QueryParam("limit") @Parameter(description = "limit") final Integer limit,
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, @QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder, @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
@QueryParam("transferType") @Parameter(description = "transferType") final Integer transferType, @QueryParam("transferType") @Parameter(description = "transferType") final Integer transferType,
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName, @QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId, @QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
@QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccount, @QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccount,
@QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType) { @QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType) {
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
sqlValidator.validate(orderBy); sqlValidator.validate(orderBy);
sqlValidator.validate(sortOrder); sqlValidator.validate(sortOrder);
sqlValidator.validate(externalId); sqlValidator.validate(externalId);
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset) final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
.orderBy(orderBy).sortOrder(sortOrder).build(); .orderBy(orderBy).sortOrder(sortOrder).build();
final LocalDate startDateRange = null; final LocalDate startDateRange = null;
final LocalDate endDateRange = null; final LocalDate endDateRange = null;
StandingInstructionDTO standingInstructionDTO = new StandingInstructionDTO(searchParameters, transferType, clientName, clientId, StandingInstructionDTO standingInstructionDTO = new StandingInstructionDTO(searchParameters, transferType, clientName, clientId,
fromAccount, fromAccountType, startDateRange, endDateRange); fromAccount, fromAccountType, startDateRange, endDateRange);
return standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO); return standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO);
} }
@GET @GET
@Path("{standingInstructionId}") @Path("{standingInstructionId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Standing Instruction", description = "Example Requests :\n" + "\n" + "standinginstructions/1") @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))) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsStandingInstructionIdResponse.class)))
public StandingInstructionData retrieveOne( public StandingInstructionData retrieveOne(
@PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId, @PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId,
@Context final UriInfo uriInfo, @QueryParam("externalId") @Parameter(description = "externalId") final String externalId, @Context final UriInfo uriInfo, @QueryParam("externalId") @Parameter(description = "externalId") final String externalId,
@QueryParam("offset") @Parameter(description = "offset") final Integer offset, @QueryParam("offset") @Parameter(description = "offset") final Integer offset,
@QueryParam("limit") @Parameter(description = "limit") final Integer limit, @QueryParam("limit") @Parameter(description = "limit") final Integer limit,
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, @QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) { @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
sqlValidator.validate(orderBy); sqlValidator.validate(orderBy);
sqlValidator.validate(sortOrder); sqlValidator.validate(sortOrder);
StandingInstructionData standingInstructionData = standingInstructionReadPlatformService.retrieveOne(standingInstructionId); StandingInstructionData standingInstructionData = standingInstructionReadPlatformService.retrieveOne(standingInstructionId);
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset) final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
.orderBy(orderBy).sortOrder(sortOrder).build(); .orderBy(orderBy).sortOrder(sortOrder).build();
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
if (!associationParameters.isEmpty()) { if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) { if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList("transactions", "template")); associationParameters.addAll(Arrays.asList("transactions", "template"));
} }
if (associationParameters.contains("transactions")) { if (associationParameters.contains("transactions")) {
Page<AccountTransferData> transfers = accountTransfersReadPlatformService Page<AccountTransferData> transfers = accountTransfersReadPlatformService
.retrieveByStandingInstruction(standingInstructionId, searchParameters); .retrieveByStandingInstruction(standingInstructionId, searchParameters);
standingInstructionData = StandingInstructionData.withTransferData(standingInstructionData, transfers); standingInstructionData = StandingInstructionData.withTransferData(standingInstructionData, transfers);
} }
if (associationParameters.contains("template")) { if (associationParameters.contains("template")) {
final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate( final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate(
standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(), standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(),
standingInstructionData.getFromAccount().getId(), standingInstructionData.getFromAccountType().getValue(), standingInstructionData.getFromAccount().getId(), standingInstructionData.fromAccountTypeEnum().getValue(),
standingInstructionData.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(), standingInstructionData.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(),
standingInstructionData.getToAccount().getId(), standingInstructionData.getToAccountType().getValue(), standingInstructionData.getToAccount().getId(), standingInstructionData.toAccountTypeEnum().getValue(),
standingInstructionData.getTransferType().getValue()); standingInstructionData.transferTypeEnum().getValue());
standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData); standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData);
} }
} }
return standingInstructionData; return standingInstructionData;
} }
} }
@@ -1,79 +1,79 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.data; package org.apache.fineract.portfolio.account.data;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName; 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.fromAccountTypeParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName; 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.fromOfficeIdParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName; 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.toAccountTypeParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName; 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.toOfficeIdParamName;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
public class AccountTransfersDetailDataValidator { public class AccountTransfersDetailDataValidator {
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@Autowired @Autowired
public AccountTransfersDetailDataValidator(final FromJsonHelper fromApiJsonHelper) { public AccountTransfersDetailDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
} }
public void validate(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { public void validate(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
final JsonElement element = command.parsedJson(); final JsonElement element = command.parsedJson();
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero();
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero();
final Long fromAccountId = this.fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element); final Long fromAccountId = this.fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element);
baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero();
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull() baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull()
.isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero();
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero();
final Long toAccountId = this.fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element); final Long toAccountId = this.fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element);
baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero();
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull().isOneOfTheseValues(Integer.valueOf(1), baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull().isOneOfTheseValues(Integer.valueOf(1),
Integer.valueOf(2)); Integer.valueOf(2), Integer.valueOf(3));
} }
} }
@@ -1,329 +1,359 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.data; package org.apache.fineract.portfolio.account.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.MonthDay; import java.time.MonthDay;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import lombok.Getter; import lombok.Getter;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.organisation.office.data.OfficeData; import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
import org.apache.fineract.portfolio.account.domain.AccountTransferType; import org.apache.fineract.portfolio.account.domain.AccountTransferType;
import org.apache.fineract.portfolio.account.domain.StandingInstructionType; import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
/** /**
* Immutable data object representing a savings account. * Immutable data object representing a savings account.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class StandingInstructionData { public final class StandingInstructionData {
@Getter @Getter
private final Long id; private final Long id;
@Getter @Getter
private final Long accountDetailId; private final Long accountDetailId;
@Getter @Getter
private final String name; private final String name;
private final OfficeData fromOffice; @Getter
@Getter private final OfficeData fromOffice;
private final ClientData fromClient; @Getter
private final EnumOptionData fromAccountType; private final ClientData fromClient;
@Getter @Getter
private final PortfolioAccountData fromAccount; private final EnumOptionData fromAccountType;
private final OfficeData toOffice; @Getter
@Getter private final PortfolioAccountData fromAccount;
private final ClientData toClient; @Getter
private final EnumOptionData toAccountType; private final OfficeData toOffice;
@Getter @Getter
private final PortfolioAccountData toAccount; private final ClientData toClient;
private final EnumOptionData transferType; @Getter
private final EnumOptionData priority; private final EnumOptionData toAccountType;
private final EnumOptionData instructionType; @Getter
@Getter private final PortfolioAccountData toAccount;
private final EnumOptionData status; @Getter
@Getter private final EnumOptionData transferType;
private final BigDecimal amount; @Getter
@Getter private final EnumOptionData priority;
private final LocalDate validFrom; @Getter
private final LocalDate validTill; private final EnumOptionData instructionType;
private final EnumOptionData recurrenceType; @Getter
private final EnumOptionData recurrenceFrequency; private final EnumOptionData status;
@Getter @Getter
private final Integer recurrenceInterval; private final BigDecimal amount;
private final MonthDay recurrenceOnMonthDay; @Getter
private final Page<AccountTransferData> transactions; private final LocalDate validFrom;
@Getter
private final Collection<OfficeData> fromOfficeOptions; private final LocalDate validTill;
private final Collection<ClientData> fromClientOptions; @Getter
private final Collection<EnumOptionData> fromAccountTypeOptions; private final EnumOptionData recurrenceType;
private final Collection<PortfolioAccountData> fromAccountOptions; @Getter
private final Collection<OfficeData> toOfficeOptions; private final EnumOptionData recurrenceFrequency;
private final Collection<ClientData> toClientOptions; @Getter
private final Collection<EnumOptionData> toAccountTypeOptions; private final Integer recurrenceInterval;
private final Collection<PortfolioAccountData> toAccountOptions; @Getter
private final Collection<EnumOptionData> transferTypeOptions; private final MonthDay recurrenceOnMonthDay;
private final Collection<EnumOptionData> statusOptions; @Getter
private final Collection<EnumOptionData> instructionTypeOptions; private final Page<AccountTransferData> transactions;
private final Collection<EnumOptionData> priorityOptions;
private final Collection<EnumOptionData> recurrenceTypeOptions; @Getter
private final Collection<EnumOptionData> recurrenceFrequencyOptions; private final Collection<OfficeData> fromOfficeOptions;
@Getter
public static StandingInstructionData template(final Collection<OfficeData> fromOfficeOptions, private final Collection<ClientData> fromClientOptions;
final Collection<ClientData> fromClientOptions, final Collection<EnumOptionData> fromAccountTypeOptions, @Getter
final Collection<PortfolioAccountData> fromAccountOptions, final Collection<OfficeData> toOfficeOptions, private final Collection<EnumOptionData> fromAccountTypeOptions;
final Collection<ClientData> toClientOptions, final Collection<EnumOptionData> toAccountTypeOptions, @Getter
final Collection<PortfolioAccountData> toAccountOptions, final Collection<EnumOptionData> transferTypeOptions, private final Collection<PortfolioAccountData> fromAccountOptions;
final Collection<EnumOptionData> statusOptions, final Collection<EnumOptionData> instructionTypeOptions, @Getter
final Collection<EnumOptionData> priorityOptions, final Collection<EnumOptionData> recurrenceTypeOptions, private final Collection<OfficeData> toOfficeOptions;
final Collection<EnumOptionData> recurrenceFrequencyOptions) { @Getter
private final Collection<ClientData> toClientOptions;
final Long id = null; @Getter
final Long accountDetailId = null; private final Collection<EnumOptionData> toAccountTypeOptions;
final String name = null; @Getter
final OfficeData fromOffice = null; private final Collection<PortfolioAccountData> toAccountOptions;
final OfficeData toOffice = null; @Getter
final ClientData fromClient = null; private final Collection<EnumOptionData> transferTypeOptions;
final ClientData toClient = null; @Getter
final EnumOptionData fromAccountType = null; private final Collection<EnumOptionData> statusOptions;
final PortfolioAccountData fromAccount = null; @Getter
final EnumOptionData toAccountType = null; private final Collection<EnumOptionData> instructionTypeOptions;
final PortfolioAccountData toAccount = null; @Getter
final EnumOptionData transferType = null; private final Collection<EnumOptionData> priorityOptions;
final EnumOptionData priority = null; @Getter
final EnumOptionData instructionType = null; private final Collection<EnumOptionData> recurrenceTypeOptions;
final EnumOptionData status = null; @Getter
final BigDecimal amount = null; private final Collection<EnumOptionData> recurrenceFrequencyOptions;
final LocalDate validFrom = null;
final LocalDate validTill = null; public static StandingInstructionData template(final Collection<OfficeData> fromOfficeOptions,
final EnumOptionData recurrenceType = null; final Collection<ClientData> fromClientOptions, final Collection<EnumOptionData> fromAccountTypeOptions,
final EnumOptionData recurrenceFrequency = null; final Collection<PortfolioAccountData> fromAccountOptions, final Collection<OfficeData> toOfficeOptions,
final Integer recurrenceInterval = null; final Collection<ClientData> toClientOptions, final Collection<EnumOptionData> toAccountTypeOptions,
final MonthDay recurrenceOnMonthDay = null; final Collection<PortfolioAccountData> toAccountOptions, final Collection<EnumOptionData> transferTypeOptions,
final Page<AccountTransferData> transactions = null; final Collection<EnumOptionData> statusOptions, final Collection<EnumOptionData> instructionTypeOptions,
final Collection<EnumOptionData> priorityOptions, final Collection<EnumOptionData> recurrenceTypeOptions,
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, final Collection<EnumOptionData> recurrenceFrequencyOptions) {
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, final Long id = null;
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, final Long accountDetailId = null;
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, final String name = null;
recurrenceFrequencyOptions); final OfficeData fromOffice = null;
} final OfficeData toOffice = null;
final ClientData fromClient = null;
public static StandingInstructionData instance(final Long id, final Long accountDetailId, final String name, final ClientData toClient = null;
final OfficeData fromOffice, final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, final EnumOptionData fromAccountType = null;
final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final EnumOptionData toAccountType, final PortfolioAccountData fromAccount = null;
final PortfolioAccountData toAccount, final EnumOptionData transferType, final EnumOptionData priority, final EnumOptionData toAccountType = null;
final EnumOptionData instructionType, final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, final PortfolioAccountData toAccount = null;
final LocalDate validTill, final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, final EnumOptionData transferType = null;
final Integer recurrenceInterval, final MonthDay recurrenceOnMonthDay) { final EnumOptionData priority = null;
final Page<AccountTransferData> transactions = null; final EnumOptionData instructionType = null;
final Collection<OfficeData> fromOfficeOptions = null; final EnumOptionData status = null;
final Collection<ClientData> fromClientOptions = null; final BigDecimal amount = null;
final Collection<EnumOptionData> fromAccountTypeOptions = null; final LocalDate validFrom = null;
final Collection<PortfolioAccountData> fromAccountOptions = null; final LocalDate validTill = null;
final Collection<OfficeData> toOfficeOptions = null; final EnumOptionData recurrenceType = null;
final Collection<ClientData> toClientOptions = null; final EnumOptionData recurrenceFrequency = null;
final Collection<EnumOptionData> toAccountTypeOptions = null; final Integer recurrenceInterval = null;
final Collection<PortfolioAccountData> toAccountOptions = null; final MonthDay recurrenceOnMonthDay = null;
final Collection<EnumOptionData> transferTypeOptions = null; final Page<AccountTransferData> transactions = null;
final Collection<EnumOptionData> statusOptions = null;
final Collection<EnumOptionData> instructionTypeOptions = null; return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
final Collection<EnumOptionData> priorityOptions = null; toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
final Collection<EnumOptionData> recurrenceTypeOptions = null; recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
final Collection<EnumOptionData> recurrenceFrequencyOptions = null; fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, recurrenceFrequencyOptions);
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, }
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, public static StandingInstructionData instance(final Long id, final Long accountDetailId, final String name,
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, final OfficeData fromOffice, final OfficeData toOffice, final ClientData fromClient, final ClientData toClient,
recurrenceFrequencyOptions); 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,
public static StandingInstructionData withTemplateData(StandingInstructionData instructionData, StandingInstructionData templateData) { final LocalDate validTill, final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency,
return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name, final Integer recurrenceInterval, final MonthDay recurrenceOnMonthDay) {
instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount, final Page<AccountTransferData> transactions = null;
instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount, final Collection<OfficeData> fromOfficeOptions = null;
instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status, final Collection<ClientData> fromClientOptions = null;
instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType, final Collection<EnumOptionData> fromAccountTypeOptions = null;
instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, final Collection<PortfolioAccountData> fromAccountOptions = null;
instructionData.transactions, templateData.fromOfficeOptions, templateData.fromClientOptions, final Collection<OfficeData> toOfficeOptions = null;
templateData.fromAccountTypeOptions, templateData.fromAccountOptions, templateData.toOfficeOptions, final Collection<ClientData> toClientOptions = null;
templateData.toClientOptions, templateData.toAccountTypeOptions, templateData.toAccountOptions, final Collection<EnumOptionData> toAccountTypeOptions = null;
templateData.transferTypeOptions, templateData.statusOptions, templateData.instructionTypeOptions, final Collection<PortfolioAccountData> toAccountOptions = null;
templateData.priorityOptions, templateData.recurrenceTypeOptions, templateData.recurrenceFrequencyOptions); final Collection<EnumOptionData> transferTypeOptions = null;
} final Collection<EnumOptionData> statusOptions = null;
final Collection<EnumOptionData> instructionTypeOptions = null;
private StandingInstructionData(final Long id, final Long accountDetailId, final String name, final OfficeData fromOffice, final Collection<EnumOptionData> priorityOptions = null;
final ClientData fromClient, final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final Collection<EnumOptionData> recurrenceTypeOptions = null;
final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount, final Collection<EnumOptionData> recurrenceFrequencyOptions = null;
final EnumOptionData transferType, final EnumOptionData priority, final EnumOptionData instructionType,
final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, LocalDate validTill, return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, final Integer recurrenceInterval, toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
final MonthDay recurrenceOnMonthDay, final Page<AccountTransferData> transactions, recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
final Collection<OfficeData> fromOfficeOptions, final Collection<ClientData> fromClientOptions, fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
final Collection<EnumOptionData> fromAccountTypeOptions, final Collection<PortfolioAccountData> fromAccountOptions, toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
final Collection<OfficeData> toOfficeOptions, final Collection<ClientData> toClientOptions, recurrenceFrequencyOptions);
final Collection<EnumOptionData> toAccountTypeOptions, final Collection<PortfolioAccountData> toAccountOptions, }
final Collection<EnumOptionData> transferTypeOptions, final Collection<EnumOptionData> statusOptions,
final Collection<EnumOptionData> instructionTypeOptions, final Collection<EnumOptionData> priorityOptions, public static StandingInstructionData withTemplateData(StandingInstructionData instructionData, StandingInstructionData templateData) {
final Collection<EnumOptionData> recurrenceTypeOptions, final Collection<EnumOptionData> recurrenceFrequencyOptions) { return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name,
this.id = id; instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount,
this.accountDetailId = accountDetailId; instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount,
this.name = name; instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status,
this.fromOffice = fromOffice; instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType,
this.fromClient = fromClient; instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay,
this.fromAccountType = fromAccountType; instructionData.transactions, templateData.fromOfficeOptions, templateData.fromClientOptions,
this.fromAccount = fromAccount; templateData.fromAccountTypeOptions, templateData.fromAccountOptions, templateData.toOfficeOptions,
this.toOffice = toOffice; templateData.toClientOptions, templateData.toAccountTypeOptions, templateData.toAccountOptions,
this.toClient = toClient; templateData.transferTypeOptions, templateData.statusOptions, templateData.instructionTypeOptions,
this.toAccountType = toAccountType; templateData.priorityOptions, templateData.recurrenceTypeOptions, templateData.recurrenceFrequencyOptions);
this.toAccount = toAccount; }
this.transferType = transferType;
private StandingInstructionData(final Long id, final Long accountDetailId, final String name, final OfficeData fromOffice,
this.priority = priority; final ClientData fromClient, final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount,
this.instructionType = instructionType; final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount,
this.status = status; final EnumOptionData transferType, final EnumOptionData priority, final EnumOptionData instructionType,
this.amount = amount; final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, LocalDate validTill,
this.validFrom = validFrom; final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, final Integer recurrenceInterval,
this.validTill = validTill; final MonthDay recurrenceOnMonthDay, final Page<AccountTransferData> transactions,
this.recurrenceType = recurrenceType; final Collection<OfficeData> fromOfficeOptions, final Collection<ClientData> fromClientOptions,
this.recurrenceFrequency = recurrenceFrequency; final Collection<EnumOptionData> fromAccountTypeOptions, final Collection<PortfolioAccountData> fromAccountOptions,
this.recurrenceInterval = recurrenceInterval; final Collection<OfficeData> toOfficeOptions, final Collection<ClientData> toClientOptions,
this.recurrenceOnMonthDay = recurrenceOnMonthDay; final Collection<EnumOptionData> toAccountTypeOptions, final Collection<PortfolioAccountData> toAccountOptions,
final Collection<EnumOptionData> transferTypeOptions, final Collection<EnumOptionData> statusOptions,
this.fromOfficeOptions = fromOfficeOptions; final Collection<EnumOptionData> instructionTypeOptions, final Collection<EnumOptionData> priorityOptions,
this.fromClientOptions = fromClientOptions; final Collection<EnumOptionData> recurrenceTypeOptions, final Collection<EnumOptionData> recurrenceFrequencyOptions) {
this.fromAccountTypeOptions = fromAccountTypeOptions; this.id = id;
this.fromAccountOptions = fromAccountOptions; this.accountDetailId = accountDetailId;
this.toOfficeOptions = toOfficeOptions; this.name = name;
this.toClientOptions = toClientOptions; this.fromOffice = fromOffice;
this.toAccountTypeOptions = toAccountTypeOptions; this.fromClient = fromClient;
this.toAccountOptions = toAccountOptions; this.fromAccountType = fromAccountType;
this.transferTypeOptions = transferTypeOptions; this.fromAccount = fromAccount;
this.statusOptions = statusOptions; this.toOffice = toOffice;
this.instructionTypeOptions = instructionTypeOptions; this.toClient = toClient;
this.priorityOptions = priorityOptions; this.toAccountType = toAccountType;
this.recurrenceTypeOptions = recurrenceTypeOptions; this.toAccount = toAccount;
this.recurrenceFrequencyOptions = recurrenceFrequencyOptions; this.transferType = transferType;
this.transactions = transactions;
} this.priority = priority;
this.instructionType = instructionType;
public static StandingInstructionData template(OfficeData fromOffice, ClientData fromClient, EnumOptionData fromAccountType, this.status = status;
PortfolioAccountData fromAccount, LocalDate transferDate, OfficeData toOffice, ClientData toClient, this.amount = amount;
EnumOptionData toAccountType, PortfolioAccountData toAccount, final Collection<OfficeData> fromOfficeOptions, this.validFrom = validFrom;
final Collection<ClientData> fromClientOptions, final Collection<EnumOptionData> fromAccountTypeOptions, this.validTill = validTill;
final Collection<PortfolioAccountData> fromAccountOptions, final Collection<OfficeData> toOfficeOptions, this.recurrenceType = recurrenceType;
final Collection<ClientData> toClientOptions, final Collection<EnumOptionData> toAccountTypeOptions, this.recurrenceFrequency = recurrenceFrequency;
final Collection<PortfolioAccountData> toAccountOptions, final Collection<EnumOptionData> transferTypeOptions, this.recurrenceInterval = recurrenceInterval;
final Collection<EnumOptionData> statusOptions, final Collection<EnumOptionData> instructionTypeOptions, this.recurrenceOnMonthDay = recurrenceOnMonthDay;
final Collection<EnumOptionData> priorityOptions, final Collection<EnumOptionData> recurrenceTypeOptions,
final Collection<EnumOptionData> recurrenceFrequencyOptions) { this.fromOfficeOptions = fromOfficeOptions;
final Long id = null; this.fromClientOptions = fromClientOptions;
final Long accountDetailId = null; this.fromAccountTypeOptions = fromAccountTypeOptions;
final String name = null; this.fromAccountOptions = fromAccountOptions;
final EnumOptionData transferType = null; this.toOfficeOptions = toOfficeOptions;
final EnumOptionData priority = null; this.toClientOptions = toClientOptions;
final EnumOptionData instructionType = null; this.toAccountTypeOptions = toAccountTypeOptions;
final EnumOptionData status = null; this.toAccountOptions = toAccountOptions;
final BigDecimal amount = null; this.transferTypeOptions = transferTypeOptions;
final LocalDate validFrom = null; this.statusOptions = statusOptions;
final LocalDate validTill = null; this.instructionTypeOptions = instructionTypeOptions;
final EnumOptionData recurrenceType = null; this.priorityOptions = priorityOptions;
final EnumOptionData recurrenceFrequency = null; this.recurrenceTypeOptions = recurrenceTypeOptions;
final Integer recurrenceInterval = null; this.recurrenceFrequencyOptions = recurrenceFrequencyOptions;
final MonthDay recurrenceOnMonthDay = null; this.transactions = transactions;
final Page<AccountTransferData> transactions = null; }
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice, public static StandingInstructionData template(OfficeData fromOffice, ClientData fromClient, EnumOptionData fromAccountType,
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill, PortfolioAccountData fromAccount, LocalDate transferDate, OfficeData toOffice, ClientData toClient,
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions, EnumOptionData toAccountType, PortfolioAccountData toAccount, final Collection<OfficeData> fromOfficeOptions,
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, final Collection<ClientData> fromClientOptions, final Collection<EnumOptionData> fromAccountTypeOptions,
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions, final Collection<PortfolioAccountData> fromAccountOptions, final Collection<OfficeData> toOfficeOptions,
recurrenceFrequencyOptions); final Collection<ClientData> toClientOptions, final Collection<EnumOptionData> toAccountTypeOptions,
} final Collection<PortfolioAccountData> toAccountOptions, final Collection<EnumOptionData> transferTypeOptions,
final Collection<EnumOptionData> statusOptions, final Collection<EnumOptionData> instructionTypeOptions,
public static StandingInstructionData withTransferData(StandingInstructionData instructionData, final Collection<EnumOptionData> priorityOptions, final Collection<EnumOptionData> recurrenceTypeOptions,
final Page<AccountTransferData> transactions) { final Collection<EnumOptionData> recurrenceFrequencyOptions) {
return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name, final Long id = null;
instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount, final Long accountDetailId = null;
instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount, final String name = null;
instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status, final EnumOptionData transferType = null;
instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType, final EnumOptionData priority = null;
instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, transactions, final EnumOptionData instructionType = null;
instructionData.fromOfficeOptions, instructionData.fromClientOptions, instructionData.fromAccountTypeOptions, final EnumOptionData status = null;
instructionData.fromAccountOptions, instructionData.toOfficeOptions, instructionData.toClientOptions, final BigDecimal amount = null;
instructionData.toAccountTypeOptions, instructionData.toAccountOptions, instructionData.transferTypeOptions, final LocalDate validFrom = null;
instructionData.statusOptions, instructionData.instructionTypeOptions, instructionData.priorityOptions, final LocalDate validTill = null;
instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions); final EnumOptionData recurrenceType = null;
} final EnumOptionData recurrenceFrequency = null;
final Integer recurrenceInterval = null;
public StandingInstructionType getInstructionType() { final MonthDay recurrenceOnMonthDay = null;
return Optional.ofNullable(this.instructionType).map(e -> StandingInstructionType.fromInt(e.getId().intValue())).orElse(null); final Page<AccountTransferData> transactions = null;
} return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
public AccountTransferRecurrenceType getRecurrenceType() { recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
return Optional.ofNullable(this.recurrenceType).map(e -> AccountTransferRecurrenceType.fromInt(e.getId().intValue())).orElse(null); fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
} toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
recurrenceFrequencyOptions);
public PeriodFrequencyType getRecurrenceFrequency() { }
return Optional.ofNullable(this.recurrenceFrequency).map(e -> PeriodFrequencyType.fromInt(e.getId().intValue())).orElse(null);
} public static StandingInstructionData withTransferData(StandingInstructionData instructionData,
final Page<AccountTransferData> transactions) {
public PortfolioAccountType getFromAccountType() { return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name,
return Optional.ofNullable(this.fromAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null); instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount,
} instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount,
instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status,
public PortfolioAccountType getToAccountType() { instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType,
return Optional.ofNullable(this.toAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null); instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, transactions,
} instructionData.fromOfficeOptions, instructionData.fromClientOptions, instructionData.fromAccountTypeOptions,
instructionData.fromAccountOptions, instructionData.toOfficeOptions, instructionData.toClientOptions,
public AccountTransferType getTransferType() { instructionData.toAccountTypeOptions, instructionData.toAccountOptions, instructionData.transferTypeOptions,
return Optional.ofNullable(this.transferType).map(e -> AccountTransferType.fromInt(e.getId().intValue())).orElse(null); instructionData.statusOptions, instructionData.instructionTypeOptions, instructionData.priorityOptions,
} instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions);
}
public Integer getRecurrenceOnDay() {
return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.DAY_OF_MONTH)).orElse(0); /**
} * Returns the domain enum for instruction type — used internally by business logic.
* NOTE: renamed from getInstructionType() to avoid overriding Lombok @Getter (which must
public Integer getRecurrenceOnMonth() { * return EnumOptionData so Jackson serializes it as an object, not a plain string).
return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.MONTH_OF_YEAR)).orElse(0); */
} public StandingInstructionType instructionTypeEnum() {
return Optional.ofNullable(this.instructionType).map(e -> StandingInstructionType.fromInt(e.getId().intValue())).orElse(null);
public Integer toTransferType() { }
Integer transferType = null;
AccountTransferType accountTransferType = getTransferType(); public AccountTransferRecurrenceType recurrenceTypeEnum() {
if (accountTransferType.isChargePayment()) { return Optional.ofNullable(this.recurrenceType).map(e -> AccountTransferRecurrenceType.fromInt(e.getId().intValue())).orElse(null);
transferType = LoanTransactionType.CHARGE_PAYMENT.getValue(); }
} else if (accountTransferType.isLoanRepayment()) {
transferType = LoanTransactionType.REPAYMENT.getValue(); public PeriodFrequencyType recurrenceFrequencyEnum() {
} return Optional.ofNullable(this.recurrenceFrequency).map(e -> PeriodFrequencyType.fromInt(e.getId().intValue())).orElse(null);
return transferType; }
}
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;
}
}
@@ -1,294 +1,296 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.data; package org.apache.fineract.portfolio.account.data;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; 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.toAccountTypeParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.MonthDay; import java.time.MonthDay;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.account.AccountDetailConstants; import org.apache.fineract.portfolio.account.AccountDetailConstants;
import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants; import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants;
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
import org.apache.fineract.portfolio.account.domain.AccountTransferType; import org.apache.fineract.portfolio.account.domain.AccountTransferType;
import org.apache.fineract.portfolio.account.domain.StandingInstructionType; import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
public class StandingInstructionDataValidator { public class StandingInstructionDataValidator {
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
private final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator; private final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator;
private static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName, private static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName,
AccountDetailConstants.dateFormatParamName, AccountDetailConstants.fromOfficeIdParamName, AccountDetailConstants.dateFormatParamName, AccountDetailConstants.fromOfficeIdParamName,
AccountDetailConstants.fromClientIdParamName, AccountDetailConstants.fromAccountTypeParamName, AccountDetailConstants.fromClientIdParamName, AccountDetailConstants.fromAccountTypeParamName,
AccountDetailConstants.fromAccountIdParamName, AccountDetailConstants.toOfficeIdParamName, AccountDetailConstants.fromAccountIdParamName, AccountDetailConstants.toOfficeIdParamName,
AccountDetailConstants.toClientIdParamName, AccountDetailConstants.toAccountTypeParamName, AccountDetailConstants.toClientIdParamName, AccountDetailConstants.toAccountTypeParamName,
AccountDetailConstants.toAccountIdParamName, AccountDetailConstants.transferTypeParamName, AccountDetailConstants.toAccountIdParamName, AccountDetailConstants.transferTypeParamName,
StandingInstructionApiConstants.priorityParamName, StandingInstructionApiConstants.instructionTypeParamName, StandingInstructionApiConstants.priorityParamName, StandingInstructionApiConstants.instructionTypeParamName,
StandingInstructionApiConstants.statusParamName, StandingInstructionApiConstants.amountParamName, StandingInstructionApiConstants.statusParamName, StandingInstructionApiConstants.amountParamName,
StandingInstructionApiConstants.validFromParamName, StandingInstructionApiConstants.validTillParamName, StandingInstructionApiConstants.validFromParamName, StandingInstructionApiConstants.validTillParamName,
StandingInstructionApiConstants.recurrenceTypeParamName, StandingInstructionApiConstants.recurrenceFrequencyParamName, StandingInstructionApiConstants.recurrenceTypeParamName, StandingInstructionApiConstants.recurrenceFrequencyParamName,
StandingInstructionApiConstants.recurrenceIntervalParamName, StandingInstructionApiConstants.recurrenceOnMonthDayParamName, StandingInstructionApiConstants.recurrenceIntervalParamName, StandingInstructionApiConstants.recurrenceOnMonthDayParamName,
StandingInstructionApiConstants.nameParamName, StandingInstructionApiConstants.monthDayFormatParamName)); StandingInstructionApiConstants.nameParamName, StandingInstructionApiConstants.monthDayFormatParamName));
private static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName, private static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName,
AccountDetailConstants.dateFormatParamName, StandingInstructionApiConstants.priorityParamName, AccountDetailConstants.dateFormatParamName, StandingInstructionApiConstants.priorityParamName,
StandingInstructionApiConstants.instructionTypeParamName, StandingInstructionApiConstants.statusParamName, StandingInstructionApiConstants.instructionTypeParamName, StandingInstructionApiConstants.statusParamName,
StandingInstructionApiConstants.amountParamName, StandingInstructionApiConstants.validFromParamName, StandingInstructionApiConstants.amountParamName, StandingInstructionApiConstants.validFromParamName,
StandingInstructionApiConstants.validTillParamName, StandingInstructionApiConstants.recurrenceTypeParamName, StandingInstructionApiConstants.validTillParamName, StandingInstructionApiConstants.recurrenceTypeParamName,
StandingInstructionApiConstants.recurrenceFrequencyParamName, StandingInstructionApiConstants.recurrenceIntervalParamName, StandingInstructionApiConstants.recurrenceFrequencyParamName, StandingInstructionApiConstants.recurrenceIntervalParamName,
StandingInstructionApiConstants.recurrenceOnMonthDayParamName, StandingInstructionApiConstants.monthDayFormatParamName)); StandingInstructionApiConstants.recurrenceOnMonthDayParamName, StandingInstructionApiConstants.monthDayFormatParamName));
@Autowired @Autowired
public StandingInstructionDataValidator(final FromJsonHelper fromApiJsonHelper, public StandingInstructionDataValidator(final FromJsonHelper fromApiJsonHelper,
final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator) { final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator) {
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
this.accountTransfersDetailDataValidator = accountTransfersDetailDataValidator; this.accountTransfersDetailDataValidator = accountTransfersDetailDataValidator;
} }
public void validateForCreate(final JsonCommand command) { public void validateForCreate(final JsonCommand command) {
final String json = command.json(); final String json = command.json();
if (StringUtils.isBlank(json)) { if (StringUtils.isBlank(json)) {
throw new InvalidJsonException(); throw new InvalidJsonException();
} }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType(); final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CREATE_REQUEST_DATA_PARAMETERS); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CREATE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); .resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
this.accountTransfersDetailDataValidator.validate(command, baseDataValidator); this.accountTransfersDetailDataValidator.validate(command, baseDataValidator);
final JsonElement element = command.parsedJson(); final JsonElement element = command.parsedJson();
final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element, final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element,
Locale.getDefault()); Locale.getDefault());
baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, 2); baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, 2);
final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName, final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName,
element); element);
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull(); baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull();
final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName, final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName,
element); element);
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill) baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill)
.validateDateAfter(validFrom); .validateDateAfter(validFrom);
final BigDecimal transferAmount = this.fromApiJsonHelper final BigDecimal transferAmount = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element); .extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element);
baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount(); baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount();
final Integer transferType = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); final Integer transferType = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(transferTypeParamName).value(transferType).notNull().inMinMaxRange(1, 3); baseDataValidator.reset().parameter(transferTypeParamName).value(transferType).notNull().inMinMaxRange(1, 3);
final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element, final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element,
Locale.getDefault()); Locale.getDefault());
baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull().inMinMaxRange(1, baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull().inMinMaxRange(1,
4); 4);
final Integer standingInstructionType = this.fromApiJsonHelper final Integer standingInstructionType = this.fromApiJsonHelper
.extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault()); .extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
.notNull().inMinMaxRange(1, 2); .notNull().inMinMaxRange(1, 2);
final Integer recurrenceType = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName, final Integer recurrenceType = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName,
element, Locale.getDefault()); element, Locale.getDefault());
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
.inMinMaxRange(1, 2); .inMinMaxRange(1, 2);
boolean isPeriodic = false; boolean isPeriodic = false;
if (recurrenceType != null) { if (recurrenceType != null) {
isPeriodic = AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence(); isPeriodic = AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence();
} }
final Integer recurrenceFrequency = this.fromApiJsonHelper final Integer recurrenceFrequency = this.fromApiJsonHelper
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault()); .extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency)
.inMinMaxRange(0, 3); .inMinMaxRange(0, 3);
if (recurrenceFrequency != null) { if (recurrenceFrequency != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(recurrenceFrequency); PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(recurrenceFrequency);
if (frequencyType.isMonthly() || frequencyType.isYearly()) { if (frequencyType.isMonthly() || frequencyType.isYearly()) {
final MonthDay monthDay = this.fromApiJsonHelper final MonthDay monthDay = this.fromApiJsonHelper
.extractMonthDayNamed(StandingInstructionApiConstants.recurrenceOnMonthDayParamName, element); .extractMonthDayNamed(StandingInstructionApiConstants.recurrenceOnMonthDayParamName, element);
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceOnMonthDayParamName).value(monthDay) baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceOnMonthDayParamName).value(monthDay)
.notNull(); .notNull();
} }
} }
final Integer recurrenceInterval = this.fromApiJsonHelper final Integer recurrenceInterval = this.fromApiJsonHelper
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault()); .extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault());
if (isPeriodic) { if (isPeriodic) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval)
.notNull(); .notNull();
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency)
.notNull(); .notNull();
} }
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval)
.integerGreaterThanZero(); .integerGreaterThanZero();
final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element); final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element);
baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull(); baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull();
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
if (toAccountType != null && PortfolioAccountType.fromInt(toAccountType).isSavingsAccount()) { if (toAccountType != null && (PortfolioAccountType.fromInt(toAccountType).isSavingsAccount()
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) || PortfolioAccountType.fromInt(toAccountType).isCurrentAccount())) {
.notNull().inMinMaxRange(1, 1); baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() .notNull().inMinMaxRange(1, 1);
.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(); if (standingInstructionType != null && StandingInstructionType.fromInt(standingInstructionType).isFixedAmoutTransfer()) {
} baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).notNull();
}
String errorCode = null;
AccountTransferType accountTransferType = AccountTransferType.fromInt(transferType); String errorCode = null;
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); AccountTransferType accountTransferType = AccountTransferType.fromInt(transferType);
if (fromAccountType != null && toAccountType != null) { final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
PortfolioAccountType fromPortfolioAccountType = PortfolioAccountType.fromInt(fromAccountType); if (fromAccountType != null && toAccountType != null) {
PortfolioAccountType toPortfolioAccountType = PortfolioAccountType.fromInt(toAccountType); PortfolioAccountType fromPortfolioAccountType = PortfolioAccountType.fromInt(fromAccountType);
if (accountTransferType.isAccountTransfer() PortfolioAccountType toPortfolioAccountType = PortfolioAccountType.fromInt(toAccountType);
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) { if (accountTransferType.isAccountTransfer()
errorCode = "not.account.transfer"; && (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) {
} else if (accountTransferType.isLoanRepayment() errorCode = "not.account.transfer";
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isSavingsAccount())) { } else if (accountTransferType.isLoanRepayment()
errorCode = "not.loan.repayment"; && (fromPortfolioAccountType.isLoanAccount()
} || (toPortfolioAccountType.isSavingsAccount() || toPortfolioAccountType.isCurrentAccount()))) {
if (errorCode != null) { errorCode = "not.loan.repayment";
baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode); }
} if (errorCode != null) {
} baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors); }
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
public void validateForUpdate(final JsonCommand command) { }
final String json = command.json();
public void validateForUpdate(final JsonCommand command) {
if (StringUtils.isBlank(json)) { final String json = command.json();
throw new InvalidJsonException();
} if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType(); }
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, UPDATE_REQUEST_DATA_PARAMETERS);
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, UPDATE_REQUEST_DATA_PARAMETERS);
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
final JsonElement element = command.parsedJson(); .resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validFromParamName, element)) {
final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName, final JsonElement element = command.parsedJson();
element); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validFromParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull(); 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); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validTillParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill).notNull(); 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); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.amountParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount(); 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()); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.statusParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element,
2); 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()); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.priorityParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull() final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element,
.inMinMaxRange(1, 4); 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()); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.instructionTypeParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType) final Integer standingInstructionType = this.fromApiJsonHelper
.notNull().inMinMaxRange(1, 2); .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()); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceTypeParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull() final Integer recurrenceType = this.fromApiJsonHelper
.inMinMaxRange(1, 2); .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()); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceFrequencyParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency) final Integer recurrenceFrequency = this.fromApiJsonHelper
.inMinMaxRange(0, 3); .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()); if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceIntervalParamName, element)) {
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval) final Integer recurrenceInterval = this.fromApiJsonHelper
.integerGreaterThanZero(); .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(); 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); }
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { }
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
} if (!dataValidationErrors.isEmpty()) {
} throw new PlatformApiDataValidationException(dataValidationErrors);
} }
}
}
@@ -1,214 +1,314 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.domain; package org.apache.fineract.portfolio.account.domain;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName; 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.fromClientIdParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName; 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.toAccountIdParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName; 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.toOfficeIdParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import java.util.Locale; import java.util.Locale;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper; import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.springframework.beans.factory.annotation.Autowired; import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.springframework.stereotype.Service; import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
import org.springframework.beans.factory.annotation.Autowired;
@Service import org.springframework.stereotype.Service;
public class AccountTransferDetailAssembler {
@Service
private final ClientRepositoryWrapper clientRepository; public class AccountTransferDetailAssembler {
private final OfficeRepositoryWrapper officeRepositoryWrapper;
private final SavingsAccountAssembler savingsAccountAssembler; private final ClientRepositoryWrapper clientRepository;
private final FromJsonHelper fromApiJsonHelper; private final OfficeRepositoryWrapper officeRepositoryWrapper;
private final LoanAssembler loanAccountAssembler; private final SavingsAccountAssembler savingsAccountAssembler;
private final FromJsonHelper fromApiJsonHelper;
@Autowired private final LoanAssembler loanAccountAssembler;
public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository, private final CurrentAccountRepository currentAccountRepository;
final OfficeRepositoryWrapper officeRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler,
final FromJsonHelper fromApiJsonHelper, final LoanAssembler loanAccountAssembler) { @Autowired
this.clientRepository = clientRepository; public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository,
this.officeRepositoryWrapper = officeRepositoryWrapper; final OfficeRepositoryWrapper officeRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler,
this.savingsAccountAssembler = savingsAccountAssembler; final FromJsonHelper fromApiJsonHelper, final LoanAssembler loanAccountAssembler,
this.fromApiJsonHelper = fromApiJsonHelper; final CurrentAccountRepository currentAccountRepository) {
this.loanAccountAssembler = loanAccountAssembler; this.clientRepository = clientRepository;
} this.officeRepositoryWrapper = officeRepositoryWrapper;
this.savingsAccountAssembler = savingsAccountAssembler;
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) { this.fromApiJsonHelper = fromApiJsonHelper;
this.loanAccountAssembler = loanAccountAssembler;
final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); this.currentAccountRepository = currentAccountRepository;
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false); }
final boolean backdatedTxnsAllowedTill = false; public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill); final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName);
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false);
return assembleSavingsToSavingsTransfer(command, fromSavingsAccount, toSavingsAccount);
final boolean backdatedTxnsAllowedTill = false;
} final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill);
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) {
return assembleSavingsToSavingsTransfer(command, fromSavingsAccount, toSavingsAccount);
final Long fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
final boolean backdatedTxnsAllowedTill = false; }
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, backdatedTxnsAllowedTill);
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) {
final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId); final Long fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
final boolean backdatedTxnsAllowedTill = false;
return assembleSavingsToLoanTransfer(command, fromSavingsAccount, toLoanAccount); final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, backdatedTxnsAllowedTill);
} final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId);
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) {
return assembleSavingsToLoanTransfer(command, fromSavingsAccount, toLoanAccount);
final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId); }
final boolean backdatedTxnsAllowedTill = false;
final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName); public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) {
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill);
final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
return assembleLoanToSavingsTransfer(command, fromLoanAccount, toSavingsAccount); final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId);
} final boolean backdatedTxnsAllowedTill = false;
final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill);
final SavingsAccount toSavingsAccount) {
return assembleLoanToSavingsTransfer(command, fromLoanAccount, toSavingsAccount);
final JsonElement element = command.parsedJson(); }
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount,
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); final SavingsAccount toSavingsAccount) {
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); final JsonElement element = command.parsedJson();
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
toSavingsAccount, transfertype); final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
} final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient,
final Loan toLoanAccount) { toSavingsAccount, transfertype);
final JsonElement element = command.parsedJson(); }
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount,
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); final Loan toLoanAccount) {
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); final JsonElement element = command.parsedJson();
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
transfertype); final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
} final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount, return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
final SavingsAccount toSavingsAccount) { transfertype);
final JsonElement element = command.parsedJson(); }
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount,
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId); final SavingsAccount toSavingsAccount) {
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); final JsonElement element = command.parsedJson();
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault()); final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
transfertype); 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());
public AccountTransferDetails assembleSavingsToLoanTransfer(final SavingsAccount fromSavingsAccount, final Loan toLoanAccount,
Integer transferType) { return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
final Office fromOffice = fromSavingsAccount.office(); transfertype);
final Client fromClient = fromSavingsAccount.getClient(); }
final Office toOffice = toLoanAccount.getOffice();
final Client toClient = toLoanAccount.client(); public AccountTransferDetails assembleSavingsToLoanTransfer(final SavingsAccount fromSavingsAccount, final Loan toLoanAccount,
Integer transferType) {
return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, final Office fromOffice = fromSavingsAccount.office();
transferType); final Client fromClient = fromSavingsAccount.getClient();
final Office toOffice = toLoanAccount.getOffice();
} final Client toClient = toLoanAccount.client();
public AccountTransferDetails assembleSavingsToSavingsTransfer(final SavingsAccount fromSavingsAccount, return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
final SavingsAccount toSavingsAccount, Integer transferType) { transferType);
final Office fromOffice = fromSavingsAccount.office();
final Client fromClient = fromSavingsAccount.getClient(); }
final Office toOffice = toSavingsAccount.office();
final Client toClient = toSavingsAccount.getClient(); public AccountTransferDetails assembleSavingsToSavingsTransfer(final SavingsAccount fromSavingsAccount,
final SavingsAccount toSavingsAccount, Integer transferType) {
return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, final Office fromOffice = fromSavingsAccount.office();
toSavingsAccount, transferType); final Client fromClient = fromSavingsAccount.getClient();
} final Office toOffice = toSavingsAccount.office();
final Client toClient = toSavingsAccount.getClient();
public AccountTransferDetails assembleLoanToSavingsTransfer(final Loan fromLoanAccount, final SavingsAccount toSavingsAccount,
Integer transferType) { return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient,
final Office fromOffice = fromLoanAccount.getOffice(); toSavingsAccount, transferType);
final Client fromClient = fromLoanAccount.client(); }
final Office toOffice = toSavingsAccount.office();
final Client toClient = toSavingsAccount.getClient(); public AccountTransferDetails assembleLoanToSavingsTransfer(final Loan fromLoanAccount, final SavingsAccount toSavingsAccount,
Integer transferType) {
return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, final Office fromOffice = fromLoanAccount.getOffice();
transferType); final Client fromClient = fromLoanAccount.client();
} final Office toOffice = toSavingsAccount.office();
final Client toClient = toSavingsAccount.getClient();
public AccountTransferDetails assembleLoanToLoanTransfer(Loan fromLoanAccount, Loan toLoanAccount, Integer transferType) {
final Office fromOffice = fromLoanAccount.getOffice(); return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
final Client fromClient = fromLoanAccount.client(); transferType);
final Office toOffice = toLoanAccount.getOffice(); }
final Client toClient = toLoanAccount.client();
public AccountTransferDetails assembleLoanToLoanTransfer(Loan fromLoanAccount, Loan toLoanAccount, Integer transferType) {
return AccountTransferDetails.loanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount, final Office fromOffice = fromLoanAccount.getOffice();
transferType); 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);
}
}
@@ -1,165 +1,216 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.domain; package org.apache.fineract.portfolio.account.domain;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne; import jakarta.persistence.OneToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
@Entity
@Table(name = "m_account_transfer_details") @Entity
public class AccountTransferDetails extends AbstractPersistableCustom<Long> { @Table(name = "m_account_transfer_details")
public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
@ManyToOne
@JoinColumn(name = "from_office_id", nullable = false) @ManyToOne
private Office fromOffice; @JoinColumn(name = "from_office_id", nullable = false)
private Office fromOffice;
@ManyToOne
@JoinColumn(name = "from_client_id", nullable = false) @ManyToOne
private Client fromClient; @JoinColumn(name = "from_client_id", nullable = false)
private Client fromClient;
@ManyToOne
@JoinColumn(name = "from_savings_account_id", nullable = true) @ManyToOne
private SavingsAccount fromSavingsAccount; @JoinColumn(name = "from_savings_account_id", nullable = true)
private SavingsAccount fromSavingsAccount;
@ManyToOne
@JoinColumn(name = "to_office_id", nullable = false) @ManyToOne
private Office toOffice; @JoinColumn(name = "to_office_id", nullable = false)
private Office toOffice;
@ManyToOne
@JoinColumn(name = "to_client_id", nullable = false) @ManyToOne
private Client toClient; @JoinColumn(name = "to_client_id", nullable = false)
private Client toClient;
@ManyToOne
@JoinColumn(name = "to_savings_account_id", nullable = true) @ManyToOne
private SavingsAccount toSavingsAccount; @JoinColumn(name = "to_savings_account_id", nullable = true)
private SavingsAccount toSavingsAccount;
@ManyToOne
@JoinColumn(name = "to_loan_account_id", nullable = true) @ManyToOne
private Loan toLoanAccount; @JoinColumn(name = "to_loan_account_id", nullable = true)
private Loan toLoanAccount;
@ManyToOne
@JoinColumn(name = "from_loan_account_id", nullable = true) @ManyToOne
private Loan fromLoanAccount; @JoinColumn(name = "from_loan_account_id", nullable = true)
private Loan fromLoanAccount;
@Column(name = "transfer_type")
private Integer transferType; @ManyToOne
@JoinColumn(name = "from_current_account_id", nullable = true)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountTransferDetails", orphanRemoval = true, fetch = FetchType.EAGER) private CurrentAccount fromCurrentAccount;
private List<AccountTransferTransaction> accountTransferTransactions = new ArrayList<>();
@ManyToOne
@OneToOne(mappedBy = "accountTransferDetails", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER) @JoinColumn(name = "to_current_account_id", nullable = true)
private AccountTransferStandingInstruction accountTransferStandingInstruction; private CurrentAccount toCurrentAccount;
public static AccountTransferDetails savingsToSavingsTransfer(final Office fromOffice, final Client fromClient, @Column(name = "transfer_type")
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, private Integer transferType;
Integer transferType) {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountTransferDetails", orphanRemoval = true, fetch = FetchType.EAGER)
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, toSavingsAccount, null, private List<AccountTransferTransaction> accountTransferTransactions = new ArrayList<>();
transferType, null);
} @OneToOne(mappedBy = "accountTransferDetails", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER)
private AccountTransferStandingInstruction accountTransferStandingInstruction;
public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient,
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount, public static AccountTransferDetails savingsToSavingsTransfer(final Office fromOffice, final Client fromClient,
Integer transferType) { final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, null, toLoanAccount, Integer transferType) {
transferType, null);
} return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, toSavingsAccount,
null, 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, toOffice, toClient, toSavingsAccount, null, public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient,
transferType, null); 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,
protected AccountTransferDetails() { null, transferType, null);
// }
}
public static AccountTransferDetails loanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount,
private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, Integer transferType) {
final Loan fromLoanAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, toSavingsAccount, null,
final Loan toLoanAccount, final Integer transferType, null, transferType, null);
final AccountTransferStandingInstruction accountTransferStandingInstruction) { }
this.fromOffice = fromOffice;
this.fromClient = fromClient; protected AccountTransferDetails() {
this.fromSavingsAccount = fromSavingsAccount; //
this.fromLoanAccount = fromLoanAccount; }
this.toOffice = toOffice;
this.toClient = toClient; private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount,
this.toSavingsAccount = toSavingsAccount; final Loan fromLoanAccount, final CurrentAccount fromCurrentAccount,
this.toLoanAccount = toLoanAccount; final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
this.transferType = transferType; final Loan toLoanAccount, final CurrentAccount toCurrentAccount,
this.accountTransferStandingInstruction = accountTransferStandingInstruction; final Integer transferType,
} final AccountTransferStandingInstruction accountTransferStandingInstruction) {
this.fromOffice = fromOffice;
public SavingsAccount toSavingsAccount() { this.fromClient = fromClient;
return this.toSavingsAccount; this.fromSavingsAccount = fromSavingsAccount;
} this.fromLoanAccount = fromLoanAccount;
this.fromCurrentAccount = fromCurrentAccount;
public SavingsAccount fromSavingsAccount() { this.toOffice = toOffice;
return this.fromSavingsAccount; this.toClient = toClient;
} this.toSavingsAccount = toSavingsAccount;
this.toLoanAccount = toLoanAccount;
public void addAccountTransferTransaction(AccountTransferTransaction accountTransferTransaction) { this.toCurrentAccount = toCurrentAccount;
this.accountTransferTransactions.add(accountTransferTransaction); this.transferType = transferType;
} this.accountTransferStandingInstruction = accountTransferStandingInstruction;
}
public void updateAccountTransferStandingInstruction(final AccountTransferStandingInstruction accountTransferStandingInstruction) {
this.accountTransferStandingInstruction = accountTransferStandingInstruction; public SavingsAccount toSavingsAccount() {
} return this.toSavingsAccount;
}
public Loan toLoanAccount() {
return this.toLoanAccount; public SavingsAccount fromSavingsAccount() {
} return this.fromSavingsAccount;
}
public Loan fromLoanAccount() {
return this.fromLoanAccount; public void addAccountTransferTransaction(AccountTransferTransaction accountTransferTransaction) {
} this.accountTransferTransactions.add(accountTransferTransaction);
}
public AccountTransferStandingInstruction accountTransferStandingInstruction() {
return this.accountTransferStandingInstruction; public void updateAccountTransferStandingInstruction(final AccountTransferStandingInstruction accountTransferStandingInstruction) {
} this.accountTransferStandingInstruction = accountTransferStandingInstruction;
}
public AccountTransferType transferType() {
return AccountTransferType.fromInt(this.transferType); public Loan toLoanAccount() {
} return this.toLoanAccount;
}
public static AccountTransferDetails loanToLoanTransfer(Office fromOffice, Client fromClient, Loan fromLoanAccount, Office toOffice,
Client toClient, Loan toLoanAccount, Integer transferType) { public Loan fromLoanAccount() {
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, null, toLoanAccount, return this.fromLoanAccount;
transferType, null); }
}
public CurrentAccount fromCurrentAccount() {
public List<AccountTransferTransaction> getAccountTransferTransactions() { return this.fromCurrentAccount;
return accountTransferTransactions; }
}
} 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<AccountTransferTransaction> getAccountTransferTransactions() {
return accountTransferTransactions;
}
}
@@ -1,290 +1,302 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.domain; package org.apache.fineract.portfolio.account.domain;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName; 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.STANDING_INSTRUCTION_RESOURCE_NAME;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName; 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.instructionTypeParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; 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.recurrenceFrequencyParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; 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.recurrenceOnMonthDayParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; 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.statusParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint; import jakarta.persistence.UniqueConstraint;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.MonthDay; import java.time.MonthDay;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
@Entity @Entity
@Table(name = "m_account_transfer_standing_instructions", uniqueConstraints = { @Table(name = "m_account_transfer_standing_instructions", uniqueConstraints = {
@UniqueConstraint(columnNames = { "name" }, name = "name") }) @UniqueConstraint(columnNames = { "name" }, name = "name") })
public class AccountTransferStandingInstruction extends AbstractPersistableCustom<Long> { public class AccountTransferStandingInstruction extends AbstractPersistableCustom<Long> {
@ManyToOne @ManyToOne
@JoinColumn(name = "account_transfer_details_id", nullable = true) @JoinColumn(name = "account_transfer_details_id", nullable = true)
private AccountTransferDetails accountTransferDetails; private AccountTransferDetails accountTransferDetails;
@Column(name = "name") @Column(name = "name")
private String name; private String name;
@Column(name = "priority") @Column(name = "priority")
private Integer priority; private Integer priority;
@Column(name = "instruction_type") @Column(name = "instruction_type")
private Integer instructionType; private Integer instructionType;
@Column(name = "status") @Column(name = "status")
private Integer status; private Integer status;
@Column(name = "amount", scale = 6, precision = 19, nullable = true) @Column(name = "amount", scale = 6, precision = 19, nullable = true)
private BigDecimal amount; private BigDecimal amount;
@Column(name = "valid_from") @Column(name = "valid_from")
private LocalDate validFrom; private LocalDate validFrom;
@Column(name = "valid_till") @Column(name = "valid_till")
private LocalDate validTill; private LocalDate validTill;
@Column(name = "recurrence_type") @Column(name = "recurrence_type")
private Integer recurrenceType; private Integer recurrenceType;
@Column(name = "recurrence_frequency") @Column(name = "recurrence_frequency")
private Integer recurrenceFrequency; private Integer recurrenceFrequency;
@Column(name = "recurrence_interval") @Column(name = "recurrence_interval")
private Integer recurrenceInterval; private Integer recurrenceInterval;
@Column(name = "recurrence_on_day") @Column(name = "recurrence_on_day")
private Integer recurrenceOnDay; private Integer recurrenceOnDay;
@Column(name = "recurrence_on_month") @Column(name = "recurrence_on_month")
private Integer recurrenceOnMonth; private Integer recurrenceOnMonth;
@Column(name = "last_run_date") @Column(name = "last_run_date")
private LocalDate latsRunDate; private LocalDate latsRunDate;
protected AccountTransferStandingInstruction() { protected AccountTransferStandingInstruction() {
} }
public static AccountTransferStandingInstruction create(final AccountTransferDetails accountTransferDetails, final String name, 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 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 LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval,
final MonthDay recurrenceOnMonthDay) { final MonthDay recurrenceOnMonthDay) {
Integer recurrenceOnDay = null; Integer recurrenceOnDay = null;
Integer recurrenceOnMonth = null; Integer recurrenceOnMonth = null;
if (recurrenceOnMonthDay != null) { if (recurrenceOnMonthDay != null) {
recurrenceOnDay = recurrenceOnMonthDay.getDayOfMonth(); recurrenceOnDay = recurrenceOnMonthDay.getDayOfMonth();
recurrenceOnMonth = recurrenceOnMonthDay.getMonthValue(); recurrenceOnMonth = recurrenceOnMonthDay.getMonthValue();
} }
return new AccountTransferStandingInstruction(accountTransferDetails, name, priority, instructionType, status, amount, validFrom, return new AccountTransferStandingInstruction(accountTransferDetails, name, priority, instructionType, status, amount, validFrom,
validTill, recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnDay, recurrenceOnMonth); validTill, recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnDay, recurrenceOnMonth);
} }
private AccountTransferStandingInstruction(final AccountTransferDetails accountTransferDetails, final String name, private AccountTransferStandingInstruction(final AccountTransferDetails accountTransferDetails, final String name,
final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom, 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 LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval,
final Integer recurrenceOnDay, final Integer recurrenceOnMonth) { final Integer recurrenceOnDay, final Integer recurrenceOnMonth) {
this.accountTransferDetails = accountTransferDetails; this.accountTransferDetails = accountTransferDetails;
this.name = name; this.name = name;
this.priority = priority; this.priority = priority;
this.instructionType = instructionType; this.instructionType = instructionType;
this.status = status; this.status = status;
this.amount = amount; this.amount = amount;
this.validFrom = validFrom; this.validFrom = validFrom;
this.validTill = validTill; this.validTill = validTill;
this.recurrenceType = recurrenceType; this.recurrenceType = recurrenceType;
this.recurrenceFrequency = recurrenceFrequency; this.recurrenceFrequency = recurrenceFrequency;
this.recurrenceInterval = recurrenceInterval; this.recurrenceInterval = recurrenceInterval;
this.recurrenceOnDay = recurrenceOnDay; this.recurrenceOnDay = recurrenceOnDay;
this.recurrenceOnMonth = recurrenceOnMonth; this.recurrenceOnMonth = recurrenceOnMonth;
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(STANDING_INSTRUCTION_RESOURCE_NAME); .resource(STANDING_INSTRUCTION_RESOURCE_NAME);
validateDependencies(baseDataValidator); validateDependencies(baseDataValidator);
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
public Map<String, Object> update(JsonCommand command) { public Map<String, Object> update(JsonCommand command) {
final Map<String, Object> actualChanges = new HashMap<>(); final Map<String, Object> actualChanges = new HashMap<>();
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(STANDING_INSTRUCTION_RESOURCE_NAME); .resource(STANDING_INSTRUCTION_RESOURCE_NAME);
if (StandingInstructionStatus.fromInt(this.status).isDeleted()) { if (StandingInstructionStatus.fromInt(this.status).isDeleted()) {
baseDataValidator.reset().parameter(statusParamName).failWithCode("can.not.modify.once.deleted"); baseDataValidator.reset().parameter(statusParamName).failWithCode("can.not.modify.once.deleted");
} }
if (command.isChangeInDateParameterNamed(validFromParamName, this.validFrom)) { if (command.isChangeInDateParameterNamed(validFromParamName, this.validFrom)) {
this.validFrom = command.localDateValueOfParameterNamed(validFromParamName); this.validFrom = command.localDateValueOfParameterNamed(validFromParamName);
actualChanges.put(validFromParamName, this.validFrom); actualChanges.put(validFromParamName, this.validFrom);
} }
if (command.isChangeInDateParameterNamed(validTillParamName, this.validTill)) { if (command.isChangeInDateParameterNamed(validTillParamName, this.validTill)) {
this.validTill = command.localDateValueOfParameterNamed(validTillParamName); this.validTill = command.localDateValueOfParameterNamed(validTillParamName);
actualChanges.put(validTillParamName, this.validTill); actualChanges.put(validTillParamName, this.validTill);
} }
if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) { if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName); final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName);
actualChanges.put(amountParamName, newValue); actualChanges.put(amountParamName, newValue);
this.amount = newValue; this.amount = newValue;
} }
if (command.isChangeInIntegerParameterNamed(statusParamName, this.status)) { if (command.isChangeInIntegerParameterNamed(statusParamName, this.status)) {
final Integer newValue = command.integerValueOfParameterNamed(statusParamName); final Integer newValue = command.integerValueOfParameterNamed(statusParamName);
actualChanges.put(statusParamName, newValue); actualChanges.put(statusParamName, newValue);
this.status = newValue; this.status = newValue;
} }
if (command.isChangeInIntegerParameterNamed(priorityParamName, this.priority)) { if (command.isChangeInIntegerParameterNamed(priorityParamName, this.priority)) {
final Integer newValue = command.integerValueOfParameterNamed(priorityParamName); final Integer newValue = command.integerValueOfParameterNamed(priorityParamName);
actualChanges.put(priorityParamName, newValue); actualChanges.put(priorityParamName, newValue);
this.priority = newValue; this.priority = newValue;
} }
if (command.isChangeInIntegerParameterNamed(instructionTypeParamName, this.instructionType)) { if (command.isChangeInIntegerParameterNamed(instructionTypeParamName, this.instructionType)) {
final Integer newValue = command.integerValueOfParameterNamed(instructionTypeParamName); final Integer newValue = command.integerValueOfParameterNamed(instructionTypeParamName);
actualChanges.put(instructionTypeParamName, newValue); actualChanges.put(instructionTypeParamName, newValue);
this.instructionType = newValue; this.instructionType = newValue;
} }
if (command.isChangeInIntegerParameterNamed(recurrenceTypeParamName, this.recurrenceType)) { if (command.isChangeInIntegerParameterNamed(recurrenceTypeParamName, this.recurrenceType)) {
final Integer newValue = command.integerValueOfParameterNamed(recurrenceTypeParamName); final Integer newValue = command.integerValueOfParameterNamed(recurrenceTypeParamName);
actualChanges.put(recurrenceTypeParamName, newValue); actualChanges.put(recurrenceTypeParamName, newValue);
this.recurrenceType = newValue; this.recurrenceType = newValue;
} }
if (command.isChangeInIntegerParameterNamed(recurrenceFrequencyParamName, this.recurrenceFrequency)) { if (command.isChangeInIntegerParameterNamed(recurrenceFrequencyParamName, this.recurrenceFrequency)) {
final Integer newValue = command.integerValueOfParameterNamed(recurrenceFrequencyParamName); final Integer newValue = command.integerValueOfParameterNamed(recurrenceFrequencyParamName);
actualChanges.put(recurrenceFrequencyParamName, newValue); actualChanges.put(recurrenceFrequencyParamName, newValue);
this.recurrenceFrequency = newValue; this.recurrenceFrequency = newValue;
} }
if (command.hasParameter(recurrenceOnMonthDayParamName)) { if (command.hasParameter(recurrenceOnMonthDayParamName)) {
final MonthDay monthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName); final MonthDay monthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(recurrenceOnMonthDayParamName); final String actualValueEntered = command.stringValueOfParameterNamed(recurrenceOnMonthDayParamName);
final Integer dayOfMonthValue = monthDay.getDayOfMonth(); final Integer dayOfMonthValue = monthDay.getDayOfMonth();
if (!this.recurrenceOnDay.equals(dayOfMonthValue)) { if (!this.recurrenceOnDay.equals(dayOfMonthValue)) {
actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered); actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered);
this.recurrenceOnDay = dayOfMonthValue; this.recurrenceOnDay = dayOfMonthValue;
} }
final Integer monthOfYear = monthDay.getMonthValue(); final Integer monthOfYear = monthDay.getMonthValue();
if (!this.recurrenceOnMonth.equals(monthOfYear)) { if (!this.recurrenceOnMonth.equals(monthOfYear)) {
actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered); actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered);
this.recurrenceOnMonth = monthOfYear; this.recurrenceOnMonth = monthOfYear;
} }
} }
if (command.isChangeInIntegerParameterNamed(recurrenceIntervalParamName, this.recurrenceInterval)) { if (command.isChangeInIntegerParameterNamed(recurrenceIntervalParamName, this.recurrenceInterval)) {
final Integer newValue = command.integerValueOfParameterNamed(recurrenceIntervalParamName); final Integer newValue = command.integerValueOfParameterNamed(recurrenceIntervalParamName);
actualChanges.put(recurrenceIntervalParamName, newValue); actualChanges.put(recurrenceIntervalParamName, newValue);
this.recurrenceInterval = newValue; this.recurrenceInterval = newValue;
} }
validateDependencies(baseDataValidator); validateDependencies(baseDataValidator);
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
return actualChanges; return actualChanges;
} }
private void validateDependencies(final DataValidatorBuilder baseDataValidator) { private void validateDependencies(final DataValidatorBuilder baseDataValidator) {
if (this.validTill != null && this.validFrom != null) { if (this.validTill != null && this.validFrom != null) {
baseDataValidator.reset().parameter(validTillParamName).value(this.validTill).validateDateAfter(this.validFrom); baseDataValidator.reset().parameter(validTillParamName).value(this.validTill).validateDateAfter(this.validFrom);
} }
if (AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence()) { if (AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence()) {
baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(this.recurrenceFrequency).notNull(); baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(this.recurrenceFrequency).notNull();
baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(this.recurrenceInterval).notNull(); baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(this.recurrenceInterval).notNull();
if (this.recurrenceFrequency != null) { if (this.recurrenceFrequency != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency); PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency);
if (frequencyType.isMonthly()) { if (frequencyType.isMonthly()) {
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull(); baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull();
} else if (frequencyType.isYearly()) { } else if (frequencyType.isYearly()) {
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull(); baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull();
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnMonth).notNull(); baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnMonth).notNull();
} }
} }
} }
if (this.accountTransferDetails.toSavingsAccount() != null) { // For savings-to-savings or current-to-savings: enforce Fixed instruction + Periodic recurrence
baseDataValidator.reset().parameter(instructionTypeParamName).value(this.instructionType).notNull().inMinMaxRange(1, 1); if (this.accountTransferDetails.toSavingsAccount() != null
baseDataValidator.reset().parameter(recurrenceTypeParamName).value(this.recurrenceType).notNull().inMinMaxRange(1, 1); || 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();
} 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)) { // Account Transfer: from must be savings OR current; to must be savings OR current
errorCode = "not.account.transfer"; boolean fromAccountValid = this.accountTransferDetails.fromSavingsAccount() != null
} else if (this.accountTransferDetails.transferType().isLoanRepayment() || this.accountTransferDetails.fromCurrentAccount() != null;
&& (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toLoanAccount() == null)) { boolean toAccountValid = this.accountTransferDetails.toSavingsAccount() != null
errorCode = "not.loan.repayment"; || this.accountTransferDetails.toCurrentAccount() != null;
}
if (errorCode != null) { // Loan Repayment: from must be savings OR current; to must be loan
baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode); boolean fromAccountValidForLoan = this.accountTransferDetails.fromSavingsAccount() != null
} || this.accountTransferDetails.fromCurrentAccount() != null;
} String errorCode = null;
if (this.accountTransferDetails.transferType().isAccountTransfer()
public void updateLatsRunDate(LocalDate latsRunDate) { && !(fromAccountValid && toAccountValid)) {
this.latsRunDate = latsRunDate; errorCode = "not.account.transfer";
} } else if (this.accountTransferDetails.transferType().isLoanRepayment()
&& !(fromAccountValidForLoan && this.accountTransferDetails.toLoanAccount() != null)) {
public void updateStatus(Integer status) { errorCode = "not.loan.repayment";
this.status = status; }
} if (errorCode != null) {
baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode);
/** }
* 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(); public void updateLatsRunDate(LocalDate latsRunDate) {
this.name = this.name + "_deleted_" + this.getId(); 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();
}
}
@@ -1,93 +1,143 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.domain; 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.amountParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName; 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.nameParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName; 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.recurrenceFrequencyParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName; 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.recurrenceOnMonthDayParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName; 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.statusParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName; import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName; import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.MonthDay; import java.time.MonthDay;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.springframework.beans.factory.annotation.Autowired; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.springframework.stereotype.Service; import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
@Service import org.springframework.beans.factory.annotation.Autowired;
public class StandingInstructionAssembler { import org.springframework.stereotype.Service;
private final AccountTransferDetailAssembler accountTransferDetailAssembler; @Service
public class StandingInstructionAssembler {
@Autowired
public StandingInstructionAssembler(final AccountTransferDetailAssembler accountTransferDetailAssembler) { private final AccountTransferDetailAssembler accountTransferDetailAssembler;
this.accountTransferDetailAssembler = accountTransferDetailAssembler; @Autowired
} public StandingInstructionAssembler(final AccountTransferDetailAssembler accountTransferDetailAssembler) {
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) { this.accountTransferDetailAssembler = accountTransferDetailAssembler;
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(command); }
assembleStandingInstruction(command, accountTransferDetails);
return accountTransferDetails; public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
} final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(command);
assembleStandingInstruction(command, accountTransferDetails);
public void assembleStandingInstruction(final JsonCommand command, final AccountTransferDetails accountTransferDetails) { return accountTransferDetails;
final LocalDate validFrom = command.localDateValueOfParameterNamed(validFromParamName); }
final LocalDate validTill = command.localDateValueOfParameterNamed(validTillParamName);
BigDecimal amount = null; public void assembleStandingInstruction(final JsonCommand command, final AccountTransferDetails accountTransferDetails) {
final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName); final LocalDate validFrom = command.localDateValueOfParameterNamed(validFromParamName);
if (transferAmount != null) { final LocalDate validTill = command.localDateValueOfParameterNamed(validTillParamName);
final Money monetaryAmount = Money.of(accountTransferDetails.fromSavingsAccount().getCurrency(), transferAmount); BigDecimal amount = null;
amount = monetaryAmount.getAmount(); final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName);
} if (transferAmount != null) {
final Integer status = command.integerValueOfParameterNamed(statusParamName); final MonetaryCurrency currency = resolveCurrency(accountTransferDetails);
final Integer priority = command.integerValueOfParameterNamed(priorityParamName); if (currency != null) {
final Integer standingInstructionType = command.integerValueOfParameterNamed(instructionTypeParamName); final Money monetaryAmount = Money.of(currency, transferAmount);
final Integer recurrenceType = command.integerValueOfParameterNamed(recurrenceTypeParamName); amount = monetaryAmount.getAmount();
final Integer recurrenceFrequency = command.integerValueOfParameterNamed(recurrenceFrequencyParamName); } else {
final MonthDay recurrenceOnMonthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName); amount = transferAmount;
final Integer recurrenceInterval = command.integerValueOfParameterNamed(recurrenceIntervalParamName); }
final String name = command.stringValueOfParameterNamed(nameParamName); }
AccountTransferStandingInstruction accountTransferStandingInstruction = AccountTransferStandingInstruction.create( final Integer status = command.integerValueOfParameterNamed(statusParamName);
accountTransferDetails, name, priority, standingInstructionType, status, amount, validFrom, validTill, recurrenceType, final Integer priority = command.integerValueOfParameterNamed(priorityParamName);
recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay); final Integer standingInstructionType = command.integerValueOfParameterNamed(instructionTypeParamName);
accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction); final Integer recurrenceType = command.integerValueOfParameterNamed(recurrenceTypeParamName);
} final Integer recurrenceFrequency = command.integerValueOfParameterNamed(recurrenceFrequencyParamName);
final MonthDay recurrenceOnMonthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName);
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) { final Integer recurrenceInterval = command.integerValueOfParameterNamed(recurrenceIntervalParamName);
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command); final String name = command.stringValueOfParameterNamed(nameParamName);
assembleStandingInstruction(command, accountTransferDetails); AccountTransferStandingInstruction accountTransferStandingInstruction = AccountTransferStandingInstruction.create(
return accountTransferDetails; accountTransferDetails, name, priority, standingInstructionType, status, amount, validFrom, validTill, recurrenceType,
} recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay);
accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction);
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) { }
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToSavingsTransfer(command);
assembleStandingInstruction(command, accountTransferDetails); /**
return accountTransferDetails; * 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;
}
}
@@ -1,172 +1,172 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.jobs.executestandinginstructions; package org.apache.fineract.portfolio.account.jobs.executestandinginstructions;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformServiceUnavailableException; import org.apache.fineract.infrastructure.core.exception.AbstractPlatformServiceUnavailableException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
import org.apache.fineract.portfolio.account.data.AccountTransferDTO; import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
import org.apache.fineract.portfolio.account.data.StandingInstructionData; import org.apache.fineract.portfolio.account.data.StandingInstructionData;
import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData; import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData;
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
import org.apache.fineract.portfolio.account.domain.StandingInstructionType; import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService; import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; 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.DefaultScheduledDateGenerator;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.exception.InsufficientAccountBalanceException; import org.apache.fineract.portfolio.savings.exception.InsufficientAccountBalanceException;
import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus; import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class ExecuteStandingInstructionsTasklet implements Tasklet { public class ExecuteStandingInstructionsTasklet implements Tasklet {
private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; private final StandingInstructionReadPlatformService standingInstructionReadPlatformService;
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseSpecificSQLGenerator sqlGenerator;
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
@Override @Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
Collection<StandingInstructionData> instructionData = standingInstructionReadPlatformService Collection<StandingInstructionData> instructionData = standingInstructionReadPlatformService
.retrieveAll(StandingInstructionStatus.ACTIVE.getValue()); .retrieveAll(StandingInstructionStatus.ACTIVE.getValue());
List<Throwable> errors = new ArrayList<>(); List<Throwable> errors = new ArrayList<>();
for (StandingInstructionData data : instructionData) { for (StandingInstructionData data : instructionData) {
boolean isDueForTransfer = false; boolean isDueForTransfer = false;
AccountTransferRecurrenceType recurrenceType = data.getRecurrenceType(); AccountTransferRecurrenceType recurrenceType = data.recurrenceTypeEnum();
StandingInstructionType instructionType = data.getInstructionType(); StandingInstructionType instructionType = data.instructionTypeEnum();
LocalDate transactionDate = DateUtils.getBusinessLocalDate(); LocalDate transactionDate = DateUtils.getBusinessLocalDate();
if (recurrenceType.isPeriodicRecurrence()) { if (recurrenceType.isPeriodicRecurrence()) {
final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
PeriodFrequencyType frequencyType = data.getRecurrenceFrequency(); PeriodFrequencyType frequencyType = data.recurrenceFrequencyEnum();
LocalDate startDate = data.getValidFrom(); LocalDate startDate = data.getValidFrom();
if (frequencyType.isMonthly()) { if (frequencyType.isMonthly()) {
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()); startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay());
if (DateUtils.isBefore(startDate, data.getValidFrom())) { if (DateUtils.isBefore(startDate, data.getValidFrom())) {
startDate = startDate.plusMonths(1); startDate = startDate.plusMonths(1);
} }
} else if (frequencyType.isYearly()) { } else if (frequencyType.isYearly()) {
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()).withMonth(data.getRecurrenceOnMonth()); startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()).withMonth(data.getRecurrenceOnMonth());
if (DateUtils.isBefore(startDate, data.getValidFrom())) { if (DateUtils.isBefore(startDate, data.getValidFrom())) {
startDate = startDate.plusYears(1); startDate = startDate.plusYears(1);
} }
} }
isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.getRecurrenceInterval(), startDate, isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.getRecurrenceInterval(), startDate,
transactionDate); transactionDate);
} }
BigDecimal transactionAmount = data.getAmount(); BigDecimal transactionAmount = data.getAmount();
if (data.getToAccountType().isLoanAccount() if (data.toAccountTypeEnum() != null && data.toAccountTypeEnum().isLoanAccount()
&& (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) { && (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) {
StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService
.retriveLoanDuesData(data.getToAccount().getId()); .retriveLoanDuesData(data.getToAccount().getId());
if (data.getInstructionType().isDuesAmoutTransfer()) { if (data.instructionTypeEnum().isDuesAmoutTransfer()) {
transactionAmount = standingInstructionDuesData.totalDueAmount(); transactionAmount = standingInstructionDuesData.totalDueAmount();
} }
if (recurrenceType.isDuesRecurrence()) { if (recurrenceType.isDuesRecurrence()) {
isDueForTransfer = isDueForTransfer(standingInstructionDuesData); isDueForTransfer = isDueForTransfer(standingInstructionDuesData);
} }
} }
if (isDueForTransfer && transactionAmount != null && transactionAmount.compareTo(BigDecimal.ZERO) > 0) { if (isDueForTransfer && transactionAmount != null && transactionAmount.compareTo(BigDecimal.ZERO) > 0) {
final SavingsAccount fromSavingsAccount = null; final SavingsAccount fromSavingsAccount = null;
final boolean isRegularTransaction = true; final boolean isRegularTransaction = true;
final boolean isExceptionForBalanceCheck = false; final boolean isExceptionForBalanceCheck = false;
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount,
data.getFromAccountType(), data.getToAccountType(), data.getFromAccount().getId(), data.getToAccount().getId(), data.fromAccountTypeEnum(), data.toAccountTypeEnum(), data.getFromAccount().getId(), data.getToAccount().getId(),
data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null,
data.getTransferType().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount, data.transferTypeEnum().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount,
isRegularTransaction, isExceptionForBalanceCheck); isRegularTransaction, isExceptionForBalanceCheck);
final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId()); final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());
if (transferCompleted) { if (transferCompleted) {
final String updateQuery = "UPDATE m_account_transfer_standing_instructions SET last_run_date = ? where id = ?"; final String updateQuery = "UPDATE m_account_transfer_standing_instructions SET last_run_date = ? where id = ?";
jdbcTemplate.update(updateQuery, transactionDate, data.getId()); jdbcTemplate.update(updateQuery, transactionDate, data.getId());
} }
} }
} }
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
throw new JobExecutionException(errors); throw new JobExecutionException(errors);
} }
return RepeatStatus.FINISHED; return RepeatStatus.FINISHED;
} }
private boolean transferAmount(final List<Throwable> errors, final AccountTransferDTO accountTransferDTO, final Long instructionId) { private boolean transferAmount(final List<Throwable> errors, final AccountTransferDTO accountTransferDTO, final Long instructionId) {
boolean transferCompleted = true; boolean transferCompleted = true;
StringBuilder errorLog = new StringBuilder(); StringBuilder errorLog = new StringBuilder();
StringBuilder updateQuery = new StringBuilder( StringBuilder updateQuery = new StringBuilder(
"INSERT INTO m_account_transfer_standing_instructions_history (standing_instruction_id, " + sqlGenerator.escape("status") "INSERT INTO m_account_transfer_standing_instructions_history (standing_instruction_id, " + sqlGenerator.escape("status")
+ ", amount,execution_time, error_log) VALUES ("); + ", amount,execution_time, error_log) VALUES (");
try { try {
accountTransfersWritePlatformService.transferFunds(accountTransferDTO); accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
} catch (final PlatformApiDataValidationException e) { } catch (final PlatformApiDataValidationException e) {
errors.add(new Exception("Validation exception while transfering funds for standing Instruction id" + instructionId + " from " errors.add(new Exception("Validation exception while transfering funds for standing Instruction id" + instructionId + " from "
+ accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
errorLog.append("Validation exception while trasfering funds ").append(e.getDefaultUserMessage()); errorLog.append("Validation exception while trasfering funds ").append(e.getDefaultUserMessage());
} catch (final InsufficientAccountBalanceException e) { } catch (final InsufficientAccountBalanceException e) {
errors.add(new Exception("InsufficientAccountBalance Exception while trasfering funds for standing Instruction id" errors.add(new Exception("InsufficientAccountBalance Exception while trasfering funds for standing Instruction id"
+ instructionId + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); + instructionId + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
errorLog.append("InsufficientAccountBalance Exception "); errorLog.append("InsufficientAccountBalance Exception ");
} catch (final AbstractPlatformServiceUnavailableException e) { } catch (final AbstractPlatformServiceUnavailableException e) {
errors.add(new Exception("Platform exception while trasfering funds for standing Instruction id" + instructionId + " from " errors.add(new Exception("Platform exception while trasfering funds for standing Instruction id" + instructionId + " from "
+ accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
errorLog.append("Platform exception while trasfering funds ").append(e.getDefaultUserMessage()); errorLog.append("Platform exception while trasfering funds ").append(e.getDefaultUserMessage());
} catch (Exception e) { } catch (Exception e) {
errors.add(new Exception("Unhandled System Exception while trasfering funds for standing Instruction id" + instructionId errors.add(new Exception("Unhandled System Exception while trasfering funds for standing Instruction id" + instructionId
+ " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e)); + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
errorLog.append("Exception while trasfering funds ").append(e.getMessage()); errorLog.append("Exception while trasfering funds ").append(e.getMessage());
} }
updateQuery.append(instructionId).append(","); updateQuery.append(instructionId).append(",");
if (errorLog.length() > 0) { if (errorLog.length() > 0) {
transferCompleted = false; transferCompleted = false;
updateQuery.append("'failed'").append(","); updateQuery.append("'failed'").append(",");
} else { } else {
updateQuery.append("'success'").append(","); updateQuery.append("'success'").append(",");
} }
updateQuery.append(accountTransferDTO.getTransactionAmount().doubleValue()); updateQuery.append(accountTransferDTO.getTransactionAmount().doubleValue());
updateQuery.append(", now(),"); updateQuery.append(", now(),");
updateQuery.append("'").append(errorLog).append("')"); updateQuery.append("'").append(errorLog).append("')");
jdbcTemplate.update(updateQuery.toString()); jdbcTemplate.update(updateQuery.toString());
return transferCompleted; return transferCompleted;
} }
public boolean isDueForTransfer(StandingInstructionDuesData standingInstructionDuesData) { public boolean isDueForTransfer(StandingInstructionDuesData standingInstructionDuesData) {
return standingInstructionDuesData.dueDate() != null return standingInstructionDuesData.dueDate() != null
&& !standingInstructionDuesData.dueDate().isAfter(LocalDate.now(DateUtils.getDateTimeZoneOfTenant())); && !standingInstructionDuesData.dueDate().isAfter(LocalDate.now(DateUtils.getDateTimeZoneOfTenant()));
} }
} }
@@ -1,202 +1,206 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.service; package org.apache.fineract.portfolio.account.service;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType; import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
import org.apache.fineract.portfolio.account.domain.AccountTransferType; import org.apache.fineract.portfolio.account.domain.AccountTransferType;
import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority; import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority;
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
import org.apache.fineract.portfolio.account.domain.StandingInstructionType; import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
public final class AccountTransferEnumerations { public final class AccountTransferEnumerations {
private AccountTransferEnumerations() { private AccountTransferEnumerations() {
} }
public static EnumOptionData accountType(final Integer type) { public static EnumOptionData accountType(final Integer type) {
return accountType(PortfolioAccountType.fromInt(type)); return accountType(PortfolioAccountType.fromInt(type));
} }
public static EnumOptionData accountType(final PortfolioAccountType type) { public static EnumOptionData accountType(final PortfolioAccountType type) {
EnumOptionData optionData = null; EnumOptionData optionData = null;
if (type != null) { if (type != null) {
switch (type) { switch (type) {
case INVALID: case INVALID:
break; break;
case LOAN: case LOAN:
optionData = new EnumOptionData(PortfolioAccountType.LOAN.getValue().longValue(), PortfolioAccountType.LOAN.getCode(), optionData = new EnumOptionData(PortfolioAccountType.LOAN.getValue().longValue(), PortfolioAccountType.LOAN.getCode(),
"Loan Account"); "Loan Account");
break; break;
case SAVINGS: case SAVINGS:
optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(), optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(),
PortfolioAccountType.SAVINGS.getCode(), "Savings Account"); PortfolioAccountType.SAVINGS.getCode(), "Savings Account");
break; break;
} case CURRENT:
} optionData = new EnumOptionData(PortfolioAccountType.CURRENT.getValue().longValue(),
PortfolioAccountType.CURRENT.getCode(), "Current Account");
return optionData; break;
} }
}
public static EnumOptionData recurrenceType(final Integer type) {
return recurrenceType(AccountTransferRecurrenceType.fromInt(type)); return optionData;
} }
public static EnumOptionData recurrenceType(final AccountTransferRecurrenceType type) { public static EnumOptionData recurrenceType(final Integer type) {
EnumOptionData optionData = null; return recurrenceType(AccountTransferRecurrenceType.fromInt(type));
if (type != null) { }
switch (type) {
case INVALID: public static EnumOptionData recurrenceType(final AccountTransferRecurrenceType type) {
break; EnumOptionData optionData = null;
case PERIODIC: if (type != null) {
optionData = new EnumOptionData(AccountTransferRecurrenceType.PERIODIC.getValue().longValue(), switch (type) {
AccountTransferRecurrenceType.PERIODIC.getCode(), "Periodic Recurrence"); case INVALID:
break; break;
case AS_PER_DUES: case PERIODIC:
optionData = new EnumOptionData(AccountTransferRecurrenceType.AS_PER_DUES.getValue().longValue(), optionData = new EnumOptionData(AccountTransferRecurrenceType.PERIODIC.getValue().longValue(),
AccountTransferRecurrenceType.AS_PER_DUES.getCode(), "As Per Dues Recurrence"); AccountTransferRecurrenceType.PERIODIC.getCode(), "Periodic Recurrence");
break; break;
} case AS_PER_DUES:
} optionData = new EnumOptionData(AccountTransferRecurrenceType.AS_PER_DUES.getValue().longValue(),
return optionData; AccountTransferRecurrenceType.AS_PER_DUES.getCode(), "As Per Dues Recurrence");
} break;
}
public static EnumOptionData transferType(final Integer type) { }
return transferType(AccountTransferType.fromInt(type)); return optionData;
} }
public static EnumOptionData transferType(final AccountTransferType type) { public static EnumOptionData transferType(final Integer type) {
EnumOptionData optionData = null; return transferType(AccountTransferType.fromInt(type));
if (type != null) { }
switch (type) {
case INVALID: public static EnumOptionData transferType(final AccountTransferType type) {
break; EnumOptionData optionData = null;
case ACCOUNT_TRANSFER: if (type != null) {
optionData = new EnumOptionData(AccountTransferType.ACCOUNT_TRANSFER.getValue().longValue(), switch (type) {
AccountTransferType.ACCOUNT_TRANSFER.getCode(), "Account Transfer"); case INVALID:
break; break;
case LOAN_REPAYMENT: case ACCOUNT_TRANSFER:
optionData = new EnumOptionData(AccountTransferType.LOAN_REPAYMENT.getValue().longValue(), optionData = new EnumOptionData(AccountTransferType.ACCOUNT_TRANSFER.getValue().longValue(),
AccountTransferType.LOAN_REPAYMENT.getCode(), "Loan Repayment"); AccountTransferType.ACCOUNT_TRANSFER.getCode(), "Account Transfer");
break; break;
case CHARGE_PAYMENT: case LOAN_REPAYMENT:
optionData = new EnumOptionData(AccountTransferType.CHARGE_PAYMENT.getValue().longValue(), optionData = new EnumOptionData(AccountTransferType.LOAN_REPAYMENT.getValue().longValue(),
AccountTransferType.CHARGE_PAYMENT.getCode(), "Charge Payment"); AccountTransferType.LOAN_REPAYMENT.getCode(), "Loan Repayment");
break; break;
case INTEREST_TRANSFER: case CHARGE_PAYMENT:
optionData = new EnumOptionData(AccountTransferType.INTEREST_TRANSFER.getValue().longValue(), optionData = new EnumOptionData(AccountTransferType.CHARGE_PAYMENT.getValue().longValue(),
AccountTransferType.INTEREST_TRANSFER.getCode(), "Interest Transfer"); AccountTransferType.CHARGE_PAYMENT.getCode(), "Charge Payment");
break; break;
case LOAN_DOWN_PAYMENT: case INTEREST_TRANSFER:
optionData = new EnumOptionData(AccountTransferType.LOAN_DOWN_PAYMENT.getValue().longValue(), optionData = new EnumOptionData(AccountTransferType.INTEREST_TRANSFER.getValue().longValue(),
AccountTransferType.LOAN_DOWN_PAYMENT.getCode(), "Loan Down Payment"); AccountTransferType.INTEREST_TRANSFER.getCode(), "Interest Transfer");
break; break;
case LOAN_DOWN_PAYMENT:
} optionData = new EnumOptionData(AccountTransferType.LOAN_DOWN_PAYMENT.getValue().longValue(),
} AccountTransferType.LOAN_DOWN_PAYMENT.getCode(), "Loan Down Payment");
return optionData; break;
}
}
public static EnumOptionData standingInstructionPriority(final Integer type) { }
return standingInstructionPriority(StandingInstructionPriority.fromInt(type)); return optionData;
} }
public static EnumOptionData standingInstructionPriority(final StandingInstructionPriority type) { public static EnumOptionData standingInstructionPriority(final Integer type) {
EnumOptionData optionData = null; return standingInstructionPriority(StandingInstructionPriority.fromInt(type));
if (type != null) { }
switch (type) {
case INVALID: public static EnumOptionData standingInstructionPriority(final StandingInstructionPriority type) {
break; EnumOptionData optionData = null;
case HIGH: if (type != null) {
optionData = new EnumOptionData(StandingInstructionPriority.HIGH.getValue().longValue(), switch (type) {
StandingInstructionPriority.HIGH.getCode(), "High Priority"); case INVALID:
break; break;
case LOW: case HIGH:
optionData = new EnumOptionData(StandingInstructionPriority.LOW.getValue().longValue(), optionData = new EnumOptionData(StandingInstructionPriority.HIGH.getValue().longValue(),
StandingInstructionPriority.LOW.getCode(), "Low Priority"); StandingInstructionPriority.HIGH.getCode(), "High Priority");
break; break;
case MEDIUM: case LOW:
optionData = new EnumOptionData(StandingInstructionPriority.MEDIUM.getValue().longValue(), optionData = new EnumOptionData(StandingInstructionPriority.LOW.getValue().longValue(),
StandingInstructionPriority.MEDIUM.getCode(), "Medium Priority"); StandingInstructionPriority.LOW.getCode(), "Low Priority");
break; break;
case URGENT: case MEDIUM:
optionData = new EnumOptionData(StandingInstructionPriority.URGENT.getValue().longValue(), optionData = new EnumOptionData(StandingInstructionPriority.MEDIUM.getValue().longValue(),
StandingInstructionPriority.URGENT.getCode(), "Urgent Priority"); StandingInstructionPriority.MEDIUM.getCode(), "Medium Priority");
break; break;
} case URGENT:
} optionData = new EnumOptionData(StandingInstructionPriority.URGENT.getValue().longValue(),
return optionData; StandingInstructionPriority.URGENT.getCode(), "Urgent Priority");
} break;
}
public static EnumOptionData standingInstructionStatus(final Integer type) { }
return standingInstructionStatus(StandingInstructionStatus.fromInt(type)); return optionData;
} }
public static EnumOptionData standingInstructionStatus(final StandingInstructionStatus type) { public static EnumOptionData standingInstructionStatus(final Integer type) {
EnumOptionData optionData = null; return standingInstructionStatus(StandingInstructionStatus.fromInt(type));
if (type != null) { }
switch (type) {
case INVALID: public static EnumOptionData standingInstructionStatus(final StandingInstructionStatus type) {
break; EnumOptionData optionData = null;
case ACTIVE: if (type != null) {
optionData = new EnumOptionData(StandingInstructionStatus.ACTIVE.getValue().longValue(), switch (type) {
StandingInstructionStatus.ACTIVE.getCode(), "Active"); case INVALID:
break; break;
case DELETED: case ACTIVE:
optionData = new EnumOptionData(StandingInstructionStatus.DELETED.getValue().longValue(), optionData = new EnumOptionData(StandingInstructionStatus.ACTIVE.getValue().longValue(),
StandingInstructionStatus.DELETED.getCode(), "Deleted"); StandingInstructionStatus.ACTIVE.getCode(), "Active");
break; break;
case DISABLED: case DELETED:
optionData = new EnumOptionData(StandingInstructionStatus.DISABLED.getValue().longValue(), optionData = new EnumOptionData(StandingInstructionStatus.DELETED.getValue().longValue(),
StandingInstructionStatus.DISABLED.getCode(), "Disabled"); StandingInstructionStatus.DELETED.getCode(), "Deleted");
break; break;
} case DISABLED:
} optionData = new EnumOptionData(StandingInstructionStatus.DISABLED.getValue().longValue(),
return optionData; StandingInstructionStatus.DISABLED.getCode(), "Disabled");
} break;
}
public static EnumOptionData standingInstructionType(final Integer type) { }
return standingInstructionType(StandingInstructionType.fromInt(type)); return optionData;
} }
public static EnumOptionData standingInstructionType(final StandingInstructionType type) { public static EnumOptionData standingInstructionType(final Integer type) {
EnumOptionData optionData = null; return standingInstructionType(StandingInstructionType.fromInt(type));
if (type != null) { }
switch (type) {
case INVALID: public static EnumOptionData standingInstructionType(final StandingInstructionType type) {
break; EnumOptionData optionData = null;
case DUES: if (type != null) {
optionData = new EnumOptionData(StandingInstructionType.DUES.getValue().longValue(), switch (type) {
StandingInstructionType.DUES.getCode(), "Dues"); case INVALID:
break; break;
case FIXED: case DUES:
optionData = new EnumOptionData(StandingInstructionType.FIXED.getValue().longValue(), optionData = new EnumOptionData(StandingInstructionType.DUES.getValue().longValue(),
StandingInstructionType.FIXED.getCode(), "Fixed"); StandingInstructionType.DUES.getCode(), "Dues");
break; break;
case FIXED:
} optionData = new EnumOptionData(StandingInstructionType.FIXED.getValue().longValue(),
} StandingInstructionType.FIXED.getCode(), "Fixed");
return optionData; break;
}
}
} }
return optionData;
}
}
@@ -1,396 +1,482 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.service; package org.apache.fineract.portfolio.account.service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO; import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO;
import org.apache.fineract.portfolio.account.data.PortfolioAccountData; import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException; import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccountReadPlatformService { public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccountReadPlatformService {
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
// mapper // mapper
private final PortfolioSavingsAccountMapper savingsAccountMapper; private final PortfolioSavingsAccountMapper savingsAccountMapper;
private final PortfolioLoanAccountMapper loanAccountMapper; private final PortfolioLoanAccountMapper loanAccountMapper;
private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper; private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper;
private final PortfolioCurrentAccountMapper currentAccountMapper;
public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) {
this.jdbcTemplate = jdbcTemplate; public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) {
this.savingsAccountMapper = new PortfolioSavingsAccountMapper(); this.jdbcTemplate = jdbcTemplate;
this.loanAccountMapper = new PortfolioLoanAccountMapper(); this.savingsAccountMapper = new PortfolioSavingsAccountMapper();
this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator); 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) {
return retrieveOne(accountId, accountTypeId, null);
@Override }
public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) {
@Override
Object[] sqlParams = new Object[] { accountId }; public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) {
PortfolioAccountData accountData = null;
try { Object[] sqlParams = new Object[] { accountId };
String sql = null; PortfolioAccountData accountData = null;
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId); try {
switch (accountType) { String sql = null;
case INVALID: final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId);
break; switch (accountType) {
case LOAN: case INVALID:
break;
sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?"; case LOAN:
if (currencyCode != null) {
sql += " and la.currency_code = ?"; sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?";
sqlParams = new Object[] { accountId, currencyCode }; if (currencyCode != null) {
} sql += " and la.currency_code = ?";
sqlParams = new Object[] { accountId, currencyCode };
accountData = this.jdbcTemplate.queryForObject(sql, this.loanAccountMapper, sqlParams); }
break;
case SAVINGS: accountData = this.jdbcTemplate.queryForObject(sql, this.loanAccountMapper, sqlParams);
sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?"; break;
if (currencyCode != null) { case SAVINGS:
sql += " and sa.currency_code = ?"; sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?";
sqlParams = new Object[] { accountId, currencyCode }; if (currencyCode != null) {
} sql += " and sa.currency_code = ?";
sqlParams = new Object[] { accountId, currencyCode };
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams); }
break;
} accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
} catch (final EmptyResultDataAccessException e) { break;
throw new AccountTransferNotFoundException(accountId, e); case CURRENT:
} sql = "select " + this.currentAccountMapper.schema() + " where ca.id = ?";
if (currencyCode != null) {
return accountData; sql += " and ca.currency_code = ?";
} sqlParams = new Object[] { accountId, currencyCode };
}
@Override
public Collection<PortfolioAccountData> retrieveAllForLookup(final PortfolioAccountDTO portfolioAccountDTO) { accountData = this.jdbcTemplate.queryForObject(sql, this.currentAccountMapper, sqlParams);
break;
final List<Object> sqlParams = new ArrayList<>(); }
// sqlParams.add(portfolioAccountDTO.getClientId()); } catch (final EmptyResultDataAccessException e) {
Collection<PortfolioAccountData> accounts = null; throw new AccountTransferNotFoundException(accountId, e);
String sql = null; }
long defaultAccountStatus = 300; // Active Status
if (portfolioAccountDTO.getAccountStatus() != null) { return accountData;
defaultAccountStatus = portfolioAccountDTO.getFirstAccountStatus(); }
}
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(portfolioAccountDTO.getAccountTypeId()); @Override
switch (accountType) { public Collection<PortfolioAccountData> retrieveAllForLookup(final PortfolioAccountDTO portfolioAccountDTO) {
case INVALID:
break; final List<Object> sqlParams = new ArrayList<>();
case LOAN: // sqlParams.add(portfolioAccountDTO.getClientId());
sql = "select " + this.loanAccountMapper.schema() + " where "; Collection<PortfolioAccountData> accounts = null;
if (portfolioAccountDTO.getClientId() != null) { String sql = null;
sql += " la.client_id = ? and la.loan_status_id in (?) "; long defaultAccountStatus = 300; // Active Status
sqlParams.add(portfolioAccountDTO.getClientId()); if (portfolioAccountDTO.getAccountStatus() != null) {
sqlParams.add(defaultAccountStatus); defaultAccountStatus = portfolioAccountDTO.getFirstAccountStatus();
} else { }
sql += " la.loan_status_id in (?) "; final PortfolioAccountType accountType = PortfolioAccountType.fromInt(portfolioAccountDTO.getAccountTypeId());
sqlParams.add(defaultAccountStatus); switch (accountType) {
} case INVALID:
if (portfolioAccountDTO.getCurrencyCode() != null) { break;
sql += " and la.currency_code = ?"; case LOAN:
sqlParams.add(portfolioAccountDTO.getCurrencyCode()); sql = "select " + this.loanAccountMapper.schema() + " where ";
} if (portfolioAccountDTO.getClientId() != null) {
sql += " la.client_id = ? and la.loan_status_id in (?) ";
accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams.toArray()); // NOSONAR sqlParams.add(portfolioAccountDTO.getClientId());
break; sqlParams.add(defaultAccountStatus);
case SAVINGS: } else {
sql = "select " + this.savingsAccountMapper.schema() + " where "; sql += " la.loan_status_id in (?) ";
if (portfolioAccountDTO.getClientId() != null) { sqlParams.add(defaultAccountStatus);
sql += " sa.client_id = ? and sa.status_enum in (?) "; }
sqlParams.add(portfolioAccountDTO.getClientId()); if (portfolioAccountDTO.getCurrencyCode() != null) {
sqlParams.add(defaultAccountStatus); sql += " and la.currency_code = ?";
} else { sqlParams.add(portfolioAccountDTO.getCurrencyCode());
sql += " sa.status_enum in (?) "; }
sqlParams.add(defaultAccountStatus);
} accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams.toArray()); // NOSONAR
if (portfolioAccountDTO.getCurrencyCode() != null) { break;
sql += " and sa.currency_code = ?"; case SAVINGS:
sqlParams.add(portfolioAccountDTO.getCurrencyCode()); sql = "select " + this.savingsAccountMapper.schema() + " where ";
} if (portfolioAccountDTO.getClientId() != null) {
sql += " sa.client_id = ? and sa.status_enum in (?) ";
if (portfolioAccountDTO.getDepositType() != null) { sqlParams.add(portfolioAccountDTO.getClientId());
sql += " and sa.deposit_type_enum = ?"; sqlParams.add(defaultAccountStatus);
sqlParams.add(portfolioAccountDTO.getDepositType().shortValue()); } else {
} sql += " sa.status_enum in (?) ";
sqlParams.add(defaultAccountStatus);
if (portfolioAccountDTO.isExcludeOverDraftAccounts()) { }
sql += " and sa.allow_overdraft = false"; if (portfolioAccountDTO.getCurrencyCode() != null) {
} sql += " and sa.currency_code = ?";
sqlParams.add(portfolioAccountDTO.getCurrencyCode());
if (portfolioAccountDTO.getClientId() == null && portfolioAccountDTO.getGroupId() != null) { }
sql += " and sa.group_id = ? ";
sqlParams.add(portfolioAccountDTO.getGroupId()); if (portfolioAccountDTO.getDepositType() != null) {
} sql += " and sa.deposit_type_enum = ?";
sqlParams.add(portfolioAccountDTO.getDepositType().shortValue());
accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams.toArray()); // NOSONAR }
break;
} if (portfolioAccountDTO.isExcludeOverDraftAccounts()) {
sql += " and sa.allow_overdraft = false";
return accounts; }
}
if (portfolioAccountDTO.getClientId() == null && portfolioAccountDTO.getGroupId() != null) {
private static final class PortfolioSavingsAccountMapper implements RowMapper<PortfolioAccountData> { sql += " and sa.group_id = ? ";
sqlParams.add(portfolioAccountDTO.getGroupId());
private final String schemaSql; }
PortfolioSavingsAccountMapper() { accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams.toArray()); // NOSONAR
break;
final StringBuilder sqlBuilder = new StringBuilder(400); case CURRENT:
sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, "); sql = "select " + this.currentAccountMapper.schema() + " where ";
sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); if (portfolioAccountDTO.getClientId() != null) {
sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); sql += " ca.client_id = ? and ca.status_enum in (?) ";
sqlBuilder.append("sp.id as productId, sp.name as productName, "); sqlParams.add(portfolioAccountDTO.getClientId());
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); sqlParams.add(defaultAccountStatus);
sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits,"); } else {
sqlBuilder.append("sa.currency_multiplesof as inMultiplesOf, "); sql += " ca.status_enum in (?) ";
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlParams.add(defaultAccountStatus);
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); }
sqlBuilder.append("from m_savings_account sa "); if (portfolioAccountDTO.getCurrencyCode() != null) {
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id "); sql += " and ca.currency_code = ?";
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); sqlParams.add(portfolioAccountDTO.getCurrencyCode());
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 "); accounts = this.jdbcTemplate.query(sql, this.currentAccountMapper, sqlParams.toArray()); // NOSONAR
break;
this.schemaSql = sqlBuilder.toString(); }
}
return accounts;
public String schema() { }
return this.schemaSql;
} private static final class PortfolioSavingsAccountMapper implements RowMapper<PortfolioAccountData> {
@Override private final String schemaSql;
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
PortfolioSavingsAccountMapper() {
final Long id = rs.getLong("id");
final String accountNo = rs.getString("accountNo"); final StringBuilder sqlBuilder = new StringBuilder(400);
final String externalId = rs.getString("externalId"); 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, ");
final Long groupId = JdbcSupport.getLong(rs, "groupId"); sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
final String groupName = rs.getString("groupName"); sqlBuilder.append("sp.id as productId, sp.name as productName, ");
final Long clientId = JdbcSupport.getLong(rs, "clientId"); sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
final String clientName = rs.getString("clientName"); sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits,");
sqlBuilder.append("sa.currency_multiplesof as inMultiplesOf, ");
final Long productId = rs.getLong("productId"); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
final String productName = rs.getString("productName"); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
sqlBuilder.append("from m_savings_account sa ");
final Long fieldOfficerId = rs.getLong("fieldOfficerId"); sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
final String fieldOfficerName = rs.getString("fieldOfficerName"); 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 ");
final String currencyCode = rs.getString("currencyCode"); sqlBuilder.append("left join m_group g ON g.id = sa.group_id ");
final String currencyName = rs.getString("currencyName"); sqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id ");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); this.schemaSql = sqlBuilder.toString();
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); }
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, public String schema() {
currencyNameCode); return this.schemaSql;
}
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, currency); @Override
} public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
}
final Long id = rs.getLong("id");
private static final class PortfolioLoanAccountMapper implements RowMapper<PortfolioAccountData> { final String accountNo = rs.getString("accountNo");
final String externalId = rs.getString("externalId");
private final String schemaSql;
final Long groupId = JdbcSupport.getLong(rs, "groupId");
PortfolioLoanAccountMapper() { final String groupName = rs.getString("groupName");
final Long clientId = JdbcSupport.getLong(rs, "clientId");
final StringBuilder sqlBuilder = new StringBuilder(400); final String clientName = rs.getString("clientName");
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, "); final Long productId = rs.getLong("productId");
sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); final String productName = rs.getString("productName");
sqlBuilder.append("lp.id as productId, lp.name as productName, ");
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); final Long fieldOfficerId = rs.getLong("fieldOfficerId");
sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,"); final String fieldOfficerName = rs.getString("fieldOfficerName");
sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("la.total_overpaid_derived as totalOverpaid, "); final String currencyCode = rs.getString("currencyCode");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); final String currencyName = rs.getString("currencyName");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); final String currencyNameCode = rs.getString("currencyNameCode");
sqlBuilder.append("from m_loan la "); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id "); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
sqlBuilder.append("join m_currency curr on curr.code = la.currency_code "); final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
sqlBuilder.append("left join m_client c ON c.id = la.client_id "); final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
sqlBuilder.append("left join m_group g ON g.id = la.group_id "); currencyNameCode);
sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id ");
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
this.schemaSql = sqlBuilder.toString(); fieldOfficerId, fieldOfficerName, currency);
} }
}
public String schema() {
return this.schemaSql; private static final class PortfolioLoanAccountMapper implements RowMapper<PortfolioAccountData> {
}
private final String schemaSql;
@Override
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { PortfolioLoanAccountMapper() {
final Long id = rs.getLong("id"); final StringBuilder sqlBuilder = new StringBuilder(400);
final String accountNo = rs.getString("accountNo"); sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, ");
final String externalId = rs.getString("externalId"); sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
final Long groupId = JdbcSupport.getLong(rs, "groupId"); sqlBuilder.append("lp.id as productId, lp.name as productName, ");
final String groupName = rs.getString("groupName"); sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
final Long clientId = JdbcSupport.getLong(rs, "clientId"); sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,");
final String clientName = rs.getString("clientName"); sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("la.total_overpaid_derived as totalOverpaid, ");
final Long productId = rs.getLong("productId"); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
final String productName = rs.getString("productName"); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
sqlBuilder.append("from m_loan la ");
final Long fieldOfficerId = rs.getLong("fieldOfficerId"); sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id ");
final String fieldOfficerName = rs.getString("fieldOfficerName"); 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 ");
final String currencyCode = rs.getString("currencyCode"); sqlBuilder.append("left join m_group g ON g.id = la.group_id ");
final String currencyName = rs.getString("currencyName"); sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id ");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); this.schemaSql = sqlBuilder.toString();
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); }
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid"); public String schema() {
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, return this.schemaSql;
currencyNameCode); }
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, @Override
fieldOfficerId, fieldOfficerName, currency, amtForTransfer); 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");
private static final class PortfolioLoanAccountRefundByTransferMapper implements RowMapper<PortfolioAccountData> { final String externalId = rs.getString("externalId");
private String schemaSql; final Long groupId = JdbcSupport.getLong(rs, "groupId");
private final DatabaseSpecificSQLGenerator sqlGenerator; final String groupName = rs.getString("groupName");
final Long clientId = JdbcSupport.getLong(rs, "clientId");
PortfolioLoanAccountRefundByTransferMapper(DatabaseSpecificSQLGenerator sqlGenerator) { final String clientName = rs.getString("clientName");
this.sqlGenerator = sqlGenerator;
} final Long productId = rs.getLong("productId");
final String productName = rs.getString("productName");
public String schema() {
final StringBuilder amountQueryString = new StringBuilder(400); final Long fieldOfficerId = rs.getLong("fieldOfficerId");
amountQueryString.append("(select (SUM(COALESCE(mr.principal_completed_derived, 0)) +"); final String fieldOfficerName = rs.getString("fieldOfficerName");
amountQueryString.append("SUM(COALESCE(mr.interest_completed_derived, 0)) + ");
amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + "); final String currencyCode = rs.getString("currencyCode");
amountQueryString.append(" SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived"); final String currencyName = rs.getString("currencyName");
amountQueryString.append(" from m_loan ml INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id"); final String currencyNameCode = rs.getString("currencyNameCode");
amountQueryString.append(" where ml.id=? and ml.loan_status_id = 300"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
amountQueryString.append(" and mr.duedate >= " + sqlGenerator.currentBusinessDate() + " group by ml.id having"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
amountQueryString.append(" (SUM(COALESCE(mr.principal_completed_derived, 0)) + "); final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
amountQueryString.append(" SUM(COALESCE(mr.interest_completed_derived, 0)) + "); final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid");
amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + "); final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
amountQueryString.append("SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) > 0) as totalOverpaid "); currencyNameCode);
final StringBuilder sqlBuilder = new StringBuilder(400); return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, "); fieldOfficerId, fieldOfficerName, currency, amtForTransfer);
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, "); private static final class PortfolioLoanAccountRefundByTransferMapper implements RowMapper<PortfolioAccountData> {
sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,");
sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, "); private String schemaSql;
sqlBuilder.append(amountQueryString.toString()); private final DatabaseSpecificSQLGenerator sqlGenerator;
sqlBuilder.append(", ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); PortfolioLoanAccountRefundByTransferMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); this.sqlGenerator = sqlGenerator;
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 "); public String schema() {
sqlBuilder.append("left join m_client c ON c.id = la.client_id "); final StringBuilder amountQueryString = new StringBuilder(400);
sqlBuilder.append("left join m_group g ON g.id = la.group_id "); amountQueryString.append("(select (SUM(COALESCE(mr.principal_completed_derived, 0)) +");
sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id "); amountQueryString.append("SUM(COALESCE(mr.interest_completed_derived, 0)) + ");
amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + ");
this.schemaSql = sqlBuilder.toString(); 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");
return this.schemaSql; 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)) + ");
@Override amountQueryString.append(" SUM(COALESCE(mr.interest_completed_derived, 0)) + ");
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + ");
amountQueryString.append("SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) > 0) as totalOverpaid ");
final Long id = rs.getLong("id");
final String accountNo = rs.getString("accountNo"); final StringBuilder sqlBuilder = new StringBuilder(400);
final String externalId = rs.getString("externalId"); 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, ");
final Long groupId = JdbcSupport.getLong(rs, "groupId"); sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
final String groupName = rs.getString("groupName"); sqlBuilder.append("lp.id as productId, lp.name as productName, ");
final Long clientId = JdbcSupport.getLong(rs, "clientId"); sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
final String clientName = rs.getString("clientName"); sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,");
sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, ");
final Long productId = rs.getLong("productId"); sqlBuilder.append(amountQueryString.toString());
final String productName = rs.getString("productName"); sqlBuilder.append(", ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
final Long fieldOfficerId = rs.getLong("fieldOfficerId"); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
final String fieldOfficerName = rs.getString("fieldOfficerName"); sqlBuilder.append("from m_loan la ");
sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id ");
final String currencyCode = rs.getString("currencyCode"); sqlBuilder.append("join m_currency curr on curr.code = la.currency_code ");
final String currencyName = rs.getString("currencyName"); sqlBuilder.append("left join m_client c ON c.id = la.client_id ");
final String currencyNameCode = rs.getString("currencyNameCode"); sqlBuilder.append("left join m_group g ON g.id = la.group_id ");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id ");
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); this.schemaSql = sqlBuilder.toString();
final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, return this.schemaSql;
currencyNameCode); }
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, @Override
fieldOfficerId, fieldOfficerName, currency, amtForTransfer); 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");
@Override final String externalId = rs.getString("externalId");
public PortfolioAccountData retrieveOneByPaidInAdvance(Long accountId, Integer accountTypeId) {
// TODO Auto-generated method stub final Long groupId = JdbcSupport.getLong(rs, "groupId");
Object[] sqlParams = new Object[] { accountId, accountId }; final String groupName = rs.getString("groupName");
PortfolioAccountData accountData = null; final Long clientId = JdbcSupport.getLong(rs, "clientId");
// String currencyCode = null; final String clientName = rs.getString("clientName");
try {
String sql = null; final Long productId = rs.getLong("productId");
// final PortfolioAccountType accountType = final String productName = rs.getString("productName");
// PortfolioAccountType.fromInt(accountTypeId);
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
sql = "select " + this.accountRefundByTransferMapper.schema() + " where la.id = ?"; final String fieldOfficerName = rs.getString("fieldOfficerName");
/*
* if (currencyCode != null) { sql += " and la.currency_code = ?"; sqlParams = new Object[] {accountId , final String currencyCode = rs.getString("currencyCode");
* accountId,currencyCode }; } final String currencyName = rs.getString("currencyName");
*/ final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
accountData = this.jdbcTemplate.queryForObject(sql, this.accountRefundByTransferMapper, sqlParams); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
} catch (final EmptyResultDataAccessException e) { final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid");
throw new AccountTransferNotFoundException(accountId, e); final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
} currencyNameCode);
return accountData; 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<PortfolioAccountData> {
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);
}
}
}
@@ -1,145 +1,179 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.service; package org.apache.fineract.portfolio.account.service;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName; 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.fromClientIdParamName;
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName; import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName;
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName; import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler; import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants; import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants;
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator; import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails; import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction; import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction;
import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler; import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler;
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus; import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
import org.apache.fineract.portfolio.account.exception.StandingInstructionNotFoundException; import org.apache.fineract.portfolio.account.exception.StandingInstructionNotFoundException;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.NonTransientDataAccessException; import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class StandingInstructionWritePlatformServiceImpl implements StandingInstructionWritePlatformService { public class StandingInstructionWritePlatformServiceImpl implements StandingInstructionWritePlatformService {
private final StandingInstructionDataValidator standingInstructionDataValidator; private final StandingInstructionDataValidator standingInstructionDataValidator;
private final StandingInstructionAssembler standingInstructionAssembler; private final StandingInstructionAssembler standingInstructionAssembler;
private final AccountTransferDetailRepository accountTransferDetailRepository; private final AccountTransferDetailRepository accountTransferDetailRepository;
private final StandingInstructionRepository standingInstructionRepository; private final StandingInstructionRepository standingInstructionRepository;
@Transactional @Transactional
@Override @Override
public CommandProcessingResult create(final JsonCommand command) { public CommandProcessingResult create(final JsonCommand command) {
this.standingInstructionDataValidator.validateForCreate(command); this.standingInstructionDataValidator.validateForCreate(command);
final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName); final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName);
final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId); final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId);
final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName); final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName);
final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId); final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId);
final Long fromClientId = command.longValueOfParameterNamed(fromClientIdParamName); final Long fromClientId = command.longValueOfParameterNamed(fromClientIdParamName);
Long standingInstructionId = null; Long standingInstructionId = null;
try { try {
if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) { if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) {
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler final AccountTransferDetails standingInstruction = this.standingInstructionAssembler
.assembleSavingsToSavingsTransfer(command); .assembleSavingsToSavingsTransfer(command);
this.accountTransferDetailRepository.saveAndFlush(standingInstruction); this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
} else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) { } else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) {
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToLoanTransfer(command); final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToLoanTransfer(command);
this.accountTransferDetailRepository.saveAndFlush(standingInstruction); this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
} else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) { } else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) {
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command);
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command); this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
this.accountTransferDetailRepository.saveAndFlush(standingInstruction); standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId(); } else if (isCurrentToCurrentAccountTransfer(fromAccountType, toAccountType)) {
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToCurrentTransfer(command);
} this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
} catch (final JpaSystemException | DataIntegrityViolationException dve) { standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
final Throwable throwable = dve.getMostSpecificCause(); } else if (isCurrentToSavingsAccountTransfer(fromAccountType, toAccountType)) {
handleDataIntegrityIssues(command, throwable, dve); final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToSavingsTransfer(command);
return CommandProcessingResult.empty(); this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
} standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(standingInstructionId) } else if (isSavingsToCurrentAccountTransfer(fromAccountType, toAccountType)) {
.withClientId(fromClientId); final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToCurrentTransfer(command);
return builder.build(); this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
} standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
} else if (isCurrentToLoanAccountTransfer(fromAccountType, toAccountType)) {
private void handleDataIntegrityIssues(final JsonCommand command, Throwable realCause, final NonTransientDataAccessException dve) { final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToLoanTransfer(command);
if (realCause.getMessage().contains("name")) { this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
final String name = command.stringValueOfParameterNamed(StandingInstructionApiConstants.nameParamName); standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
throw new PlatformDataIntegrityException("error.msg.standinginstruction.duplicate.name", }
"Standinginstruction with name `" + name + "` already exists", "name", name); } catch (final JpaSystemException | DataIntegrityViolationException dve) {
} final Throwable throwable = dve.getMostSpecificCause();
log.error("Error occured.", dve); handleDataIntegrityIssues(command, throwable, dve);
throw ErrorHandler.getMappable(dve, "error.msg.client.unknown.data.integrity.issue", return CommandProcessingResult.empty();
"Unknown data integrity issue with resource: " + realCause.getMessage()); }
} final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(standingInstructionId)
.withClientId(fromClientId);
private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { return builder.build();
return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount(); }
}
private void handleDataIntegrityIssues(final JsonCommand command, Throwable realCause, final NonTransientDataAccessException dve) {
private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { if (realCause.getMessage().contains("name")) {
return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount(); final String name = command.stringValueOfParameterNamed(StandingInstructionApiConstants.nameParamName);
} throw new PlatformDataIntegrityException("error.msg.standinginstruction.duplicate.name",
"Standinginstruction with name `" + name + "` already exists", "name", name);
private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, }
final PortfolioAccountType toAccountType) { log.error("Error occured.", dve);
return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount(); throw ErrorHandler.getMappable(dve, "error.msg.client.unknown.data.integrity.issue",
} "Unknown data integrity issue with resource: " + realCause.getMessage());
}
@Override
public CommandProcessingResult update(final Long id, final JsonCommand command) { private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
this.standingInstructionDataValidator.validateForUpdate(command); return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount();
AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id) }
.orElseThrow(() -> new StandingInstructionNotFoundException(id));
final Map<String, Object> actualChanges = standingInstructionsForUpdate.update(command); private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(id).with(actualChanges).build(); return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount();
} }
@Override private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType,
public CommandProcessingResult delete(final Long id) { final PortfolioAccountType toAccountType) {
AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id).orElseThrow(); return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount();
// update the "deleted" and "name" properties of the standing }
// instruction
standingInstructionsForUpdate.delete(); private boolean isCurrentToCurrentAccountTransfer(final PortfolioAccountType fromAccountType,
final PortfolioAccountType toAccountType) {
final Map<String, Object> actualChanges = new HashMap<>(); return fromAccountType.isCurrentAccount() && toAccountType.isCurrentAccount();
actualChanges.put(statusParamName, StandingInstructionStatus.DELETED.getValue()); }
return new CommandProcessingResultBuilder().withEntityId(id).with(actualChanges).build();
} 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<String, Object> 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<String, Object> actualChanges = new HashMap<>();
actualChanges.put(statusParamName, StandingInstructionStatus.DELETED.getValue());
return new CommandProcessingResultBuilder().withEntityId(id).with(actualChanges).build();
}
}
@@ -1,134 +1,138 @@
/** /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file * regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the * to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at * with the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the * KIND, either express or implied. See the License for the
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.account.starter; package org.apache.fineract.portfolio.account.starter;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator; import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator; import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository;
import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler; import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator;
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
import org.apache.fineract.portfolio.account.domain.AccountTransferRepository; import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler;
import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler; import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
import org.apache.fineract.portfolio.account.mapper.AccountTransfersMapper; import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler;
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService; import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformServiceImpl; import org.apache.fineract.portfolio.account.mapper.AccountTransfersMapper;
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformServiceImpl; import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformServiceImpl;
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformServiceImpl; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformServiceImpl;
import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformService; import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformServiceImpl; import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformServiceImpl;
import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformService; import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformService;
import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformServiceImpl; import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformServiceImpl;
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformService;
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformServiceImpl; import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformServiceImpl;
import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformService; import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformServiceImpl; import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformServiceImpl;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformService;
import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService; import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformServiceImpl;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService; import org.apache.fineract.portfolio.savings.domain.GSIMRepositoy;
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService; import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
import org.springframework.context.annotation.Bean; import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.springframework.context.annotation.Configuration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration import org.springframework.jdbc.core.JdbcTemplate;
public class AccountConfiguration {
@Configuration
@Bean public class AccountConfiguration {
@ConditionalOnMissingBean(AccountAssociationsReadPlatformService.class)
public AccountAssociationsReadPlatformService accountAssociationsReadPlatformService(JdbcTemplate jdbcTemplate) { @Bean
return new AccountAssociationsReadPlatformServiceImpl(jdbcTemplate); @ConditionalOnMissingBean(AccountAssociationsReadPlatformService.class)
} public AccountAssociationsReadPlatformService accountAssociationsReadPlatformService(JdbcTemplate jdbcTemplate) {
return new AccountAssociationsReadPlatformServiceImpl(jdbcTemplate);
@Bean }
@ConditionalOnMissingBean(AccountTransfersReadPlatformService.class)
public AccountTransfersReadPlatformService accountTransfersReadPlatformService(JdbcTemplate jdbcTemplate, @Bean
ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService, @ConditionalOnMissingBean(AccountTransfersReadPlatformService.class)
PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, ColumnValidator columnValidator, public AccountTransfersReadPlatformService accountTransfersReadPlatformService(JdbcTemplate jdbcTemplate,
DatabaseSpecificSQLGenerator sqlGenerator, AccountTransfersMapper accountTransfersMapper, PaginationHelper paginationHelper, ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService,
SqlValidator sqlValidator) { PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, ColumnValidator columnValidator,
return new AccountTransfersReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService, DatabaseSpecificSQLGenerator sqlGenerator, AccountTransfersMapper accountTransfersMapper, PaginationHelper paginationHelper,
portfolioAccountReadPlatformService, columnValidator, sqlGenerator, accountTransfersMapper, paginationHelper, sqlValidator); SqlValidator sqlValidator) {
} return new AccountTransfersReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService,
portfolioAccountReadPlatformService, columnValidator, sqlGenerator, accountTransfersMapper, paginationHelper, sqlValidator);
@Bean }
@ConditionalOnMissingBean(AccountTransfersWritePlatformService.class)
public AccountTransfersWritePlatformService accountTransfersWritePlatformService( @Bean
AccountTransfersDataValidator accountTransfersDataValidator, AccountTransferAssembler accountTransferAssembler, @ConditionalOnMissingBean(AccountTransfersWritePlatformService.class)
AccountTransferRepository accountTransferRepository, SavingsAccountAssembler savingsAccountAssembler, public AccountTransfersWritePlatformService accountTransfersWritePlatformService(
SavingsAccountDomainService savingsAccountDomainService, LoanAssembler loanAccountAssembler, AccountTransfersDataValidator accountTransfersDataValidator, AccountTransferAssembler accountTransferAssembler,
LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService, AccountTransferRepository accountTransferRepository, SavingsAccountAssembler savingsAccountAssembler,
AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService, SavingsAccountDomainService savingsAccountDomainService, LoanAssembler loanAccountAssembler,
GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory, LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService,
FineractProperties fineractProperties) { AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService,
return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler, GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory,
accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler, FineractProperties fineractProperties, CurrentAccountRepository currentAccountRepository,
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService, CurrentAccountTransactionRepository currentAccountTransactionRepository) {
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties); return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler,
} accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler,
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService,
@Bean gsimRepository, configurationDomainService, externalIdFactory, fineractProperties,
@ConditionalOnMissingBean(PortfolioAccountReadPlatformService.class) currentAccountRepository, currentAccountTransactionRepository);
public PortfolioAccountReadPlatformService portfolioAccountReadPlatformService(JdbcTemplate jdbcTemplate, }
DatabaseSpecificSQLGenerator sqlGenerator) {
return new PortfolioAccountReadPlatformServiceImpl(jdbcTemplate, sqlGenerator); @Bean
} @ConditionalOnMissingBean(PortfolioAccountReadPlatformService.class)
public PortfolioAccountReadPlatformService portfolioAccountReadPlatformService(JdbcTemplate jdbcTemplate,
@Bean DatabaseSpecificSQLGenerator sqlGenerator) {
@ConditionalOnMissingBean(StandingInstructionHistoryReadPlatformService.class) return new PortfolioAccountReadPlatformServiceImpl(jdbcTemplate, sqlGenerator);
public StandingInstructionHistoryReadPlatformService standingInstructionHistoryReadPlatformService(JdbcTemplate jdbcTemplate, }
ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) {
return new StandingInstructionHistoryReadPlatformServiceImpl(jdbcTemplate, columnValidator, sqlGenerator, paginationHelper); @Bean
} @ConditionalOnMissingBean(StandingInstructionHistoryReadPlatformService.class)
public StandingInstructionHistoryReadPlatformService standingInstructionHistoryReadPlatformService(JdbcTemplate jdbcTemplate,
@Bean ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) {
@ConditionalOnMissingBean(StandingInstructionReadPlatformService.class) return new StandingInstructionHistoryReadPlatformServiceImpl(jdbcTemplate, columnValidator, sqlGenerator, paginationHelper);
public StandingInstructionReadPlatformService standingInstructionReadPlatformService(JdbcTemplate jdbcTemplate, }
ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService,
PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, @Bean
DropdownReadPlatformService dropdownReadPlatformService, ColumnValidator columnValidator, @ConditionalOnMissingBean(StandingInstructionReadPlatformService.class)
DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { public StandingInstructionReadPlatformService standingInstructionReadPlatformService(JdbcTemplate jdbcTemplate,
return new StandingInstructionReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService, ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService,
portfolioAccountReadPlatformService, dropdownReadPlatformService, columnValidator, sqlGenerator, paginationHelper); PortfolioAccountReadPlatformService portfolioAccountReadPlatformService,
} DropdownReadPlatformService dropdownReadPlatformService, ColumnValidator columnValidator,
DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) {
@Bean return new StandingInstructionReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService,
@ConditionalOnMissingBean(StandingInstructionWritePlatformService.class) portfolioAccountReadPlatformService, dropdownReadPlatformService, columnValidator, sqlGenerator, paginationHelper);
public StandingInstructionWritePlatformService standingInstructionWritePlatformService( }
StandingInstructionDataValidator standingInstructionDataValidator, StandingInstructionAssembler standingInstructionAssembler,
AccountTransferDetailRepository accountTransferDetailRepository, StandingInstructionRepository standingInstructionRepository) { @Bean
return new StandingInstructionWritePlatformServiceImpl(standingInstructionDataValidator, standingInstructionAssembler, @ConditionalOnMissingBean(StandingInstructionWritePlatformService.class)
accountTransferDetailRepository, standingInstructionRepository); public StandingInstructionWritePlatformService standingInstructionWritePlatformService(
} StandingInstructionDataValidator standingInstructionDataValidator, StandingInstructionAssembler standingInstructionAssembler,
} AccountTransferDetailRepository accountTransferDetailRepository, StandingInstructionRepository standingInstructionRepository) {
return new StandingInstructionWritePlatformServiceImpl(standingInstructionDataValidator, standingInstructionAssembler,
accountTransferDetailRepository, standingInstructionRepository);
}
}
@@ -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.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.jdbcUrl=${FINERACT_HIKARI_JDBC_URL:jdbc:postgresql://localhost:5432/fineract_tenants}
spring.datasource.hikari.username=${FINERACT_HIKARI_USERNAME:postgres} 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.minimumIdle=${FINERACT_HIKARI_MINIMUM_IDLE:3}
spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10} spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10}
spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000} spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000}
@@ -317,4 +317,5 @@
<include file="parts/0298_unique_external_id_on_current_account.xml" relativeToChangelogFile="true"/> <include file="parts/0298_unique_external_id_on_current_account.xml" relativeToChangelogFile="true"/>
<include file="parts/0299_seed_indian_states.xml" relativeToChangelogFile="true"/> <include file="parts/0299_seed_indian_states.xml" relativeToChangelogFile="true"/>
<include file="parts/0300_rename_fd_base_interest_rate_tables_to_cbs.xml" relativeToChangelogFile="true"/> <include file="parts/0300_rename_fd_base_interest_rate_tables_to_cbs.xml" relativeToChangelogFile="true"/>
<include file="parts/0301_add_current_account_to_transfer_details.xml" relativeToChangelogFile="true"/>
</databaseChangeLog> </databaseChangeLog>
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<!-- Add from_current_account_id column to m_account_transfer_details -->
<changeSet id="0301-1" author="cbs">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_account_transfer_details" columnName="from_current_account_id"/>
</not>
</preConditions>
<addColumn tableName="m_account_transfer_details">
<column name="from_current_account_id" type="BIGINT">
<constraints nullable="true"
foreignKeyName="FK_atd_from_current_account"
references="m_current_account(id)"/>
</column>
</addColumn>
</changeSet>
<!-- Add to_current_account_id column to m_account_transfer_details -->
<changeSet id="0301-2" author="cbs">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="m_account_transfer_details" columnName="to_current_account_id"/>
</not>
</preConditions>
<addColumn tableName="m_account_transfer_details">
<column name="to_current_account_id" type="BIGINT">
<constraints nullable="true"
foreignKeyName="FK_atd_to_current_account"
references="m_current_account(id)"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>