Skip to content

Commit

Permalink
[MODINVOICE-482] Using the new transaction batch endpoint (#468)
Browse files Browse the repository at this point in the history
  • Loading branch information
damien-git authored Jan 31, 2024
1 parent 712d91b commit d59c14e
Show file tree
Hide file tree
Showing 18 changed files with 335 additions and 539 deletions.
24 changes: 3 additions & 21 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,7 @@
"voucher-storage.voucher-number.get",
"acquisitions-units-storage.units.collection.get",
"acquisitions-units-storage.memberships.collection.get",
"finance.invoice-transaction-summaries.item.post",
"finance.invoice-transaction-summaries.item.put",
"finance.order-transaction-summaries.item.put",
"finance.payments.item.post",
"finance.credits.item.post",
"finance.credits.item.put",
"finance.payments.item.put",
"finance.pending-payments.item.post",
"finance.pending-payments.item.put",
"finance.encumbrances.item.put",
"finance.transactions.batch",
"finance.transactions.collection.get",
"finance.funds.item.get",
"finance.funds.collection.get",
Expand All @@ -97,8 +88,7 @@
"finance.exchange-rate.item.get",
"finance.fiscal-years.item.get",
"finance.fiscal-years.collection.get",
"organizations-storage.organizations.item.get",
"finance.release-encumbrance.item.post"
"organizations-storage.organizations.item.get"
]
},
{
Expand Down Expand Up @@ -622,7 +612,7 @@
},
{
"id": "finance.transactions",
"version": "5.0"
"version": "5.1"
},
{
"id": "batch-group-storage.batch-groups",
Expand Down Expand Up @@ -656,14 +646,6 @@
"id": "finance.budgets",
"version": "2.0"
},
{
"id": "finance.invoice-transaction-summaries",
"version": "2.1"
},
{
"id": "finance.order-transaction-summaries",
"version": "1.2"
},
{
"id": "finance.expense-classes",
"version": "3.0"
Expand Down
23 changes: 4 additions & 19 deletions src/main/java/org/folio/config/ServicesConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
import org.folio.services.finance.fiscalyear.FiscalYearService;
import org.folio.services.finance.transaction.BaseTransactionService;
import org.folio.services.finance.transaction.EncumbranceService;
import org.folio.services.finance.transaction.InvoiceTransactionSummaryService;
import org.folio.services.finance.transaction.OrderTransactionSummaryService;
import org.folio.services.finance.transaction.PaymentCreditWorkflowService;
import org.folio.services.finance.transaction.PendingPaymentWorkflowService;
import org.folio.services.invoice.BaseInvoiceService;
Expand Down Expand Up @@ -52,9 +50,8 @@ BaseTransactionService transactionService(RestClient restClient) {
}

@Bean
EncumbranceService encumbranceService(BaseTransactionService transactionService,
OrderTransactionSummaryService orderTransactionSummaryService) {
return new EncumbranceService(transactionService, orderTransactionSummaryService);
EncumbranceService encumbranceService(BaseTransactionService transactionService) {
return new EncumbranceService(transactionService);
}

@Bean
Expand Down Expand Up @@ -99,26 +96,15 @@ FundService fundService(RestClient restClient) {
@Bean
PendingPaymentWorkflowService pendingPaymentService(BaseTransactionService baseTransactionService,
EncumbranceService encumbranceService,
InvoiceTransactionSummaryService invoiceTransactionSummaryService,
FundAvailabilityHolderValidator fundAvailabilityValidator) {
return new PendingPaymentWorkflowService(baseTransactionService, encumbranceService, invoiceTransactionSummaryService, fundAvailabilityValidator);
return new PendingPaymentWorkflowService(baseTransactionService, encumbranceService, fundAvailabilityValidator);
}

@Bean
PaymentCreditWorkflowService paymentCreditService(BaseTransactionService baseTransactionService) {
return new PaymentCreditWorkflowService(baseTransactionService);
}

@Bean
InvoiceTransactionSummaryService invoiceTransactionSummaryService(RestClient restClient) {
return new InvoiceTransactionSummaryService(restClient);
}

@Bean
OrderTransactionSummaryService orderTransactionSummaryService(RestClient restClient) {
return new OrderTransactionSummaryService(restClient);
}

@Bean
FundAvailabilityHolderValidator budgetValidationService() {
return new FundAvailabilityHolderValidator();
Expand Down Expand Up @@ -210,12 +196,11 @@ OrderLineService orderLineService(RestClient restClient) {
@Bean
InvoiceCancelService invoiceCancelService(BaseTransactionService baseTransactionService,
EncumbranceService encumbranceService,
InvoiceTransactionSummaryService invoiceTransactionSummaryService,
VoucherService voucherService,
OrderLineService orderLineService,
OrderService orderService,
InvoiceWorkflowDataHolderBuilder invoiceWorkflowDataHolderBuilder) {
return new InvoiceCancelService(baseTransactionService, encumbranceService, invoiceTransactionSummaryService,
return new InvoiceCancelService(baseTransactionService, encumbranceService,
voucherService, orderLineService, orderService, invoiceWorkflowDataHolderBuilder);
}
@Bean
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/folio/invoices/utils/ErrorCodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public enum ErrorCodes {
USER_HAS_NO_PAY_PERMISSIONS("userHasNoInvoicePayPermission", "User does not have permissions to pay this invoice - operation is restricted"),
USER_HAS_NO_CANCEL_PERMISSIONS("userHasNoInvoiceCancelPermission", "User does not have permissions to cancel this invoice - operation is restricted"),
ACQ_UNITS_NOT_FOUND("acqUnitsNotFound", "Acquisitions units assigned to the record not found"),
PENDING_PAYMENT_ERROR("pendingPaymentError", "Failed to create pending payment"),
PENDING_PAYMENT_ERROR("pendingPaymentError", "Failed to create pending payments: %s"),
INVOICE_PAYMENT_FAILURE("invoicePaymentFailure", "Invoice payment failure"),
CURRENT_FISCAL_YEAR_NOT_FOUND("currentFYearNotFound", "Current fiscal year not found for ledger"),
TRANSACTION_CREATION_FAILURE("transactionCreationFailure", "One or more transactions record(s) failed to be created"),
Expand Down
18 changes: 2 additions & 16 deletions src/main/java/org/folio/invoices/utils/ResourcePathResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,14 @@ private ResourcePathResolver() {
public static final String INVOICE_DOCUMENTS = "invoiceDocuments";
public static final String BATCH_VOUCHER_EXPORT_CONFIGS = "batchVoucherExportConfigs";
public static final String BATCH_VOUCHER_EXPORT_CONFIGS_CREDENTIALS = "batchVoucherExportConfigsCredentials";
public static final String INVOICE_TRANSACTION_SUMMARIES = "invoiceSummary";
public static final String ORDER_TRANSACTION_SUMMARIES = "orderSummary";
public static final String FINANCE_STORAGE_TRANSACTIONS = "finance-storage/transactions";
public static final String BATCH_GROUPS = "batch-groups";
public static final String BATCH_VOUCHER_STORAGE = "batch-voucher/batch-vouchers";
public static final String BATCH_VOUCHER_EXPORTS_STORAGE = "batch-voucher/batch-voucher-exports";
public static final String BUDGETS = "finance.budgets";
public static final String CURRENT_BUDGET = "finance.current-budgets";
public static final String LEDGERS = "finance.ledgers";
public static final String FINANCE_TRANSACTIONS = "finance/transactions";
public static final String FINANCE_RELEASE_ENCUMBRANCE = "finance/release-encumbrance";
public static final String FINANCE_INVOICE_PAYMENTS_SUMMARIES = "finance/invoice-payment-summaries";
public static final String FINANCE_PAYMENTS = "finance/payments";
public static final String FINANCE_CREDITS ="finance/credits";
public static final String FINANCE_PENDING_PAYMENTS ="finance/pending-payments";
public static final String FINANCE_BATCH_TRANSACTIONS = "batchTransactions";
public static final String EXPENSE_CLASSES_URL = "expenseClassUrl";
public static final String BUDGET_EXPENSE_CLASSES = "finance-storage.budget-expense-classes";
public static final String FINANCE_EXCHANGE_RATE = "finance/exchange-rate";
Expand Down Expand Up @@ -70,17 +63,11 @@ private ResourcePathResolver() {
apis.put(INVOICE_DOCUMENTS, "/invoice-storage/invoices/%s/documents");
apis.put(BATCH_VOUCHER_EXPORT_CONFIGS, "/batch-voucher-storage/export-configurations");
apis.put(BATCH_VOUCHER_EXPORT_CONFIGS_CREDENTIALS, "/batch-voucher-storage/export-configurations/%s/credentials");
apis.put(INVOICE_TRANSACTION_SUMMARIES, "/finance/invoice-transaction-summaries");
apis.put(ORDER_TRANSACTION_SUMMARIES, "/finance/order-transaction-summaries");
apis.put(BATCH_GROUPS, "/batch-group-storage/batch-groups");
apis.put(BATCH_VOUCHER_STORAGE, "/batch-voucher-storage/batch-vouchers");
apis.put(BATCH_VOUCHER_EXPORTS_STORAGE, "/batch-voucher-storage/batch-voucher-exports");
apis.put(FINANCE_TRANSACTIONS, "/finance/transactions");
apis.put(FINANCE_RELEASE_ENCUMBRANCE, "/finance/release-encumbrance");
apis.put(FINANCE_STORAGE_TRANSACTIONS, "/finance-storage/transactions");
apis.put(FINANCE_INVOICE_PAYMENTS_SUMMARIES, "/finance/invoice-payment-summaries");
apis.put(FINANCE_PAYMENTS, "/finance/payments");
apis.put(FINANCE_CREDITS, "/finance/credits");
apis.put(FINANCE_BATCH_TRANSACTIONS, "/finance/transactions/batch-all-or-nothing");
apis.put(BUDGETS, "/finance/budgets");
apis.put(CURRENT_BUDGET, "/finance/funds/%s/budget");
apis.put(LEDGERS, "/finance/ledgers");
Expand All @@ -89,7 +76,6 @@ private ResourcePathResolver() {
apis.put(FINANCE_EXCHANGE_RATE, "/finance/exchange-rate");
apis.put(TENANT_CONFIGURATION_ENTRIES, "/configurations/entries");
apis.put(FISCAL_YEARS, "/finance/fiscal-years");
apis.put(FINANCE_PENDING_PAYMENTS, "/finance/pending-payments");

SUB_OBJECT_COLLECTION_APIS = Collections.unmodifiableMap(apis);
SUB_OBJECT_ITEM_APIS = Collections.unmodifiableMap(
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/folio/rest/core/RestClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ public Future<Void> postEmptyBody(RequestEntry requestEntry, RequestContext requ
.mapEmpty();
}

public <T> Future<Void> postEmptyResponse(String endpoint, T entity, RequestContext requestContext) {
log.info(REQUEST_MESSAGE_LOG_INFO, HttpMethod.POST, endpoint);
log.debug(REQUEST_MESSAGE_LOG_DEBUG, () -> HttpMethod.POST, () -> JsonObject.mapFrom(entity).encodePrettily());
var caseInsensitiveHeader = convertToCaseInsensitiveMap(requestContext.getHeaders());
return getVertxWebClient(requestContext.getContext())
.postAbs(buildAbsEndpoint(caseInsensitiveHeader, endpoint))
.putHeaders(caseInsensitiveHeader)
.expect(SUCCESS_RESPONSE_PREDICATE)
.sendJson(entity)
.onFailure(log::error)
.mapEmpty();
}

protected MultiMap convertToCaseInsensitiveMap(Map<String, String> okapiHeaders) {
return MultiMap.caseInsensitiveMultiMap()
.addAll(okapiHeaders)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,37 @@
import static java.util.stream.Collectors.toList;
import static org.folio.invoices.utils.HelperUtils.collectResultsOnSuccess;
import static org.folio.invoices.utils.HelperUtils.convertIdsToCqlQuery;
import static org.folio.invoices.utils.ResourcePathResolver.FINANCE_RELEASE_ENCUMBRANCE;
import static org.folio.invoices.utils.ResourcePathResolver.FINANCE_BATCH_TRANSACTIONS;
import static org.folio.invoices.utils.ResourcePathResolver.FINANCE_TRANSACTIONS;
import static org.folio.invoices.utils.ResourcePathResolver.resourcesPath;
import static org.folio.rest.RestConstants.MAX_IDS_FOR_GET_RQ;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import io.vertx.core.json.JsonObject;
import org.apache.commons.collections4.CollectionUtils;
import org.folio.okapi.common.GenericCompositeFuture;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.rest.acq.model.finance.Batch;
import org.folio.rest.acq.model.finance.Encumbrance;
import org.folio.rest.acq.model.finance.Transaction;
import org.folio.rest.acq.model.finance.Transaction.TransactionType;
import org.folio.rest.acq.model.finance.TransactionCollection;
import org.folio.rest.acq.model.finance.TransactionPatch;
import org.folio.rest.core.RestClient;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.core.models.RequestEntry;

import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertxconcurrent.Semaphore;
import one.util.streamex.StreamEx;

public class BaseTransactionService {
private static final Logger logger = LogManager.getLogger();

private static final String TRANSACTIONS_ENDPOINT = resourcesPath(FINANCE_TRANSACTIONS);

private static final Map<TransactionType, String> TRANSACTION_ENDPOINTS = Map.of(
TransactionType.PAYMENT, "/finance/payments",
TransactionType.CREDIT, "/finance/credits",
TransactionType.PENDING_PAYMENT, "/finance/pending-payments",
TransactionType.ENCUMBRANCE, "/finance/encumbrances"
);

private final RestClient restClient;

public BaseTransactionService(RestClient restClient) {
Expand All @@ -51,7 +46,9 @@ public Future<TransactionCollection> getTransactions(String query, int offset, i
.withQuery(query)
.withOffset(offset)
.withLimit(limit);
return restClient.get(requestEntry, TransactionCollection.class, requestContext);
return restClient.get(requestEntry, TransactionCollection.class, requestContext)
.onSuccess(v -> logger.info("getTransactions completed successfully"))
.onFailure(t -> logger.error("getTransactions failed, query={}", query, t));
}

public Future<List<Transaction>> getTransactions(List<String> transactionIds, RequestContext requestContext) {
Expand All @@ -74,63 +71,57 @@ private Future<TransactionCollection> getTransactionsChunk(List<String> transact
return this.getTransactions(query, 0, transactionIds.size(), requestContext);
}

public Future<Transaction> createTransaction(Transaction transaction, RequestContext requestContext) {
return Optional.ofNullable(getEndpoint(transaction))
.map(RequestEntry::new)
.map(requestEntry -> restClient.post(requestEntry, transaction, Transaction.class, requestContext))
.orElseGet(this::unsupportedOperation);
public Future<Void> batchAllOrNothing(List<Transaction> transactionsToCreate, List<Transaction> transactionsToUpdate,
List<String> idsOfTransactionsToDelete, List<TransactionPatch> transactionPatches, RequestContext requestContext) {
Batch batch = new Batch();
if (transactionsToCreate != null) {
transactionsToCreate.forEach(tr -> {
if (tr.getId() == null) {
tr.setId(UUID.randomUUID().toString());
}
});
batch.setTransactionsToCreate(transactionsToCreate);
}
if (transactionsToUpdate != null) {
batch.setTransactionsToUpdate(transactionsToUpdate);
}
if (idsOfTransactionsToDelete != null) {
batch.setIdsOfTransactionsToDelete(idsOfTransactionsToDelete);
}
if (transactionPatches != null) {
batch.setTransactionPatches(transactionPatches);
}
String endpoint = resourcesPath(FINANCE_BATCH_TRANSACTIONS);
return restClient.postEmptyResponse(endpoint, batch, requestContext)
.onSuccess(v -> logger.info("batchAllOrNothing completed successfully"))
.onFailure(t -> logger.error("batchAllOrNothing failed, batch={}", JsonObject.mapFrom(batch), t));
}

public Future<Void> updateTransaction(Transaction transaction, RequestContext requestContext) {
return Optional.ofNullable(getByIdEndpoint(transaction))
.map(RequestEntry::new)
.map(requestEntry -> requestEntry.withId(transaction.getId()))
.map(requestEntry -> restClient.put(requestEntry, transaction, requestContext))
.orElseGet(this::unsupportedOperation);
public Future<Void> batchCreate(List<Transaction> transactions, RequestContext requestContext) {
return batchAllOrNothing(transactions, null, null, null, requestContext);
}

public Future<Void> updateTransactions(List<Transaction> transactions, RequestContext requestContext) {
if (CollectionUtils.isEmpty(transactions)) {
return Future.succeededFuture();
}
return requestContext.getContext()
.<List<Future<Void>>>executeBlocking(promise -> {
List<Future<Void>> futures = new ArrayList<>();
var semaphore = new Semaphore(1, requestContext.getContext().owner());
for (Transaction tr : transactions) {
semaphore.acquire(() -> {
var future = updateTransaction(tr, requestContext)
.onComplete(asyncResult -> semaphore.release());

futures.add(future);
if (futures.size() == transactions.size()) {
promise.complete(futures);
}
});
}
})
.compose(GenericCompositeFuture::join)
.mapEmpty();
public Future<Void> batchUpdate(List<Transaction> transactions, RequestContext requestContext) {
return batchAllOrNothing(null, transactions, null, null, requestContext);
}

public <T> Future<T> unsupportedOperation() {
Promise<T> promise = Promise.promise();
promise.fail(new UnsupportedOperationException());
return promise.future();
public Future<Void> batchRelease(List<Transaction> transactions, RequestContext requestContext) {
// NOTE: we will have to use transactionPatches when it is available (see MODINVOICE-521)
transactions.forEach(tr -> tr.getEncumbrance().setStatus(Encumbrance.Status.RELEASED));
return batchUpdate(transactions, requestContext);
}


public String getEndpoint(Transaction transaction) {
return TRANSACTION_ENDPOINTS.get(transaction.getTransactionType());
public Future<Void> batchUnrelease(List<Transaction> transactions, RequestContext requestContext) {
// NOTE: we will have to use transactionPatches when it is available (see MODINVOICE-521)
transactions.forEach(tr -> tr.getEncumbrance().setStatus(Encumbrance.Status.UNRELEASED));
return batchUpdate(transactions, requestContext);
}

public String getByIdEndpoint(Transaction transaction) {
return TRANSACTION_ENDPOINTS.get(transaction.getTransactionType()) + "/{id}";
}

public Future<Void> releaseEncumbrance(Transaction transaction, RequestContext requestContext) {
RequestEntry requestEntry = new RequestEntry(resourcesPath(FINANCE_RELEASE_ENCUMBRANCE) + "/{id}").withId(transaction.getId());
return restClient.postEmptyBody(requestEntry, requestContext);
public Future<Void> batchCancel(List<Transaction> transactions, RequestContext requestContext) {
// NOTE: we will have to use transactionPatches when it is available (see MODINVOICE-521)
transactions.forEach(tr -> tr.setInvoiceCancelled(true));
return batchUpdate(transactions, requestContext);
}

}
Loading

0 comments on commit d59c14e

Please sign in to comment.