Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ private static void init() {
.method(POST).build(), "savingsAccountAdjustTransactionCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/savingsaccounts\\/" + NUMBER_REGEX + "\\/charges").method(POST).build(),
"createSavingsAccountChargeCommandStrategy");
commandStrategies.put(CommandContext
.resource("v1\\/savingsaccounts\\/" + NUMBER_REGEX + "\\/charges\\/" + NUMBER_REGEX + MANDATORY_COMMAND_PARAM_REGEX)
.method(POST).build(), "paySavingsAccountChargeCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/loans\\/" + NUMBER_REGEX + "\\/charges").method(POST).build(),
"createChargeCommandStrategy");
commandStrategies.put(
Expand Down Expand Up @@ -190,6 +193,8 @@ private static void init() {
"approveLoanCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/loans\\/" + NUMBER_REGEX + "\\?command=disburse").method(POST).build(),
"disburseLoanCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/loans\\/" + NUMBER_REGEX + "\\?command=disburseToSavings").method(POST).build(),
"disburseToSavingsCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/loans\\/external-id\\/" + UUID_PARAM_REGEX + MANDATORY_COMMAND_PARAM_REGEX)
.method(POST).build(), "loanStateTransistionsByExternalIdCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/rescheduleloans").method(POST).build(),
Expand Down Expand Up @@ -252,6 +257,8 @@ private static void init() {
commandStrategies.put(CommandContext
.resource("v1\\/loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/interest-pauses\\/" + NUMBER_REGEX).method(PUT).build(),
"updateLoanInterestPauseByExternalIdCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/accounttransfers").method(POST).build(),
"createAccountTransferCommandStrategy");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* 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.batch.command.internal;

import jakarta.ws.rs.core.UriInfo;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;

/**
* Batch command strategy for creating account transfers via {@code POST v1/accounttransfers}.
*
* The {@link org.apache.fineract.portfolio.account.api.AccountTransfersApiResource#create} method accepts a typed DTO
* and immediately re-serializes it to JSON before passing it to the command pipeline. This strategy bypasses that
* round-trip and passes the raw request body directly to {@link PortfolioCommandSourceWritePlatformService}, which is
* functionally equivalent.
*/
@Component
@RequiredArgsConstructor
public class CreateAccountTransferCommandStrategy implements CommandStrategy {

private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;

@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());

final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountTransfer().withJson(request.getBody()).build();

final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);

response.setStatusCode(HttpStatus.SC_OK);
response.setBody(toApiJsonSerializer.serialize(result));

return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* 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.batch.command.internal;

import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;

import com.google.common.base.Splitter;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;

/**
* Implements {@link org.apache.fineract.batch.command.CommandStrategy} to handle disbursement of a loan directly to a
* linked savings account via the {@code disburseToSavings} command. It passes the contents of the body from the
* BatchRequest to {@link org.apache.fineract.portfolio.loanaccount.api.LoansApiResource} and gets back the response.
*
* @see org.apache.fineract.batch.command.CommandStrategy
* @see org.apache.fineract.batch.domain.BatchRequest
* @see org.apache.fineract.batch.domain.BatchResponse
*/
@Component
@RequiredArgsConstructor
public class DisburseToSavingsCommandStrategy implements CommandStrategy {

private final LoansApiResource loansApiResource;

@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {

final BatchResponse response = new BatchResponse();
final String responseBody;

response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());

final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
final Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));

responseBody = loansApiResource.stateTransitions(loanId, "disburseToSavings", request.getBody());

response.setStatusCode(HttpStatus.SC_OK);
response.setBody(responseBody);

return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* 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.batch.command.internal;

import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;

import com.google.common.base.Splitter;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.command.CommandStrategyUtils;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.portfolio.savings.api.SavingsAccountChargesApiResource;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;

/**
* Implements {@link CommandStrategy} to pay (or waive) a savings account charge via the Batch API. It passes the
* contents of the body from the BatchRequest to {@link SavingsAccountChargesApiResource} and gets back the response.
*
* <p>
* Handles URLs of the form: {@code POST
* savingsaccounts/{savingsAccountId}/charges/{savingsAccountChargeId}?command=paycharge}
* </p>
*
* @see CommandStrategy
* @see BatchRequest
* @see BatchResponse
*/
@Component
@RequiredArgsConstructor
public class PaySavingsAccountChargeCommandStrategy implements CommandStrategy {

private final SavingsAccountChargesApiResource savingsAccountChargesApiResource;

@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();

response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());

final String relativeUrl = relativeUrlWithoutVersion(request);

// URL: savingsaccounts/{savingsAccountId}/charges/{savingsAccountChargeId}?command=paycharge
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);

if (pathParameters.size() < 4) {
response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
response.setBody(
"Resource with method " + request.getMethod() + " and relativeUrl " + request.getRelativeUrl() + " doesn't exist");
return response;
}

final Long savingsAccountId = Long.parseLong(pathParameters.get(1));

// The charge id may have the query string appended: "47?command=paycharge"
final String chargeIdSegment = pathParameters.get(3);
final Long savingsAccountChargeId;
final String command;

if (chargeIdSegment.contains("?")) {
savingsAccountChargeId = Long.parseLong(chargeIdSegment.substring(0, chargeIdSegment.indexOf("?")));
final Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
command = queryParameters.get("command");
} else {
response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
response.setBody(
"Resource with method " + request.getMethod() + " and relativeUrl " + request.getRelativeUrl() + " doesn't exist");
return response;
}

final String responseBody = savingsAccountChargesApiResource.payOrWaiveSavingsAccountCharge(savingsAccountId,
savingsAccountChargeId, command, request.getBody());

response.setStatusCode(HttpStatus.SC_OK);
response.setBody(responseBody);

return response;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.apache.fineract.batch.command.internal.ApproveLoanRescheduleCommandStrategy;
import org.apache.fineract.batch.command.internal.CollectChargesByLoanExternalIdCommandStrategy;
import org.apache.fineract.batch.command.internal.CollectChargesCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateAccountTransferCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateChargeByLoanExternalIdCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateChargeCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateClientCommandStrategy;
Expand All @@ -44,6 +45,7 @@
import org.apache.fineract.batch.command.internal.CreateTransactionByLoanExternalIdCommandStrategy;
import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
import org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy;
import org.apache.fineract.batch.command.internal.DisburseToSavingsCommandStrategy;
import org.apache.fineract.batch.command.internal.GetChargeByChargeExternalIdCommandStrategy;
import org.apache.fineract.batch.command.internal.GetChargeByIdCommandStrategy;
import org.apache.fineract.batch.command.internal.GetDatatableEntryByAppTableIdAndDataTableIdCommandStrategy;
Expand All @@ -57,6 +59,7 @@
import org.apache.fineract.batch.command.internal.GetReagePreviewByLoanIdCommandStrategy;
import org.apache.fineract.batch.command.internal.LoanStateTransistionsByExternalIdCommandStrategy;
import org.apache.fineract.batch.command.internal.ModifyLoanApplicationCommandStrategy;
import org.apache.fineract.batch.command.internal.PaySavingsAccountChargeCommandStrategy;
import org.apache.fineract.batch.command.internal.UnknownCommandStrategy;
import org.apache.fineract.batch.command.internal.UpdateClientCommandStrategy;
import org.apache.fineract.batch.command.internal.UpdateDatatableEntryOneToManyCommandStrategy;
Expand Down Expand Up @@ -94,6 +97,10 @@ private static Stream<Arguments> provideCommandStrategies() {
Arguments.of("savingsaccounts", HttpMethod.POST, "applySavingsCommandStrategy", mock(ApplySavingsCommandStrategy.class)),
Arguments.of("savingsaccounts/123/charges", HttpMethod.POST, "createSavingsAccountChargeCommandStrategy",
mock(CreateSavingsAccountChargeCommandStrategy.class)),
Arguments.of("savingsaccounts/123/charges/47?command=paycharge", HttpMethod.POST, "paySavingsAccountChargeCommandStrategy",
mock(PaySavingsAccountChargeCommandStrategy.class)),
Arguments.of("savingsaccounts/123/charges/47?command=waive", HttpMethod.POST, "paySavingsAccountChargeCommandStrategy",
mock(PaySavingsAccountChargeCommandStrategy.class)),
Arguments.of("loans/123/charges", HttpMethod.POST, "createChargeCommandStrategy", mock(CreateChargeCommandStrategy.class)),
Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/charges", HttpMethod.POST,
"createChargeByLoanExternalIdCommandStrategy", mock(CreateChargeByLoanExternalIdCommandStrategy.class)),
Expand Down Expand Up @@ -165,6 +172,8 @@ private static Stream<Arguments> provideCommandStrategies() {
mock(ApproveLoanCommandStrategy.class)),
Arguments.of("loans/123?command=disburse", HttpMethod.POST, "disburseLoanCommandStrategy",
mock(DisburseLoanCommandStrategy.class)),
Arguments.of("loans/123?command=disburseToSavings", HttpMethod.POST, "disburseToSavingsCommandStrategy",
mock(DisburseToSavingsCommandStrategy.class)),
Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1?command=approve", HttpMethod.POST,
"loanStateTransistionsByExternalIdCommandStrategy", mock(LoanStateTransistionsByExternalIdCommandStrategy.class)),
Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1?command=disburse", HttpMethod.POST,
Expand Down Expand Up @@ -232,7 +241,9 @@ private static Stream<Arguments> provideCommandStrategies() {
Arguments.of(
"loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions/reage-preview?frequency-number=2&frequencyType=long-string",
HttpMethod.GET, "getReagePreviewByLoanExternalIdCommandStrategy",
mock(GetReagePreviewByLoanExternalIdCommandStrategy.class)));
mock(GetReagePreviewByLoanExternalIdCommandStrategy.class)),
Arguments.of("accounttransfers", HttpMethod.POST, "createAccountTransferCommandStrategy",
mock(CreateAccountTransferCommandStrategy.class)));
}

/**
Expand Down
Loading
Loading