From 0769b41b2617617f00e9376d1f28cc8d1378a5e6 Mon Sep 17 00:00:00 2001 From: viktorpavlenko Date: Fri, 28 Mar 2025 13:14:46 +0200 Subject: [PATCH] FINERACT-2169: Refactor standing instruction api controller; --- .../core/service/CommandParameterUtil.java | 2 + .../api/StandingInstructionApiConstants.java | 9 -- .../api/StandingInstructionApiResource.java | 117 +++++++---------- .../account/data/StandingInstructionData.java | 119 +++++------------- .../StandingInstructionCreationRequest.java | 56 +++++++++ .../StandingInstructionSearchParam.java | 40 ++++++ .../StandingInstructionUpdatesRequest.java | 47 +++++++ .../ExecuteStandingInstructionsTasklet.java | 34 ++--- 8 files changed, 238 insertions(+), 186 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionCreationRequest.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionSearchParam.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionUpdatesRequest.java diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java index 51e97d735..28b715861 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java @@ -26,6 +26,8 @@ public final class CommandParameterUtil { public static final String SALE_COMMAND_VALUE = "sale"; public static final String BUY_BACK_COMMAND_VALUE = "buyback"; public static final String CANCEL_COMMAND_VALUE = "cancel"; + public static final String UPDATE_COMMAND_VALUE = "update"; + public static final String DELETE_COMMAND_VALUE = "delete"; private CommandParameterUtil() {} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiConstants.java index 3684d6bc5..50fce06b0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiConstants.java @@ -18,11 +18,6 @@ */ package org.apache.fineract.portfolio.account.api; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import org.apache.fineract.portfolio.account.AccountDetailConstants; - public final class StandingInstructionApiConstants { private StandingInstructionApiConstants() { @@ -44,8 +39,4 @@ public final class StandingInstructionApiConstants { public static final String recurrenceOnMonthDayParamName = "recurrenceOnMonthDay"; public static final String monthDayFormatParamName = "monthDayFormat"; - static final Set RESPONSE_DATA_PARAMETERS = new HashSet<>( - Arrays.asList(AccountDetailConstants.idParamName, nameParamName, priorityParamName, instructionTypeParamName, statusParamName, - AccountDetailConstants.transferTypeParamName, validFromParamName, validTillParamName)); - } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiResource.java index 560b2537d..ab9d02862 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionApiResource.java @@ -26,6 +26,7 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; @@ -39,18 +40,19 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.UriInfo; import java.time.LocalDate; import java.util.Arrays; +import java.util.Map; import java.util.Set; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.batch.command.CommandHandlerRegistry; import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.infrastructure.core.api.ApiParameterHelper; -import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException; -import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.apache.fineract.infrastructure.core.service.CommandParameterUtil; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; @@ -58,6 +60,9 @@ import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.portfolio.account.data.AccountTransferData; import org.apache.fineract.portfolio.account.data.StandingInstructionDTO; import org.apache.fineract.portfolio.account.data.StandingInstructionData; +import org.apache.fineract.portfolio.account.data.request.StandingInstructionCreationRequest; +import org.apache.fineract.portfolio.account.data.request.StandingInstructionSearchParam; +import org.apache.fineract.portfolio.account.data.request.StandingInstructionUpdatesRequest; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService; import org.springframework.stereotype.Component; @@ -72,11 +77,16 @@ public class StandingInstructionApiResource { private final PlatformSecurityContext context; private final DefaultToApiJsonSerializer toApiJsonSerializer; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - private final ApiRequestParameterHelper apiRequestParameterHelper; private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; private final SqlValidator sqlValidator; + private static final CommandHandlerRegistry COMMAND_HANDLER_REGISTRY = new CommandHandlerRegistry<>( + Map.of(CommandParameterUtil.UPDATE_COMMAND_VALUE, + (id, json) -> new CommandWrapperBuilder().updateStandingInstruction(id).withJson(json).build(), + CommandParameterUtil.DELETE_COMMAND_VALUE, + (id, json) -> new CommandWrapperBuilder().deleteStandingInstruction(id).withJson(json).build())); + @GET @Path("template") @Consumes({ MediaType.APPLICATION_JSON }) @@ -88,42 +98,27 @@ public class StandingInstructionApiResource { + "standinginstructions/template?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=1\n" + "\n" + "standinginstructions/template?fromClientId=1&fromAccountType=2&fromAccountId=1&transferType=1") @ApiResponses(@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsTemplateResponse.class)))) - public String template(@QueryParam("fromOfficeId") @Parameter(description = "fromOfficeId") final Long fromOfficeId, - @QueryParam("fromClientId") @Parameter(description = "fromClientId") final Long fromClientId, - @QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccountId, - @QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType, - @QueryParam("toOfficeId") @Parameter(description = "toOfficeId") final Long toOfficeId, - @QueryParam("toClientId") @Parameter(description = "toClientId") final Long toClientId, - @QueryParam("toAccountId") @Parameter(description = "toAccountId") final Long toAccountId, - @QueryParam("toAccountType") @Parameter(description = "toAccountType") final Integer toAccountType, - @QueryParam("transferType") @Parameter(description = "transferType") final Integer transferType, - @Context final UriInfo uriInfo) { + public StandingInstructionData template(@BeanParam StandingInstructionSearchParam instructionParam) { + context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); - this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); - - final StandingInstructionData standingInstructionData = this.standingInstructionReadPlatformService.retrieveTemplate(fromOfficeId, - fromClientId, fromAccountId, fromAccountType, toOfficeId, toClientId, toAccountId, toAccountType, transferType); - - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, standingInstructionData, - StandingInstructionApiConstants.RESPONSE_DATA_PARAMETERS); + return standingInstructionReadPlatformService.retrieveTemplate(instructionParam.getFromOfficeId(), + instructionParam.getFromClientId(), instructionParam.getFromAccountId(), instructionParam.getFromAccountType(), + instructionParam.getToOfficeId(), instructionParam.getToClientId(), instructionParam.getToAccountId(), + instructionParam.getToAccountType(), instructionParam.getTransferType()); } @POST @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Create new Standing Instruction", description = "Ability to create new instruction for transfer of monetary funds from one account to another") - @RequestBody(required = true, content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PostStandingInstructionsRequest.class))) + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = StandingInstructionCreationRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PostStandingInstructionsResponse.class))) }) - public String create(@Parameter(hidden = true) final String apiRequestBodyAsJson) { + public CommandProcessingResult create(@Parameter(hidden = true) StandingInstructionCreationRequest creationRequest) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction() + .withJson(toApiJsonSerializer.serialize(creationRequest)).build(); - final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction().withJson(apiRequestBodyAsJson) - .build(); - - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - - return this.toApiJsonSerializer.serialize(result); + return commandsSourceWritePlatformService.logCommandSource(commandRequest); } @PUT @@ -134,34 +129,19 @@ public class StandingInstructionApiResource { + "\n" + "PUT https://DomainName/api/v1/standinginstructions/1?command=update\n" + "\n\n" + "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n" + "\n" + "PUT https://DomainName/api/v1/standinginstructions/1?command=delete") - @RequestBody(required = false, content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PutStandingInstructionsStandingInstructionIdRequest.class))) + @RequestBody(content = @Content(schema = @Schema(implementation = StandingInstructionUpdatesRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PutStandingInstructionsStandingInstructionIdResponse.class))) }) - public String update( + public CommandProcessingResult update( @PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId, - @Parameter(hidden = true) final String apiRequestBodyAsJson, + @Parameter(hidden = true) StandingInstructionUpdatesRequest updatesRequest, @QueryParam("command") @Parameter(description = "command") final String commandParam) { - CommandWrapper commandRequest = null; - if (is(commandParam, "update")) { - commandRequest = new CommandWrapperBuilder().updateStandingInstruction(standingInstructionId).withJson(apiRequestBodyAsJson) - .build(); - } else if (is(commandParam, "delete")) { - commandRequest = new CommandWrapperBuilder().deleteStandingInstruction(standingInstructionId).withJson(apiRequestBodyAsJson) - .build(); - } + final String serializedUpdatesRequest = toApiJsonSerializer.serialize(updatesRequest); + final CommandWrapper commandRequest = COMMAND_HANDLER_REGISTRY.execute(StringUtils.toRootLowerCase(commandParam), + standingInstructionId, serializedUpdatesRequest, new UnrecognizedQueryParamException("command", commandParam)); - if (commandRequest == null) { - throw new UnrecognizedQueryParamException("command", commandParam); - } - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - - return this.toApiJsonSerializer.serialize(result); - - } - - private boolean is(final String commandParam, final String commandValue) { - return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + return commandsSourceWritePlatformService.logCommandSource(commandRequest); } @GET @@ -170,7 +150,7 @@ public class StandingInstructionApiResource { @Operation(summary = "List Standing Instructions", description = "Example Requests:\n" + "\n" + "standinginstructions") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsResponse.class))) }) - public String retrieveAll(@Context final UriInfo uriInfo, + public Page retrieveAll( @QueryParam("externalId") @Parameter(description = "externalId") final String externalId, @QueryParam("offset") @Parameter(description = "offset") final Integer offset, @QueryParam("limit") @Parameter(description = "limit") final Integer limit, @@ -182,7 +162,7 @@ public class StandingInstructionApiResource { @QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccount, @QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType) { - this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); sqlValidator.validate(orderBy); sqlValidator.validate(sortOrder); @@ -195,10 +175,7 @@ public class StandingInstructionApiResource { StandingInstructionDTO standingInstructionDTO = new StandingInstructionDTO(searchParameters, transferType, clientName, clientId, fromAccount, fromAccountType, startDateRange, endDateRange); - final Page transfers = this.standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO); - - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, transfers, StandingInstructionApiConstants.RESPONSE_DATA_PARAMETERS); + return standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO); } @GET @@ -208,7 +185,7 @@ public class StandingInstructionApiResource { @Operation(summary = "Retrieve Standing Instruction", description = "Example Requests :\n" + "\n" + "standinginstructions/1") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsStandingInstructionIdResponse.class))) }) - public String retrieveOne( + public StandingInstructionData retrieveOne( @PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId, @Context final UriInfo uriInfo, @QueryParam("externalId") @Parameter(description = "externalId") final String externalId, @QueryParam("offset") @Parameter(description = "offset") final Integer offset, @@ -216,36 +193,34 @@ public class StandingInstructionApiResource { @QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) { - this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); + context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME); sqlValidator.validate(orderBy); sqlValidator.validate(sortOrder); - StandingInstructionData standingInstructionData = this.standingInstructionReadPlatformService.retrieveOne(standingInstructionId); + StandingInstructionData standingInstructionData = standingInstructionReadPlatformService.retrieveOne(standingInstructionId); final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset) .orderBy(orderBy).sortOrder(sortOrder).build(); final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); - Page transfers = null; if (!associationParameters.isEmpty()) { if (associationParameters.contains("all")) { associationParameters.addAll(Arrays.asList("transactions", "template")); } if (associationParameters.contains("transactions")) { - transfers = this.accountTransfersReadPlatformService.retrieveByStandingInstruction(standingInstructionId, searchParameters); + Page transfers = accountTransfersReadPlatformService + .retrieveByStandingInstruction(standingInstructionId, searchParameters); standingInstructionData = StandingInstructionData.withTransferData(standingInstructionData, transfers); } if (associationParameters.contains("template")) { - final StandingInstructionData templateData = this.standingInstructionReadPlatformService.retrieveTemplate( - standingInstructionData.fromClient().getOfficeId(), standingInstructionData.fromClient().getId(), - standingInstructionData.fromAccount().getId(), standingInstructionData.fromAccountType().getValue(), - standingInstructionData.toClient().getOfficeId(), standingInstructionData.toClient().getId(), - standingInstructionData.toAccount().getId(), standingInstructionData.toAccountType().getValue(), - standingInstructionData.transferType().getValue()); + final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate( + standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(), + standingInstructionData.getFromAccount().getId(), standingInstructionData.getFromAccountType().getValue(), + standingInstructionData.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(), + standingInstructionData.getToAccount().getId(), standingInstructionData.getToAccountType().getValue(), + standingInstructionData.getTransferType().getValue()); standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData); } } - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, standingInstructionData, - StandingInstructionApiConstants.RESPONSE_DATA_PARAMETERS); + return standingInstructionData; } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java index 9a9a5cc94..15229bcb0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java @@ -23,6 +23,8 @@ import java.time.LocalDate; import java.time.MonthDay; import java.time.temporal.ChronoField; import java.util.Collection; +import java.util.Optional; +import lombok.Getter; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.organisation.office.data.OfficeData; @@ -40,26 +42,36 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; @SuppressWarnings("unused") public final class StandingInstructionData { + @Getter private final Long id; + @Getter private final Long accountDetailId; + @Getter private final String name; private final OfficeData fromOffice; + @Getter private final ClientData fromClient; private final EnumOptionData fromAccountType; + @Getter private final PortfolioAccountData fromAccount; private final OfficeData toOffice; + @Getter private final ClientData toClient; private final EnumOptionData toAccountType; + @Getter private final PortfolioAccountData toAccount; private final EnumOptionData transferType; private final EnumOptionData priority; private final EnumOptionData instructionType; private final EnumOptionData status; + @Getter private final BigDecimal amount; + @Getter private final LocalDate validFrom; private final LocalDate validTill; private final EnumOptionData recurrenceType; private final EnumOptionData recurrenceFrequency; + @Getter private final Integer recurrenceInterval; private final MonthDay recurrenceOnMonthDay; private final Page transactions; @@ -269,97 +281,42 @@ public final class StandingInstructionData { instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions); } - public StandingInstructionType instructionType() { - StandingInstructionType standingInstructionType = null; - if (this.instructionType != null) { - standingInstructionType = StandingInstructionType.fromInt(this.instructionType.getId().intValue()); - } - return standingInstructionType; + public StandingInstructionType getInstructionType() { + return Optional.ofNullable(this.instructionType).map(e -> StandingInstructionType.fromInt(e.getId().intValue())).orElse(null); + } - public AccountTransferRecurrenceType recurrenceType() { - AccountTransferRecurrenceType recurrenceType = null; - if (this.recurrenceType != null) { - recurrenceType = AccountTransferRecurrenceType.fromInt(this.recurrenceType.getId().intValue()); - } - return recurrenceType; + public AccountTransferRecurrenceType getRecurrenceType() { + return Optional.ofNullable(this.recurrenceType).map(e -> AccountTransferRecurrenceType.fromInt(e.getId().intValue())).orElse(null); } - public PeriodFrequencyType recurrenceFrequency() { - PeriodFrequencyType frequencyType = null; - if (this.recurrenceFrequency != null) { - frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency.getId().intValue()); - } - return frequencyType; + public PeriodFrequencyType getRecurrenceFrequency() { + return Optional.ofNullable(this.recurrenceFrequency).map(e -> PeriodFrequencyType.fromInt(e.getId().intValue())).orElse(null); } - public PortfolioAccountType fromAccountType() { - PortfolioAccountType accountType = null; - if (this.fromAccountType != null) { - accountType = PortfolioAccountType.fromInt(this.fromAccountType.getId().intValue()); - } - return accountType; + public PortfolioAccountType getFromAccountType() { + return Optional.ofNullable(this.fromAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null); } - public PortfolioAccountType toAccountType() { - PortfolioAccountType accountType = null; - if (this.toAccountType != null) { - accountType = PortfolioAccountType.fromInt(this.toAccountType.getId().intValue()); - } - return accountType; + public PortfolioAccountType getToAccountType() { + return Optional.ofNullable(this.toAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null); } - public AccountTransferType transferType() { - AccountTransferType accountTransferType = null; - if (this.transferType != null) { - accountTransferType = AccountTransferType.fromInt(this.transferType.getId().intValue()); - } - return accountTransferType; + public AccountTransferType getTransferType() { + return Optional.ofNullable(this.transferType).map(e -> AccountTransferType.fromInt(e.getId().intValue())).orElse(null); } - public Integer recurrenceInterval() { - return this.recurrenceInterval; + public Integer getRecurrenceOnDay() { + return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.DAY_OF_MONTH)).orElse(0); } - public Integer recurrenceOnDay() { - Integer recurrenceOnDay = 0; - if (this.recurrenceOnMonthDay != null) { - recurrenceOnDay = this.recurrenceOnMonthDay.get(ChronoField.DAY_OF_MONTH); - } - return recurrenceOnDay; - } - - public Integer recurrenceOnMonth() { - Integer recurrenceOnMonth = 0; - if (this.recurrenceOnMonthDay != null) { - recurrenceOnMonth = this.recurrenceOnMonthDay.get(ChronoField.MONTH_OF_YEAR); - } - return recurrenceOnMonth; - } - - public LocalDate validFrom() { - return this.validFrom; - } - - public BigDecimal amount() { - return this.amount; - } - - public PortfolioAccountData fromAccount() { - return this.fromAccount; - } - - public PortfolioAccountData toAccount() { - return this.toAccount; - } - - public String name() { - return this.name; + 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 = transferType(); + AccountTransferType accountTransferType = getTransferType(); if (accountTransferType.isChargePayment()) { transferType = LoanTransactionType.CHARGE_PAYMENT.getValue(); } else if (accountTransferType.isLoanRepayment()) { @@ -368,20 +325,4 @@ public final class StandingInstructionData { return transferType; } - public Long accountDetailId() { - return this.accountDetailId; - } - - public ClientData fromClient() { - return this.fromClient; - } - - public ClientData toClient() { - return this.toClient; - } - - public Long getId() { - return this.id; - } - } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionCreationRequest.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionCreationRequest.java new file mode 100644 index 000000000..b917a8f89 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionCreationRequest.java @@ -0,0 +1,56 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.account.data.request; + +import java.io.Serial; +import java.io.Serializable; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class StandingInstructionCreationRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String toOfficeId; + private String amount; + private String validTill; + private String toAccountType; + private String dateFormat; + private String recurrenceOnMonthDay; + private String toAccountId; + private String fromClientId; + private String validFrom; + private String locale; + private String priority; + private String recurrenceType; + private String fromAccountType; + private String recurrenceInterval; + private String monthDayFormat; + private String toClientId; + private String instructionType; + private String fromAccountId; + private String recurrenceFrequency; + private String fromOfficeId; + private String name; + private String transferType; + private String status; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionSearchParam.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionSearchParam.java new file mode 100644 index 000000000..324e27760 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionSearchParam.java @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.account.data.request; + +import jakarta.ws.rs.QueryParam; +import java.io.Serial; +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +public class StandingInstructionSearchParam extends AccountTransSearchParam implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @QueryParam("transferType") + private Integer transferType; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionUpdatesRequest.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionUpdatesRequest.java new file mode 100644 index 000000000..26d78ac7e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/StandingInstructionUpdatesRequest.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.account.data.request; + +import java.io.Serial; +import java.io.Serializable; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class StandingInstructionUpdatesRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String amount; + private String validTill; + private String dateFormat; + private String validFrom; + private String locale; + private String priority; + private String recurrenceType; + private String recurrenceInterval; + private String instructionType; + private String recurrenceFrequency; + private String recurrenceOnMonthDay; + private String name; + private String monthDayFormat; + private String status; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/jobs/executestandinginstructions/ExecuteStandingInstructionsTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/jobs/executestandinginstructions/ExecuteStandingInstructionsTasklet.java index 5b5550f81..0b4a9e064 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/jobs/executestandinginstructions/ExecuteStandingInstructionsTasklet.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/jobs/executestandinginstructions/ExecuteStandingInstructionsTasklet.java @@ -66,34 +66,34 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet { List errors = new ArrayList<>(); for (StandingInstructionData data : instructionData) { boolean isDueForTransfer = false; - AccountTransferRecurrenceType recurrenceType = data.recurrenceType(); - StandingInstructionType instructionType = data.instructionType(); + AccountTransferRecurrenceType recurrenceType = data.getRecurrenceType(); + StandingInstructionType instructionType = data.getInstructionType(); LocalDate transactionDate = DateUtils.getBusinessLocalDate(); if (recurrenceType.isPeriodicRecurrence()) { final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); - PeriodFrequencyType frequencyType = data.recurrenceFrequency(); - LocalDate startDate = data.validFrom(); + PeriodFrequencyType frequencyType = data.getRecurrenceFrequency(); + LocalDate startDate = data.getValidFrom(); if (frequencyType.isMonthly()) { - startDate = startDate.withDayOfMonth(data.recurrenceOnDay()); - if (DateUtils.isBefore(startDate, data.validFrom())) { + startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()); + if (DateUtils.isBefore(startDate, data.getValidFrom())) { startDate = startDate.plusMonths(1); } } else if (frequencyType.isYearly()) { - startDate = startDate.withDayOfMonth(data.recurrenceOnDay()).withMonth(data.recurrenceOnMonth()); - if (DateUtils.isBefore(startDate, data.validFrom())) { + startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()).withMonth(data.getRecurrenceOnMonth()); + if (DateUtils.isBefore(startDate, data.getValidFrom())) { startDate = startDate.plusYears(1); } } - isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.recurrenceInterval(), startDate, + isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.getRecurrenceInterval(), startDate, transactionDate); } - BigDecimal transactionAmount = data.amount(); - if (data.toAccountType().isLoanAccount() + BigDecimal transactionAmount = data.getAmount(); + if (data.getToAccountType().isLoanAccount() && (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) { StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService - .retriveLoanDuesData(data.toAccount().getId()); - if (data.instructionType().isDuesAmoutTransfer()) { + .retriveLoanDuesData(data.getToAccount().getId()); + if (data.getInstructionType().isDuesAmoutTransfer()) { transactionAmount = standingInstructionDuesData.totalDueAmount(); } if (recurrenceType.isDuesRecurrence()) { @@ -105,10 +105,10 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet { final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; final boolean isExceptionForBalanceCheck = false; - AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, data.fromAccountType(), - data.toAccountType(), data.fromAccount().getId(), data.toAccount().getId(), - data.name() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, - data.transferType().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount, + AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, + data.getFromAccountType(), data.getToAccountType(), data.getFromAccount().getId(), data.getToAccount().getId(), + data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, + data.getTransferType().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck); final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());