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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@
"orders-storage.po-lines.item.get",
"orders-storage.po-lines.item.put",
"orders-storage.pieces.collection.get",
"orders-storage.batch-tracking.item.post",
"user-tenants.collection.get",
"consortia.sharing-instances.item.post",
"orders-storage.settings.collection.get",
Expand Down Expand Up @@ -1484,6 +1485,10 @@
"id": "orders-storage.settings",
"version": "1.0"
},
{
"id": "orders-storage.batch-tracking",
"version": "1.0"
},
{
"id": "circulation",
"version": "10.0 11.0 12.0 13.0 14.0"
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/org/folio/config/ApplicationConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.folio.service.SuffixService;
import org.folio.service.TagService;
import org.folio.service.UserService;
import org.folio.service.batch.BatchTrackingService;
import org.folio.service.caches.CommonSettingsCache;
import org.folio.service.caches.ExportConfigsCache;
import org.folio.service.caches.InventoryCache;
Expand Down Expand Up @@ -849,9 +850,10 @@ OrderLineUpdateInstanceStrategy withHoldingOrderLineUpdateInstanceStrategy(Inven
InventoryItemManager inventoryItemManager,
InventoryHoldingManager inventoryHoldingManager,
PieceStorageService pieceStorageService,
PurchaseOrderLineService purchaseOrderLineService) {
PurchaseOrderLineService purchaseOrderLineService,
BatchTrackingService batchTrackingService) {
return new WithHoldingOrderLineUpdateInstanceStrategy(inventoryInstanceManager, inventoryItemManager,
inventoryHoldingManager, pieceStorageService, purchaseOrderLineService);
inventoryHoldingManager, pieceStorageService, purchaseOrderLineService, batchTrackingService);
}

@Bean
Expand Down Expand Up @@ -922,4 +924,9 @@ OrderFiscalYearService orderFiscalYearService(TransactionService transactionServ
FundService fundService, PurchaseOrderStorageService purchaseOrderStorageService) {
return new OrderFiscalYearService(transactionService, fiscalYearService, fundService, purchaseOrderStorageService);
}

@Bean
BatchTrackingService batchTrackingService(RestClient restClient) {
return new BatchTrackingService(restClient);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class ResourcePathResolver {
public static final String ACQUISITIONS_UNITS = "acquisitionsUnits";
public static final String ACQUISITION_METHODS = "acquisitionMethods";
public static final String ACQUISITIONS_MEMBERSHIPS = "acquisitionsMemberships";
public static final String BATCH_TRACKING = "batchTracking";
public static final String PURCHASE_ORDER_STORAGE = "purchaseOrder";
public static final String PIECES_STORAGE = "pieces";
public static final String PIECES_STORAGE_BATCH = "pieces-batch";
Expand Down Expand Up @@ -73,6 +74,7 @@ public class ResourcePathResolver {
apis.put(ACQUISITIONS_UNITS, "/acquisitions-units-storage/units");
apis.put(ACQUISITION_METHODS, "/orders-storage/acquisition-methods");
apis.put(ACQUISITIONS_MEMBERSHIPS, "/acquisitions-units-storage/memberships");
apis.put(BATCH_TRACKING, "/orders-storage/batch-tracking");
apis.put(PO_LINES_STORAGE, "/orders-storage/po-lines");
apis.put(PO_LINES_BATCH_STORAGE, "/orders-storage/po-lines-batch");
apis.put(PO_LINES_BUSINESS, "/orders/order-lines");
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/org/folio/service/batch/BatchTrackingService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.folio.service.batch;

import io.vertx.core.Future;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.rest.acq.model.BatchTracking;
import org.folio.rest.core.RestClient;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.core.models.RequestEntry;

import static org.folio.orders.utils.ResourcePathResolver.BATCH_TRACKING;
import static org.folio.orders.utils.ResourcePathResolver.resourcesPath;

@Log4j2
@AllArgsConstructor
public class BatchTrackingService {

private static final String BATCH_TRACKING_HEADER = "X-Batch-Tracking-Id";
private static final String BATCH_TRACKING_ENDPOINT = resourcesPath(BATCH_TRACKING);

protected final RestClient restClient;

public Future<Void> createBatchTrackingRecord(String id, int totalRecords, RequestContext requestContext) {
var batchTracking = new BatchTracking().withId(id).withTotalRecords(totalRecords);
var requestEntry = new RequestEntry(BATCH_TRACKING_ENDPOINT);
return restClient.post(requestEntry, batchTracking, BatchTracking.class, requestContext)
.map(tracking -> requestContext.getHeaders().put(BATCH_TRACKING_HEADER, tracking.getId())) // Add the batch tracking ID to the request headers for downstream services to use.
.onFailure(t -> log.error("Failed to create batch tracking record for batchId: {}", batchTracking.getId(), t))
.recover(t -> Future.succeededFuture()) // In case of failure, we return a succeeded future to avoid failing the entire batch process.
.mapEmpty();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.folio.rest.jaxrs.model.Piece;
import org.folio.rest.jaxrs.model.ReplaceInstanceRef;
import org.folio.rest.tools.utils.TenantTool;
import org.folio.service.batch.BatchTrackingService;
import org.folio.service.inventory.InventoryHoldingManager;
import org.folio.service.inventory.InventoryInstanceManager;
import org.folio.service.inventory.InventoryItemManager;
Expand All @@ -33,6 +34,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;

Expand All @@ -56,15 +58,18 @@ public class WithHoldingOrderLineUpdateInstanceStrategy extends BaseOrderLineUpd

private final PieceStorageService pieceStorageService;
private final PurchaseOrderLineService purchaseOrderLineService;
private final BatchTrackingService batchTrackingService;

public WithHoldingOrderLineUpdateInstanceStrategy(InventoryInstanceManager inventoryInstanceManager,
InventoryItemManager inventoryItemManager,
InventoryHoldingManager inventoryHoldingManager,
PieceStorageService pieceStorageService,
PurchaseOrderLineService purchaseOrderLineService) {
PurchaseOrderLineService purchaseOrderLineService,
BatchTrackingService batchTrackingService) {
super(inventoryInstanceManager, inventoryItemManager, inventoryHoldingManager);
this.pieceStorageService = pieceStorageService;
this.purchaseOrderLineService = purchaseOrderLineService;
this.batchTrackingService = batchTrackingService;
}

Future<Void> processHoldings(OrderLineUpdateInstanceHolder holder, RequestContext requestContext) {
Expand Down Expand Up @@ -233,6 +238,7 @@ private Future<Map<String, List<Location>>> retrieveProcessableLocations(OrderLi

private Future<Void> updateItemsHolding(String holdingId, String newHoldingId, String poLineId, RequestContext requestContext) {
return inventoryItemManager.getItemsByHoldingIdAndOrderLineId(holdingId, poLineId, requestContext)
.compose(items -> batchTrackingService.createBatchTrackingRecord(UUID.randomUUID().toString(), items.size(), requestContext).map(items))
.compose(items -> updateItemsInInventory(items, newHoldingId, requestContext))
.onSuccess(v -> log.info("updateItemsHolding:: existing items for holdingId: {} have been updated with new holdingId: {}", holdingId, newHoldingId))
.onFailure(e -> log.error("Failed to update items for holdingId: {} with new holdingId: {}", holdingId, newHoldingId, e));
Expand Down
5 changes: 5 additions & 0 deletions src/test/java/org/folio/ApiTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.folio.service.SuffixServiceTest;
import org.folio.service.TagServiceTest;
import org.folio.service.UserServiceTest;
import org.folio.service.batch.BatchTrackingServiceTest;
import org.folio.service.consortium.ConsortiumConfigurationServiceTest;
import org.folio.service.consortium.SharingInstanceServiceTest;
import org.folio.service.exchange.CacheableExchangeRateServiceTest;
Expand Down Expand Up @@ -595,4 +596,8 @@ class CacheableExchangeRateServiceTestNested extends CacheableExchangeRateServic
@Nested
class EncumbranceUtilsNestest extends EncumbranceUtilsTest {
}

@Nested
class BatchTrackingServiceTestNested extends BatchTrackingServiceTest {
}
}
4 changes: 4 additions & 0 deletions src/test/java/org/folio/rest/impl/MockServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import static org.folio.orders.utils.ResourcePathResolver.ACQUISITIONS_MEMBERSHIPS;
import static org.folio.orders.utils.ResourcePathResolver.ACQUISITIONS_UNITS;
import static org.folio.orders.utils.ResourcePathResolver.ACQUISITION_METHODS;
import static org.folio.orders.utils.ResourcePathResolver.BATCH_TRACKING;
import static org.folio.orders.utils.ResourcePathResolver.BUDGETS;
import static org.folio.orders.utils.ResourcePathResolver.CONFIGURATION_ENTRIES;
import static org.folio.orders.utils.ResourcePathResolver.CURRENT_BUDGET;
Expand Down Expand Up @@ -170,6 +171,7 @@
import org.folio.OrganizationCollection;
import org.folio.helper.BaseHelper;
import org.folio.rest.RestVerticle;
import org.folio.rest.acq.model.BatchTracking;
import org.folio.rest.acq.model.OrderInvoiceRelationshipCollection;
import org.folio.rest.acq.model.Piece;
import org.folio.rest.acq.model.PieceCollection;
Expand Down Expand Up @@ -626,6 +628,7 @@ private Router defineRoutes() {
router.post(resourcesPath(TAGS)).handler(ctx -> handlePostGenericSubObj(ctx, TAGS));
router.post(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)).handler(ctx -> handlePostGenericSubObj(ctx, DATA_EXPORT_SPRING_CREATE_JOB));
router.post(resourcesPath(DATA_EXPORT_SPRING_EXECUTE_JOB)).handler(ctx -> handlePostGenericSubObj(ctx, DATA_EXPORT_SPRING_EXECUTE_JOB));
router.post(resourcesPath(BATCH_TRACKING)).handler(ctx -> handlePostGenericSubObj(ctx, BATCH_TRACKING));
// GET
router.get(resourcePath(PURCHASE_ORDER_STORAGE)).handler(this::handleGetPurchaseOrderById);
router.get(resourcesPath(PURCHASE_ORDER_STORAGE)).handler(this::handleGetPurchaseOrderByQuery);
Expand Down Expand Up @@ -2514,6 +2517,7 @@ private Class<?> getSubObjClass(String subObj) {
case SUFFIXES -> Suffix.class;
case TAGS -> Tag.class;
case DATA_EXPORT_SPRING_CREATE_JOB, DATA_EXPORT_SPRING_EXECUTE_JOB -> Object.class;
case BATCH_TRACKING -> BatchTracking.class;
default -> {
fail("The sub-object is unknown");
yield null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.folio.service.batch;

import static io.vertx.core.Future.failedFuture;
import static io.vertx.core.Future.succeededFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import io.vertx.core.Future;
import io.vertx.junit5.VertxExtension;
import io.vertx.junit5.VertxTestContext;
import org.folio.CopilotGenerated;
import org.folio.rest.acq.model.BatchTracking;
import org.folio.rest.core.RestClient;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.core.models.RequestEntry;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;

@CopilotGenerated(model = "Claude Opus 4.5")
@ExtendWith({ VertxExtension.class, MockitoExtension.class })
public class BatchTrackingServiceTest {

private static final String BATCH_TRACKING_HEADER = "X-Batch-Tracking-Id";

@InjectMocks
private BatchTrackingService batchTrackingService;
@Mock
private RestClient restClient;
@Mock
private RequestContext requestContext;

@Test
void shouldCreateBatchTrackingRecordAndPopulateHeader(VertxTestContext vertxTestContext) {
// Given
String batchId = UUID.randomUUID().toString();
int totalRecords = 10;
Map<String, String> headers = new HashMap<>();
BatchTracking createdBatchTracking = new BatchTracking().withId(batchId).withTotalRecords(totalRecords);

when(requestContext.getHeaders()).thenReturn(headers);
doReturn(succeededFuture(createdBatchTracking))
.when(restClient).post(any(RequestEntry.class), any(BatchTracking.class), eq(BatchTracking.class), eq(requestContext));

// When
Future<Void> future = batchTrackingService.createBatchTrackingRecord(batchId, totalRecords, requestContext);

// Then
vertxTestContext.assertComplete(future)
.onComplete(result -> {
assertTrue(result.succeeded());
assertEquals(batchId, headers.get(BATCH_TRACKING_HEADER));
vertxTestContext.completeNow();
});
}

@Test
void shouldReturnSucceededFutureWhenRestClientFails(VertxTestContext vertxTestContext) {
// Given
String batchId = UUID.randomUUID().toString();
int totalRecords = 5;

doReturn(failedFuture(new RuntimeException("Connection error")))
.when(restClient).post(any(RequestEntry.class), any(BatchTracking.class), eq(BatchTracking.class), eq(requestContext));

// When
Future<Void> future = batchTrackingService.createBatchTrackingRecord(batchId, totalRecords, requestContext);

// Then
vertxTestContext.assertComplete(future)
.onComplete(result -> {
assertTrue(result.succeeded());
vertxTestContext.completeNow();
});
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.folio.rest.jaxrs.model.ReplaceInstanceRef;
import org.folio.service.AcquisitionsUnitsService;
import org.folio.service.ProtectionService;
import org.folio.service.batch.BatchTrackingService;
import org.folio.service.caches.CommonSettingsCache;
import org.folio.service.caches.InventoryCache;
import org.folio.service.settings.CommonSettingsRetriever;
Expand Down Expand Up @@ -313,9 +314,10 @@ OrderLineUpdateInstanceStrategy withHoldingOrderLineUpdateInstanceStrategy(Inven
InventoryItemManager inventoryItemManager,
InventoryHoldingManager inventoryHoldingManager,
PieceStorageService pieceStorageService,
PurchaseOrderLineService purchaseOrderLineService) {
PurchaseOrderLineService purchaseOrderLineService,
BatchTrackingService batchTrackingService) {
return new WithHoldingOrderLineUpdateInstanceStrategy(inventoryInstanceManager, inventoryItemManager,
inventoryHoldingManager, pieceStorageService, purchaseOrderLineService);
inventoryHoldingManager, pieceStorageService, purchaseOrderLineService, batchTrackingService);
}

@Bean
Expand All @@ -333,5 +335,10 @@ OrderLineUpdateInstanceStrategyResolver updateInstanceStrategyResolver(OrderLine
SettingsRetriever settingsRetriever(RestClient restClient) {
return new SettingsRetriever(restClient);
}

@Bean
BatchTrackingService batchTrackingService(RestClient restClient) {
return new BatchTrackingService(restClient);
}
}
}
Loading