diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json
index bd147774..5c7f25a8 100644
--- a/descriptors/ModuleDescriptor-template.json
+++ b/descriptors/ModuleDescriptor-template.json
@@ -23,13 +23,13 @@
"finance-storage.budgets.collection.get",
"finance-storage.group-fund-fiscal-years.collection.get",
"finance-storage.group-fund-fiscal-years.item.put",
- "finance-storage.transactions.item.post",
"finance-storage.fiscal-years.item.get",
"finance-storage.fiscal-years.collection.get",
"finance-storage.budget-expense-classes.collection.get",
"finance-storage.budget-expense-classes.item.post",
"finance-storage.funds.item.get",
"finance-storage.ledgers.item.get",
+ "finance-storage.transactions.batch",
"configuration.entries.collection.get"
]
},
@@ -599,43 +599,15 @@
},
{
"id": "finance.transactions",
- "version": "5.1",
+ "version": "6.0",
"handlers": [
{
"methods": ["POST"],
"pathPattern": "/finance/allocations",
"permissionsRequired": ["finance.allocations.item.post"],
"modulePermissions": [
- "finance-storage.transactions.item.post",
- "finance-storage.funds.item.get"
- ]
- },
- {
- "methods": ["POST"],
- "pathPattern": "/finance/encumbrances",
- "permissionsRequired": ["finance.encumbrances.item.post"],
- "modulePermissions": [
- "finance-storage.transactions.item.post"
- ]
- },
- {
- "methods": ["PUT"],
- "pathPattern": "/finance/encumbrances/{id}",
- "permissionsRequired": ["finance.encumbrances.item.put"],
- "modulePermissions": [
- "finance-storage.transactions.item.put"
- ]
- },
- {
- "methods": ["DELETE"],
- "pathPattern": "/finance/encumbrances/{id}",
- "permissionsRequired": ["finance.encumbrances.item.delete"],
- "modulePermissions": [
- "finance-storage.transactions.item.get",
- "finance-storage.transactions.item.put",
- "finance-storage.transactions.item.delete",
- "finance-storage.transactions.collection.get",
- "finance-storage.order-transaction-summaries.item.put"
+ "finance-storage.funds.item.get",
+ "finance-storage.transactions.batch"
]
},
{
@@ -643,58 +615,8 @@
"pathPattern": "/finance/transfers",
"permissionsRequired": ["finance.transfers.item.post"],
"modulePermissions": [
- "finance-storage.transactions.item.post",
- "finance-storage.funds.item.get"
- ]
- },
- {
- "methods": ["POST"],
- "pathPattern": "/finance/payments",
- "permissionsRequired": ["finance.payments.item.post"],
- "modulePermissions": [
- "finance-storage.transactions.item.post"
- ]
- },
- {
- "methods": ["PUT"],
- "pathPattern": "/finance/payments/{id}",
- "permissionsRequired": ["finance.payments.item.put"],
- "modulePermissions": [
- "finance-storage.transactions.item.put",
- "finance-storage.transactions.item.get"
- ]
- },
- {
- "methods": ["POST"],
- "pathPattern": "/finance/pending-payments",
- "permissionsRequired": ["finance.pending-payments.item.post"],
- "modulePermissions": [
- "finance-storage.transactions.item.post"
- ]
- },
- {
- "methods": ["PUT"],
- "pathPattern": "/finance/pending-payments/{id}",
- "permissionsRequired": ["finance.pending-payments.item.put"],
- "modulePermissions": [
- "finance-storage.transactions.item.put"
- ]
- },
- {
- "methods": ["POST"],
- "pathPattern": "/finance/credits",
- "permissionsRequired": ["finance.credits.item.post"],
- "modulePermissions": [
- "finance-storage.transactions.item.post"
- ]
- },
- {
- "methods": ["PUT"],
- "pathPattern": "/finance/credits/{id}",
- "permissionsRequired": ["finance.credits.item.put"],
- "modulePermissions": [
- "finance-storage.transactions.item.put",
- "finance-storage.transactions.item.get"
+ "finance-storage.funds.item.get",
+ "finance-storage.transactions.batch"
]
},
{
@@ -742,28 +664,6 @@
}
]
},
- {
- "id": "finance.order-transaction-summaries",
- "version": "1.2",
- "handlers": [
- {
- "methods": ["POST"],
- "pathPattern": "/finance/order-transaction-summaries",
- "permissionsRequired": ["finance.order-transaction-summaries.item.post"],
- "modulePermissions": [
- "finance-storage.order-transaction-summaries.item.post"
- ]
- },
- {
- "methods": ["PUT"],
- "pathPattern": "/finance/order-transaction-summaries/{id}",
- "permissionsRequired": ["finance.order-transaction-summaries.item.put"],
- "modulePermissions": [
- "finance-storage.order-transaction-summaries.item.put"
- ]
- }
- ]
- },
{
"id": "finance.group-fiscal-year-summaries",
"version": "1.3",
@@ -780,28 +680,6 @@
}
]
},
- {
- "id": "finance.invoice-transaction-summaries",
- "version": "2.1",
- "handlers": [
- {
- "methods": ["POST"],
- "pathPattern": "/finance/invoice-transaction-summaries",
- "permissionsRequired": ["finance.invoice-transaction-summaries.item.post"],
- "modulePermissions": [
- "finance-storage.invoice-transaction-summaries.item.post"
- ]
- },
- {
- "methods": ["PUT"],
- "pathPattern": "/finance/invoice-transaction-summaries/{id}",
- "permissionsRequired": ["finance.invoice-transaction-summaries.item.put"],
- "modulePermissions": [
- "finance-storage.invoice-transaction-summaries.item.put"
- ]
- }
- ]
- },
{
"id": "finance.exchange-rate",
"version": "1.0",
@@ -912,20 +790,12 @@
},
{
"id": "finance-storage.transactions",
- "version":"4.0"
+ "version":"4.1"
},
{
"id": "configuration",
"version":"2.0"
},
- {
- "id": "finance-storage.order-transaction-summaries",
- "version":"1.1"
- },
- {
- "id": "finance-storage.invoice-transaction-summaries",
- "version":"2.1"
- },
{
"id": "finance-storage.ledger-rollovers",
"version":"2.0"
@@ -1328,56 +1198,11 @@
"displayName" : "Allocations - create a new transaction of type allocation",
"description" : "Create a new allocation"
},
- {
- "permissionName" : "finance.encumbrances.item.post",
- "displayName" : "Encumbrances - create a new transaction of type encumbrance",
- "description" : "Create a new encumbrance"
- },
- {
- "permissionName" : "finance.encumbrances.item.put",
- "displayName" : "Encumbrances - update transaction of type encumbrance",
- "description" : "Update an encumbrance"
- },
- {
- "permissionName" : "finance.encumbrances.item.delete",
- "displayName" : "Encumbrances - delete transaction of type encumbrance",
- "description" : "Delete an encumbrance"
- },
{
"permissionName" : "finance.transfers.item.post",
"displayName" : "Transfers - create a new transaction of type transfer",
"description" : "Create a new transfer"
},
- {
- "permissionName" : "finance.payments.item.post",
- "displayName" : "Payments - create a new transaction of type payment",
- "description" : "Create a new payment"
- },
- {
- "permissionName" : "finance.payments.item.put",
- "displayName" : "Payments - update a transaction of type payment",
- "description" : "Update a payment"
- },
- {
- "permissionName" : "finance.pending-payments.item.post",
- "displayName" : "Pending payments - create a new transaction of type pending payment",
- "description" : "Create a new pending payment"
- },
- {
- "permissionName" : "finance.pending-payments.item.put",
- "displayName" : "Pending payments - update a transaction of type pending payment",
- "description" : "Update a pending payment"
- },
- {
- "permissionName" : "finance.credits.item.post",
- "displayName" : "Credits - create a new transaction of type credit",
- "description" : "Create a new credit"
- },
- {
- "permissionName" : "finance.credits.item.put",
- "displayName" : "Credits - update a transaction of type credit",
- "description" : "Update a credit"
- },
{
"permissionName": "finance.transactions.collection.get",
"displayName": "Finances - Get finance transaction collection",
@@ -1399,16 +1224,7 @@
"description" : "All permissions for the transaction APIs",
"subPermissions" : [
"finance.allocations.item.post",
- "finance.encumbrances.item.post",
- "finance.encumbrances.item.put",
- "finance.encumbrances.item.delete",
"finance.transfers.item.post",
- "finance.payments.item.post",
- "finance.payments.item.put",
- "finance.pending-payments.item.post",
- "finance.pending-payments.item.put",
- "finance.credits.item.post",
- "finance.credits.item.put",
"finance.transactions.collection.get",
"finance.transactions.item.get",
"finance.release-encumbrance.item.post",
@@ -1431,31 +1247,6 @@
"displayName": "Finances - get group fiscal year summaries",
"description": "Get group fiscal year summaries collection"
},
- {
- "permissionName" : "finance.order-transaction-summaries.item.post",
- "displayName" : "Finances - Post Order transaction Summaries",
- "description" : "Post Order Transaction Summary"
- },
- {
- "permissionName" : "finance.order-transaction-summaries.item.put",
- "displayName" : "Finances - Put Order transaction Summaries",
- "description" : "Put Order Transaction Summary"
- },
- {
- "permissionName" : "finance.order-transaction-summaries.item.get",
- "displayName" : "Finances - Get Order transaction Summaries",
- "description" : "Get Order Transaction Summary"
- },
- {
- "permissionName" : "finance.invoice-transaction-summaries.item.post",
- "displayName" : "Finances - Post Invoice transaction Summaries",
- "description" : "Post Invoice Transaction Summary"
- },
- {
- "permissionName" : "finance.invoice-transaction-summaries.item.put",
- "displayName" : "Finances - Put Invoice transaction Summaries",
- "description" : "Put Invoice Transaction Summary"
- },
{
"permissionName" : "finance.exchange-rate.item.get",
"displayName" : "Finances - Exchange Rate",
@@ -1486,10 +1277,6 @@
"finance.transactions.all",
"finance.fund-codes-expense-classes.collection.get",
"finance.group-fiscal-year-summaries.collection.get",
- "finance.order-transaction-summaries.item.post",
- "finance.order-transaction-summaries.item.put",
- "finance.invoice-transaction-summaries.item.post",
- "finance.invoice-transaction-summaries.item.put",
"finance.exchange-rate.item.get",
"finance.calculate-exchange.item.get",
"finance.expense-classes.all",
diff --git a/ramls/release-encumbrance.raml b/ramls/release-encumbrance.raml
index 8d73b01c..a513817d 100644
--- a/ramls/release-encumbrance.raml
+++ b/ramls/release-encumbrance.raml
@@ -9,7 +9,7 @@ documentation:
- title: Release encumbrance API
content: |
This documents the API calls that release any remaining money encumbered back to the budget's available pool.
- It should only be used by the UI, and only to release a single encumbrance - batch-all-or-nothing should be used otherwise.
+ It should only be used by the UI, and only to release a single encumbrance - batch-all-or-nothing should be used otherwise.
types:
errors: !include raml-util/schemas/errors.schema
@@ -28,10 +28,10 @@ traits:
displayName: Finance release encumbrance
description: |
Finance release encumbrance APIs.
- It should only be used by the UI, and only to release a single encumbrance - batch-all-or-nothing should be used otherwise.
+ It should only be used by the UI, and only to release a single encumbrance - batch-all-or-nothing should be used otherwise.
post:
is: [validate]
- description: Release encumbrance (use only in UI for a single encumbrance - use batch-all-or-nothing otherwise)
+ description: Release encumbrance (use only in UI for a single encumbrance - use batch-all-or-nothing otherwise)
responses:
204:
description: "Encumbrance successfully released"
diff --git a/ramls/transaction-summary.raml b/ramls/transaction-summary.raml
deleted file mode 100644
index a41625be..00000000
--- a/ramls/transaction-summary.raml
+++ /dev/null
@@ -1,71 +0,0 @@
-#%RAML 1.0
-
-title: Finance Transaction Summaries
-version: v1.1
-protocols: [ HTTP, HTTPS ]
-baseUri: https://github.com/folio-org/mod-finance
-
-documentation:
- - title: Transaction Summaries API
- content: This documents the API calls that can be made to create Transaction summaries for Orders and Invoices
-
-types:
- order-transaction-summary: !include acq-models/mod-finance/schemas/order_transaction_summary.json
- invoice-transaction-summary: !include acq-models/mod-finance/schemas/invoice_transaction_summary.json
- errors: !include raml-util/schemas/errors.schema
-
-
-traits:
- validate: !include raml-util/traits/validation.raml
-
-resourceTypes:
- post-with-201: !include rtypes/post-json-201.raml
- put-json: !include rtypes/post-put-json.raml
-
-/finance/order-transaction-summaries:
- displayName: Create a transaction allocation
- description: Create a new order transaction summary
- type:
- post-with-201:
- requestSchema: order-transaction-summary
- responseSchema: order-transaction-summary
- requestExample: !include acq-models/mod-finance/examples/order_transaction_summary.sample
- responseExample: !include acq-models/mod-finance/examples/order_transaction_summary.sample
- is: [validate]
- post:
- description: Create a new order transaction summary, for an order with number of transactions(encumbrances)
-
- /{id}:
- displayName: Create a transaction summary
- description: Create a new order transaction summary
- type:
- put-json:
- schemaItem: order-transaction-summary
- exampleItem: !include acq-models/mod-finance/examples/order_transaction_summary.sample
- is: [validate]
- put:
- description: Updated order transaction summary, for an order with number of transactions(encumbrances)
-
-
-/finance/invoice-transaction-summaries:
- displayName: Create a transaction allocation
- description: Create a new invoice transaction summary
- type:
- post-with-201:
- requestSchema: invoice-transaction-summary
- responseSchema: invoice-transaction-summary
- requestExample: !include acq-models/mod-finance/examples/invoice_transaction_summary.sample
- responseExample: !include acq-models/mod-finance/examples/invoice_transaction_summary.sample
- is: [validate]
- post:
- description: Create a new invoice transaction summary, for an order with number of transactions(encumbrances) and number of payment credits
- /{id}:
- displayName: Update a transaction summary
- description: Update a new invoice transaction summary
- type:
- put-json:
- schemaItem: invoice-transaction-summary
- exampleItem: !include acq-models/mod-finance/examples/invoice_transaction_summary.sample
- is: [validate]
- put:
- description: Updated invoice transaction summary, for an invoice with number of pending payments and number of payment/credits
diff --git a/ramls/transaction.raml b/ramls/transaction.raml
index d1c53253..f2ae624f 100644
--- a/ramls/transaction.raml
+++ b/ramls/transaction.raml
@@ -1,7 +1,7 @@
#%RAML 1.0
title: "mod-finance"
baseUri: https://github.com/folio-org/mod-finance
-version: v1.1
+version: v6.0
documentation:
- title: mod-finance (Transactions)
@@ -23,14 +23,13 @@ traits:
resourceTypes:
post-with-201: !include rtypes/post-json-201.raml
- put-json: !include rtypes/post-put-json.raml
get-only: !include raml-util/rtypes/get-only-with-json-response.raml
collection-item-get: !include raml-util/rtypes/item-collection-get-with-json-response.raml
/finance:
/allocations:
displayName: Create a transaction allocation
- description: Create an allocation; DEPRECATED - use batch-all-or-nothing instead
+ description: Create an allocation; DEPRECATED - use batch-all-or-nothing instead
type:
post-with-201:
requestSchema: transaction
@@ -39,11 +38,11 @@ resourceTypes:
responseExample: !include acq-models/mod-finance/examples/transaction_allocation.sample
is: [validate]
post:
- description: Create an allocation by transaction; DEPRECATED - use batch-all-or-nothing instead
+ description: Create an allocation by transaction; DEPRECATED - use batch-all-or-nothing instead
/transfers:
displayName: Create a transaction transfer
- description: Create a transfer; DEPRECATED - use batch-all-or-nothing instead
+ description: Create a transfer; DEPRECATED - use batch-all-or-nothing instead
type:
post-with-201:
requestSchema: transaction
@@ -52,101 +51,7 @@ resourceTypes:
responseExample: !include acq-models/mod-finance/examples/transaction_transfer.sample
is: [validate]
post:
- description: Create a transfer by transaction; DEPRECATED - use batch-all-or-nothing instead
-
- /encumbrances:
- displayName: Create a transaction encumbrance
- description: Create a encumbrance; DEPRECATED - use batch-all-or-nothing instead
- type:
- post-with-201:
- requestSchema: transaction
- responseSchema: transaction
- requestExample: !include acq-models/mod-finance/examples/transaction_encumbrance.sample
- responseExample: !include acq-models/mod-finance/examples/transaction_encumbrance.sample
- is: [validate]
- post:
- description: Create an encumbrance by transaction
- /{id}:
- displayName: Transaction instance
- put:
- description: Update a Transaction instance; DEPRECATED - use batch-all-or-nothing instead
- body:
- application/json:
- type: transaction
- delete:
- is: [validate]
- description: Delete an encumbrance with given transactionId; DEPRECATED - use batch-all-or-nothing instead
-
- /payments:
- displayName: Create a transaction payment
- description: Create a payment; DEPRECATED - use batch-all-or-nothing instead
- type:
- post-with-201:
- requestSchema: transaction
- responseSchema: transaction
- requestExample: !include acq-models/mod-finance/examples/transaction_payment.sample
- responseExample: !include acq-models/mod-finance/examples/transaction_payment.sample
- is: [validate]
- post:
- description: Create a payment by transaction; DEPRECATED - use batch-all-or-nothing instead
- /{id}:
- displayName: Transaction payment
- type:
- put-json:
- schemaItem: transaction
- exampleItem: !include acq-models/mod-finance/examples/transaction_payment.sample
- is: [validate]
- put:
- description: |
- Update a payment transaction (only allowed to set invoiceCancelled to true);
- DEPRECATED - use batch-all-or-nothing instead
-
- /pending-payments:
- displayName: Create a transaction pending payment
- description: Create a pending payment; DEPRECATED - use batch-all-or-nothing instead
- type:
- post-with-201:
- requestSchema: transaction
- responseSchema: transaction
- requestExample: !include acq-models/mod-finance/examples/transaction_pending_payment.sample
- responseExample: !include acq-models/mod-finance/examples/transaction_pending_payment.sample
- is: [validate]
- post:
- description: Create a pending payment by transaction; DEPRECATED - use batch-all-or-nothing instead
-
- /{id}:
- displayName: Transaction pending payment
- type:
- put-json:
- schemaItem: transaction
- exampleItem: !include acq-models/mod-finance/examples/transaction_pending_payment.sample
- is: [validate]
- put:
- description: Update a pending payment by transaction; DEPRECATED - use batch-all-or-nothing instead
-
- /credits:
- displayName: Create a transaction credit
- description: Create a credit; DEPRECATED - use batch-all-or-nothing instead
- type:
- post-with-201:
- requestSchema: transaction
- responseSchema: transaction
- requestExample: !include acq-models/mod-finance/examples/transaction_credit.sample
- responseExample: !include acq-models/mod-finance/examples/transaction_credit.sample
- is: [validate]
- post:
- description: Create a credit by transaction; DEPRECATED - use batch-all-or-nothing instead
- /{id}:
- displayName: Transaction credit
- type:
- put-json:
- schemaItem: transaction
- exampleItem: !include acq-models/mod-finance/examples/transaction_credit.sample
- is: [validate]
- put:
- description: |
- Update a credit transaction (only allowed to set invoiceCancelled to true);
- DEPRECATED - use batch-all-or-nothing instead
+ description: Create a transfer by transaction; DEPRECATED - use batch-all-or-nothing instead
/transactions:
displayName: Transactions
diff --git a/ramls/unrelease-encumbrance.raml b/ramls/unrelease-encumbrance.raml
index 28de3cf7..d7f130c1 100644
--- a/ramls/unrelease-encumbrance.raml
+++ b/ramls/unrelease-encumbrance.raml
@@ -9,7 +9,7 @@ documentation:
- title: Unrelease encumbrance API
content: |
This documents the API calls that unrelease any remaining money encumbered back to the budget's available pool.
- It should only be used by the UI, and only to unrelease a single encumbrance - batch-all-or-nothing should be used otherwise
+ It should only be used by the UI, and only to unrelease a single encumbrance - batch-all-or-nothing should be used otherwise
types:
errors: !include raml-util/schemas/errors.schema
@@ -28,10 +28,10 @@ traits:
displayName: Finance unrelease encumbrance
description: |
Finance unrelease encumbrance APIs.
- It should only be used by the UI, and only to release a single encumbrance - batch-all-or-nothing should be used otherwise.
+ It should only be used by the UI, and only to release a single encumbrance - batch-all-or-nothing should be used otherwise.
post:
is: [validate]
- description: Unrelease encumbrance (use only in UI for a single encumbrance - use batch-all-or-nothing otherwise)
+ description: Unrelease encumbrance (use only in UI for a single encumbrance - use batch-all-or-nothing otherwise)
responses:
204:
description: "Encumbrance successfully unreleased"
diff --git a/src/main/java/org/folio/config/ServicesConfiguration.java b/src/main/java/org/folio/config/ServicesConfiguration.java
index 9c6c077d..4e24e012 100644
--- a/src/main/java/org/folio/config/ServicesConfiguration.java
+++ b/src/main/java/org/folio/config/ServicesConfiguration.java
@@ -1,7 +1,5 @@
package org.folio.config;
-import java.util.Set;
-
import org.folio.rest.core.RestClient;
import org.folio.services.ExpenseClassService;
import org.folio.services.budget.BudgetExpenseClassService;
@@ -10,6 +8,7 @@
import org.folio.services.budget.RecalculateBudgetService;
import org.folio.services.budget.CreateBudgetService;
import org.folio.services.configuration.ConfigurationEntriesService;
+import org.folio.services.fiscalyear.FiscalYearApiService;
import org.folio.services.fiscalyear.FiscalYearService;
import org.folio.services.fund.FundCodeExpenseClassesService;
import org.folio.services.fund.FundDetailsService;
@@ -30,20 +29,8 @@
import org.folio.services.protection.AcqUnitMembershipsService;
import org.folio.services.protection.AcqUnitsService;
import org.folio.services.protection.ProtectionService;
-import org.folio.services.transactions.AllocationService;
-import org.folio.services.transactions.BaseTransactionService;
-import org.folio.services.transactions.BatchTransactionService;
-import org.folio.services.transactions.CommonTransactionService;
-import org.folio.services.transactions.CreditService;
-import org.folio.services.transactions.EncumbranceService;
-import org.folio.services.transactions.PaymentService;
-import org.folio.services.transactions.PendingPaymentService;
-import org.folio.services.transactions.TransactionManagingService;
-import org.folio.services.transactions.TransactionRestrictService;
+import org.folio.services.transactions.TransactionApiService;
import org.folio.services.transactions.TransactionService;
-import org.folio.services.transactions.TransactionStrategyFactory;
-import org.folio.services.transactions.TransactionTypeManagingStrategy;
-import org.folio.services.transactions.TransferService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@@ -61,19 +48,24 @@ ExpenseClassService expenseClassService(RestClient restClient) {
}
@Bean
- BudgetExpenseClassService budgetExpenseClassService(RestClient restClient, CommonTransactionService transactionService) {
+ BudgetExpenseClassService budgetExpenseClassService(RestClient restClient, TransactionService transactionService) {
return new BudgetExpenseClassService(restClient, transactionService);
}
@Bean
- CommonTransactionService transactionService(RestClient restClient) {
- return new CommonTransactionService(restClient);
+ TransactionApiService transactionApiService(TransactionService transactionService, FundService fundService) {
+ return new TransactionApiService(transactionService, fundService);
+ }
+
+ @Bean
+ TransactionService transactionService(RestClient restClient, FiscalYearService fiscalYearService) {
+ return new TransactionService(restClient, fiscalYearService);
}
@Bean
BudgetExpenseClassTotalsService budgetExpenseClassTotalsService(RestClient restClient,
ExpenseClassService expenseClassService,
- CommonTransactionService transactionService,
+ TransactionService transactionService,
BudgetExpenseClassService budgetExpenseClassService) {
return new BudgetExpenseClassTotalsService(restClient, expenseClassService, transactionService, budgetExpenseClassService);
}
@@ -97,8 +89,14 @@ BudgetService budgetService(RestClient restClient,
}
@Bean
- FiscalYearService fiscalYearService(RestClient restClient, ConfigurationEntriesService configurationEntriesService, BudgetService budgetService, AcqUnitsService acqUnitsService){
- return new FiscalYearService(restClient, configurationEntriesService, budgetService, acqUnitsService);
+ FiscalYearApiService fiscalYearApiService(FiscalYearService fiscalYearService, ConfigurationEntriesService configurationEntriesService,
+ BudgetService budgetService, AcqUnitsService acqUnitsService){
+ return new FiscalYearApiService(fiscalYearService, configurationEntriesService, budgetService, acqUnitsService);
+ }
+
+ @Bean
+ FiscalYearService fiscalYearService(RestClient restClient){
+ return new FiscalYearService(restClient);
}
@Bean
@@ -133,8 +131,8 @@ LedgerRolloverBudgetsService ledgerRolloverBudgetsService(RestClient restClient)
@Bean
LedgerTotalsService ledgerTotalsService(FiscalYearService fiscalYearService, BudgetService budgetService,
- BaseTransactionService baseTransactionService) {
- return new LedgerTotalsService(fiscalYearService, budgetService, baseTransactionService);
+ TransactionService transactionService) {
+ return new LedgerTotalsService(fiscalYearService, budgetService, transactionService);
}
@Bean
@@ -148,7 +146,7 @@ GroupService groupService(RestClient restClient, AcqUnitsService acquisitionUnit
}
@Bean
- GroupExpenseClassTotalsService groupExpenseClassTotalsService(GroupFundFiscalYearService groupFundFiscalYearService, CommonTransactionService transactionService, ExpenseClassService expenseClassService) {
+ GroupExpenseClassTotalsService groupExpenseClassTotalsService(GroupFundFiscalYearService groupFundFiscalYearService, TransactionService transactionService, ExpenseClassService expenseClassService) {
return new GroupExpenseClassTotalsService(groupFundFiscalYearService, transactionService, expenseClassService);
}
@@ -157,61 +155,6 @@ LedgerDetailsService ledgerDetailsService(FiscalYearService fiscalYearService, L
return new LedgerDetailsService(fiscalYearService, ledgerService, configurationEntriesService);
}
- @Bean
- TransactionService baseTransactionService(RestClient restClient) {
- return new BaseTransactionService(restClient);
- }
-
- @Bean
- TransactionRestrictService transactionRestrictService(FundService fundService) {
- return new TransactionRestrictService(fundService);
- }
-
- @Bean
- TransactionManagingService allocationService(BaseTransactionService transactionService, TransactionRestrictService transactionRestrictService) {
- return new AllocationService(transactionService, transactionRestrictService);
- }
-
- @Bean
- TransactionManagingService creditService(BaseTransactionService transactionService) {
- return new CreditService(transactionService);
- }
-
- @Bean
- TransactionManagingService paymentService(BaseTransactionService transactionService) {
- return new PaymentService(transactionService);
- }
-
- @Bean
- TransactionManagingService encumbranceService(BaseTransactionService transactionService) {
- return new EncumbranceService(transactionService);
- }
-
- @Bean
- TransactionManagingService transferService(BaseTransactionService transactionService, TransactionRestrictService transactionRestrictService) {
- return new TransferService(transactionService, transactionRestrictService);
- }
-
- @Bean
- TransactionManagingService pendingPaymentService(BaseTransactionService transactionService) {
- return new PendingPaymentService(transactionService);
- }
-
- @Bean
- TransactionService commonTransactionService(RestClient restClient) {
- return new CommonTransactionService(restClient);
- }
-
- @Bean
- TransactionStrategyFactory transactionStrategyFactory(Set transactionTypeManagingStrategies) {
- return new TransactionStrategyFactory(transactionTypeManagingStrategies);
- }
-
- @Bean
- public BatchTransactionService batchTransactionService(RestClient restClient, BaseTransactionService baseTransactionService) {
- return new BatchTransactionService(restClient, baseTransactionService);
- }
-
@Bean
FundFiscalYearService fundFiscalYearService(LedgerDetailsService ledgerDetailsService, FundService fundService) {
return new FundFiscalYearService(ledgerDetailsService, fundService);
@@ -222,16 +165,16 @@ CreateBudgetService createBudgetService(RestClient restClient,
GroupFundFiscalYearService groupFundFiscalYearService,
FundFiscalYearService fundFiscalYearService,
BudgetExpenseClassService budgetExpenseClassService,
- CommonTransactionService transactionService,
+ TransactionService transactionService,
FundDetailsService fundDetailsService) {
return new CreateBudgetService(restClient, groupFundFiscalYearService, fundFiscalYearService,
- budgetExpenseClassService, transactionService, fundDetailsService);
+ budgetExpenseClassService, transactionService, fundDetailsService);
}
@Bean
GroupFiscalYearTotalsService groupFiscalYearTotalsService(RestClient restClient, GroupFundFiscalYearService groupFundFiscalYearService,
- BaseTransactionService baseTransactionService) {
- return new GroupFiscalYearTotalsService(restClient, groupFundFiscalYearService, baseTransactionService);
+ TransactionService transactionService) {
+ return new GroupFiscalYearTotalsService(restClient, groupFundFiscalYearService, transactionService);
}
@Bean
@@ -255,8 +198,8 @@ public ProtectionService protectionService(AcqUnitsService acqUnitsService, AcqU
}
@Bean
- public RecalculateBudgetService recalculateBudgetService(BudgetService budgetService, CommonTransactionService commonTransactionService) {
- return new RecalculateBudgetService(budgetService, commonTransactionService);
+ public RecalculateBudgetService recalculateBudgetService(BudgetService budgetService, TransactionService transactionService) {
+ return new RecalculateBudgetService(budgetService, transactionService);
}
@Bean
diff --git a/src/main/java/org/folio/rest/exception/HttpException.java b/src/main/java/org/folio/rest/exception/HttpException.java
index 30b72cc4..1bdb9c62 100644
--- a/src/main/java/org/folio/rest/exception/HttpException.java
+++ b/src/main/java/org/folio/rest/exception/HttpException.java
@@ -9,7 +9,9 @@
import org.apache.commons.lang3.StringUtils;
import org.folio.rest.jaxrs.model.Error;
import org.folio.rest.jaxrs.model.Errors;
+import org.folio.rest.jaxrs.model.Parameter;
import org.folio.rest.util.ErrorCodes;
+import org.folio.rest.util.HelperUtils;
public class HttpException extends RuntimeException {
@Serial
@@ -35,13 +37,33 @@ public HttpException(int code, ErrorCodes errCodes) {
this.code = code;
}
+ public HttpException(int code, ErrorCodes errCodes, Throwable cause) {
+ super(errCodes.getDescription());
+ Error error = new Error()
+ .withCode(errCodes.getCode())
+ .withMessage(errCodes.getDescription());
+ if (cause.getMessage() != null) {
+ Parameter causeParam = new Parameter()
+ .withKey("cause")
+ .withValue(cause.getMessage());
+ error.setParameters(Collections.singletonList(causeParam));
+ }
+ this.errors = new Errors()
+ .withErrors(Collections.singletonList(error))
+ .withTotalRecords(1);
+ this.code = code;
+ }
+
public HttpException(int code, Error error) {
+ super(HelperUtils.errorAsString(error));
this.code = code;
- this.errors = new Errors().withErrors(Collections.singletonList(error))
- .withTotalRecords(1);
+ this.errors = new Errors()
+ .withErrors(Collections.singletonList(error))
+ .withTotalRecords(1);
}
public HttpException(int code, Errors errors) {
+ super(HelperUtils.errorsAsString(errors));
this.code = code;
this.errors = errors;
}
diff --git a/src/main/java/org/folio/rest/helper/TransactionSummariesHelper.java b/src/main/java/org/folio/rest/helper/TransactionSummariesHelper.java
deleted file mode 100644
index d8541e76..00000000
--- a/src/main/java/org/folio/rest/helper/TransactionSummariesHelper.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.folio.rest.helper;
-
-import static org.folio.rest.util.ResourcePathResolver.INVOICE_TRANSACTION_SUMMARIES;
-import static org.folio.rest.util.ResourcePathResolver.ORDER_TRANSACTION_SUMMARIES;
-import static org.folio.rest.util.ResourcePathResolver.resourceByIdPath;
-import static org.folio.rest.util.ResourcePathResolver.resourcesPath;
-
-import java.util.Map;
-
-import org.folio.rest.core.RestClient;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.exception.HttpException;
-import org.folio.rest.jaxrs.model.InvoiceTransactionSummary;
-import org.folio.rest.jaxrs.model.OrderTransactionSummary;
-import org.folio.rest.util.ErrorCodes;
-import org.folio.spring.SpringContextUtil;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import io.vertx.core.Context;
-import io.vertx.core.Future;
-import io.vertx.core.Vertx;
-
-public class TransactionSummariesHelper extends AbstractHelper {
- @Autowired
- private RestClient restClient;
-
- public TransactionSummariesHelper(Map okapiHeaders, Context ctx) {
- super(okapiHeaders, ctx);
- SpringContextUtil.autowireDependencies(this, Vertx.currentContext());
- }
-
- public Future createOrderTransactionSummary(OrderTransactionSummary orderSummary, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- validateOrderTransactionCount(orderSummary.getNumTransactions());
- return null;
- })
- .compose(ok -> restClient.post(resourcesPath(ORDER_TRANSACTION_SUMMARIES), orderSummary, OrderTransactionSummary.class, requestContext))
- .map(createdSummary -> orderSummary.withId(createdSummary.getId()));
- }
-
- public Future createInvoiceTransactionSummary(InvoiceTransactionSummary invoiceSummary, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- validateInvoiceTransactionCount(invoiceSummary.getNumPaymentsCredits(), invoiceSummary.getNumPendingPayments());
- return null;
- })
- .compose(ok -> restClient.post(resourcesPath(INVOICE_TRANSACTION_SUMMARIES), invoiceSummary, InvoiceTransactionSummary.class, requestContext))
- .map(createdSummary -> invoiceSummary.withId(createdSummary.getId()));
- }
-
- /**
- * There has to be atleast 1 transaction that needs to be updated upon invoice approval otherwise throw an exception
- */
- private void validateOrderTransactionCount(Integer numTransactions) {
- if (numTransactions <= 0) {
- throw new HttpException(422, ErrorCodes.INVALID_ORDER_TRANSACTION_COUNT);
- }
- }
-
- /**
- * There has to be atleast 1 payment/credits, otherwise no point in having an invoice. Also, it is possible that there are no
- * encumbrances related to an invoice upon approval. If not, throw an exception
- */
- private void validateInvoiceTransactionCount(Integer numPaymentsCredits, Integer numEncumbrances) {
- if (numPaymentsCredits <= 0 || numEncumbrances < 0) {
- throw new HttpException(422, ErrorCodes.INVALID_INVOICE_TRANSACTION_COUNT);
- }
- }
-
- public Future updateOrderTransactionSummary(OrderTransactionSummary orderSummary, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- validateOrderTransactionCount(orderSummary.getNumTransactions());
- return null;
- })
- .compose(v -> restClient.put(resourceByIdPath(ORDER_TRANSACTION_SUMMARIES, orderSummary.getId()), orderSummary, requestContext));
- }
-
- public Future updateInvoiceTransactionSummary(InvoiceTransactionSummary invoiceSummary, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v-> {
- validateInvoiceTransactionCount(invoiceSummary.getNumPaymentsCredits(), invoiceSummary.getNumPendingPayments());
- return null;
- })
- .compose(ok -> restClient.put(resourceByIdPath(INVOICE_TRANSACTION_SUMMARIES, invoiceSummary.getId()), invoiceSummary, requestContext));
- }
-}
diff --git a/src/main/java/org/folio/rest/impl/EncumbranceApi.java b/src/main/java/org/folio/rest/impl/EncumbranceApi.java
index e5af40a4..f88af942 100644
--- a/src/main/java/org/folio/rest/impl/EncumbranceApi.java
+++ b/src/main/java/org/folio/rest/impl/EncumbranceApi.java
@@ -10,7 +10,7 @@
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.jaxrs.resource.FinanceReleaseEncumbranceId;
import org.folio.rest.jaxrs.resource.FinanceUnreleaseEncumbranceId;
-import org.folio.services.transactions.BatchTransactionService;
+import org.folio.services.transactions.TransactionApiService;
import org.folio.spring.SpringContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -22,7 +22,7 @@
public class EncumbranceApi extends BaseApi implements FinanceReleaseEncumbranceId, FinanceUnreleaseEncumbranceId {
@Autowired
- private BatchTransactionService batchTransactionService;
+ private TransactionApiService transactionApiService;
public EncumbranceApi() {
SpringContextUtil.autowireDependencies(this, Vertx.currentContext());
@@ -32,7 +32,7 @@ public EncumbranceApi() {
@Validate
public void postFinanceReleaseEncumbranceById(String id, Map okapiHeaders,
Handler> asyncResultHandler, Context vertxContext) {
- batchTransactionService.releaseEncumbrance(id, new RequestContext(vertxContext, okapiHeaders))
+ transactionApiService.releaseEncumbrance(id, new RequestContext(vertxContext, okapiHeaders))
.onSuccess(v -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
.onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
}
@@ -41,7 +41,7 @@ public void postFinanceReleaseEncumbranceById(String id, Map oka
@Validate
public void postFinanceUnreleaseEncumbranceById(String id, Map okapiHeaders,
Handler> asyncResultHandler, Context vertxContext) {
- batchTransactionService.unreleaseEncumbrance(id, new RequestContext(vertxContext, okapiHeaders))
+ transactionApiService.unreleaseEncumbrance(id, new RequestContext(vertxContext, okapiHeaders))
.onSuccess(v -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
.onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
}
diff --git a/src/main/java/org/folio/rest/impl/FiscalYearsApi.java b/src/main/java/org/folio/rest/impl/FiscalYearsApi.java
index 86e0a40c..3a5e3b74 100644
--- a/src/main/java/org/folio/rest/impl/FiscalYearsApi.java
+++ b/src/main/java/org/folio/rest/impl/FiscalYearsApi.java
@@ -17,7 +17,7 @@
import org.folio.rest.exception.HttpException;
import org.folio.rest.jaxrs.model.FiscalYear;
import org.folio.rest.jaxrs.resource.FinanceFiscalYears;
-import org.folio.services.fiscalyear.FiscalYearService;
+import org.folio.services.fiscalyear.FiscalYearApiService;
import org.folio.spring.SpringContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,7 +33,7 @@ public class FiscalYearsApi extends BaseApi implements FinanceFiscalYears {
private static final String FISCAL_YEAR_CODE_PATTERN = "^[a-zA-Z]+[0-9]{4}$";
@Autowired
- private FiscalYearService fiscalYearService;
+ private FiscalYearApiService fiscalYearApiService;
public FiscalYearsApi() {
SpringContextUtil.autowireDependencies(this, Vertx.currentContext());
@@ -57,7 +57,7 @@ public void postFinanceFiscalYears(FiscalYear fiscalYear, Map he
// series should always be calculated
setFYearWithSeries(fiscalYear);
- fiscalYearService.createFiscalYear(fiscalYear, new RequestContext(ctx, headers))
+ fiscalYearApiService.createFiscalYear(fiscalYear, new RequestContext(ctx, headers))
.onSuccess(fy -> handler
.handle(succeededFuture(buildResponseWithLocation(headers.get(OKAPI_URL), String.format(FISCAL_YEARS_LOCATION_PREFIX, fy.getId()), fy))))
.onFailure(fail -> handleErrorResponse(handler, fail));
@@ -72,7 +72,7 @@ private boolean isFiscalYearValid(FiscalYear fiscalYear) {
public void getFinanceFiscalYears(String totalRecords, int offset, int limit, String query, Map headers,
Handler> handler, Context ctx) {
- fiscalYearService.getFiscalYearsWithAcqUnitsRestriction(query, offset, limit, new RequestContext(ctx, headers))
+ fiscalYearApiService.getFiscalYearsWithAcqUnitsRestriction(query, offset, limit, new RequestContext(ctx, headers))
.onSuccess(fiscalYears -> handler.handle(succeededFuture(buildOkResponse(fiscalYears))))
.onFailure(fail -> handleErrorResponse(handler, fail));
}
@@ -103,7 +103,7 @@ public void putFinanceFiscalYearsById(String id, FiscalYear fiscalYearRequest, M
// series should always be calculated
setFYearWithSeries(fiscalYearRequest);
- fiscalYearService.updateFiscalYear(fiscalYearRequest, new RequestContext(ctx, headers))
+ fiscalYearApiService.updateFiscalYear(fiscalYearRequest, new RequestContext(ctx, headers))
.onSuccess(fiscalYear -> handler.handle(succeededFuture(buildNoContentResponse())))
.onFailure(fail -> handleErrorResponse(handler, fail));
}
@@ -113,7 +113,7 @@ public void putFinanceFiscalYearsById(String id, FiscalYear fiscalYearRequest, M
public void getFinanceFiscalYearsById(String id, boolean withFinancialSummary, Map headers, Handler> handler,
Context ctx) {
- fiscalYearService.getFiscalYearById(id, withFinancialSummary, new RequestContext(ctx, headers))
+ fiscalYearApiService.getFiscalYearById(id, withFinancialSummary, new RequestContext(ctx, headers))
.onSuccess(fiscalYear -> handler.handle(succeededFuture(buildOkResponse(fiscalYear))))
.onFailure(fail -> handleErrorResponse(handler, fail));
}
@@ -122,7 +122,7 @@ public void getFinanceFiscalYearsById(String id, boolean withFinancialSummary, M
@Override
public void deleteFinanceFiscalYearsById(String id, Map headers,
Handler> handler, Context ctx) {
- fiscalYearService.deleteFiscalYear(id, new RequestContext(ctx, headers))
+ fiscalYearApiService.deleteFiscalYear(id, new RequestContext(ctx, headers))
.onSuccess(fiscalYear -> handler.handle(succeededFuture(buildNoContentResponse())))
.onFailure(fail -> handleErrorResponse(handler, fail));
}
diff --git a/src/main/java/org/folio/rest/impl/TransactionSummariesAPI.java b/src/main/java/org/folio/rest/impl/TransactionSummariesAPI.java
deleted file mode 100644
index d541e453..00000000
--- a/src/main/java/org/folio/rest/impl/TransactionSummariesAPI.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.folio.rest.impl;
-
-import static io.vertx.core.Future.succeededFuture;
-import static org.apache.commons.lang3.StringUtils.isEmpty;
-import static org.folio.rest.util.ErrorCodes.MISMATCH_BETWEEN_ID_IN_PATH_AND_BODY;
-import static org.folio.rest.util.HelperUtils.getEndpoint;
-import static org.folio.rest.util.HelperUtils.handleErrorResponse;
-
-import java.util.Map;
-
-import javax.ws.rs.core.Response;
-
-import org.folio.rest.annotations.Validate;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.helper.TransactionSummariesHelper;
-import org.folio.rest.jaxrs.model.InvoiceTransactionSummary;
-import org.folio.rest.jaxrs.model.OrderTransactionSummary;
-import org.folio.rest.jaxrs.resource.FinanceInvoiceTransactionSummaries;
-import org.folio.rest.jaxrs.resource.FinanceOrderTransactionSummaries;
-
-import io.vertx.core.AsyncResult;
-import io.vertx.core.Context;
-import io.vertx.core.Handler;
-
-public class TransactionSummariesAPI implements FinanceOrderTransactionSummaries, FinanceInvoiceTransactionSummaries {
-
- private static final String ORDER_TRANSACTION_SUMMARIES_LOCATION_PREFIX = getEndpoint(FinanceOrderTransactionSummaries.class) + "/%s";
- private static final String INVOICE_TRANSACTION_SUMMARIES_LOCATION_PREFIX = getEndpoint(FinanceInvoiceTransactionSummaries.class) + "/%s";
-
- @Override
- @Validate
- public void postFinanceOrderTransactionSummaries(OrderTransactionSummary entity, Map okapiHeaders,
- Handler> asyncResultHandler, Context vertxContext) {
- TransactionSummariesHelper helper = new TransactionSummariesHelper(okapiHeaders, vertxContext);
- helper.createOrderTransactionSummary(entity, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(orderTxSummary -> asyncResultHandler.handle(succeededFuture(
- helper.buildResponseWithLocation(String.format(ORDER_TRANSACTION_SUMMARIES_LOCATION_PREFIX, orderTxSummary.getId()), orderTxSummary))))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, helper, fail));
- }
-
- @Override
- @Validate
- public void putFinanceOrderTransactionSummariesById(String id, OrderTransactionSummary entity,
- Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) {
- TransactionSummariesHelper helper = new TransactionSummariesHelper(okapiHeaders, vertxContext);
-
- // Set id if this is available only in path
- if (isEmpty(entity.getId())) {
- entity.setId(id);
- } else if (!id.equals(entity.getId())) {
- helper.addProcessingError(MISMATCH_BETWEEN_ID_IN_PATH_AND_BODY.toError());
- asyncResultHandler.handle(succeededFuture(helper.buildErrorResponse(422)));
- return;
- }
-
- helper.updateOrderTransactionSummary(entity, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(types -> asyncResultHandler.handle(succeededFuture(helper.buildNoContentResponse())))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, helper, fail));
- }
-
- @Override
- @Validate
- public void postFinanceInvoiceTransactionSummaries(InvoiceTransactionSummary entity,
- Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) {
- TransactionSummariesHelper helper = new TransactionSummariesHelper(okapiHeaders, vertxContext);
- helper.createInvoiceTransactionSummary(entity, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(invoiceTxSummary -> asyncResultHandler.handle(succeededFuture(helper
- .buildResponseWithLocation(String.format(INVOICE_TRANSACTION_SUMMARIES_LOCATION_PREFIX, invoiceTxSummary.getId()), invoiceTxSummary))))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, helper, fail));
- }
-
- @Override
- @Validate
- public void putFinanceInvoiceTransactionSummariesById(String id, InvoiceTransactionSummary entity,
- Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) {
- TransactionSummariesHelper helper = new TransactionSummariesHelper(okapiHeaders, vertxContext);
-
- // Set id if this is available only in path
- if (isEmpty(entity.getId())) {
- entity.setId(id);
- } else if (!id.equals(entity.getId())) {
- helper.addProcessingError(MISMATCH_BETWEEN_ID_IN_PATH_AND_BODY.toError());
- asyncResultHandler.handle(succeededFuture(helper.buildErrorResponse(422)));
- return;
- }
-
- helper.updateInvoiceTransactionSummary(entity, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(types -> asyncResultHandler.handle(succeededFuture(helper.buildNoContentResponse())))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, helper, fail));
- }
-
-}
-
diff --git a/src/main/java/org/folio/rest/impl/TransactionsApi.java b/src/main/java/org/folio/rest/impl/TransactionsApi.java
index 6f562e6a..d7df4d22 100644
--- a/src/main/java/org/folio/rest/impl/TransactionsApi.java
+++ b/src/main/java/org/folio/rest/impl/TransactionsApi.java
@@ -1,8 +1,6 @@
package org.folio.rest.impl;
import static io.vertx.core.Future.succeededFuture;
-import static org.apache.commons.lang3.StringUtils.isEmpty;
-import static org.folio.rest.util.ErrorCodes.MISMATCH_BETWEEN_ID_IN_PATH_AND_BODY;
import static org.folio.rest.util.HelperUtils.OKAPI_URL;
import static org.folio.rest.util.HelperUtils.getEndpoint;
@@ -12,14 +10,10 @@
import org.folio.rest.annotations.Validate;
import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.exception.HttpException;
import org.folio.rest.jaxrs.model.Batch;
import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.rest.jaxrs.model.Transaction.TransactionType;
import org.folio.rest.jaxrs.resource.Finance;
-import org.folio.services.transactions.BatchTransactionService;
-import org.folio.services.transactions.TransactionService;
-import org.folio.services.transactions.TransactionStrategyFactory;
+import org.folio.services.transactions.TransactionApiService;
import org.folio.spring.SpringContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,11 +27,7 @@ public class TransactionsApi extends BaseApi implements Finance {
private static final String TRANSACTIONS_LOCATION_PREFIX = getEndpoint(Finance.class) + "/%s";
@Autowired
- private TransactionStrategyFactory transactionStrategyFactory;
- @Autowired
- private TransactionService transactionService;
- @Autowired
- private BatchTransactionService batchTransactionService;
+ private TransactionApiService transactionApiService;
public TransactionsApi() {
SpringContextUtil.autowireDependencies(this, Vertx.currentContext());
@@ -48,8 +38,9 @@ public TransactionsApi() {
public void postFinanceAllocations(Transaction allocation, Map okapiHeaders,
Handler> asyncResultHandler, Context vertxContext) {
- transactionStrategyFactory.createTransaction(Transaction.TransactionType.ALLOCATION, allocation, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(type -> asyncResultHandler.handle(succeededFuture(buildResponseWithLocation(okapiHeaders.get(OKAPI_URL), String.format(TRANSACTIONS_LOCATION_PREFIX, type.getId()), type))))
+ transactionApiService.createAllocation(allocation, new RequestContext(vertxContext, okapiHeaders))
+ .onSuccess(tr -> asyncResultHandler.handle(succeededFuture(buildResponseWithLocation(
+ okapiHeaders.get(OKAPI_URL), String.format(TRANSACTIONS_LOCATION_PREFIX, tr.getId()), tr))))
.onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
}
@@ -58,57 +49,18 @@ public void postFinanceAllocations(Transaction allocation, Map o
public void postFinanceTransfers(Transaction transfer, Map okapiHeaders,
Handler> asyncResultHandler, Context vertxContext) {
- transactionStrategyFactory.createTransaction(Transaction.TransactionType.TRANSFER, transfer, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(type -> asyncResultHandler
- .handle(succeededFuture(buildResponseWithLocation(okapiHeaders.get(OKAPI_URL), String.format(TRANSACTIONS_LOCATION_PREFIX, type.getId()), type))))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- }
-
- @Validate
- @Override
- public void postFinanceEncumbrances(Transaction encumbrance, Map okapiHeaders,
- Handler> asyncResultHandler, Context vertxContext) {
-
- transactionStrategyFactory.createTransaction(Transaction.TransactionType.ENCUMBRANCE, encumbrance, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(type -> asyncResultHandler
- .handle(succeededFuture(buildResponseWithLocation(okapiHeaders.get(OKAPI_URL), String.format(TRANSACTIONS_LOCATION_PREFIX, type.getId()), type))))
+ transactionApiService.createTransfer(transfer, new RequestContext(vertxContext, okapiHeaders))
+ .onSuccess(tr -> asyncResultHandler.handle(succeededFuture(buildResponseWithLocation(
+ okapiHeaders.get(OKAPI_URL), String.format(TRANSACTIONS_LOCATION_PREFIX, tr.getId()), tr))))
.onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
}
- @Validate
- @Override
- public void putFinanceEncumbrancesById(String id, Transaction encumbrance, Map okapiHeaders,
- Handler> asyncResultHandler, Context vertxContext) {
- if (isEmpty(encumbrance.getId())) {
- encumbrance.setId(id);
- }
- if (id.equals(encumbrance.getId())) {
-
- transactionStrategyFactory.updateTransaction(Transaction.TransactionType.ENCUMBRANCE, encumbrance, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(types -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- } else {
- asyncResultHandler.handle(succeededFuture(buildErrorResponse(new HttpException(422, MISMATCH_BETWEEN_ID_IN_PATH_AND_BODY))));
- }
- }
-
- @Validate
- @Override
- public void deleteFinanceEncumbrancesById(String id, Map okapiHeaders,
- Handler> asyncResultHandler, Context vertxContext) {
- transactionService.retrieveTransactionById(id, new RequestContext(vertxContext, okapiHeaders))
- .compose(transaction -> transactionStrategyFactory.deleteTransaction(Transaction.TransactionType.ENCUMBRANCE,
- transaction, new RequestContext(vertxContext, okapiHeaders)))
- .onSuccess(types -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- }
-
@Validate
@Override
public void getFinanceTransactions(String query, String totalRecords, int offset, int limit, Map okapiHeaders,
Handler> asyncResultHandler, Context vertxContext) {
- transactionService.retrieveTransactions(query, offset, limit, new RequestContext(vertxContext, okapiHeaders))
+ transactionApiService.getTransactionCollectionByQuery(query, offset, limit, new RequestContext(vertxContext, okapiHeaders))
.onSuccess(types -> asyncResultHandler.handle(succeededFuture(buildOkResponse(types))))
.onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
}
@@ -118,94 +70,17 @@ public void getFinanceTransactions(String query, String totalRecords, int offset
public void getFinanceTransactionsById(String id, Map okapiHeaders,
Handler> asyncResultHandler, Context vertxContext) {
- transactionService.retrieveTransactionById(id, new RequestContext(vertxContext, okapiHeaders))
+ transactionApiService.getTransactionById(id, new RequestContext(vertxContext, okapiHeaders))
.onSuccess(type -> asyncResultHandler.handle(succeededFuture(buildOkResponse(type))))
.onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
}
@Validate
@Override
- public void postFinancePayments(Transaction payment, Map okapiHeaders,
- Handler> asyncResultHandler, Context vertxContext) {
-
- transactionStrategyFactory.createTransaction(Transaction.TransactionType.PAYMENT, payment, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(type -> asyncResultHandler
- .handle(succeededFuture(buildResponseWithLocation(okapiHeaders.get(OKAPI_URL), String.format(TRANSACTIONS_LOCATION_PREFIX, type.getId()), type))))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- }
-
- @Override
- public void putFinancePaymentsById(String id, Transaction payment,
- Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) {
- if (isEmpty(payment.getId())) {
- payment.setId(id);
- } else if (!id.equals(payment.getId())) {
- asyncResultHandler.handle(succeededFuture(buildErrorResponse(new HttpException(422, MISMATCH_BETWEEN_ID_IN_PATH_AND_BODY))));
- return;
- }
- transactionStrategyFactory.updateTransaction(
- TransactionType.PAYMENT, payment, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(types -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- }
-
- @Validate
- @Override
- public void postFinancePendingPayments(Transaction pendingPayment, Map okapiHeaders,
- Handler> asyncResultHandler, Context vertxContext) {
-
- transactionStrategyFactory.createTransaction(Transaction.TransactionType.PENDING_PAYMENT, pendingPayment, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(transaction -> asyncResultHandler
- .handle(succeededFuture(buildResponseWithLocation(okapiHeaders.get(OKAPI_URL), String.format(TRANSACTIONS_LOCATION_PREFIX, transaction.getId()), transaction))))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- }
-
- @Override
- public void putFinancePendingPaymentsById(String id, Transaction pendingPayment,
- Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) {
- if (isEmpty(pendingPayment.getId())) {
- pendingPayment.setId(id);
- } else if (!id.equals(pendingPayment.getId())) {
- asyncResultHandler.handle(succeededFuture(buildErrorResponse(new HttpException(422, MISMATCH_BETWEEN_ID_IN_PATH_AND_BODY))));
- return;
- }
- transactionStrategyFactory.updateTransaction(
- TransactionType.PENDING_PAYMENT, pendingPayment, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(types -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- }
-
- @Validate
- @Override
- public void postFinanceCredits(Transaction credit, Map okapiHeaders,
+ public void postFinanceTransactionsBatchAllOrNothing(Batch batch, Map okapiHeaders,
Handler> asyncResultHandler, Context vertxContext) {
- transactionStrategyFactory.createTransaction(Transaction.TransactionType.CREDIT, credit, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(type -> asyncResultHandler
- .handle(succeededFuture(buildResponseWithLocation(okapiHeaders.get(OKAPI_URL), String.format(TRANSACTIONS_LOCATION_PREFIX, type.getId()), type))))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- }
-
- @Override
- public void putFinanceCreditsById(String id, Transaction credit,
- Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) {
- if (isEmpty(credit.getId())) {
- credit.setId(id);
- } else if (!id.equals(credit.getId())) {
- asyncResultHandler.handle(succeededFuture(buildErrorResponse(new HttpException(422, MISMATCH_BETWEEN_ID_IN_PATH_AND_BODY))));
- return;
- }
- transactionStrategyFactory.updateTransaction(
- TransactionType.CREDIT, credit, new RequestContext(vertxContext, okapiHeaders))
- .onSuccess(types -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
- .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
- }
-
- @Validate
- @Override
- public void postFinanceTransactionsBatchAllOrNothing(Batch batch, Map okapiHeaders,
- Handler> asyncResultHandler, Context vertxContext) {
- batchTransactionService.processBatch(batch, new RequestContext(vertxContext, okapiHeaders))
+ transactionApiService.processBatch(batch, new RequestContext(vertxContext, okapiHeaders))
.onSuccess(types -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
.onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
}
diff --git a/src/main/java/org/folio/rest/util/HelperUtils.java b/src/main/java/org/folio/rest/util/HelperUtils.java
index 97826bf2..4fa8b751 100644
--- a/src/main/java/org/folio/rest/util/HelperUtils.java
+++ b/src/main/java/org/folio/rest/util/HelperUtils.java
@@ -5,7 +5,6 @@
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.folio.rest.util.ErrorCodes.GENERIC_ERROR_CODE;
-import static org.folio.rest.util.ErrorCodes.NEGATIVE_VALUE;
import java.math.BigDecimal;
import java.net.URLEncoder;
@@ -38,7 +37,6 @@
import org.folio.rest.helper.AbstractHelper;
import org.folio.rest.jaxrs.model.Error;
import org.folio.rest.jaxrs.model.Errors;
-import org.folio.rest.jaxrs.model.Parameter;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
@@ -186,22 +184,6 @@ public static javax.ws.rs.core.Response.ResponseBuilder createResponseBuilder(in
};
}
- public static void validateAmount(double doubleAmount, String fieldName) {
- BigDecimal amount = BigDecimal.valueOf(doubleAmount);
- if (isNegative(amount)) {
- Parameter parameter = new Parameter().withKey("field").withValue(fieldName);
- throw new HttpException(422, NEGATIVE_VALUE.toError().withParameters(Collections.singletonList(parameter)));
- }
- }
-
- private static boolean isNegative(BigDecimal amount) {
- return amount.compareTo(BigDecimal.ZERO) < 0;
- }
-
- public static Future unsupportedOperationExceptionFuture() {
- return Future.failedFuture(new UnsupportedOperationException());
- }
-
public static double calculateTotals(List budgets, ToDoubleFunction getDouble) {
return budgets.stream()
.map(budget -> BigDecimal.valueOf(getDouble.applyAsDouble(budget)))
@@ -239,4 +221,32 @@ public static void removeInitialAllocationByFunds(List allocationTo
}
});
}
+
+ public static String errorsAsString(Errors errors) {
+ if (errors == null) {
+ return GENERIC_ERROR_CODE.getDescription();
+ }
+ List errorList = errors.getErrors();
+ if (errorList.isEmpty()) {
+ return GENERIC_ERROR_CODE.getDescription();
+ }
+ if (errorList.size() == 1) {
+ return errorAsString(errorList.get(0));
+ }
+ return JsonObject.mapFrom(errorList).encode();
+ }
+
+ public static String errorAsString(Error error) {
+ if (error == null) {
+ return GENERIC_ERROR_CODE.getDescription();
+ }
+ if (!error.getParameters().isEmpty()) {
+ return JsonObject.mapFrom(error).encode();
+ }
+ if (error.getMessage() == null) {
+ return GENERIC_ERROR_CODE.getDescription();
+ }
+ return error.getMessage();
+ }
+
}
diff --git a/src/main/java/org/folio/services/budget/BudgetExpenseClassService.java b/src/main/java/org/folio/services/budget/BudgetExpenseClassService.java
index bc434a00..4bc9b194 100644
--- a/src/main/java/org/folio/services/budget/BudgetExpenseClassService.java
+++ b/src/main/java/org/folio/services/budget/BudgetExpenseClassService.java
@@ -34,7 +34,7 @@
import org.folio.rest.jaxrs.model.BudgetExpenseClassCollection;
import org.folio.rest.jaxrs.model.SharedBudget;
import org.folio.rest.jaxrs.model.StatusExpenseClass;
-import org.folio.services.transactions.CommonTransactionService;
+import org.folio.services.transactions.TransactionService;
import io.vertx.core.Future;
@@ -43,9 +43,9 @@ public class BudgetExpenseClassService {
private static final Logger log = LogManager.getLogger();
private final RestClient restClient;
- private final CommonTransactionService transactionService;
+ private final TransactionService transactionService;
- public BudgetExpenseClassService(RestClient restClient, CommonTransactionService transactionService) {
+ public BudgetExpenseClassService(RestClient restClient, TransactionService transactionService) {
this.restClient = restClient;
this.transactionService = transactionService;
}
@@ -118,7 +118,7 @@ private Future deleteBudgetExpenseClasses(List deleteL
private Future checkNoTransactionsAssigned(List deleteList, SharedBudget budget, RequestContext requestContext) {
log.debug("checkNoTransactionsAssigned:: Checking there is no transaction assigned for budget: {} and deleteList with size: {}", budget.getId(), deleteList.size());
- return transactionService.retrieveTransactions(deleteList, budget, requestContext)
+ return transactionService.getBudgetTransactionsWithExpenseClasses(deleteList, budget, requestContext)
.map(transactions -> {
if (isNotEmpty(transactions)) {
log.error("checkNoTransactionsAssigned:: There is assigned transaction for budget: '{}'", budget.getId());
diff --git a/src/main/java/org/folio/services/budget/BudgetExpenseClassTotalsService.java b/src/main/java/org/folio/services/budget/BudgetExpenseClassTotalsService.java
index bf060c2f..51177ab9 100644
--- a/src/main/java/org/folio/services/budget/BudgetExpenseClassTotalsService.java
+++ b/src/main/java/org/folio/services/budget/BudgetExpenseClassTotalsService.java
@@ -28,7 +28,7 @@
import org.folio.rest.jaxrs.model.Transaction;
import org.folio.rest.util.MoneyUtils;
import org.folio.services.ExpenseClassService;
-import org.folio.services.transactions.CommonTransactionService;
+import org.folio.services.transactions.TransactionService;
import io.vertx.core.Future;
import org.javamoney.moneta.Money;
@@ -37,12 +37,12 @@ public class BudgetExpenseClassTotalsService {
private final RestClient restClient;
private final ExpenseClassService expenseClassService;
- private final CommonTransactionService transactionService;
+ private final TransactionService transactionService;
private final BudgetExpenseClassService budgetExpenseClassService;
public BudgetExpenseClassTotalsService(RestClient restClient,
ExpenseClassService expenseClassService,
- CommonTransactionService transactionService,
+ TransactionService transactionService,
BudgetExpenseClassService budgetExpenseClassService) {
this.restClient = restClient;
this.expenseClassService = expenseClassService;
@@ -53,7 +53,7 @@ public BudgetExpenseClassTotalsService(RestClient restClient,
public Future getExpenseClassTotals(String budgetId, RequestContext requestContext) {
return restClient.get(resourceByIdPath(BUDGETS_STORAGE, budgetId), Budget.class, requestContext)
.compose(budget -> expenseClassService.getExpenseClassesByBudgetId(budgetId, requestContext)
- .compose(expenseClasses -> transactionService.retrieveTransactions(budget, requestContext)
+ .compose(expenseClasses -> transactionService.getBudgetTransactions(budget, requestContext)
.map(transactions -> buildBudgetExpenseClassesTotals(expenseClasses, transactions, budget))))
.compose(budgetExpenseClassTotalsCollection -> budgetExpenseClassService.getBudgetExpenseClasses(budgetId, requestContext)
.map(budgetExpenseClasses -> updateExpenseClassStatus(budgetExpenseClassTotalsCollection, budgetExpenseClasses)));
diff --git a/src/main/java/org/folio/services/budget/CreateBudgetService.java b/src/main/java/org/folio/services/budget/CreateBudgetService.java
index 99121d37..967ed7c6 100644
--- a/src/main/java/org/folio/services/budget/CreateBudgetService.java
+++ b/src/main/java/org/folio/services/budget/CreateBudgetService.java
@@ -25,9 +25,9 @@
import org.folio.services.fund.FundDetailsService;
import org.folio.services.fund.FundFiscalYearService;
import org.folio.services.group.GroupFundFiscalYearService;
-import org.folio.services.transactions.CommonTransactionService;
import io.vertx.core.Future;
+import org.folio.services.transactions.TransactionService;
public class CreateBudgetService {
@@ -37,14 +37,14 @@ public class CreateBudgetService {
private final GroupFundFiscalYearService groupFundFiscalYearService;
private final FundFiscalYearService fundFiscalYearService;
private final BudgetExpenseClassService budgetExpenseClassService;
- private final CommonTransactionService transactionService;
+ private final TransactionService transactionService;
private final FundDetailsService fundDetailsService;
public CreateBudgetService(RestClient restClient,
GroupFundFiscalYearService groupFundFiscalYearService,
FundFiscalYearService fundFiscalYearService,
BudgetExpenseClassService budgetExpenseClassService,
- CommonTransactionService transactionService,
+ TransactionService transactionService,
FundDetailsService fundDetailsService) {
this.restClient = restClient;
this.groupFundFiscalYearService = groupFundFiscalYearService;
@@ -95,10 +95,10 @@ private Future allocateToBudget(Budget createdBudget, RequestContext req
if (createdBudget.getAllocated() > 0d) {
log.info("allocateToBudget:: allocation for created budget '{}' is greater than zero, allocation transaction is being created", createdBudget.getId());
return transactionService.createAllocationTransaction(createdBudget, requestContext)
- .map(transaction -> createdBudget)
- .recover(e -> {
- log.error("Failed to create allocation transaction for budget '{}': {}", createdBudget.getId(), e.getMessage(), e);
- return Future.failedFuture(new HttpException(500, ErrorCodes.ALLOCATION_TRANSFER_FAILED));
+ .map(v -> createdBudget)
+ .recover(t -> {
+ log.error("Failed to create allocation transaction for budget '{}': {}", createdBudget.getId(), t.getMessage(), t);
+ return Future.failedFuture(new HttpException(500, ErrorCodes.ALLOCATION_TRANSFER_FAILED, t));
});
}
log.info("allocateToBudget:: Allocation for createdBudget '{}' is zero or less, no transaction needed", createdBudget.getId());
diff --git a/src/main/java/org/folio/services/budget/RecalculateBudgetService.java b/src/main/java/org/folio/services/budget/RecalculateBudgetService.java
index 9e6ea82f..14253839 100644
--- a/src/main/java/org/folio/services/budget/RecalculateBudgetService.java
+++ b/src/main/java/org/folio/services/budget/RecalculateBudgetService.java
@@ -6,22 +6,22 @@
import org.folio.rest.jaxrs.model.SharedBudget;
import org.folio.rest.jaxrs.model.Transaction;
import org.folio.rest.util.BudgetUtils;
-import org.folio.services.transactions.CommonTransactionService;
+import org.folio.services.transactions.TransactionService;
import java.util.List;
public class RecalculateBudgetService {
private final BudgetService budgetService;
- private final CommonTransactionService transactionService;
+ private final TransactionService transactionService;
- public RecalculateBudgetService(BudgetService budgetService, CommonTransactionService transactionService) {
+ public RecalculateBudgetService(BudgetService budgetService, TransactionService transactionService) {
this.budgetService = budgetService;
this.transactionService = transactionService;
}
public Future recalculateBudget(String budgetId, RequestContext requestContext) {
return budgetService.getBudgetById(budgetId, requestContext)
- .compose(budget -> transactionService.retrieveTransactions(BudgetUtils.convertToBudget(budget), requestContext)
+ .compose(budget -> transactionService.getBudgetTransactions(BudgetUtils.convertToBudget(budget), requestContext)
.map(transactions -> recalculateBudgetBasedOnTransactions(budget, transactions)))
.compose(budget -> budgetService.updateBudgetWithAmountFields(budget, requestContext))
.mapEmpty();
diff --git a/src/main/java/org/folio/services/fiscalyear/FiscalYearApiService.java b/src/main/java/org/folio/services/fiscalyear/FiscalYearApiService.java
new file mode 100644
index 00000000..91eeae85
--- /dev/null
+++ b/src/main/java/org/folio/services/fiscalyear/FiscalYearApiService.java
@@ -0,0 +1,105 @@
+package org.folio.services.fiscalyear;
+
+import static io.vertx.core.Future.succeededFuture;
+import static org.folio.rest.util.HelperUtils.combineCqlExpressions;
+
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.folio.rest.core.models.RequestContext;
+import org.folio.rest.jaxrs.model.Budget;
+import org.folio.rest.jaxrs.model.FinancialSummary;
+import org.folio.rest.jaxrs.model.FiscalYear;
+import org.folio.rest.jaxrs.model.FiscalYearsCollection;
+import org.folio.rest.util.HelperUtils;
+import org.folio.services.budget.BudgetService;
+import org.folio.services.configuration.ConfigurationEntriesService;
+import org.folio.services.protection.AcqUnitsService;
+
+import io.vertx.core.Future;
+
+public class FiscalYearApiService {
+
+ private static final Logger log = LogManager.getLogger();
+
+ private final FiscalYearService fiscalYearService;
+ private final ConfigurationEntriesService configurationEntriesService;
+ private final BudgetService budgetService;
+ private final AcqUnitsService acqUnitsService;
+
+ /**
+ * This class is only used by FiscalYearApi
+ */
+ public FiscalYearApiService(FiscalYearService fiscalYearService, ConfigurationEntriesService configurationEntriesService,
+ BudgetService budgetService, AcqUnitsService acqUnitsService) {
+ this.fiscalYearService = fiscalYearService;
+ this.configurationEntriesService = configurationEntriesService;
+ this.budgetService = budgetService;
+ this.acqUnitsService = acqUnitsService;
+ }
+
+ public Future createFiscalYear(FiscalYear fiscalYear, RequestContext requestContext) {
+ log.debug("createFiscalYear:: Creating fiscal year: {}", fiscalYear.getId());
+ return configurationEntriesService.getSystemCurrency(requestContext)
+ .compose(currency -> {
+ fiscalYear.setCurrency(currency);
+ return fiscalYearService.createFiscalYear(fiscalYear, requestContext);
+ });
+ }
+
+ public Future getFiscalYearsWithAcqUnitsRestriction(String query, int offset, int limit,
+ RequestContext requestContext) {
+ return acqUnitsService.buildAcqUnitsCqlClause(requestContext)
+ .map(clause -> StringUtils.isEmpty(query) ? clause : combineCqlExpressions("and", clause, query))
+ .compose(effectiveQuery -> fiscalYearService.getFiscalYearsWithoutAcqUnitsRestriction(effectiveQuery, offset,
+ limit, requestContext));
+ }
+
+ public Future getFiscalYearById(String id, boolean withFinancialSummary, RequestContext requestContext) {
+ return fiscalYearService.getFiscalYearById(id, requestContext)
+ .compose(fiscalYear -> {
+ if (withFinancialSummary) {
+ return withFinancialSummary(fiscalYear, requestContext);
+ }
+ return succeededFuture(fiscalYear);
+ });
+ }
+
+ public Future updateFiscalYear(FiscalYear fiscalYear, RequestContext requestContext) {
+ return configurationEntriesService.getSystemCurrency(requestContext)
+ .compose(currency -> {
+ fiscalYear.setCurrency(currency);
+ return fiscalYearService.updateFiscalYear(fiscalYear, requestContext);
+ });
+ }
+
+ public Future deleteFiscalYear(String id, RequestContext requestContext) {
+ return fiscalYearService.deleteFiscalYear(id, requestContext);
+ }
+
+ private Future withFinancialSummary(FiscalYear fiscalYear, RequestContext requestContext) {
+ String query = "fiscalYearId==" + fiscalYear.getId();
+ return budgetService.getBudgets(query, 0, Integer.MAX_VALUE, requestContext)
+ .map(budgetsCollection -> populateFinancialSummary(fiscalYear, budgetsCollection.getBudgets()));
+ }
+
+ private FiscalYear populateFinancialSummary(FiscalYear fiscalYear, List budgets) {
+ FinancialSummary financialSummary = new FinancialSummary()
+ .withAllocated(HelperUtils.calculateTotals(budgets, Budget::getAllocated))
+ .withAvailable(HelperUtils.calculateTotals(budgets, Budget::getAvailable))
+ .withUnavailable(HelperUtils.calculateTotals(budgets, Budget::getUnavailable))
+ .withInitialAllocation(HelperUtils.calculateTotals(budgets, Budget::getInitialAllocation))
+ .withAllocationTo(HelperUtils.calculateTotals(budgets, Budget::getAllocationTo))
+ .withAllocationFrom(HelperUtils.calculateTotals(budgets, Budget::getAllocationFrom))
+ .withAwaitingPayment(HelperUtils.calculateTotals(budgets, Budget::getAwaitingPayment))
+ .withEncumbered(HelperUtils.calculateTotals(budgets, Budget::getEncumbered))
+ .withExpenditures(HelperUtils.calculateTotals(budgets, Budget::getExpenditures))
+ .withOverEncumbrance(HelperUtils.calculateTotals(budgets, Budget::getOverEncumbrance))
+ .withOverExpended(HelperUtils.calculateTotals(budgets, Budget::getOverExpended))
+ .withTotalFunding(HelperUtils.calculateTotals(budgets, Budget::getTotalFunding))
+ .withCashBalance(HelperUtils.calculateTotals(budgets, Budget::getCashBalance));
+ return fiscalYear.withFinancialSummary(financialSummary);
+ }
+}
diff --git a/src/main/java/org/folio/services/fiscalyear/FiscalYearService.java b/src/main/java/org/folio/services/fiscalyear/FiscalYearService.java
index 8d63dda5..f7ebc593 100644
--- a/src/main/java/org/folio/services/fiscalyear/FiscalYearService.java
+++ b/src/main/java/org/folio/services/fiscalyear/FiscalYearService.java
@@ -1,30 +1,19 @@
package org.folio.services.fiscalyear;
-import static io.vertx.core.Future.succeededFuture;
import static org.folio.rest.util.ErrorCodes.FISCAL_YEARS_NOT_FOUND;
-import static org.folio.rest.util.HelperUtils.combineCqlExpressions;
import static org.folio.rest.util.ResourcePathResolver.FISCAL_YEARS_STORAGE;
import static org.folio.rest.util.ResourcePathResolver.resourceByIdPath;
import static org.folio.rest.util.ResourcePathResolver.resourcesPath;
-import java.util.List;
-
import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.rest.core.RestClient;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.core.models.RequestEntry;
import org.folio.rest.exception.HttpException;
-import org.folio.rest.jaxrs.model.Budget;
-import org.folio.rest.jaxrs.model.FinancialSummary;
import org.folio.rest.jaxrs.model.FiscalYear;
import org.folio.rest.jaxrs.model.FiscalYearsCollection;
-import org.folio.rest.util.HelperUtils;
-import org.folio.services.budget.BudgetService;
-import org.folio.services.configuration.ConfigurationEntriesService;
-import org.folio.services.protection.AcqUnitsService;
import io.vertx.core.Future;
@@ -33,24 +22,13 @@ public class FiscalYearService {
private static final Logger log = LogManager.getLogger();
private final RestClient restClient;
- private final ConfigurationEntriesService configurationEntriesService;
- private final BudgetService budgetService;
- private final AcqUnitsService acqUnitsService;
- public FiscalYearService(RestClient fiscalYearRestClient, ConfigurationEntriesService configurationEntriesService, BudgetService budgetService, AcqUnitsService acqUnitsService) {
- this.restClient = fiscalYearRestClient;
- this.configurationEntriesService = configurationEntriesService;
- this.budgetService = budgetService;
- this.acqUnitsService = acqUnitsService;
+ public FiscalYearService(RestClient restClient) {
+ this.restClient = restClient;
}
public Future createFiscalYear(FiscalYear fiscalYear, RequestContext requestContext) {
- log.debug("createFiscalYear:: Creating fiscal year: {}", fiscalYear.getId());
- return configurationEntriesService.getSystemCurrency(requestContext)
- .compose(currency -> {
- fiscalYear.setCurrency(currency);
- return restClient.post(resourcesPath(FISCAL_YEARS_STORAGE), fiscalYear, FiscalYear.class, requestContext);
- });
+ return restClient.post(resourcesPath(FISCAL_YEARS_STORAGE), fiscalYear, FiscalYear.class, requestContext);
}
public Future getFiscalYearsWithoutAcqUnitsRestriction(String query, int offset, int limit, RequestContext requestContext) {
@@ -61,25 +39,8 @@ public Future getFiscalYearsWithoutAcqUnitsRestriction(St
return restClient.get(requestEntry.buildEndpoint(), FiscalYearsCollection.class, requestContext);
}
- public Future getFiscalYearsWithAcqUnitsRestriction(String query, int offset, int limit, RequestContext requestContext) {
- return acqUnitsService.buildAcqUnitsCqlClause(requestContext)
- .map(clause -> StringUtils.isEmpty(query) ? clause : combineCqlExpressions("and", clause, query))
- .map(effectiveQuery -> new RequestEntry(resourcesPath(FISCAL_YEARS_STORAGE))
- .withOffset(offset)
- .withLimit(limit)
- .withQuery(effectiveQuery)
- )
- .compose(requestEntry -> restClient.get(requestEntry.buildEndpoint(), FiscalYearsCollection.class, requestContext));
- }
-
- public Future getFiscalYearById(String id, boolean withFinancialSummary, RequestContext requestContext) {
- return restClient.get(resourceByIdPath(FISCAL_YEARS_STORAGE, id), FiscalYear.class, requestContext)
- .compose(fiscalYear -> {
- if (withFinancialSummary) {
- return withFinancialSummary(fiscalYear, requestContext);
- }
- return succeededFuture(fiscalYear);
- });
+ public Future getFiscalYearById(String id, RequestContext requestContext) {
+ return restClient.get(resourceByIdPath(FISCAL_YEARS_STORAGE, id), FiscalYear.class, requestContext);
}
public Future getFiscalYearByFiscalYearCode(String fiscalYearCode, RequestContext requestContext) {
@@ -102,45 +63,12 @@ public String getFiscalYearByFiscalYearCode(String fiscalYearCode) {
return String.format("code=%s", fiscalYearCode);
}
- public Future getFiscalYearById(String id, RequestContext requestContext) {
- return getFiscalYearById(id, false, requestContext);
- }
-
public Future updateFiscalYear(FiscalYear fiscalYear, RequestContext requestContext) {
- return configurationEntriesService.getSystemCurrency(requestContext)
- .compose(currency -> {
- fiscalYear.setCurrency(currency);
- return restClient.put(resourceByIdPath(FISCAL_YEARS_STORAGE, fiscalYear.getId()), fiscalYear, requestContext);
- });
-
+ return restClient.put(resourceByIdPath(FISCAL_YEARS_STORAGE, fiscalYear.getId()), fiscalYear, requestContext);
}
public Future deleteFiscalYear(String id, RequestContext requestContext) {
return restClient.delete(resourceByIdPath(FISCAL_YEARS_STORAGE, id), requestContext);
}
-
- public Future withFinancialSummary(FiscalYear fiscalYear, RequestContext requestContext) {
- String query = "fiscalYearId==" + fiscalYear.getId();
- return budgetService.getBudgets(query, 0, Integer.MAX_VALUE, requestContext)
- .map(budgetsCollection -> populateFinancialSummary(fiscalYear, budgetsCollection.getBudgets()));
- }
-
- private FiscalYear populateFinancialSummary(FiscalYear fiscalYear, List budgets) {
- FinancialSummary financialSummary = new FinancialSummary()
- .withAllocated(HelperUtils.calculateTotals(budgets, Budget::getAllocated))
- .withAvailable(HelperUtils.calculateTotals(budgets, Budget::getAvailable))
- .withUnavailable(HelperUtils.calculateTotals(budgets, Budget::getUnavailable))
- .withInitialAllocation(HelperUtils.calculateTotals(budgets, Budget::getInitialAllocation))
- .withAllocationTo(HelperUtils.calculateTotals(budgets, Budget::getAllocationTo))
- .withAllocationFrom(HelperUtils.calculateTotals(budgets, Budget::getAllocationFrom))
- .withAwaitingPayment(HelperUtils.calculateTotals(budgets, Budget::getAwaitingPayment))
- .withEncumbered(HelperUtils.calculateTotals(budgets, Budget::getEncumbered))
- .withExpenditures(HelperUtils.calculateTotals(budgets, Budget::getExpenditures))
- .withOverEncumbrance(HelperUtils.calculateTotals(budgets, Budget::getOverEncumbrance))
- .withOverExpended(HelperUtils.calculateTotals(budgets, Budget::getOverExpended))
- .withTotalFunding(HelperUtils.calculateTotals(budgets, Budget::getTotalFunding))
- .withCashBalance(HelperUtils.calculateTotals(budgets, Budget::getCashBalance));
- return fiscalYear.withFinancialSummary(financialSummary);
- }
}
diff --git a/src/main/java/org/folio/services/fund/FundCodeExpenseClassesService.java b/src/main/java/org/folio/services/fund/FundCodeExpenseClassesService.java
index df850028..e3fbc657 100644
--- a/src/main/java/org/folio/services/fund/FundCodeExpenseClassesService.java
+++ b/src/main/java/org/folio/services/fund/FundCodeExpenseClassesService.java
@@ -102,7 +102,7 @@ public Future getFundCodeVsExpenseClassesWithF
.compose(fcecHolder -> getActiveBudgetsByFiscalYear(fcecHolder.getFiscalYear(), requestContext))
.map(fundCodeExpenseClassesHolder::withBudgetCollectionList)
.map(holder -> holder.getBudgetCollection().getBudgets().stream().map(Budget::getFundId).distinct().collect(Collectors.toList()))
- .compose(fundsId -> fundService.getFunds(fundsId, requestContext))
+ .compose(fundsId -> fundService.getFundsByIds(fundsId, requestContext))
.map(fundCodeExpenseClassesHolder::withFundList)
.map(fcecHolder -> fcecHolder.getFundList().stream().map(Fund::getLedgerId).collect(Collectors.toList()))
.compose(ledgerIds -> ledgerService.getLedgers(ledgerIds, requestContext))
diff --git a/src/main/java/org/folio/services/fund/FundService.java b/src/main/java/org/folio/services/fund/FundService.java
index b0153bc4..b62abf2c 100644
--- a/src/main/java/org/folio/services/fund/FundService.java
+++ b/src/main/java/org/folio/services/fund/FundService.java
@@ -12,7 +12,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.folio.rest.core.RestClient;
@@ -64,23 +63,23 @@ public Future getFundsWithoutAcqUnitsRestriction(String query,
return restClient.get(requestEntry.buildEndpoint(), FundsCollection.class, requestContext);
}
- public Future> getFunds(List fundIds, RequestContext requestContext) {
+ public Future> getFundsByIds(List fundIds, RequestContext requestContext) {
return collectResultsOnSuccess(
ofSubLists(new ArrayList<>(fundIds), MAX_IDS_FOR_GET_RQ)
- .map(ids -> getFundsByIds(ids, requestContext))
- .collect(Collectors.toList()))
+ .map(ids -> getFundsByIdsChunk(ids, requestContext))
+ .toList())
.map(lists -> lists.stream()
.flatMap(Collection::stream)
- .collect(Collectors.toList())
+ .toList()
);
}
- public Future> getFundsByIds(List ids, RequestContext requestContext) {
+ private Future> getFundsByIdsChunk(List ids, RequestContext requestContext) {
String query = HelperUtils.convertIdsToCqlQuery(ids);
var requestEntry = new RequestEntry(resourcesPath(FUNDS_STORAGE))
.withQuery(query)
.withOffset(0)
- .withLimit(MAX_IDS_FOR_GET_RQ);
+ .withLimit(Integer.MAX_VALUE);
return restClient.get(requestEntry.buildEndpoint(), FundsCollection.class, requestContext)
.map(FundsCollection::getFunds);
}
diff --git a/src/main/java/org/folio/services/group/GroupExpenseClassTotalsService.java b/src/main/java/org/folio/services/group/GroupExpenseClassTotalsService.java
index 37ab126a..f271b2c2 100644
--- a/src/main/java/org/folio/services/group/GroupExpenseClassTotalsService.java
+++ b/src/main/java/org/folio/services/group/GroupExpenseClassTotalsService.java
@@ -28,7 +28,7 @@
import org.folio.rest.util.MoneyUtils;
import org.folio.services.ExpenseClassService;
import org.folio.services.budget.RecalculatedBudgetBuilder;
-import org.folio.services.transactions.CommonTransactionService;
+import org.folio.services.transactions.TransactionService;
import org.javamoney.moneta.Money;
import org.javamoney.moneta.function.MonetaryFunctions;
@@ -39,10 +39,10 @@ public class GroupExpenseClassTotalsService {
private static final Logger log = LogManager.getLogger();
private final GroupFundFiscalYearService groupFundFiscalYearService;
- private final CommonTransactionService transactionService;
+ private final TransactionService transactionService;
private final ExpenseClassService expenseClassService;
- public GroupExpenseClassTotalsService(GroupFundFiscalYearService groupFundFiscalYearService, CommonTransactionService transactionService, ExpenseClassService expenseClassService) {
+ public GroupExpenseClassTotalsService(GroupFundFiscalYearService groupFundFiscalYearService, TransactionService transactionService, ExpenseClassService expenseClassService) {
this.groupFundFiscalYearService = groupFundFiscalYearService;
this.transactionService = transactionService;
this.expenseClassService = expenseClassService;
@@ -67,7 +67,7 @@ private Future getGroupExpenseClassTotals(Lis
private Future> getTransactions(List groupFundFiscalYears, String fiscalYearId, RequestContext requestContext) {
List fundIds = groupFundFiscalYears.stream().map(GroupFundFiscalYear::getFundId).collect(Collectors.toList());
- return transactionService.retrieveTransactionsByFundIds(fundIds, fiscalYearId, requestContext);
+ return transactionService.getTransactionsByFundIds(fundIds, fiscalYearId, requestContext);
}
private Future> getExpenseClasses(List groupFundFiscalYears, RequestContext requestContext) {
diff --git a/src/main/java/org/folio/services/group/GroupFiscalYearTotalsService.java b/src/main/java/org/folio/services/group/GroupFiscalYearTotalsService.java
index ba71a273..dfa290f8 100644
--- a/src/main/java/org/folio/services/group/GroupFiscalYearTotalsService.java
+++ b/src/main/java/org/folio/services/group/GroupFiscalYearTotalsService.java
@@ -36,7 +36,7 @@
import org.folio.rest.jaxrs.model.Transaction;
import org.folio.rest.jaxrs.model.Transaction.TransactionType;
import org.folio.rest.util.HelperUtils;
-import org.folio.services.transactions.BaseTransactionService;
+import org.folio.services.transactions.TransactionService;
import io.vertx.core.Future;
@@ -46,13 +46,13 @@ public class GroupFiscalYearTotalsService {
private final RestClient restClient;
private final GroupFundFiscalYearService groupFundFiscalYearService;
- private final BaseTransactionService baseTransactionService;
+ private final TransactionService transactionService;
public GroupFiscalYearTotalsService(RestClient restClient, GroupFundFiscalYearService groupFundFiscalYearService,
- BaseTransactionService baseTransactionService) {
+ TransactionService transactionService) {
this.restClient = restClient;
this.groupFundFiscalYearService = groupFundFiscalYearService;
- this.baseTransactionService = baseTransactionService;
+ this.transactionService = transactionService;
}
public Future getGroupFiscalYearSummaries(String query, RequestContext requestContext) {
@@ -83,7 +83,7 @@ public Future getGroupFiscalYearSummaries(Stri
return buildHolderSkeletons(fundIdFiscalYearIdBudgetsMap, collection, groupFundFiscalYearsCollection);
}))
.compose(holders -> updateHoldersWithAllocations(holders, requestContext)
- .map(holder -> {
+ .map(v -> {
log.debug("getGroupFiscalYearSummaries:: Updating group summary with allocation fields for '{}' holder(s)", holders.size());
updateGroupSummaryWithAllocation(holders);
return null;
@@ -144,6 +144,7 @@ private GroupFiscalYearSummaryCollection convertHolders(List holders) {
holders.forEach(holder -> {
+ holder.withToAllocations(new ArrayList<>(holder.getToAllocations()));
removeInitialAllocationByFunds(holder.getToAllocations());
GroupFiscalYearSummary summary = holder.getGroupFiscalYearSummary();
summary.withAllocationTo(HelperUtils.calculateTotals(holder.getToAllocations(), Transaction::getAmount))
@@ -259,8 +260,8 @@ private Future updateHoldersWithTransfers(List updateHolderWithAllocations(RequestContext requestContext, GroupFiscalYearTransactionsHolder holder) {
List groupFundIds = holder.getGroupFundIds();
String fiscalYearId = holder.getGroupFiscalYearSummary().getFiscalYearId();
- var fromAllocations = baseTransactionService.retrieveFromTransactions(groupFundIds, fiscalYearId, List.of(TransactionType.ALLOCATION), requestContext);
- var toAllocations = baseTransactionService.retrieveToTransactions(groupFundIds, fiscalYearId, List.of(TransactionType.ALLOCATION), requestContext);
+ var fromAllocations = transactionService.getTransactionsFromFunds(groupFundIds, fiscalYearId, List.of(TransactionType.ALLOCATION), requestContext);
+ var toAllocations = transactionService.getTransactionsToFunds(groupFundIds, fiscalYearId, List.of(TransactionType.ALLOCATION), requestContext);
return GenericCompositeFuture.join(List.of(fromAllocations, toAllocations))
.map(cf -> holder.withToAllocations(toAllocations.result()).withFromAllocations(fromAllocations.result()));
@@ -271,8 +272,8 @@ private Future updateHolderWithTransfers(Requ
String fiscalYearId = holder.getGroupFiscalYearSummary().getFiscalYearId();
List trTypes = List.of(TransactionType.TRANSFER, TransactionType.ROLLOVER_TRANSFER);
- var fromTransfers = baseTransactionService.retrieveFromTransactions(groupFundIds, fiscalYearId, trTypes, requestContext);
- var toTransfers = baseTransactionService.retrieveToTransactions(groupFundIds, fiscalYearId, trTypes, requestContext);
+ var fromTransfers = transactionService.getTransactionsFromFunds(groupFundIds, fiscalYearId, trTypes, requestContext);
+ var toTransfers = transactionService.getTransactionsToFunds(groupFundIds, fiscalYearId, trTypes, requestContext);
return GenericCompositeFuture.join(List.of(fromTransfers, toTransfers))
.map(cf -> holder.withToTransfers(toTransfers.result()).withFromTransfers(fromTransfers.result()));
}
diff --git a/src/main/java/org/folio/services/ledger/LedgerTotalsService.java b/src/main/java/org/folio/services/ledger/LedgerTotalsService.java
index 7eea1e00..210a930f 100644
--- a/src/main/java/org/folio/services/ledger/LedgerTotalsService.java
+++ b/src/main/java/org/folio/services/ledger/LedgerTotalsService.java
@@ -4,6 +4,7 @@
import static org.folio.rest.util.HelperUtils.removeInitialAllocationByFunds;
import java.math.BigDecimal;
+import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -24,9 +25,9 @@
import org.folio.rest.util.HelperUtils;
import org.folio.services.budget.BudgetService;
import org.folio.services.fiscalyear.FiscalYearService;
-import org.folio.services.transactions.BaseTransactionService;
import io.vertx.core.Future;
+import org.folio.services.transactions.TransactionService;
public class LedgerTotalsService {
@@ -35,12 +36,12 @@ public class LedgerTotalsService {
private final FiscalYearService fiscalYearService;
private final BudgetService budgetService;
- private final BaseTransactionService baseTransactionService;
+ private final TransactionService transactionService;
- public LedgerTotalsService(FiscalYearService fiscalYearService, BudgetService budgetService, BaseTransactionService baseTransactionService) {
+ public LedgerTotalsService(FiscalYearService fiscalYearService, BudgetService budgetService, TransactionService transactionService) {
this.fiscalYearService = fiscalYearService;
this.budgetService = budgetService;
- this.baseTransactionService = baseTransactionService;
+ this.transactionService = transactionService;
}
public Future populateLedgerTotals(Ledger ledger, String fiscalYearId, RequestContext requestContext) {
@@ -77,18 +78,19 @@ public Future populateLedgerTotals(Ledger ledger, FiscalYear fiscalYear,
}
private void updateLedgerWithAllocation(LedgerFiscalYearTransactionsHolder holder) {
- removeInitialAllocationByFunds(holder.getToAllocations());
- Ledger ledger = holder.getLedger();
- ledger.withAllocationTo(HelperUtils.calculateTotals(holder.getToAllocations(), Transaction::getAmount))
- .withAllocationFrom(HelperUtils.calculateTotals(holder.getFromAllocations(), Transaction::getAmount));
+ holder.withToAllocations(new ArrayList<>(holder.getToAllocations()));
+ removeInitialAllocationByFunds(holder.getToAllocations());
+ Ledger ledger = holder.getLedger();
+ ledger.withAllocationTo(HelperUtils.calculateTotals(holder.getToAllocations(), Transaction::getAmount))
+ .withAllocationFrom(HelperUtils.calculateTotals(holder.getFromAllocations(), Transaction::getAmount));
}
private Future updateHolderWithAllocations(LedgerFiscalYearTransactionsHolder holder,
RequestContext requestContext) {
List ledgerFundIds = holder.getLedgerFundIds();
String fiscalYearId = holder.getFiscalYearId();
- var fromAllocations = baseTransactionService.retrieveFromTransactions(ledgerFundIds, fiscalYearId, List.of(TransactionType.ALLOCATION), requestContext);
- var toAllocations = baseTransactionService.retrieveToTransactions(ledgerFundIds, fiscalYearId, List.of(TransactionType.ALLOCATION), requestContext);
+ var fromAllocations = transactionService.getTransactionsFromFunds(ledgerFundIds, fiscalYearId, List.of(TransactionType.ALLOCATION), requestContext);
+ var toAllocations = transactionService.getTransactionsToFunds(ledgerFundIds, fiscalYearId, List.of(TransactionType.ALLOCATION), requestContext);
return GenericCompositeFuture.join(List.of(fromAllocations, toAllocations))
.map(cf -> holder.withToAllocations(toAllocations.result()).withFromAllocations(fromAllocations.result()));
}
@@ -98,8 +100,8 @@ private Future updateHolderWithTransfers(Led
List ledgerFundIds = holder.getLedgerFundIds();
String fiscalYearId = holder.getFiscalYearId();
List trTypes = List.of(TransactionType.TRANSFER, TransactionType.ROLLOVER_TRANSFER);
- var fromTransfer = baseTransactionService.retrieveFromTransactions(ledgerFundIds, fiscalYearId, trTypes, requestContext);
- var toTransfer = baseTransactionService.retrieveToTransactions(ledgerFundIds, fiscalYearId, trTypes, requestContext);
+ var fromTransfer = transactionService.getTransactionsFromFunds(ledgerFundIds, fiscalYearId, trTypes, requestContext);
+ var toTransfer = transactionService.getTransactionsToFunds(ledgerFundIds, fiscalYearId, trTypes, requestContext);
return GenericCompositeFuture.join(List.of(fromTransfer, toTransfer))
.map(f -> holder.withToTransfers(toTransfer.result()).withFromTransfers(fromTransfer.result()));
diff --git a/src/main/java/org/folio/services/transactions/AllocationService.java b/src/main/java/org/folio/services/transactions/AllocationService.java
deleted file mode 100644
index ce642782..00000000
--- a/src/main/java/org/folio/services/transactions/AllocationService.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.folio.services.transactions;
-
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.rest.util.HelperUtils;
-
-import io.vertx.core.Future;
-
-public class AllocationService implements TransactionTypeManagingStrategy {
-
- private final TransactionService transactionService;
- private final TransactionRestrictService transactionRestrictService;
-
- public AllocationService(TransactionService transactionService, TransactionRestrictService transactionRestrictService) {
- this.transactionService = transactionService;
- this.transactionRestrictService = transactionRestrictService;
- }
-
- @Override
- public Future createTransaction(Transaction allocation, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(allocation, Transaction.TransactionType.ALLOCATION);
- return null;
- })
- .compose(aVoid -> transactionRestrictService.checkAllocation(allocation, requestContext))
- .compose(v -> transactionService.createTransaction(allocation, requestContext));
- }
-
- @Override
- public Future updateTransaction(Transaction transaction, RequestContext requestContext) {
- return HelperUtils.unsupportedOperationExceptionFuture();
- }
-
- @Override
- public Future deleteTransaction(Transaction encumbrance, RequestContext requestContext) {
- return HelperUtils.unsupportedOperationExceptionFuture();
- }
-
- @Override
- public Transaction.TransactionType getStrategyName() {
- return Transaction.TransactionType.ALLOCATION;
- }
-
-}
diff --git a/src/main/java/org/folio/services/transactions/BaseTransactionService.java b/src/main/java/org/folio/services/transactions/BaseTransactionService.java
deleted file mode 100644
index f3e088df..00000000
--- a/src/main/java/org/folio/services/transactions/BaseTransactionService.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package org.folio.services.transactions;
-
-import static one.util.streamex.StreamEx.ofSubLists;
-import static org.folio.rest.util.ErrorCodes.INVALID_TRANSACTION_TYPE;
-import static org.folio.rest.util.HelperUtils.collectResultsOnSuccess;
-import static org.folio.rest.util.HelperUtils.convertIdsToCqlQuery;
-import static org.folio.rest.util.ResourcePathResolver.TRANSACTIONS;
-import static org.folio.rest.util.ResourcePathResolver.resourceByIdPath;
-import static org.folio.rest.util.ResourcePathResolver.resourcesPath;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.folio.rest.core.RestClient;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.core.models.RequestEntry;
-import org.folio.rest.exception.HttpException;
-import org.folio.rest.jaxrs.model.Parameter;
-import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.rest.jaxrs.model.TransactionCollection;
-
-import io.vertx.core.Future;
-
-public class BaseTransactionService implements TransactionService {
-
- private static final Logger log = LogManager.getLogger();
- private static final int MAX_FUND_PER_QUERY = 5;
- private static final int MAX_TRANSACTIONS_PER_QUERY = 15;
- private static final String ALLOCATION_TYPE_TRANSACTIONS_QUERY = "(fiscalYearId==%s AND %s) AND %s";
- private static final String AWAITING_PAYMENT_WITH_ENCUMBRANCE = "awaitingPayment.encumbranceId==%s";
-
- final RestClient restClient;
-
- public BaseTransactionService(RestClient restClient) {
- this.restClient = restClient;
- }
-
- @Override
- public Future retrieveTransactions(String query, int offset, int limit, RequestContext requestContext) {
- var requestEntry = new RequestEntry(resourcesPath(TRANSACTIONS))
- .withOffset(offset)
- .withLimit(limit)
- .withQuery(query);
- return restClient.get(requestEntry.buildEndpoint(), TransactionCollection.class, requestContext);
- }
-
- @Override
- public Future retrieveTransactionById(String id, RequestContext requestContext) {
- return restClient.get(resourceByIdPath(TRANSACTIONS, id), Transaction.class, requestContext);
- }
-
- public Future createTransaction(Transaction transaction, RequestContext requestContext) {
- return restClient.post(resourcesPath(TRANSACTIONS), transaction, Transaction.class, requestContext);
- }
-
- public Future updateTransaction(Transaction transaction, RequestContext requestContext) {
- return restClient.put(resourceByIdPath(TRANSACTIONS, transaction.getId()), transaction, requestContext);
- }
-
- public Future deleteTransaction(Transaction transaction, RequestContext requestContext) {
- return restClient.delete(resourceByIdPath(TRANSACTIONS, transaction.getId()), requestContext);
- }
-
- public void validateTransactionType(Transaction transaction, Transaction.TransactionType transactionType) {
- if (transaction.getTransactionType() != transactionType) {
- log.warn("validateTransactionType:: Transaction '{}' type mismatch. '{}' expected", transaction.getId(), transactionType);
- Parameter parameter = new Parameter().withKey("expected").withValue(transactionType.name());
- throw new HttpException(422, INVALID_TRANSACTION_TYPE.toError().withParameters(Collections.singletonList(parameter)));
- }
- }
-
- public Future> retrieveFromTransactions(List fundIds, String fiscalYearId,
- List trTypes, RequestContext requestContext) {
- return collectResultsOnSuccess(
- ofSubLists(new ArrayList<>(fundIds), MAX_FUND_PER_QUERY)
- .map(ids -> retrieveAllocationTransactionsChunk(ids, fiscalYearId, trTypes, "fromFundId", requestContext)
- .map(transactions -> filterFundIdsByAllocationDirection(fundIds, transactions, Transaction::getToFundId)))
- .collect(Collectors.toList())).map(lists -> lists.stream().flatMap(Collection::stream).collect(Collectors.toList()));
- }
-
- public Future> retrieveToTransactions(List fundIds, String fiscalYearId,
- List trTypes, RequestContext requestContext) {
- return collectResultsOnSuccess(
- ofSubLists(new ArrayList<>(fundIds), MAX_FUND_PER_QUERY)
- .map(ids -> retrieveAllocationTransactionsChunk(ids, fiscalYearId, trTypes, "toFundId", requestContext)
- .map(transactions -> filterFundIdsByAllocationDirection(fundIds, transactions, Transaction::getFromFundId)))
- .collect(Collectors.toList())).map(lists -> lists.stream().flatMap(Collection::stream).collect(Collectors.toList()));
- }
-
- public Future> retrievePendingPaymentsByEncumbranceIds(List encumbranceIds,
- RequestContext requestContext) {
- return collectResultsOnSuccess(
- ofSubLists(new ArrayList<>(encumbranceIds), MAX_TRANSACTIONS_PER_QUERY)
- .map(ids -> retrievePendingPaymentsByEncumbranceIdsChunk(ids, requestContext))
- .toList())
- .map(lists -> lists.stream().flatMap(Collection::stream).toList());
- }
-
- public Future isConnectedToInvoice(String transactionId, RequestContext requestContext) {
- // We want to know if the order with the given encumbrance is connected to an invoice.
- // To avoid adding a dependency to mod-invoice, we check if there is a related awaitingPayment transaction
- String query = String.format(AWAITING_PAYMENT_WITH_ENCUMBRANCE, transactionId);
- return retrieveTransactions(query, 0, Integer.MAX_VALUE, requestContext)
- .map(collection -> collection.getTotalRecords() > 0);
- }
-
- private Future> retrieveAllocationTransactionsChunk(List fundIds, String fiscalYearId,
- List trTypes, String allocationDirection, RequestContext requestContext) {
- String fundQuery = convertIdsToCqlQuery(fundIds, allocationDirection, "==", " OR ");
- List trTypeValues = trTypes.stream().map(Transaction.TransactionType::value).collect(Collectors.toList());
- String trTypeQuery = convertIdsToCqlQuery(trTypeValues, "transactionType", "==", " OR ");
- String query = String.format(ALLOCATION_TYPE_TRANSACTIONS_QUERY, fiscalYearId, trTypeQuery, fundQuery);
- return retrieveTransactions(query, 0, Integer.MAX_VALUE, requestContext).map(TransactionCollection::getTransactions);
- }
-
- private List filterFundIdsByAllocationDirection(List fundIds, List transactions, Function allocationDirection) {
- return transactions.stream()
- .filter(transaction -> !fundIds.contains(allocationDirection.apply(transaction)))
- .collect(Collectors.toList());
- }
-
- private Future> retrievePendingPaymentsByEncumbranceIdsChunk(List encumbranceIds,
- RequestContext requestContext) {
- String query = convertIdsToCqlQuery(encumbranceIds, "awaitingPayment.encumbranceId", "==", " OR ");
- return retrieveTransactions(query, 0, Integer.MAX_VALUE, requestContext)
- .map(TransactionCollection::getTransactions);
- }
-}
diff --git a/src/main/java/org/folio/services/transactions/BatchTransactionService.java b/src/main/java/org/folio/services/transactions/BatchTransactionService.java
deleted file mode 100644
index 6a9fb53b..00000000
--- a/src/main/java/org/folio/services/transactions/BatchTransactionService.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package org.folio.services.transactions;
-
-import io.vertx.core.Future;
-import io.vertx.core.json.JsonObject;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.folio.rest.core.RestClient;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.exception.HttpException;
-import org.folio.rest.jaxrs.model.Batch;
-import org.folio.rest.jaxrs.model.Encumbrance;
-import org.folio.rest.jaxrs.model.Transaction;
-
-import java.util.List;
-import java.util.Optional;
-
-import static io.vertx.core.Future.succeededFuture;
-import static java.lang.Boolean.TRUE;
-import static java.util.Collections.singletonList;
-import static org.folio.rest.util.ErrorCodes.DELETE_CONNECTED_TO_INVOICE;
-import static org.folio.rest.util.ResourcePathResolver.BATCH_TRANSACTIONS_STORAGE;
-import static org.folio.rest.util.ResourcePathResolver.resourcesPath;
-
-public class BatchTransactionService {
- private static final Logger logger = LogManager.getLogger();
-
- private final RestClient restClient;
- private final BaseTransactionService baseTransactionService;
-
- public BatchTransactionService(RestClient restClient, BaseTransactionService baseTransactionService) {
- this.restClient = restClient;
- this.baseTransactionService = baseTransactionService;
- }
-
- public Future processBatch(Batch batch, RequestContext requestContext) {
- return checkDeletions(batch, requestContext)
- .compose(v -> restClient.postEmptyResponse(resourcesPath(BATCH_TRANSACTIONS_STORAGE), batch, requestContext))
- .onSuccess(v -> logger.info("Batch transaction successful"))
- .onFailure(t -> logger.error("Batch transaction failed, batch={}", JsonObject.mapFrom(batch).encodePrettily(), t));
- }
-
- public Future releaseEncumbrance(String id, RequestContext requestContext) {
- return baseTransactionService.retrieveTransactionById(id, requestContext)
- .compose(transaction -> releaseEncumbrance(transaction, requestContext))
- .onSuccess(v -> logger.info("Release encumbrance successful, id={}", id))
- .onFailure(t -> logger.error("Release encumbrance failed, id={}", id, t));
- }
-
- public Future unreleaseEncumbrance(String id, RequestContext requestContext) {
- return baseTransactionService.retrieveTransactionById(id, requestContext)
- .compose(transaction -> unreleaseEncumbrance(transaction, requestContext))
- .onSuccess(v -> logger.info("Unrelease encumbrance successful, id={}", id))
- .onFailure(t -> logger.error("Unrelease encumbrance failed, id={}", id, t));
- }
-
- private Future releaseEncumbrance(Transaction transaction, RequestContext requestContext) {
- baseTransactionService.validateTransactionType(transaction, Transaction.TransactionType.ENCUMBRANCE);
- if (transaction.getEncumbrance().getStatus() == Encumbrance.Status.RELEASED) {
- return succeededFuture(null);
- }
- transaction.getEncumbrance().setStatus(Encumbrance.Status.RELEASED);
- return updateTransaction(transaction, requestContext);
- }
-
- private Future unreleaseEncumbrance(Transaction transaction, RequestContext requestContext) {
- baseTransactionService.validateTransactionType(transaction, Transaction.TransactionType.ENCUMBRANCE);
- if (transaction.getEncumbrance().getStatus() != Encumbrance.Status.RELEASED) {
- return succeededFuture(null);
- }
- transaction.getEncumbrance().setStatus(Encumbrance.Status.UNRELEASED);
- return updateTransaction(transaction, requestContext);
- }
-
- private Future updateTransaction(Transaction transaction, RequestContext requestContext) {
- // This will need to change to use patch when it is available - see MODFIN-351
- Batch batch = new Batch()
- .withTransactionsToUpdate(singletonList(transaction));
- return processBatch(batch, requestContext);
- }
-
- /**
- * Check transaction deletions are allowed.
- * Usually it is not allowed to delete an encumbrance connected to an approved invoice.
- * To avoid adding a dependency to mod-invoice, we check if there is a related awaitingPayment transaction.
- * It is OK to delete an encumbrance connected to a *cancelled* invoice *if* the batch includes a change to the
- * matching pending payment to remove the link to the encumbrance.
- */
- private Future checkDeletions(Batch batch, RequestContext requestContext) {
- List ids = batch.getIdsOfTransactionsToDelete();
- if (ids.isEmpty()) {
- return succeededFuture();
- }
- return baseTransactionService.retrievePendingPaymentsByEncumbranceIds(ids, requestContext)
- .map(pendingPayments -> {
- if (pendingPayments.isEmpty()) {
- return null;
- }
- ids.forEach(id -> {
- Optional existingPP = pendingPayments.stream()
- .filter(pp -> id.equals(pp.getAwaitingPayment().getEncumbranceId())).findFirst();
- if (existingPP.isEmpty()) {
- return;
- }
- if (TRUE.equals(existingPP.get().getInvoiceCancelled())) {
- Optional matchingPPInBatch = batch.getTransactionsToUpdate().stream()
- .filter(pp -> existingPP.get().getId().equals(pp.getId())).findFirst();
- if (matchingPPInBatch.isPresent() && matchingPPInBatch.get().getAwaitingPayment().getEncumbranceId() == null) {
- return;
- }
- }
- logger.warn("validateDeletion:: Tried to delete transactions but one is connected to an invoice, id={}", id);
- throw new HttpException(422, DELETE_CONNECTED_TO_INVOICE.toError());
- });
- return null;
- });
- }
-}
diff --git a/src/main/java/org/folio/services/transactions/CommonTransactionService.java b/src/main/java/org/folio/services/transactions/CommonTransactionService.java
deleted file mode 100644
index aa651ee2..00000000
--- a/src/main/java/org/folio/services/transactions/CommonTransactionService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.folio.services.transactions;
-
-import static org.folio.rest.RestConstants.MAX_IDS_FOR_GET_RQ;
-import static org.folio.rest.util.HelperUtils.collectResultsOnSuccess;
-import static org.folio.rest.util.HelperUtils.convertIdsToCqlQuery;
-import static org.folio.rest.util.ResourcePathResolver.FISCAL_YEARS_STORAGE;
-import static org.folio.rest.util.ResourcePathResolver.resourceByIdPath;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.folio.rest.core.RestClient;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.jaxrs.model.Budget;
-import org.folio.rest.jaxrs.model.BudgetExpenseClass;
-import org.folio.rest.jaxrs.model.FiscalYear;
-import org.folio.rest.jaxrs.model.SharedBudget;
-import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.rest.jaxrs.model.TransactionCollection;
-
-import io.vertx.core.Future;
-import one.util.streamex.StreamEx;
-
-public class CommonTransactionService extends BaseTransactionService {
-
- public CommonTransactionService(RestClient restClient) {
- super(restClient);
- }
-
- public Future> retrieveTransactions(Budget budget, RequestContext requestContext) {
- String query = String.format("(fromFundId==%s OR toFundId==%s) AND fiscalYearId==%s", budget.getFundId(), budget.getFundId(), budget.getFiscalYearId());
- return retrieveTransactions(query, 0, Integer.MAX_VALUE, requestContext)
- .map(TransactionCollection::getTransactions);
- }
-
- public Future> retrieveTransactions(List budgetExpenseClasses, SharedBudget budget, RequestContext requestContext) {
- List ids = budgetExpenseClasses.stream()
- .map(BudgetExpenseClass::getExpenseClassId)
- .collect(Collectors.toList());
- String query = String.format("(fromFundId==%s OR toFundId==%s) AND fiscalYearId==%s AND %s", budget.getFundId(), budget.getFundId(), budget.getFiscalYearId(), convertIdsToCqlQuery(ids, "expenseClassId", true));
- return retrieveTransactions(query, 0, Integer.MAX_VALUE, requestContext)
- .map(TransactionCollection::getTransactions);
- }
-
- public Future> retrieveTransactionsByFundIds(List fundIds, String fiscalYearId, RequestContext requestContext) {
- List>> futures = StreamEx
- .ofSubLists(fundIds, MAX_IDS_FOR_GET_RQ)
- .map(ids -> retrieveTransactionsChunk(buildGetTransactionsQuery(fiscalYearId, ids), requestContext))
- .collect(Collectors.toList());
-
- return collectResultsOnSuccess(futures)
- .map(listList -> listList.stream()
- .flatMap(Collection::stream)
- .collect(Collectors.toList())
- );
- }
-
- private Future> retrieveTransactionsChunk(String query, RequestContext requestContext) {
- return retrieveTransactions(query, 0, Integer.MAX_VALUE, requestContext)
- .map(TransactionCollection::getTransactions);
- }
-
- private String buildGetTransactionsQuery(String fiscalYearId, List fundIds) {
- return String.format("fiscalYearId==%s AND (%s OR %s)",
- fiscalYearId,
- convertIdsToCqlQuery(fundIds, "fromFundId", true),
- convertIdsToCqlQuery(fundIds, "toFundId", true));
- }
-
- public Future createAllocationTransaction(Budget budget, RequestContext requestContext) {
- Transaction transaction = new Transaction().withAmount(budget.getAllocated())
- .withFiscalYearId(budget.getFiscalYearId()).withToFundId(budget.getFundId())
- .withTransactionType(Transaction.TransactionType.ALLOCATION).withSource(Transaction.Source.USER);
- return restClient.get(resourceByIdPath(FISCAL_YEARS_STORAGE, budget.getFiscalYearId()), FiscalYear.class, requestContext)
- .compose(fiscalYear -> createTransaction(transaction.withCurrency(fiscalYear.getCurrency()), requestContext));
- }
-
-}
diff --git a/src/main/java/org/folio/services/transactions/CreditService.java b/src/main/java/org/folio/services/transactions/CreditService.java
deleted file mode 100644
index 7822c3e8..00000000
--- a/src/main/java/org/folio/services/transactions/CreditService.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package org.folio.services.transactions;
-
-import static org.folio.rest.util.ErrorCodes.UPDATE_CREDIT_TO_CANCEL_INVOICE;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.exception.HttpException;
-import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.rest.util.HelperUtils;
-
-import io.vertx.core.Future;
-
-public class CreditService implements TransactionTypeManagingStrategy {
-
- private static final Logger log = LogManager.getLogger();
- private final TransactionService transactionService;
-
- public CreditService(TransactionService transactionService) {
- this.transactionService = transactionService;
- }
-
- @Override
- public Future createTransaction(Transaction credit, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v-> {
- transactionService.validateTransactionType(credit, Transaction.TransactionType.CREDIT);
- HelperUtils.validateAmount(credit.getAmount(), "amount");
- return null;
- })
- .compose(aVoid -> transactionService.createTransaction(credit, requestContext));
- }
-
- @Override
- public Future updateTransaction(Transaction credit, RequestContext requestContext) {
- log.debug("updateTransaction:: Updating transaction '{}'", credit.getId());
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(credit, Transaction.TransactionType.CREDIT);
- if (!Boolean.TRUE.equals(credit.getInvoiceCancelled())) {
- log.warn("updateTransaction:: Credit '{}' invoice is not cancelled", credit.getId());
- throw new HttpException(422, UPDATE_CREDIT_TO_CANCEL_INVOICE.toError());
- }
- return null;
- })
- .compose(v -> transactionService.retrieveTransactionById(credit.getId(), requestContext))
- .map(existingTransaction -> {
- if (Boolean.TRUE.equals(existingTransaction.getInvoiceCancelled())) {
- log.warn("updateTransaction:: Existing transaction '{}' already has invoiceCancelled flag set to true", existingTransaction.getId());
- throw new HttpException(422, UPDATE_CREDIT_TO_CANCEL_INVOICE.toError());
- }
- // compare new transaction with existing one: ignore invoiceCancelled and metadata changes
- existingTransaction.setInvoiceCancelled(true);
- existingTransaction.setMetadata(credit.getMetadata());
- if (!existingTransaction.equals(credit)) {
- log.warn("updateTransaction:: Existing transaction '{}' is equal to credit '{}'", existingTransaction.getId(), credit.getId());
- throw new HttpException(422, UPDATE_CREDIT_TO_CANCEL_INVOICE.toError());
- }
- return null;
- })
- .compose(v -> transactionService.updateTransaction(credit, requestContext));
- }
-
- @Override
- public Future deleteTransaction(Transaction encumbrance, RequestContext requestContext) {
- return HelperUtils.unsupportedOperationExceptionFuture();
- }
-
- @Override
- public Transaction.TransactionType getStrategyName() {
- return Transaction.TransactionType.CREDIT;
- }
-
-}
diff --git a/src/main/java/org/folio/services/transactions/EncumbranceService.java b/src/main/java/org/folio/services/transactions/EncumbranceService.java
deleted file mode 100644
index e7408274..00000000
--- a/src/main/java/org/folio/services/transactions/EncumbranceService.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.folio.services.transactions;
-
-import static org.folio.rest.util.ErrorCodes.DELETE_CONNECTED_TO_INVOICE;
-import static org.folio.rest.util.ErrorCodes.TRANSACTION_NOT_RELEASED;
-
-import java.util.Collections;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.exception.HttpException;
-import org.folio.rest.jaxrs.model.Encumbrance;
-import org.folio.rest.jaxrs.model.Parameter;
-import org.folio.rest.jaxrs.model.Transaction;
-
-import io.vertx.core.Future;
-
-public class EncumbranceService implements TransactionTypeManagingStrategy {
-
- private static final Logger log = LogManager.getLogger();
- private final TransactionService transactionService;
-
- public EncumbranceService(TransactionService transactionService) {
- this.transactionService = transactionService;
- }
-
- @Override
- public Future createTransaction(Transaction encumbrance, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(encumbrance, Transaction.TransactionType.ENCUMBRANCE);
- return null;
- })
- .compose(aVoid -> transactionService.createTransaction(encumbrance, requestContext));
- }
-
- @Override
- public Future updateTransaction(Transaction encumbrance, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(encumbrance, Transaction.TransactionType.ENCUMBRANCE);
- return null;
- })
- .compose(aVoid -> transactionService.updateTransaction(encumbrance, requestContext));
- }
-
- @Override
- public Future deleteTransaction(Transaction encumbrance, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(encumbrance, Transaction.TransactionType.ENCUMBRANCE);
- return null;
- })
- .compose(aVoid -> validateDeletion(encumbrance, requestContext))
- .compose(aVoid -> transactionService.deleteTransaction(encumbrance, requestContext));
- }
-
- @Override
- public Transaction.TransactionType getStrategyName() {
- return Transaction.TransactionType.ENCUMBRANCE;
- }
-
- private Future validateDeletion(Transaction encumbrance, RequestContext requestContext) {
- log.debug("validateDeletion:: Validation to delete encumbrance '{}'", encumbrance.getId());
- return Future.succeededFuture()
- .map(v -> {
- checkEncumbranceStatusNotReleased(encumbrance);
- return null;
- })
- .compose(v -> transactionService.isConnectedToInvoice(encumbrance.getId(), requestContext))
- .map(connected -> {
- if (connected) {
- log.warn("validateDeletion:: Tried to delete transaction {} but it is connected to an invoice.", encumbrance.getId());
- Parameter parameter = new Parameter().withKey("id").withValue(encumbrance.getId());
- throw new HttpException(422, DELETE_CONNECTED_TO_INVOICE.toError().withParameters(Collections.singletonList(parameter)));
- }
- return null;
- });
- }
-
- private void checkEncumbranceStatusNotReleased(Transaction encumbrance) {
- if (encumbrance.getEncumbrance().getStatus() != Encumbrance.Status.RELEASED) {
- log.warn("checkEncumbranceStatusNotReleased:: Transaction {} should be released before deletion", encumbrance.getId());
- Parameter parameter = new Parameter().withKey("id").withValue(encumbrance.getId());
- throw new HttpException(400, TRANSACTION_NOT_RELEASED.toError().withParameters(Collections.singletonList(parameter)));
- }
- }
-}
diff --git a/src/main/java/org/folio/services/transactions/PaymentService.java b/src/main/java/org/folio/services/transactions/PaymentService.java
deleted file mode 100644
index 09fd5919..00000000
--- a/src/main/java/org/folio/services/transactions/PaymentService.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.folio.services.transactions;
-
-import static org.folio.rest.util.ErrorCodes.UPDATE_PAYMENT_TO_CANCEL_INVOICE;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.exception.HttpException;
-import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.rest.util.HelperUtils;
-
-import io.vertx.core.Future;
-
-public class PaymentService implements TransactionTypeManagingStrategy {
- private static final Logger log = LogManager.getLogger();
- private final TransactionService transactionService;
-
- public PaymentService(TransactionService transactionService) {
- this.transactionService = transactionService;
- }
-
- @Override
- public Future createTransaction(Transaction payment, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(payment, Transaction.TransactionType.PAYMENT);
- HelperUtils.validateAmount(payment.getAmount(), "amount");
- return null;
- })
- .compose(v -> transactionService.createTransaction(payment, requestContext));
- }
-
- @Override
- public Future updateTransaction(Transaction payment, RequestContext requestContext) {
- log.debug("updateTransaction:: Updating transaction for payment '{}'", payment.getId());
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(payment, Transaction.TransactionType.PAYMENT);
- if (Boolean.FALSE.equals(payment.getInvoiceCancelled())) {
- log.warn("updateTransaction:: payment '{}' invoice is not cancelled", payment.getId());
- throw new HttpException(422, UPDATE_PAYMENT_TO_CANCEL_INVOICE.toError());
- }
- return null;
- })
- .compose(v -> transactionService.retrieveTransactionById(payment.getId(), requestContext))
- .map(existingTransaction -> {
- if (Boolean.TRUE.equals(existingTransaction.getInvoiceCancelled())) {
- log.warn("updateTransaction:: Existing transaction '{}' already has invoiceCancelled flag set to true", existingTransaction.getId());
- throw new HttpException(422, UPDATE_PAYMENT_TO_CANCEL_INVOICE.toError());
- }
- // compare new transaction with existing one: ignore invoiceCancelled and metadata changes
- existingTransaction.setInvoiceCancelled(true);
- existingTransaction.setMetadata(payment.getMetadata());
- if (!existingTransaction.equals(payment)) {
- log.warn("updateTransaction:: Existing transaction '{}' is not equal to payment '{}'", existingTransaction.getId(), payment.getId());
- throw new HttpException(422, UPDATE_PAYMENT_TO_CANCEL_INVOICE.toError());
- }
- return null;
- })
- .compose(v -> transactionService.updateTransaction(payment, requestContext));
- }
-
- @Override
- public Future deleteTransaction(Transaction encumbrance, RequestContext requestContext) {
- return HelperUtils.unsupportedOperationExceptionFuture();
- }
-
- @Override
- public Transaction.TransactionType getStrategyName() {
- return Transaction.TransactionType.PAYMENT;
- }
-
-}
diff --git a/src/main/java/org/folio/services/transactions/PendingPaymentService.java b/src/main/java/org/folio/services/transactions/PendingPaymentService.java
deleted file mode 100644
index 788e0c1e..00000000
--- a/src/main/java/org/folio/services/transactions/PendingPaymentService.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.folio.services.transactions;
-
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.rest.util.HelperUtils;
-
-import io.vertx.core.Future;
-
-public class PendingPaymentService implements TransactionTypeManagingStrategy {
-
- private final TransactionService transactionService;
-
- public PendingPaymentService(TransactionService transactionService) {
- this.transactionService = transactionService;
- }
-
- @Override
- public Future createTransaction(Transaction pendingPayment, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(pendingPayment, Transaction.TransactionType.PENDING_PAYMENT);
- return null;
- })
- .compose(aVoid -> transactionService.createTransaction(pendingPayment, requestContext));
- }
-
- @Override
- public Future updateTransaction(Transaction pendingPayment, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(pendingPayment, Transaction.TransactionType.PENDING_PAYMENT);
- return null;
- })
- .compose(aVoid -> transactionService.updateTransaction(pendingPayment, requestContext));
- }
-
- @Override
- public Future deleteTransaction(Transaction encumbrance, RequestContext requestContext) {
- return HelperUtils.unsupportedOperationExceptionFuture();
- }
-
- @Override
- public Transaction.TransactionType getStrategyName() {
- return Transaction.TransactionType.PENDING_PAYMENT;
- }
-}
diff --git a/src/main/java/org/folio/services/transactions/TransactionApiService.java b/src/main/java/org/folio/services/transactions/TransactionApiService.java
new file mode 100644
index 00000000..b331111a
--- /dev/null
+++ b/src/main/java/org/folio/services/transactions/TransactionApiService.java
@@ -0,0 +1,246 @@
+package org.folio.services.transactions;
+
+import static io.vertx.core.Future.failedFuture;
+import static io.vertx.core.Future.succeededFuture;
+import static java.lang.Boolean.TRUE;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toMap;
+import static one.util.streamex.StreamEx.ofSubLists;
+import static org.folio.rest.jaxrs.model.Transaction.TransactionType.ALLOCATION;
+import static org.folio.rest.jaxrs.model.Transaction.TransactionType.TRANSFER;
+import static org.folio.rest.util.ErrorCodes.ALLOCATION_IDS_MISMATCH;
+import static org.folio.rest.util.ErrorCodes.DELETE_CONNECTED_TO_INVOICE;
+import static org.folio.rest.util.ErrorCodes.INVALID_TRANSACTION_TYPE;
+import static org.folio.rest.util.ErrorCodes.MISSING_FUND_ID;
+import static org.folio.rest.util.HelperUtils.collectResultsOnSuccess;
+import static org.folio.rest.util.HelperUtils.convertIdsToCqlQuery;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Function;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.folio.rest.core.models.RequestContext;
+import org.folio.rest.exception.HttpException;
+import org.folio.rest.jaxrs.model.Batch;
+import org.folio.rest.jaxrs.model.Encumbrance;
+import org.folio.rest.jaxrs.model.Fund;
+import org.folio.rest.jaxrs.model.Parameter;
+import org.folio.rest.jaxrs.model.Transaction;
+import org.folio.rest.jaxrs.model.Transaction.TransactionType;
+import org.folio.rest.jaxrs.model.TransactionCollection;
+
+import io.vertx.core.Future;
+import org.folio.services.fund.FundService;
+
+/**
+ * This class is only used by the API handlers (TransactionsApi and EncumbranceApi).
+ * It does not directly use storage (using TransactionService for storage calls).
+ */
+public class TransactionApiService {
+ private static final Logger log = LogManager.getLogger();
+ private static final int MAX_TRANSACTIONS_PER_QUERY = 15;
+
+ private final TransactionService transactionService;
+ private final FundService fundService;
+
+ public TransactionApiService(TransactionService transactionService, FundService fundService) {
+ this.transactionService = transactionService;
+ this.fundService = fundService;
+ }
+
+ public Future getTransactionCollectionByQuery(String query, int offset, int limit,
+ RequestContext requestContext) {
+ return transactionService.getTransactionCollectionByQuery(query, offset, limit, requestContext);
+ }
+
+ public Future getTransactionById(String id, RequestContext requestContext) {
+ return transactionService.getTransactionById(id, requestContext);
+ }
+
+ public Future processBatch(Batch batch, RequestContext requestContext) {
+ return checkDeletions(batch, requestContext)
+ .compose(v -> checkFundAllocations(batch, requestContext))
+ .compose(v -> transactionService.processBatch(batch, requestContext));
+ }
+
+ public Future createAllocation(Transaction allocation, RequestContext requestContext) {
+ try {
+ validateTransactionType(allocation, ALLOCATION);
+ } catch (Exception ex) {
+ return failedFuture(ex);
+ }
+ if (allocation.getId() == null) {
+ allocation.setId(UUID.randomUUID().toString());
+ }
+ Batch batch = new Batch().withTransactionsToCreate(singletonList(allocation));
+ return processBatch(batch, requestContext)
+ .map(v -> allocation)
+ .onSuccess(v -> log.info("Success creating allocation, id={}", allocation.getId()))
+ .onFailure(t -> log.error("Error creating allocation, id={}", allocation.getId(), t));
+ }
+
+ public Future createTransfer(Transaction transfer, RequestContext requestContext) {
+ try {
+ validateTransactionType(transfer, TRANSFER);
+ } catch (Exception ex) {
+ return failedFuture(ex);
+ }
+ if (transfer.getId() == null) {
+ transfer.setId(UUID.randomUUID().toString());
+ }
+ Batch batch = new Batch().withTransactionsToCreate(singletonList(transfer));
+ return processBatch(batch, requestContext)
+ .map(v -> transfer)
+ .onSuccess(v -> log.info("Success creating transfer, id={}", transfer.getId()))
+ .onFailure(t -> log.error("Error creating transfer, id={}", transfer.getId(), t));
+ }
+
+ public Future releaseEncumbrance(String id, RequestContext requestContext) {
+ return getTransactionById(id, requestContext)
+ .compose(transaction -> releaseEncumbrance(transaction, requestContext))
+ .onSuccess(v -> log.info("Success releasing encumbrance, id={}", id))
+ .onFailure(t -> log.error("Error releasing encumbrance, id={}", id, t));
+ }
+
+ public Future unreleaseEncumbrance(String id, RequestContext requestContext) {
+ return getTransactionById(id, requestContext)
+ .compose(transaction -> unreleaseEncumbrance(transaction, requestContext))
+ .onSuccess(v -> log.info("Success unreleasing encumbrance, id={}", id))
+ .onFailure(t -> log.error("Error unreleasing encumbrance, id={}", id, t));
+ }
+
+
+ private void validateTransactionType(Transaction transaction, TransactionType transactionType) {
+ if (transaction.getTransactionType() != transactionType) {
+ log.warn("validateTransactionType:: Transaction '{}' type mismatch. '{}' expected", transaction.getId(), transactionType);
+ Parameter parameter = new Parameter().withKey("expected").withValue(transactionType.name());
+ throw new HttpException(422, INVALID_TRANSACTION_TYPE.toError().withParameters(Collections.singletonList(parameter)));
+ }
+ }
+
+ /**
+ * Check transaction deletions are allowed.
+ * Usually it is not allowed to delete an encumbrance connected to an approved invoice.
+ * To avoid adding a dependency to mod-invoice, we check if there is a related awaitingPayment transaction.
+ * It is OK to delete an encumbrance connected to a *cancelled* invoice *if* the batch includes a change to the
+ * matching pending payment to remove the link to the encumbrance.
+ */
+ private Future checkDeletions(Batch batch, RequestContext requestContext) {
+ List ids = batch.getIdsOfTransactionsToDelete();
+ if (ids.isEmpty()) {
+ return succeededFuture();
+ }
+ return getPendingPaymentsByEncumbranceIds(ids, requestContext)
+ .map(pendingPayments -> {
+ if (pendingPayments.isEmpty()) {
+ return null;
+ }
+ ids.forEach(id -> {
+ Optional existingPP = pendingPayments.stream()
+ .filter(pp -> id.equals(pp.getAwaitingPayment().getEncumbranceId())).findFirst();
+ if (existingPP.isEmpty()) {
+ return;
+ }
+ if (TRUE.equals(existingPP.get().getInvoiceCancelled())) {
+ Optional matchingPPInBatch = batch.getTransactionsToUpdate().stream()
+ .filter(pp -> existingPP.get().getId().equals(pp.getId())).findFirst();
+ if (matchingPPInBatch.isPresent() && matchingPPInBatch.get().getAwaitingPayment().getEncumbranceId() == null) {
+ return;
+ }
+ }
+ log.warn("validateDeletion:: Tried to delete transactions but one is connected to an invoice, id={}", id);
+ throw new HttpException(422, DELETE_CONNECTED_TO_INVOICE.toError());
+ });
+ return null;
+ });
+ }
+
+ private Future> getPendingPaymentsByEncumbranceIds(List encumbranceIds,
+ RequestContext requestContext) {
+ return collectResultsOnSuccess(ofSubLists(new ArrayList<>(encumbranceIds), MAX_TRANSACTIONS_PER_QUERY)
+ .map(ids -> getPendingPaymentsByEncumbranceIdsChunk(ids, requestContext))
+ .toList())
+ .map(lists -> lists.stream().flatMap(Collection::stream).toList());
+ }
+
+ private Future> getPendingPaymentsByEncumbranceIdsChunk(List encumbranceIds,
+ RequestContext requestContext) {
+ String query = convertIdsToCqlQuery(encumbranceIds, "awaitingPayment.encumbranceId", "==", " OR ");
+ return transactionService.getAllTransactionsByQuery(query, requestContext);
+ }
+
+ private Future checkFundAllocations(Batch batch, RequestContext requestContext) {
+ List transactionsToCheck = batch.getTransactionsToCreate().stream()
+ .filter(t -> (t.getTransactionType() == ALLOCATION && Objects.isNull(t.getFromFundId()) == Objects.isNull(t.getToFundId())) ||
+ t.getTransactionType() == TRANSFER)
+ .toList();
+ if (transactionsToCheck.isEmpty()) {
+ return succeededFuture();
+ }
+ for (Transaction t : transactionsToCheck) {
+ if (!(Objects.nonNull(t.getFromFundId()) && Objects.nonNull(t.getToFundId()))) {
+ log.warn("checkFundsAllocatedIds:: fundId is missing for transaction: {}", t.getId());
+ return failedFuture(new HttpException(422, MISSING_FUND_ID));
+ }
+ }
+ List fundIds = transactionsToCheck.stream()
+ .map(t -> List.of(t.getFromFundId(), t.getToFundId()))
+ .flatMap(Collection::stream)
+ .filter(Objects::nonNull)
+ .distinct()
+ .toList();
+ return fundService.getFundsByIds(fundIds, requestContext)
+ .map(funds -> {
+ Map fundMap = funds.stream().collect(toMap(Fund::getId, Function.identity()));
+ for (Transaction t : transactionsToCheck) {
+ if (!isAllocationAllowed(fundMap.get(t.getFromFundId()), fundMap.get(t.getToFundId()), t)) {
+ log.warn("checkFundsAllocatedIds:: Allocation ids mismatch for transaction: {}", t.getId());
+ throw new HttpException(422, ALLOCATION_IDS_MISMATCH);
+ }
+ }
+ return null;
+ })
+ .onSuccess(v -> log.debug("checkFundAllocations:: Funds checked successfully for transactions"))
+ .mapEmpty();
+ }
+
+ private boolean isAllocationAllowed(Fund fromFund, Fund toFund, Transaction transaction) {
+ return (fromFund.getAllocatedToIds().isEmpty() || fromFund.getAllocatedToIds().contains(transaction.getToFundId())) &&
+ (toFund.getAllocatedFromIds().isEmpty() || toFund.getAllocatedFromIds().contains(transaction.getFromFundId()));
+ }
+
+ private Future releaseEncumbrance(Transaction transaction, RequestContext requestContext) {
+ try {
+ validateTransactionType(transaction, TransactionType.ENCUMBRANCE);
+ } catch (Exception ex) {
+ return failedFuture(ex);
+ }
+ if (transaction.getEncumbrance().getStatus() == Encumbrance.Status.RELEASED) {
+ return succeededFuture(null);
+ }
+ transaction.getEncumbrance().setStatus(Encumbrance.Status.RELEASED);
+ return transactionService.updateTransaction(transaction, requestContext);
+ }
+
+ private Future unreleaseEncumbrance(Transaction transaction, RequestContext requestContext) {
+ try {
+ validateTransactionType(transaction, TransactionType.ENCUMBRANCE);
+ } catch (Exception ex) {
+ return failedFuture(ex);
+ }
+ if (transaction.getEncumbrance().getStatus() != Encumbrance.Status.RELEASED) {
+ return succeededFuture(null);
+ }
+ transaction.getEncumbrance().setStatus(Encumbrance.Status.UNRELEASED);
+ return transactionService.updateTransaction(transaction, requestContext);
+ }
+
+}
diff --git a/src/main/java/org/folio/services/transactions/TransactionManagingService.java b/src/main/java/org/folio/services/transactions/TransactionManagingService.java
deleted file mode 100644
index d724d857..00000000
--- a/src/main/java/org/folio/services/transactions/TransactionManagingService.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.folio.services.transactions;
-
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.jaxrs.model.Transaction;
-
-import io.vertx.core.Future;
-
-public interface TransactionManagingService {
- Future createTransaction(Transaction transaction, RequestContext requestContext);
- Future updateTransaction(Transaction transaction, RequestContext requestContext);
- Future deleteTransaction(Transaction transaction, RequestContext requestContext);
-}
diff --git a/src/main/java/org/folio/services/transactions/TransactionRestrictService.java b/src/main/java/org/folio/services/transactions/TransactionRestrictService.java
deleted file mode 100644
index da736cdb..00000000
--- a/src/main/java/org/folio/services/transactions/TransactionRestrictService.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.folio.services.transactions;
-
-import static io.vertx.core.Future.failedFuture;
-import static io.vertx.core.Future.succeededFuture;
-import static org.folio.rest.util.ErrorCodes.ALLOCATION_IDS_MISMATCH;
-import static org.folio.rest.util.ErrorCodes.MISSING_FUND_ID;
-
-import java.util.Objects;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.exception.HttpException;
-import org.folio.rest.jaxrs.model.Fund;
-import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.services.fund.FundService;
-
-import io.vertx.core.CompositeFuture;
-import io.vertx.core.Future;
-
-public class TransactionRestrictService {
-
- private static final Logger log = LogManager.getLogger();
- private final FundService fundService;
-
- public TransactionRestrictService(FundService fundService) {
- this.fundService = fundService;
- }
-
- public Future checkAllocation(Transaction transaction, RequestContext requestContext) {
- if (Objects.isNull(transaction.getFromFundId()) ^ Objects.isNull(transaction.getToFundId())) {
- return succeededFuture();
- } else {
- return checkFundsAllocatedIds(transaction, requestContext);
- }
- }
-
- public Future checkTransfer(Transaction transaction, RequestContext requestContext) {
- return checkFundsAllocatedIds(transaction, requestContext);
- }
-
- private Future checkFundsAllocatedIds(Transaction transaction, RequestContext requestContext) {
- log.debug("checkFundsAllocatedIds:: Checking funds allocated ids for transaction '{}'", transaction.getId());
- if (Objects.nonNull(transaction.getFromFundId()) && Objects.nonNull(transaction.getToFundId())) {
- var fromFund = fundService.getFundById(transaction.getFromFundId(), requestContext);
- var toFund = fundService.getFundById(transaction.getToFundId(), requestContext);
- return CompositeFuture.join(toFund, fromFund)
- .map(cf -> isAllocationAllowed(fromFund.result(), toFund.result(), transaction))
- .compose(isAllocationAllowed -> {
- if (Boolean.TRUE.equals(isAllocationAllowed)) {
- log.info("checkFundsAllocatedIds: Allocation is allowed for transaction: {}", transaction.getId());
- return succeededFuture();
- } else {
- log.warn("checkFundsAllocatedIds:: Allocation ids is mismatch in transaction: {}", transaction.getId());
- return failedFuture(new HttpException(422, ALLOCATION_IDS_MISMATCH));
- }
- });
- } else {
- log.warn("checkFundsAllocatedIds:: fundId is missing for transaction: {}", transaction.getId());
- return failedFuture(new HttpException(422, MISSING_FUND_ID));
- }
-}
-
- private boolean isAllocationAllowed(Fund fromFund, Fund toFund, Transaction transaction) {
- return (fromFund.getAllocatedToIds().isEmpty() || fromFund.getAllocatedToIds().contains(transaction.getToFundId())) &&
- (toFund.getAllocatedFromIds().isEmpty() || toFund.getAllocatedFromIds().contains(transaction.getFromFundId()));
- }
-
-}
diff --git a/src/main/java/org/folio/services/transactions/TransactionService.java b/src/main/java/org/folio/services/transactions/TransactionService.java
index 2aeebfae..53dd0e33 100644
--- a/src/main/java/org/folio/services/transactions/TransactionService.java
+++ b/src/main/java/org/folio/services/transactions/TransactionService.java
@@ -1,15 +1,172 @@
package org.folio.services.transactions;
+import static java.util.Collections.singletonList;
+import static one.util.streamex.StreamEx.ofSubLists;
+import static org.folio.rest.RestConstants.MAX_IDS_FOR_GET_RQ;
+import static org.folio.rest.util.HelperUtils.collectResultsOnSuccess;
+import static org.folio.rest.util.HelperUtils.convertIdsToCqlQuery;
+import static org.folio.rest.util.ResourcePathResolver.BATCH_TRANSACTIONS_STORAGE;
+import static org.folio.rest.util.ResourcePathResolver.TRANSACTIONS;
+import static org.folio.rest.util.ResourcePathResolver.resourceByIdPath;
+import static org.folio.rest.util.ResourcePathResolver.resourcesPath;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Function;
+
+import io.vertx.core.json.JsonObject;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.folio.rest.core.RestClient;
import org.folio.rest.core.models.RequestContext;
+import org.folio.rest.core.models.RequestEntry;
+import org.folio.rest.jaxrs.model.Batch;
+import org.folio.rest.jaxrs.model.Budget;
+import org.folio.rest.jaxrs.model.BudgetExpenseClass;
+import org.folio.rest.jaxrs.model.SharedBudget;
import org.folio.rest.jaxrs.model.Transaction;
+import org.folio.rest.jaxrs.model.Transaction.TransactionType;
import org.folio.rest.jaxrs.model.TransactionCollection;
import io.vertx.core.Future;
+import one.util.streamex.StreamEx;
+import org.folio.services.fiscalyear.FiscalYearService;
+
+public class TransactionService {
+ private static final Logger log = LogManager.getLogger();
+ private static final int MAX_FUND_PER_QUERY = 5;
+
+ private final RestClient restClient;
+ private final FiscalYearService fiscalYearService;
+
+ public TransactionService(RestClient restClient, FiscalYearService fiscalYearService) {
+ this.restClient = restClient;
+ this.fiscalYearService = fiscalYearService;
+ }
+
+ public Future getTransactionCollectionByQuery(String query, int offset, int limit,
+ RequestContext requestContext) {
+ var requestEntry = new RequestEntry(resourcesPath(TRANSACTIONS))
+ .withOffset(offset)
+ .withLimit(limit)
+ .withQuery(query);
+ return restClient.get(requestEntry.buildEndpoint(), TransactionCollection.class, requestContext);
+ }
+
+ public Future> getAllTransactionsByQuery(String query, RequestContext requestContext) {
+ return getTransactionCollectionByQuery(query, 0, Integer.MAX_VALUE, requestContext)
+ .map(TransactionCollection::getTransactions);
+ }
+
+ public Future getTransactionById(String id, RequestContext requestContext) {
+ return restClient.get(resourceByIdPath(TRANSACTIONS, id), Transaction.class, requestContext);
+ }
+
+ public Future processBatch(Batch batch, RequestContext requestContext) {
+ return restClient.postEmptyResponse(resourcesPath(BATCH_TRANSACTIONS_STORAGE), batch, requestContext)
+ .onSuccess(v -> log.info("Batch transaction successful"))
+ .onFailure(t -> log.error("Batch transaction failed, batch={}", JsonObject.mapFrom(batch).encodePrettily(), t));
+ }
+
+ public Future updateTransaction(Transaction transaction, RequestContext requestContext) {
+ // This will need to change to use patch when it is available - see MODFIN-351
+ Batch batch = new Batch().withTransactionsToUpdate(singletonList(transaction));
+ return processBatch(batch, requestContext);
+ }
+
+ public Future> getTransactionsFromFunds(List fundIds, String fiscalYearId,
+ List trTypes, RequestContext requestContext) {
+ return getTransactionsFromOrToFunds(fundIds, fiscalYearId, trTypes, "from", requestContext);
+ }
+
+ public Future> getTransactionsToFunds(List fundIds, String fiscalYearId,
+ List trTypes, RequestContext requestContext) {
+ return getTransactionsFromOrToFunds(fundIds, fiscalYearId, trTypes, "to", requestContext);
+ }
+
+ public Future> getBudgetTransactions(Budget budget, RequestContext requestContext) {
+ String query = String.format("(fromFundId==%s OR toFundId==%s) AND fiscalYearId==%s", budget.getFundId(),
+ budget.getFundId(), budget.getFiscalYearId());
+ return getAllTransactionsByQuery(query, requestContext);
+ }
+
+ public Future> getBudgetTransactionsWithExpenseClasses(List budgetExpenseClasses,
+ SharedBudget budget, RequestContext requestContext) {
+ List ids = budgetExpenseClasses.stream()
+ .map(BudgetExpenseClass::getExpenseClassId)
+ .toList();
+ String query = String.format("(fromFundId==%s OR toFundId==%s) AND fiscalYearId==%s AND %s", budget.getFundId(),
+ budget.getFundId(), budget.getFiscalYearId(), convertIdsToCqlQuery(ids, "expenseClassId", true));
+ return getAllTransactionsByQuery(query, requestContext);
+ }
+
+ public Future> getTransactionsByFundIds(List fundIds, String fiscalYearId,
+ RequestContext requestContext) {
+ List>> futures = StreamEx
+ .ofSubLists(fundIds, MAX_IDS_FOR_GET_RQ)
+ .map(ids -> getAllTransactionsByQuery(buildGetTransactionsByFundIdsQuery(fiscalYearId, ids), requestContext))
+ .toList();
+
+ return collectResultsOnSuccess(futures)
+ .map(listList -> listList.stream()
+ .flatMap(Collection::stream)
+ .toList()
+ );
+ }
+
+ public Future createTransaction(Transaction transaction, RequestContext requestContext) {
+ Batch batch = new Batch().withTransactionsToCreate(singletonList(transaction));
+ return processBatch(batch, requestContext);
+ }
+
+ public Future createAllocationTransaction(Budget budget, RequestContext requestContext) {
+ Transaction transaction = new Transaction()
+ .withTransactionType(Transaction.TransactionType.ALLOCATION)
+ .withId(UUID.randomUUID().toString())
+ .withAmount(budget.getAllocated())
+ .withFiscalYearId(budget.getFiscalYearId())
+ .withToFundId(budget.getFundId())
+ .withSource(Transaction.Source.USER);
+ return fiscalYearService.getFiscalYearById(budget.getFiscalYearId(), requestContext)
+ .compose(fiscalYear -> createTransaction(transaction.withCurrency(fiscalYear.getCurrency()), requestContext));
+ }
+
+
+ private Future> getTransactionsFromOrToFunds(List fundIds, String fiscalYearId,
+ List trTypes, String direction, RequestContext requestContext) {
+ return collectResultsOnSuccess(ofSubLists(new ArrayList<>(fundIds), MAX_FUND_PER_QUERY)
+ .map(ids -> getTransactionsByFundChunk(ids, fiscalYearId, trTypes, direction, requestContext)
+ .map(transactions -> filterFundIdsByAllocationDirection(fundIds, transactions, direction)))
+ .toList())
+ .map(lists -> lists.stream().flatMap(Collection::stream).toList());
+ }
+
+ private Future> getTransactionsByFundChunk(List fundIds, String fiscalYearId,
+ List trTypes, String direction, RequestContext requestContext) {
+ String fundIdField = "from".equals(direction) ? "fromFundId" : "toFundId";
+ String fundQuery = convertIdsToCqlQuery(fundIds, fundIdField, "==", " OR ");
+ List trTypeValues = trTypes.stream().map(TransactionType::value).toList();
+ String trTypeQuery = convertIdsToCqlQuery(trTypeValues, "transactionType", "==", " OR ");
+ String query = String.format("(fiscalYearId==%s AND %s) AND %s", fiscalYearId, trTypeQuery, fundQuery);
+ return getAllTransactionsByQuery(query, requestContext);
+ }
+
+ private List filterFundIdsByAllocationDirection(List fundIds, List transactions,
+ String direction) {
+ // Note that here getToFundId() is used when direction is from (a negation is used afterward)
+ Function getFundId = "from".equals(direction) ? Transaction::getToFundId : Transaction::getFromFundId;
+ return transactions.stream()
+ .filter(transaction -> !fundIds.contains(getFundId.apply(transaction)))
+ .toList();
+ }
-public interface TransactionService extends TransactionManagingService {
+ private String buildGetTransactionsByFundIdsQuery(String fiscalYearId, List fundIds) {
+ return String.format("fiscalYearId==%s AND (%s OR %s)",
+ fiscalYearId,
+ convertIdsToCqlQuery(fundIds, "fromFundId", true),
+ convertIdsToCqlQuery(fundIds, "toFundId", true));
+ }
- Future retrieveTransactions(String query, int offset, int limit, RequestContext requestContext);
- Future retrieveTransactionById(String id, RequestContext requestContext);
- void validateTransactionType(Transaction transaction, Transaction.TransactionType transactionType);
- Future isConnectedToInvoice(String transactionId, RequestContext requestContext);
}
diff --git a/src/main/java/org/folio/services/transactions/TransactionStrategyFactory.java b/src/main/java/org/folio/services/transactions/TransactionStrategyFactory.java
deleted file mode 100644
index 1a7f4d5a..00000000
--- a/src/main/java/org/folio/services/transactions/TransactionStrategyFactory.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.folio.services.transactions;
-
-import java.util.EnumMap;
-import java.util.Map;
-import java.util.Set;
-
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.jaxrs.model.Transaction;
-
-import io.vertx.core.Future;
-
-public class TransactionStrategyFactory {
-
- private Map strategies;
-
- public TransactionStrategyFactory(Set strategySet) {
- createStrategy(strategySet);
- }
-
- public TransactionTypeManagingStrategy findStrategy(Transaction.TransactionType transactionType) {
- return strategies.get(transactionType);
- }
-
- private void createStrategy(Set strategySet) {
- strategies = new EnumMap<>(Transaction.TransactionType.class);
- strategySet.forEach(
- strategy -> strategies.put(strategy.getStrategyName(), strategy));
- }
-
- public Future createTransaction(Transaction.TransactionType type, Transaction transaction, RequestContext requestContext) {
- return findStrategy(type).createTransaction(transaction, requestContext);
- }
-
- public Future updateTransaction(Transaction.TransactionType type, Transaction transaction, RequestContext requestContext) {
- return findStrategy(type).updateTransaction(transaction, requestContext);
- }
-
- public Future deleteTransaction(Transaction.TransactionType type, Transaction transaction, RequestContext requestContext) {
- return findStrategy(type).deleteTransaction(transaction, requestContext);
- }
-
-}
diff --git a/src/main/java/org/folio/services/transactions/TransactionTypeManagingStrategy.java b/src/main/java/org/folio/services/transactions/TransactionTypeManagingStrategy.java
deleted file mode 100644
index 92f9f902..00000000
--- a/src/main/java/org/folio/services/transactions/TransactionTypeManagingStrategy.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.folio.services.transactions;
-
-import org.folio.rest.jaxrs.model.Transaction;
-
-public interface TransactionTypeManagingStrategy extends TransactionManagingService {
- Transaction.TransactionType getStrategyName();
-}
diff --git a/src/main/java/org/folio/services/transactions/TransferService.java b/src/main/java/org/folio/services/transactions/TransferService.java
deleted file mode 100644
index 9d172d96..00000000
--- a/src/main/java/org/folio/services/transactions/TransferService.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.folio.services.transactions;
-
-import org.folio.rest.core.models.RequestContext;
-import org.folio.rest.jaxrs.model.Transaction;
-import org.folio.rest.util.HelperUtils;
-
-import io.vertx.core.Future;
-
-public class TransferService implements TransactionTypeManagingStrategy {
-
- private final TransactionRestrictService transactionRestrictService;
- private final TransactionService transactionService;
-
- public TransferService(BaseTransactionService transactionService, TransactionRestrictService transactionRestrictService) {
- this.transactionService = transactionService;
- this.transactionRestrictService = transactionRestrictService;
- }
-
- @Override
- public Future createTransaction(Transaction transfer, RequestContext requestContext) {
- return Future.succeededFuture()
- .map(v -> {
- transactionService.validateTransactionType(transfer, Transaction.TransactionType.TRANSFER);
- return null;
- })
- .compose(v -> transactionRestrictService.checkTransfer(transfer, requestContext))
- .compose(v -> transactionService.createTransaction(transfer, requestContext));
- }
-
- @Override
- public Future updateTransaction(Transaction transaction, RequestContext requestContext) {
- return HelperUtils.unsupportedOperationExceptionFuture();
- }
-
- @Override
- public Future deleteTransaction(Transaction encumbrance, RequestContext requestContext) {
- return HelperUtils.unsupportedOperationExceptionFuture();
- }
-
- @Override
- public Transaction.TransactionType getStrategyName() {
- return Transaction.TransactionType.TRANSFER;
- }
-}
diff --git a/src/test/java/org/folio/ApiTestSuite.java b/src/test/java/org/folio/ApiTestSuite.java
index d21b9726..30a36f30 100644
--- a/src/test/java/org/folio/ApiTestSuite.java
+++ b/src/test/java/org/folio/ApiTestSuite.java
@@ -25,13 +25,14 @@
import org.folio.rest.impl.LedgersApiTest;
import org.folio.rest.impl.TransactionApiTest;
import org.folio.rest.util.HelperUtilsTest;
-import org.folio.services.CommonTransactionServiceTest;
+import org.folio.services.TransactionServiceTest;
import org.folio.services.ExpenseClassServiceTest;
import org.folio.services.budget.BudgetExpenseClassServiceTest;
import org.folio.services.budget.BudgetExpenseClassTotalsServiceTest;
import org.folio.services.budget.BudgetServiceTest;
import org.folio.services.budget.CreateBudgetServiceTest;
import org.folio.services.budget.RecalculateBudgetServiceTest;
+import org.folio.services.fiscalyear.FiscalYearApiServiceTest;
import org.folio.services.fiscalyear.FiscalYearServiceTest;
import org.folio.services.fund.FundCodeExpenseClassesServiceTest;
import org.folio.services.fund.FundDetailsServiceTest;
@@ -50,7 +51,7 @@
import org.folio.services.protection.AcqUnitMembershipsServiceTest;
import org.folio.services.protection.AcqUnitsServiceTest;
import org.folio.services.protection.ProtectionServiceTest;
-import org.folio.services.transactions.BatchTransactionServiceTest;
+import org.folio.services.transactions.TransactionApiServiceTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
@@ -142,7 +143,11 @@ class ExpenseClassServiceTestNested extends ExpenseClassServiceTest {
}
@Nested
- class CommonTransactionServiceTestNested extends CommonTransactionServiceTest {
+ class TransactionServiceTestNested extends TransactionServiceTest {
+ }
+
+ @Nested
+ class TransactionApiServiceTestNested extends TransactionApiServiceTest {
}
@Nested
@@ -205,6 +210,10 @@ class CreateBudgetServiceTestNested extends CreateBudgetServiceTest {
class FundServiceTestNested extends FundServiceTest {
}
+ @Nested
+ class FiscalYearApiServiceTestNested extends FiscalYearApiServiceTest {
+ }
+
@Nested
class FiscalYearServiceTestNested extends FiscalYearServiceTest {
}
@@ -235,7 +244,4 @@ class GroupServiceNested extends GroupServiceTest {}
@Nested
class RecalculateBudgetServiceTestNested extends RecalculateBudgetServiceTest {}
- @Nested
- class BatchTransactionServiceTestNested extends BatchTransactionServiceTest {}
-
}
diff --git a/src/test/java/org/folio/rest/impl/EntitiesCrudBasicsTest.java b/src/test/java/org/folio/rest/impl/EntitiesCrudBasicsTest.java
index bf321675..5409013f 100644
--- a/src/test/java/org/folio/rest/impl/EntitiesCrudBasicsTest.java
+++ b/src/test/java/org/folio/rest/impl/EntitiesCrudBasicsTest.java
@@ -8,7 +8,6 @@
import static javax.ws.rs.core.Response.Status.NO_CONTENT;
import static javax.ws.rs.core.Response.Status.OK;
import static org.folio.rest.util.HelperUtils.ID;
-import static org.folio.rest.util.MockServer.addMockEntry;
import static org.folio.rest.util.MockServer.getCollectionRecords;
import static org.folio.rest.util.MockServer.getRecordById;
import static org.folio.rest.util.TestConfig.clearServiceInteractions;
@@ -25,22 +24,15 @@
import static org.folio.rest.util.TestEntities.FUND;
import static org.folio.rest.util.TestEntities.GROUP;
import static org.folio.rest.util.TestEntities.GROUP_FUND_FISCAL_YEAR;
-import static org.folio.rest.util.TestEntities.INVOICE_TRANSACTION_SUMMARY;
import static org.folio.rest.util.TestEntities.LEDGER;
import static org.folio.rest.util.TestEntities.LEDGER_ROLLOVER;
import static org.folio.rest.util.TestEntities.LEDGER_ROLLOVER_BUDGETS;
import static org.folio.rest.util.TestEntities.LEDGER_ROLLOVER_ERRORS;
import static org.folio.rest.util.TestEntities.LEDGER_ROLLOVER_LOGS;
import static org.folio.rest.util.TestEntities.LEDGER_ROLLOVER_PROGRESS;
-import static org.folio.rest.util.TestEntities.ORDER_TRANSACTION_SUMMARY;
import static org.folio.rest.util.TestEntities.TRANSACTIONS;
import static org.folio.rest.util.TestEntities.TRANSACTIONS_ALLOCATION;
-import static org.folio.rest.util.TestEntities.TRANSACTIONS_CREDIT;
-import static org.folio.rest.util.TestEntities.TRANSACTIONS_ENCUMBRANCE;
-import static org.folio.rest.util.TestEntities.TRANSACTIONS_PAYMENT;
-import static org.folio.rest.util.TestEntities.TRANSACTIONS_PENDING_PAYMENT;
import static org.folio.rest.util.TestEntities.TRANSACTIONS_TRANSFER;
-import static org.folio.rest.util.TestUtils.getMockData;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
@@ -53,7 +45,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
-import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
@@ -96,9 +87,7 @@
public class EntitiesCrudBasicsTest {
private static final Logger logger = LogManager.getLogger(EntitiesCrudBasicsTest.class);
- private static final List transactionEntities = Arrays.asList(TRANSACTIONS_ALLOCATION, TRANSACTIONS_ENCUMBRANCE
- , TRANSACTIONS_TRANSFER, TRANSACTIONS_PAYMENT, TRANSACTIONS_PENDING_PAYMENT
- , TRANSACTIONS_CREDIT, ORDER_TRANSACTION_SUMMARY, INVOICE_TRANSACTION_SUMMARY);
+ private static final List transactionEntities = Arrays.asList(TRANSACTIONS_ALLOCATION, TRANSACTIONS_TRANSFER);
private static boolean runningOnOwn;
@@ -120,7 +109,7 @@ static Stream getTestEntities() {
}
/**
- * Test entities except for TRANSACTIONS_ALLOCATION, TRANSACTIONS_TRANSFER, TRANSACTIONS_ENCUMBRANCE
+ * Test entities except for TRANSACTIONS_ALLOCATION, TRANSACTIONS_TRANSFER
*
* @return stream of test entities
*/
@@ -143,7 +132,10 @@ static Stream getTestEntitiesWithGetByIdEndpoint() {
}
static Stream