FINERACT-2169: Refactor standing instruction api controller;

This commit is contained in:
viktorpavlenko
2025-04-08 16:39:15 +02:00
committed by Adam Saghy
parent 056a20f58e
commit 0769b41b26
8 changed files with 238 additions and 186 deletions
@@ -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() {}
@@ -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<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(AccountDetailConstants.idParamName, nameParamName, priorityParamName, instructionTypeParamName, statusParamName,
AccountDetailConstants.transferTypeParamName, validFromParamName, validTillParamName));
}
@@ -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<StandingInstructionData> 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<String, Long, String, CommandWrapper> 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<StandingInstructionData> 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<StandingInstructionData> 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<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
Page<AccountTransferData> 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<AccountTransferData> 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;
}
}
@@ -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<AccountTransferData> 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;
}
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -66,34 +66,34 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
List<Throwable> 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());