diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 7177d5015..42ce32ec1 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -665,6 +665,31 @@ } ] }, + { + "id": "wrapper-pieces", + "version": "1.0", + "handlers": [ + { + "methods": ["GET"], + "pathPattern": "/orders/wrapper-pieces", + "permissionsRequired": ["orders.wrapper-pieces.collection.get"], + "modulePermissions": [ + "orders-storage.wrapper-pieces.collection.get", + "orders-storage.settings.collection.get", + "consortia.user-tenants.collection.get", + "user-tenants.collection.get" + ] + }, + { + "methods": ["GET"], + "pathPattern": "/orders/wrapper-pieces/{id}", + "permissionsRequired": ["orders.wrapper-pieces.item.get"], + "modulePermissions": [ + "orders-storage.wrapper-pieces.item.get" + ] + } + ] + }, { "id": "acquisition-methods", "version": "1.0", @@ -1263,6 +1288,10 @@ "id": "orders-storage.pieces", "version": "5.0" }, + { + "id": "orders-storage.wrapper-pieces", + "version": "1.0" + }, { "id": "orders-storage.receiving-history", "version": "4.0" @@ -1615,6 +1644,25 @@ "orders.piece-requests.collection.get" ] }, + { + "permissionName" : "orders.wrapper-pieces.collection.get", + "displayName" : "orders.wrapper-pieces-collection get", + "description" : "Get a collection of wrapper pieces" + }, + { + "permissionName" : "orders.wrapper-pieces.item.get", + "displayName" : "orders.wrapper-pieces-item get", + "description" : "Fetch a wrapper piece" + }, + { + "permissionName" : "orders.wrapper-pieces.all", + "displayName" : "All orders wrapper pieces perms", + "description" : "All permissions for the orders-wrapper-pieces", + "subPermissions" : [ + "orders.wrapper-pieces.collection.get", + "orders.wrapper-pieces.item.get" + ] + }, { "permissionName" : "orders.acquisition-methods.collection.get", "displayName" : "acquisition-methods-collection get", @@ -2077,6 +2125,7 @@ "orders.expect.collection.post", "orders.receiving-history.collection.get", "orders.pieces.all", + "orders.wrapper-pieces.all", "orders.acquisitions-units-assignments.all", "orders.order-templates.all", "orders.titles.all", diff --git a/ramls/acq-models b/ramls/acq-models index 494a65e44..e401fec3e 160000 --- a/ramls/acq-models +++ b/ramls/acq-models @@ -1 +1 @@ -Subproject commit 494a65e44f01d3ad01dcc46947587d81ca955083 +Subproject commit e401fec3e8084596c7b638e737567adc30acb6f2 diff --git a/ramls/wrapper-pieces.raml b/ramls/wrapper-pieces.raml new file mode 100644 index 000000000..13d180e25 --- /dev/null +++ b/ramls/wrapper-pieces.raml @@ -0,0 +1,46 @@ +#%RAML 1.0 +title: "Wrapper Pieces" +baseUri: https://github.com/folio-org/mod-orders +version: v4 + +documentation: + - title: "Wrapper Pieces" + content: Read API to manage Wrapper Pieces. + +types: + errors: !include raml-util/schemas/errors.schema + wrapper_piece: !include acq-models/mod-orders-storage/schemas/wrapper_piece.json + wrapper_piece_collection: !include acq-models/mod-orders-storage/schemas/wrapper_piece_collection.json + UUID: + type: string + pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$ + +traits: + orderable: !include raml-util/traits/orderable.raml + pageable: !include raml-util/traits/pageable.raml + searchable: !include raml-util/traits/searchable.raml + +resourceTypes: + collection-get: !include raml-util/rtypes/collection-get.raml + collection-item-get: !include raml-util/rtypes/item-collection-get-with-json-response.raml + +/orders/wrapper-pieces: + type: + collection-get: + exampleCollection: !include acq-models/mod-orders-storage/examples/wrapper_piece_collection.sample + schemaCollection: wrapper_piece_collection + get: + description: Get list of Wrapper Pieces + is: [ + searchable: {description: "with valid searchable fields: for example code", example: "[\"code\", \"MEDGRANT\", \"=\"]"}, + pageable + ] + /{id}: + uriParameters: + id: + description: The UUID of a Wrapper Piece + type: UUID + type: + collection-item-get: + exampleItem: !include acq-models/mod-orders-storage/examples/wrapper_piece_get.sample + schema: wrapper_piece diff --git a/src/main/java/org/folio/config/ApplicationConfig.java b/src/main/java/org/folio/config/ApplicationConfig.java index 7ee11cb1f..a5b70c357 100644 --- a/src/main/java/org/folio/config/ApplicationConfig.java +++ b/src/main/java/org/folio/config/ApplicationConfig.java @@ -95,6 +95,7 @@ import org.folio.service.orders.lines.update.instance.WithHoldingOrderLineUpdateInstanceStrategy; import org.folio.service.orders.lines.update.instance.WithoutHoldingOrderLineUpdateInstanceStrategy; import org.folio.service.organization.OrganizationService; +import org.folio.service.pieces.WrapperPieceStorageService; import org.folio.service.pieces.PieceChangeReceiptStatusPublisher; import org.folio.service.pieces.PieceDeleteInventoryService; import org.folio.service.pieces.ItemRecreateInventoryService; @@ -530,6 +531,14 @@ PieceStorageService pieceStorageService(ConsortiumConfigurationService consortiu return new PieceStorageService(consortiumConfigurationService, consortiumUserTenantsRetriever, settingsRetriever, restClient); } + @Bean + WrapperPieceStorageService wrapperPieceStorageService(ConsortiumConfigurationService consortiumConfigurationService, + ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever, + SettingsRetriever settingsRetriever, + RestClient restClient) { + return new WrapperPieceStorageService(consortiumConfigurationService, consortiumUserTenantsRetriever, settingsRetriever, restClient); + } + @Bean PieceService piecesService(PieceChangeReceiptStatusPublisher receiptStatusPublisher) { return new PieceService(receiptStatusPublisher); diff --git a/src/main/java/org/folio/orders/utils/ResourcePathResolver.java b/src/main/java/org/folio/orders/utils/ResourcePathResolver.java index 64f197063..1e42e2b94 100644 --- a/src/main/java/org/folio/orders/utils/ResourcePathResolver.java +++ b/src/main/java/org/folio/orders/utils/ResourcePathResolver.java @@ -27,6 +27,7 @@ private ResourcePathResolver() { public static final String PURCHASE_ORDER_STORAGE = "purchaseOrder"; public static final String PIECES_STORAGE = "pieces"; public static final String PIECES_STORAGE_BATCH = "pieces-batch"; + public static final String WRAPPER_PIECES_STORAGE = "wrapper-pieces"; public static final String ORGANIZATION_STORAGE = "organizations"; public static final String RECEIVING_HISTORY = "receiving-history"; public static final String RECEIPT_STATUS = "receiptStatus"; @@ -81,6 +82,7 @@ private ResourcePathResolver() { apis.put(PURCHASE_ORDER_STORAGE, "/orders-storage/purchase-orders"); apis.put(PIECES_STORAGE, "/orders-storage/pieces"); apis.put(PIECES_STORAGE_BATCH, "/orders-storage/pieces-batch"); + apis.put(WRAPPER_PIECES_STORAGE, "/orders-storage/wrapper-pieces"); apis.put(ORGANIZATION_STORAGE, "/organizations-storage/organizations"); apis.put(RECEIVING_HISTORY, "/orders-storage/receiving-history"); apis.put(PO_LINE_NUMBER, "/orders-storage/po-line-number"); diff --git a/src/main/java/org/folio/rest/impl/WrapperPiecesAPI.java b/src/main/java/org/folio/rest/impl/WrapperPiecesAPI.java new file mode 100644 index 000000000..e9feb95c8 --- /dev/null +++ b/src/main/java/org/folio/rest/impl/WrapperPiecesAPI.java @@ -0,0 +1,44 @@ +package org.folio.rest.impl; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Context; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import lombok.extern.log4j.Log4j2; +import org.folio.rest.core.models.RequestContext; +import org.folio.rest.jaxrs.resource.OrdersWrapperPieces; +import org.folio.service.pieces.WrapperPieceStorageService; +import org.folio.spring.SpringContextUtil; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.ws.rs.core.Response; +import java.util.Map; + +import static io.vertx.core.Future.succeededFuture; + +@Log4j2 +public class WrapperPiecesAPI extends BaseApi implements OrdersWrapperPieces { + + @Autowired + private WrapperPieceStorageService wrapperPieceStorageService; + + public WrapperPiecesAPI() { + SpringContextUtil.autowireDependencies(this, Vertx.currentContext()); + } + + @Override + public void getOrdersWrapperPieces(String query, String totalRecords, int offset, int limit, + Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) { + wrapperPieceStorageService.getWrapperPieces(limit, offset, query, new RequestContext(vertxContext, okapiHeaders)) + .onSuccess(wrapperPieces -> asyncResultHandler.handle(succeededFuture(buildOkResponse(wrapperPieces)))) + .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail)); + } + + @Override + public void getOrdersWrapperPiecesById(String id, Map okapiHeaders, + Handler> asyncResultHandler, Context vertxContext) { + wrapperPieceStorageService.getWrapperPieceById(id, new RequestContext(vertxContext, okapiHeaders)) + .onSuccess(wrapperPiece -> asyncResultHandler.handle(succeededFuture(buildOkResponse(wrapperPiece)))) + .onFailure(fail -> handleErrorResponse(asyncResultHandler, fail)); + } +} diff --git a/src/main/java/org/folio/service/pieces/PieceStorageService.java b/src/main/java/org/folio/service/pieces/PieceStorageService.java index 979e5414e..3ac8af737 100644 --- a/src/main/java/org/folio/service/pieces/PieceStorageService.java +++ b/src/main/java/org/folio/service/pieces/PieceStorageService.java @@ -46,9 +46,11 @@ public class PieceStorageService { private final ConsortiumConfigurationService consortiumConfigurationService; private final ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever; private final SettingsRetriever settingsRetriever; - private final RestClient restClient; + protected final RestClient restClient; - public PieceStorageService(ConsortiumConfigurationService consortiumConfigurationService, ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever, SettingsRetriever settingsRetriever, RestClient restClient) { + public PieceStorageService(ConsortiumConfigurationService consortiumConfigurationService, + ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever, + SettingsRetriever settingsRetriever, RestClient restClient) { this.consortiumConfigurationService = consortiumConfigurationService; this.consortiumUserTenantsRetriever = consortiumUserTenantsRetriever; this.settingsRetriever = settingsRetriever; @@ -146,7 +148,7 @@ public Future getAllPieces(int limit, int offset, String query, return restClient.get(requestEntry, PieceCollection.class, requestContext); } - private Future> getUserTenantsIfNeeded(RequestContext requestContext) { + protected Future> getUserTenantsIfNeeded(RequestContext requestContext) { return consortiumConfigurationService.getConsortiumConfiguration(requestContext) .compose(consortiumConfiguration -> { if (consortiumConfiguration.isEmpty()) { diff --git a/src/main/java/org/folio/service/pieces/WrapperPieceStorageService.java b/src/main/java/org/folio/service/pieces/WrapperPieceStorageService.java new file mode 100644 index 000000000..b3128b106 --- /dev/null +++ b/src/main/java/org/folio/service/pieces/WrapperPieceStorageService.java @@ -0,0 +1,45 @@ +package org.folio.service.pieces; + +import io.vertx.core.Future; +import lombok.extern.log4j.Log4j2; +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.WrapperPiece; +import org.folio.rest.jaxrs.model.WrapperPieceCollection; +import org.folio.service.consortium.ConsortiumConfigurationService; +import org.folio.service.consortium.ConsortiumUserTenantsRetriever; +import org.folio.service.settings.SettingsRetriever; + +import static org.folio.orders.utils.ResourcePathResolver.WRAPPER_PIECES_STORAGE; +import static org.folio.orders.utils.ResourcePathResolver.resourcesPath; + +@Log4j2 +public class WrapperPieceStorageService extends PieceStorageService { + + private static final String WRAPPER_PIECES_STORAGE_ENDPOINT = resourcesPath(WRAPPER_PIECES_STORAGE); + private static final String WRAPPER_PIECES_STORAGE_BY_ID_ENDPOINT = WRAPPER_PIECES_STORAGE_ENDPOINT + "/{id}"; + + public WrapperPieceStorageService(ConsortiumConfigurationService consortiumConfigurationService, + ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever, + SettingsRetriever settingsRetriever, RestClient restClient) { + super(consortiumConfigurationService, consortiumUserTenantsRetriever, settingsRetriever, restClient); + } + + public Future getWrapperPieces(int limit, int offset, String query, RequestContext requestContext) { + return getUserTenantsIfNeeded(requestContext) + .map(userTenants -> getQueryForUserTenants(userTenants, query)) + .compose(cql -> getAllWrapperPieces(limit, offset, cql, requestContext)); + } + + private Future getAllWrapperPieces(int limit, int offset, String query, RequestContext requestContext) { + log.debug("getAllWrapperPieces:: limit: {}, offset: {}, query: {}", limit, offset, query); + var requestEntry = new RequestEntry(WRAPPER_PIECES_STORAGE_ENDPOINT).withQuery(query).withOffset(offset).withLimit(limit); + return restClient.get(requestEntry, WrapperPieceCollection.class, requestContext); + } + + public Future getWrapperPieceById(String pieceId, RequestContext requestContext) { + var requestEntry = new RequestEntry(WRAPPER_PIECES_STORAGE_BY_ID_ENDPOINT).withId(pieceId); + return restClient.get(requestEntry, WrapperPiece.class, requestContext); + } +} diff --git a/src/test/java/org/folio/ApiTestSuite.java b/src/test/java/org/folio/ApiTestSuite.java index 62a9acd20..73b3500a5 100644 --- a/src/test/java/org/folio/ApiTestSuite.java +++ b/src/test/java/org/folio/ApiTestSuite.java @@ -39,6 +39,7 @@ import org.folio.rest.impl.ReceivingHistoryApiTest; import org.folio.rest.impl.RoutingListsApiTest; import org.folio.rest.impl.TitlesApiTest; +import org.folio.rest.impl.WrapperPiecesAPITest; import org.folio.rest.impl.crud.ConfigurationCrudTest; import org.folio.rest.impl.protection.LinesProtectionTest; import org.folio.rest.impl.protection.OrdersProtectionTest; @@ -99,6 +100,7 @@ import org.folio.service.pieces.PieceUpdateInventoryServiceTest; import org.folio.service.pieces.PieceUtilTest; import org.folio.service.pieces.PiecesClaimingServiceTest; +import org.folio.service.pieces.WrapperPieceStorageServiceTest; import org.folio.service.pieces.flows.BasePieceFlowHolderBuilderTest; import org.folio.service.pieces.flows.DefaultPieceFlowsValidatorTest; import org.folio.service.pieces.flows.create.PieceCreateFlowInventoryManagerTest; @@ -560,4 +562,12 @@ class PiecesClaimingServiceNested extends PiecesClaimingServiceTest { @Nested class PieceUtilTestNested extends PieceUtilTest { } + + @Nested + class WrapperPieceStorageServiceTestNested extends WrapperPieceStorageServiceTest { + } + + @Nested + class WrapperPiecesAPITestNested extends WrapperPiecesAPITest { + } } diff --git a/src/test/java/org/folio/RestTestUtils.java b/src/test/java/org/folio/RestTestUtils.java index 292ccd411..2aae8df2c 100644 --- a/src/test/java/org/folio/RestTestUtils.java +++ b/src/test/java/org/folio/RestTestUtils.java @@ -34,23 +34,23 @@ public class RestTestUtils { - public static Response verifyPostResponse(String url, String body, Headers headers, String - expectedContentType, int expectedCode) { + public static Response verifyPostResponse(String url, String body, Headers headers, + String expectedContentType, int expectedCode) { Response response = RestAssured - .with() - .header(X_OKAPI_URL) - .header(X_OKAPI_TOKEN) - .headers(headers) - .contentType(APPLICATION_JSON) - .body(body) - .post(url) - .then() - .log() - .all() - .statusCode(expectedCode) - .contentType(expectedContentType) - .extract() - .response(); + .with() + .header(X_OKAPI_URL) + .header(X_OKAPI_TOKEN) + .headers(headers) + .contentType(APPLICATION_JSON) + .body(body) + .post(url) + .then() + .log() + .all() + .statusCode(expectedCode) + .contentType(expectedContentType) + .extract() + .response(); // Verify no messages sent via event bus on POST (except receiving/check-in/claiming) if (!(url.startsWith(ORDERS_RECEIVING_ENDPOINT) @@ -62,7 +62,8 @@ public static Response verifyPostResponse(String url, String body, Headers heade return response; } - public static Response verifyPostResponseWithQueryParams(String url, String body, Map queryParams, Headers headers, String + public static Response verifyPostResponseWithQueryParams(String url, String body, Map queryParams, + Headers headers, String expectedContentType, int expectedCode) { Response response = RestAssured .with() @@ -99,18 +100,18 @@ public static Response verifyPut(String url, String body, String expectedContent public static Response verifyPut(String url, String body, Headers headers, String expectedContentType, int expectedCode) { Response response = RestAssured - .with() - .header(X_OKAPI_TOKEN) - .header(X_OKAPI_URL) - .headers(headers) - .body(body) - .contentType(APPLICATION_JSON) - .put(url) - .then() - .statusCode(expectedCode) - .contentType(expectedContentType) - .extract() - .response(); + .with() + .header(X_OKAPI_TOKEN) + .header(X_OKAPI_URL) + .headers(headers) + .body(body) + .contentType(APPLICATION_JSON) + .put(url) + .then() + .statusCode(expectedCode) + .contentType(expectedContentType) + .extract() + .response(); // Verify no messages sent via event bus on PUT if there is an error if (expectedCode != 204) { @@ -123,20 +124,21 @@ public static Response verifyPut(String url, String body, Headers headers, Strin public static Response verifyPatch(String url, String body, String expectedContentType, int expectedCode) { return verifyPatch(url, body, prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10), expectedContentType, expectedCode); } + public static Response verifyPatch(String url, String body, Headers headers, String expectedContentType, int expectedCode) { Response response = RestAssured - .with() - .header(X_OKAPI_TOKEN) - .header(X_OKAPI_URL) - .headers(headers) - .body(body) - .contentType(APPLICATION_JSON) - .patch(url) - .then() - .statusCode(expectedCode) - .contentType(expectedContentType) - .extract() - .response(); + .with() + .header(X_OKAPI_TOKEN) + .header(X_OKAPI_URL) + .headers(headers) + .body(body) + .contentType(APPLICATION_JSON) + .patch(url) + .then() + .statusCode(expectedCode) + .contentType(expectedContentType) + .extract() + .response(); // Verify no messages sent via event bus if (expectedCode != 204) { @@ -158,15 +160,15 @@ public static Response verifyGet(String url, String expectedContentType, int exp public static Response verifyGet(String url, Headers headers, String expectedContentType, int expectedCode) { return RestAssured - .with() - .header(X_OKAPI_URL) - .headers(headers) - .get(url) - .then() - .statusCode(expectedCode) - .contentType(expectedContentType) - .extract() - .response(); + .with() + .header(X_OKAPI_URL) + .headers(headers) + .get(url) + .then() + .statusCode(expectedCode) + .contentType(expectedContentType) + .extract() + .response(); } public static T verifySuccessGet(String url, Class clazz) { @@ -178,21 +180,21 @@ public static T verifySuccessGet(String url, Class clazz, String tenant) } public static Response verifyDeleteResponse(String url, String expectedContentType, int expectedCode) { - Headers headers = prepareHeaders(NON_EXIST_CONFIG_X_OKAPI_TENANT); + Headers headers = prepareHeaders(NON_EXIST_CONFIG_X_OKAPI_TENANT); return verifyDeleteResponse(url, headers, expectedContentType, expectedCode); } public static Response verifyDeleteResponse(String url, Headers headers, String expectedContentType, int expectedCode) { Response response = RestAssured - .with() - .header(X_OKAPI_URL) - .headers(headers) - .delete(url) - .then() - .statusCode(expectedCode) - .contentType(expectedContentType) - .extract() - .response(); + .with() + .header(X_OKAPI_URL) + .headers(headers) + .delete(url) + .then() + .statusCode(expectedCode) + .contentType(expectedContentType) + .extract() + .response(); // Verify no messages sent via event bus HandlersTestHelper.verifyOrderStatusUpdateEvent(0); @@ -207,20 +209,19 @@ public static void checkPreventProtectedFieldsModificationRule(String path, Json compPOParser.setValueAt(m.getKey(), m.getValue()); } Errors errors = verifyPut(String.format(path, compPO.getString("id")), compPOJson, "", HttpStatus.HTTP_BAD_REQUEST.toInt()) - .as(Errors.class); + .as(Errors.class); // Only one error expected assertThat(errors.getErrors(), hasSize(1)); - Error error = errors.getErrors() - .get(0); + Error error = errors.getErrors().get(0); assertThat(error.getCode(), equalTo(PROHIBITED_FIELD_CHANGING.getCode())); Object[] failedFieldNames = getModifiedProtectedFields(error); Object[] expected = updatedFields.keySet() - .stream() - .map(fieldName -> fieldName.replace(COMPOSITE_PO_LINES_PREFIX, StringUtils.EMPTY)) - .toArray(); + .stream() + .map(fieldName -> fieldName.replace(COMPOSITE_PO_LINES_PREFIX, StringUtils.EMPTY)) + .toArray(); assertThat(failedFieldNames.length, is(expected.length)); assertThat(expected, Matchers.arrayContainingInAnyOrder(failedFieldNames)); } diff --git a/src/test/java/org/folio/TestConstants.java b/src/test/java/org/folio/TestConstants.java index d258b7b7a..7ad51ba14 100644 --- a/src/test/java/org/folio/TestConstants.java +++ b/src/test/java/org/folio/TestConstants.java @@ -23,6 +23,7 @@ private TestConstants() {} public static final String ORDERS_BIND_ENDPOINT = "/orders/bind-pieces"; public static final String ORDERS_BIND_ID_ENDPOINT = "/orders/bind-pieces/%s"; public static final String PIECES_CLAIMING_ENDPOINT = "/pieces/claim"; + public static final String WRAPPER_PIECES_ENDPOINT = "/orders/wrapper-pieces"; public static final String PO_LINE_NUMBER_VALUE = "1"; public static final String BAD_QUERY = "unprocessableQuery"; diff --git a/src/test/java/org/folio/rest/impl/MockServer.java b/src/test/java/org/folio/rest/impl/MockServer.java index 53e3bbb1b..bfca261bd 100644 --- a/src/test/java/org/folio/rest/impl/MockServer.java +++ b/src/test/java/org/folio/rest/impl/MockServer.java @@ -91,6 +91,7 @@ import static org.folio.orders.utils.ResourcePathResolver.TITLES; import static org.folio.orders.utils.ResourcePathResolver.TRANSACTIONS_ENDPOINT; import static org.folio.orders.utils.ResourcePathResolver.USER_TENANTS_ENDPOINT; +import static org.folio.orders.utils.ResourcePathResolver.WRAPPER_PIECES_STORAGE; import static org.folio.orders.utils.ResourcePathResolver.resourceByIdPath; import static org.folio.orders.utils.ResourcePathResolver.resourcesPath; import static org.folio.rest.RestVerticle.OKAPI_HEADER_TENANT; @@ -128,6 +129,7 @@ import static org.folio.service.inventory.InventoryManagerTest.ONLY_NEW_HOLDING_EXIST_ID; import io.vertx.core.MultiMap; + import java.io.IOException; import java.nio.file.NoSuchFileException; import java.time.Instant; @@ -335,8 +337,10 @@ public class MockServer { public static final String CONSISTENT_ECS_PURCHASE_ORDER_ID_ELECTRONIC = "01c8d44a-dc73-4bca-a4d1-ef28bdfb9275"; public static final String CONSISTENT_ECS_PURCHASE_ORDER_ID_PHYSICAL_SINGLE_ITEM = "b25f8ef6-04c4-4290-8531-9bbcefeb8c11"; public static final String CONSISTENT_ECS_PURCHASE_ORDER_ID_PHYSICAL_MULTIPLE_ITEMS = "0c9a0e56-3518-4f0c-bfbb-98cc47b07f6c"; - public static final String MOCKDATA_LOCATIONS_JSON = "mockdata/locations/locations.json"; - public static final String MOCKDATA_MATERIAL_TYPES_JSON = "mockdata/material-types/material-types.json"; + public static final String MOCK_DATA_LOCATIONS_JSON = "mockdata/locations/locations.json"; + public static final String MOCK_DATA_MATERIAL_TYPES_JSON = "mockdata/material-types/material-types.json"; + public static final String MOCK_DATA_WRAPPER_PIECES_JSON = "mockdata/wrapper-pieces/wrapper-pieces.json"; + public static final String MOCK_DATA_WRAPPER_PIECES_BY_ID_JSON = "mockdata/wrapper-pieces/wrapper-pieces-by-id.json"; public static Table> serverRqRs = HashBasedTable.create(); public static HashMap> serverRqQueries = new HashMap<>(); @@ -376,10 +380,9 @@ public void start() throws InterruptedException, ExecutionException, TimeoutExce HttpServer server = vertx.createHttpServer(); Promise deploymentComplete = Promise.promise(); server.requestHandler(defineRoutes()).listen(port, result -> { - if(result.succeeded()) { + if (result.succeeded()) { deploymentComplete.complete(result.result()); - } - else { + } else { deploymentComplete.fail(result.cause()); } }); @@ -509,6 +512,7 @@ static List getInstanceTypesSearches() { public static List getAcqUnitsSearches() { return getCollectionRecords(getRqRsEntries(HttpMethod.GET, ACQUISITIONS_UNITS)); } + public static List getAcqUnitsRetrievals() { return getRecordsByIds(getRqRsEntries(HttpMethod.GET, ACQUISITIONS_UNITS)); } @@ -678,8 +682,10 @@ private Router defineRoutes() { router.get("/data-import-profiles/jobProfileSnapshots/:id").handler(this::handleGetJobProfileSnapshotById); router.get("/change-manager/jobExecutions/:id").handler(this::handleGetJobExecutionById); router.get("/organizations/organizations").handler(this::handleGetOrganizations); - router.get("/locations").handler(ctx -> handleGetJsonResource(ctx, MOCKDATA_LOCATIONS_JSON)); - router.get("/material-types").handler(ctx -> handleGetJsonResource(ctx, MOCKDATA_MATERIAL_TYPES_JSON)); + router.get("/locations").handler(ctx -> handleGetJsonResource(ctx, MOCK_DATA_LOCATIONS_JSON)); + router.get("/material-types").handler(ctx -> handleGetJsonResource(ctx, MOCK_DATA_MATERIAL_TYPES_JSON)); + router.get(resourcesPath(WRAPPER_PIECES_STORAGE)).handler(ctx -> handleGetJsonResource(ctx, MOCK_DATA_WRAPPER_PIECES_JSON)); + router.get(resourcesPath(WRAPPER_PIECES_STORAGE) + "/:id").handler(ctx -> handleGetJsonResource(ctx, MOCK_DATA_WRAPPER_PIECES_BY_ID_JSON)); // PUT router.put(resourcePath(PURCHASE_ORDER_STORAGE)).handler(ctx -> handlePutGenericSubObj(ctx, PURCHASE_ORDER_STORAGE)); router.put(resourcePath(PO_LINES_STORAGE)).handler(ctx -> handlePutGenericSubObj(ctx, PO_LINES_STORAGE)); @@ -820,12 +826,11 @@ private void handleGetFundById(RoutingContext ctx) { FundCollection funds = getFundsByIds(Collections.singletonList(id)) .mapTo(FundCollection.class); - if (funds.getTotalRecords() == 0){ + if (funds.getTotalRecords() == 0) { serverResponse(ctx, 404, APPLICATION_JSON, id); - } - else { + } else { JsonObject fund = new JsonObject() - .put("fund",JsonObject.mapFrom(funds.getFunds().get(0))) + .put("fund", JsonObject.mapFrom(funds.getFunds().get(0))) .put("groupIds", new JsonArray()); addServerRqRsData(HttpMethod.GET, FUNDS, fund); @@ -981,7 +986,7 @@ private void handleGetCurrentFiscalYearByLedgerId(RoutingContext ctx) { serverResponse(ctx, 500, APPLICATION_JSON, INTERNAL_SERVER_ERROR.getReasonPhrase()); } else if (ID_DOES_NOT_EXIST.equals(id)) { serverResponse(ctx, 404, APPLICATION_JSON, id); - } else if(id.equals("133a7916-f05e-4df4-8f7f-09eb2a7076d1")) { + } else if (id.equals("133a7916-f05e-4df4-8f7f-09eb2a7076d1")) { FiscalYear fiscalYear = new FiscalYear(); fiscalYear.setId("ac2164c7-ba3d-1bc2-a12c-e35ceccbfaf2"); fiscalYear.setCode("test2020"); @@ -990,7 +995,7 @@ private void handleGetCurrentFiscalYearByLedgerId(RoutingContext ctx) { fiscalYear.setPeriodStart(Date.from(Instant.now().minus(365, DAYS))); fiscalYear.setPeriodEnd(Date.from(Instant.now().plus(365, DAYS))); serverResponse(ctx, 200, APPLICATION_JSON, JsonObject.mapFrom(fiscalYear).encodePrettily()); - } else { + } else { FiscalYear fiscalYear = new FiscalYear(); fiscalYear.setId(UUID.randomUUID().toString()); fiscalYear.setCode("test2020"); @@ -1003,7 +1008,7 @@ private void handleGetCurrentFiscalYearByLedgerId(RoutingContext ctx) { } private void handleGetPoLineNumber(RoutingContext ctx) { - if(PO_NUMBER_ERROR_TENANT.equals(ctx.request().getHeader(OKAPI_HEADER_TENANT))) { + if (PO_NUMBER_ERROR_TENANT.equals(ctx.request().getHeader(OKAPI_HEADER_TENANT))) { ctx.response() .setStatusCode(500) .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) @@ -1114,14 +1119,14 @@ private void handleGetHoldingsRecords(RoutingContext ctx) { .filter(holding -> holding.getString("permanentLocationId").equals(OLD_LOCATION_ID)) .collect(toList()); holdings = new JsonObject().put("holdingsRecords", new JsonArray(holdingsList)); - } else if (queryParam.contains(OLD_LOCATION_ID) && !queryParam.contains(NON_EXISTED_NEW_HOLDING_ID)) { + } else if (queryParam.contains(OLD_LOCATION_ID) && !queryParam.contains(NON_EXISTED_NEW_HOLDING_ID)) { List holdingsList = new JsonObject(getMockData(HOLDINGS_OLD_NEW_PATH)).getJsonArray("holdingsRecords").stream() .map(o -> ((JsonObject) o)) .filter(holding -> holding.getString("permanentLocationId").equals(OLD_LOCATION_ID) || !holding.getString("permanentLocationId").equals(NON_EXISTED_NEW_HOLDING_ID)) .collect(toList()); holdings = new JsonObject().put("holdingsRecords", new JsonArray(holdingsList)); - } else { + } else { holdings = new JsonObject().put("holdingsRecords", new JsonArray()); } if (queryParam.contains(NEW_LOCATION_ID) && queryParam.contains(ONLY_NEW_HOLDING_EXIST_ID)) { @@ -1177,7 +1182,7 @@ private JsonObject getHoldingsByIds(List holdingIds) { .map(holdingId -> new JsonObject(mockData).getJsonArray("holdingsRecords").getJsonObject(0).put(ID, holdingId)) .toList(); } catch (IOException e) { - holdingRecords = Collections.emptyList(); + holdingRecords = Collections.emptyList(); } } @@ -1255,7 +1260,7 @@ private void handleGetInventoryItemRecords(RoutingContext ctx) { int lineIndex = query.indexOf(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER) + ITEM_PURCHASE_ORDER_LINE_IDENTIFIER.length() + 2; String purchaseOrderLineIdentifier = query.substring(lineIndex, lineIndex + 36); int holdingIndex = query.indexOf(ITEM_HOLDINGS_RECORD_ID) + ITEM_HOLDINGS_RECORD_ID.length() + 2; - String holdingsRecordId = query.substring(holdingIndex, holdingIndex + 36); + String holdingsRecordId = query.substring(holdingIndex, holdingIndex + 36); final Iterator iterator = jsonArray.iterator(); while (iterator.hasNext()) { JsonObject item = (JsonObject) iterator.next(); @@ -1296,19 +1301,19 @@ private void handleGetInventoryItemRecordById(RoutingContext ctx) { JsonArray jsonArray = items.getJsonArray(ITEMS); final Iterator iterator = jsonArray.iterator(); - while (iterator.hasNext()) { - JsonObject item = (JsonObject) iterator.next(); - if (!id.equals(item.getString(ID))) { - iterator.remove(); - } + while (iterator.hasNext()) { + JsonObject item = (JsonObject) iterator.next(); + if (!id.equals(item.getString(ID))) { + iterator.remove(); } + } - if (!jsonArray.isEmpty()) { + if (!jsonArray.isEmpty()) { items.put(TOTAL_RECORDS, jsonArray.size()); addServerRqRsData(HttpMethod.GET, ITEM_RECORDS, jsonArray.getJsonObject(0)); ctx.response().setStatusCode(200).putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) - .end(jsonArray.getJsonObject(0).encodePrettily()); + .end(jsonArray.getJsonObject(0).encodePrettily()); } else { ctx.response().setStatusCode(404).end(); } @@ -1424,7 +1429,7 @@ private void handleGetAccessProviders(RoutingContext ctx) { body = new JsonObject().put(ORGANIZATIONS, organizations); } } - } catch(IOException e) { + } catch (IOException e) { ctx.response() .setStatusCode(HttpStatus.HTTP_NOT_FOUND.toInt()) .end(); @@ -1585,13 +1590,11 @@ private void handleGetReceivingHistory(RoutingContext ctx) { JsonObject receivingHistory; if (queryParam.contains(RECEIVING_HISTORY_PURCHASE_ORDER_ID)) { receivingHistory = new JsonObject(getMockData(RECEIVING_HISTORY_MOCK_DATA_PATH + "receivingHistory.json")); - } else if(queryParam.contains(INTERNAL_SERVER_ERROR.getReasonPhrase())) { + } else if (queryParam.contains(INTERNAL_SERVER_ERROR.getReasonPhrase())) { throw new HttpException(500, "Exception in orders-storage module"); - } - else if(queryParam.contains(BAD_QUERY)) { + } else if (queryParam.contains(BAD_QUERY)) { throw new HttpException(400, "QueryValidationException"); - } - else { + } else { receivingHistory = new JsonObject(); receivingHistory.put("receivingHistory", new JsonArray()); receivingHistory.put("totalRecords", 0); @@ -1622,18 +1625,18 @@ private void handleConfigurationModuleResponse(RoutingContext ctx) { return; } - String tenant = ctx.request().getHeader(OKAPI_HEADER_TENANT) ; + String tenant = ctx.request().getHeader(OKAPI_HEADER_TENANT); if (PO_NUMBER_ERROR_X_OKAPI_TENANT.getValue().equals(tenant)) { tenant = EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10.getValue(); } if (NON_EXIST_INSTANCE_STATUS_TENANT_HEADER.getValue().equals(tenant) || - NON_EXIST_INSTANCE_TYPE_TENANT_HEADER.getValue().equals(tenant) || - NON_EXIST_LOAN_TYPE_TENANT_HEADER.getValue().equals(tenant)) { + NON_EXIST_INSTANCE_TYPE_TENANT_HEADER.getValue().equals(tenant) || + NON_EXIST_LOAN_TYPE_TENANT_HEADER.getValue().equals(tenant)) { tenant = EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_1.getValue(); } try { serverResponse(ctx, 200, APPLICATION_JSON, getMockData(String.format(CONFIG_MOCK_PATH, tenant))); - } catch(Exception exc){ + } catch (Exception exc) { serverResponse(ctx, 200, APPLICATION_JSON, getMockData(String.format(CONFIG_MOCK_PATH, EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10.getValue()))); } } catch (IOException e) { @@ -1647,7 +1650,8 @@ private void handleDeleteGenericSubObj(RoutingContext ctx, String subObj) { addServerRqRsData(HttpMethod.DELETE, subObj, new JsonObject().put(ID, id)); if (ID_DOES_NOT_EXIST.equals(id)) { serverResponse(ctx, 404, TEXT_PLAIN, id); - } if (ID_BAD_FORMAT.equals(id)) { + } + if (ID_BAD_FORMAT.equals(id)) { serverResponse(ctx, 400, TEXT_PLAIN, id); } else if (ID_FOR_INTERNAL_SERVER_ERROR.equals(id) || ORDER_DELETE_ERROR_TENANT.equals(tenant)) { serverResponse(ctx, 500, APPLICATION_JSON, INTERNAL_SERVER_ERROR.getReasonPhrase()); @@ -1705,7 +1709,7 @@ private void handleGetPoLines(RoutingContext ctx) { poLineCollection = buildPoLineCollection(tenant, compPO.getJsonArray(COMPOSITE_PO_LINES), poId); } } else { - // Attempt to find POLine in mock server memory + // Attempt to find POLine in mock server memory poLineCollection.getPoLines().addAll(postedPoLines.stream() .map(jsonObj -> jsonObj.mapTo(PoLine.class)) .toList()); @@ -1770,7 +1774,7 @@ private PoLineCollection buildPoLineCollection(String tenant, JsonArray lines, S replaceObjectsByIds(line, ALERTS, REPORTING_CODES); return line.mapTo(PoLine.class); }) - .map(line -> line.withPurchaseOrderId(StringUtils.isNotEmpty(poId) ? poId : UUID.randomUUID().toString() )) + .map(line -> line.withPurchaseOrderId(StringUtils.isNotEmpty(poId) ? poId : UUID.randomUUID().toString())) .collect(Collectors.toList()); // Set PO Line number if empty @@ -1850,7 +1854,8 @@ private void handleGetGenericSubObj(RoutingContext ctx, String subObj) { if (ID_DOES_NOT_EXIST.equals(id)) { serverResponse(ctx, 404, APPLICATION_JSON, id); - } if (ID_BAD_FORMAT.equals(id)) { + } + if (ID_BAD_FORMAT.equals(id)) { serverResponse(ctx, 400, APPLICATION_JSON, id); } else if (ID_FOR_INTERNAL_SERVER_ERROR.equals(id)) { serverResponse(ctx, 500, APPLICATION_JSON, INTERNAL_SERVER_ERROR.getReasonPhrase()); @@ -1873,7 +1878,7 @@ private void handleGetGenericSubObjs(RoutingContext ctx, String subObj) { } else { try { JsonObject collection; - if(REASONS_FOR_CLOSURE.equals(subObj)) { + if (REASONS_FOR_CLOSURE.equals(subObj)) { ReasonForClosureCollection reasonsCollection = new ReasonForClosureCollection(); List reasons = Lists.newArrayList(REASON_FOR_CLOSURE.getTestSample().mapTo(ReasonForClosure.class)); collection = JsonObject.mapFrom(reasonsCollection.withReasonsForClosure(reasons).withTotalRecords(reasons.size())); @@ -1881,7 +1886,7 @@ private void handleGetGenericSubObjs(RoutingContext ctx, String subObj) { PrefixCollection prefixCollection = new PrefixCollection(); List prefixes = Lists.newArrayList(PREFIX.getTestSample().mapTo(Prefix.class)); collection = JsonObject.mapFrom(prefixCollection.withPrefixes(prefixes).withTotalRecords(prefixes.size())); - } else if(SUFFIXES.equals(subObj)) { + } else if (SUFFIXES.equals(subObj)) { SuffixCollection suffixCollection = new SuffixCollection(); List suffixes = Lists.newArrayList(SUFFIX.getTestSample().mapTo(Suffix.class)); collection = JsonObject.mapFrom(suffixCollection.withSuffixes(suffixes).withTotalRecords(suffixes.size())); @@ -1975,7 +1980,7 @@ private void handleGetPieces(RoutingContext ctx) { status = condition.split("receivingStatus==")[1]; } } - logger.info("poLineId: {}", polId); + logger.info("poLineId: {}", polId); logger.info("receivingStatus: {}", status); String path = PIECE_RECORDS_MOCK_DATA_PATH + String.format("pieceRecords-%s.json", polId); @@ -2127,7 +2132,8 @@ private void handlePutGenericSubObj(RoutingContext ctx, String subObj) { if (ID_DOES_NOT_EXIST.equals(id)) { serverResponse(ctx, 404, APPLICATION_JSON, id); - } if (ID_BAD_FORMAT.equals(id)) { + } + if (ID_BAD_FORMAT.equals(id)) { serverResponse(ctx, 400, APPLICATION_JSON, id); } else if (ID_FOR_INTERNAL_SERVER_ERROR.equals(id) || ctx.body().asString().contains("500500500500")) { serverResponse(ctx, 500, APPLICATION_JSON, INTERNAL_SERVER_ERROR.getReasonPhrase()); @@ -2426,7 +2432,7 @@ private void handleTransactionGetEntry(RoutingContext ctx) { } serverResponse(ctx, HttpStatus.HTTP_OK.toInt(), APPLICATION_JSON, body); addServerRqRsData(HttpMethod.GET, TRANSACTIONS_ENDPOINT, new JsonObject(body)); - } catch(IOException e) { + } catch (IOException e) { logger.error("handleTransactionGetEntry error", e); } } @@ -2488,7 +2494,7 @@ private void handlePostPOLine(RoutingContext ctx) { } private void handleGetPoNumber(RoutingContext ctx) { - if(PO_NUMBER_ERROR_TENANT.equals(ctx.request().getHeader(OKAPI_HEADER_TENANT))) { + if (PO_NUMBER_ERROR_TENANT.equals(ctx.request().getHeader(OKAPI_HEADER_TENANT))) { ctx.response() .setStatusCode(500) .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) @@ -2512,7 +2518,7 @@ private void handleGetContributorNameTypes(RoutingContext ctx) { serverResponse(ctx, HttpStatus.HTTP_OK.toInt(), APPLICATION_JSON, body); addServerRqRsData(HttpMethod.GET, CONTRIBUTOR_NAME_TYPES, new JsonObject(body)); - } else if (StringUtils.isEmpty(queryParam) || queryParam.startsWith("id==")){ + } else if (StringUtils.isEmpty(queryParam) || queryParam.startsWith("id==")) { List contributorNameTypeIds = extractIdsFromQuery(queryParam); JsonObject contributorNameTypeCollection = getMockAsJson(CONTRIBUTOR_NAME_TYPES_PATH); @@ -2633,9 +2639,9 @@ private void handleGetAcquisitionsMemberships(RoutingContext ctx) { if (StringUtils.isNotEmpty(userId)) { memberships.getAcquisitionsUnitMemberships().removeIf(membership -> !membership.getUserId().equals(userId)); List acquisitionsUnitIds = extractValuesFromQuery(ACQUISITIONS_UNIT_ID, query); - if (!acquisitionsUnitIds.isEmpty()) { - memberships.getAcquisitionsUnitMemberships().removeIf(membership -> !acquisitionsUnitIds.contains(membership.getAcquisitionsUnitId())); - } + if (!acquisitionsUnitIds.isEmpty()) { + memberships.getAcquisitionsUnitMemberships().removeIf(membership -> !acquisitionsUnitIds.contains(membership.getAcquisitionsUnitId())); + } } JsonObject data = JsonObject.mapFrom(memberships.withTotalRecords(memberships.getAcquisitionsUnitMemberships().size())); @@ -2920,8 +2926,7 @@ private void handleGetInvoiceLines(RoutingContext ctx) { .withInvoiceId(UUID.randomUUID().toString()) .withReleaseEncumbrance(true); invoiceLineCollection = new InvoiceLineCollection().withInvoiceLines(List.of(invoiceLine)).withTotalRecords(1); - } - else { + } else { serverResponse(ctx, HttpStatus.HTTP_NOT_FOUND.toInt(), APPLICATION_JSON, "invoice line not found"); return; } @@ -2938,15 +2943,16 @@ private void handlePatchOrderLines(RoutingContext ctx) { if (ID_DOES_NOT_EXIST.equals(id)) { serverResponse(ctx, 404, APPLICATION_JSON, id); - } if (ID_BAD_FORMAT.equals(id)) { + } + if (ID_BAD_FORMAT.equals(id)) { serverResponse(ctx, 400, APPLICATION_JSON, id); } else if (ID_FOR_INTERNAL_SERVER_ERROR.equals(id) || ctx.body().asString().contains("500500500500")) { serverResponse(ctx, 500, APPLICATION_JSON, INTERNAL_SERVER_ERROR.getReasonPhrase()); } else { ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) - .setStatusCode(204) - .end(); + .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) + .setStatusCode(204) + .end(); } } diff --git a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java index 3dc00a35b..244532c20 100644 --- a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java +++ b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java @@ -4,8 +4,7 @@ import io.restassured.http.Header; import io.vertx.core.json.JsonObject; import io.vertx.junit5.VertxExtension; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import lombok.extern.log4j.Log4j2; import org.folio.ApiTestSuite; import org.folio.Organization; import org.folio.config.ApplicationConfig; @@ -70,17 +69,17 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +@Log4j2 @ExtendWith(VertxExtension.class) public class PiecesClaimingApiTest { - private static final Logger logger = LogManager.getLogger(); private static boolean runningOnOwn; - private static final String ORGANIZATIONS_KEY = "organizations"; - private static final String PO_LINES_KEY = "poLines"; - private static final String PIECES_KEY = "pieces"; - private static final String ENTRY_ID = "id"; - private static final String CLAIMING_MOCK_DATA_FOLDER = "claiming/"; + static final String ORGANIZATIONS_KEY = "organizations"; + static final String PO_LINES_KEY = "poLines"; + static final String PIECES_KEY = "pieces"; + static final String ENTRY_ID = "id"; + static final String CLAIMING_MOCK_DATA_FOLDER = "claiming/"; @BeforeAll static void before() throws InterruptedException, ExecutionException, TimeoutException { @@ -140,7 +139,7 @@ public MockHitDto(int pieceSearches, int polSearches, int purchaseOrderRetrieval void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx, MockHitDto dto, String payloadFile, Header header, ClaimingPieceResult.Status expectedStatus, HttpResponseStatus expectedResponseStatus) { - logger.info("Testing postPiecesClaim, name: {}", name); + log.info("Testing postPiecesClaim, name: {}", name); var organization = getMockAsJson(ORGANIZATION_COLLECTION) .getJsonArray(ORGANIZATIONS_KEY).getJsonObject(vendorIdx) @@ -182,7 +181,7 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx .filter(Objects::nonNull).filter(poLineId -> poLineId.equals(purchaseOrder.getId())) .toList(); - purchaseOrderRetrievals.forEach(entry -> logger.info("Retrieved PurchaseOrder: {}", entry)); + purchaseOrderRetrievals.forEach(entry -> log.info("Retrieved PurchaseOrder: {}", entry)); var organizationSearches = getOrganizationSearches(); var pieceUpdates = getPieceBatchUpdates(); @@ -209,10 +208,10 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx assertThat(jobCreations, hasSize(dto.jobCreations)); assertThat(claimingResults.getClaimingPieceResults().size(), equalTo(dto.claimingResults)); - pieceUpdates.forEach(pieceUpdate -> logger.info("Updated Piece: {}", pieceUpdate.encodePrettily())); + pieceUpdates.forEach(pieceUpdate -> log.info("Updated Piece: {}", pieceUpdate.encodePrettily())); var claimedPieceIds = jobCreations.stream() - .peek(job -> logger.info("Created job: {}", JsonObject.mapFrom(job).encodePrettily())) + .peek(job -> log.info("Created job: {}", JsonObject.mapFrom(job).encodePrettily())) .map(job -> job.getJsonObject(EXPORT_TYPE_SPECIFIC_PARAMETERS.getValue()) .getJsonObject(VENDOR_EDI_ORDERS_EXPORT_CONFIG.getValue()) .getJsonArray(CLAIM_PIECE_IDS.getValue()).size()) diff --git a/src/test/java/org/folio/rest/impl/WrapperPiecesAPITest.java b/src/test/java/org/folio/rest/impl/WrapperPiecesAPITest.java new file mode 100644 index 000000000..c76ff8586 --- /dev/null +++ b/src/test/java/org/folio/rest/impl/WrapperPiecesAPITest.java @@ -0,0 +1,124 @@ +package org.folio.rest.impl; + +import io.restassured.http.Headers; +import io.vertx.junit5.VertxExtension; +import lombok.extern.log4j.Log4j2; +import org.folio.ApiTestSuite; +import org.folio.HttpStatus; +import org.folio.Organization; +import org.folio.config.ApplicationConfig; +import org.folio.rest.jaxrs.model.CompositePoLine; +import org.folio.rest.jaxrs.model.Piece; +import org.folio.rest.jaxrs.model.WrapperPiece; +import org.folio.rest.jaxrs.model.WrapperPieceCollection; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.folio.RestTestUtils.prepareHeaders; +import static org.folio.RestTestUtils.verifyGet; +import static org.folio.TestConfig.clearServiceInteractions; +import static org.folio.TestConfig.initSpringContext; +import static org.folio.TestConfig.isVerticleNotDeployed; +import static org.folio.TestConstants.EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10_CLAIMS; +import static org.folio.TestConstants.WRAPPER_PIECES_ENDPOINT; +import static org.folio.TestUtils.getMinimalOrder; +import static org.folio.TestUtils.getMockAsJson; +import static org.folio.orders.utils.ResourcePathResolver.ORGANIZATION_STORAGE; +import static org.folio.orders.utils.ResourcePathResolver.PIECES_STORAGE; +import static org.folio.orders.utils.ResourcePathResolver.PO_LINES_STORAGE; +import static org.folio.orders.utils.ResourcePathResolver.PURCHASE_ORDER_STORAGE; +import static org.folio.rest.impl.MockServer.ORGANIZATION_COLLECTION; +import static org.folio.rest.impl.MockServer.PIECES_COLLECTION; +import static org.folio.rest.impl.MockServer.PO_LINES_COLLECTION; +import static org.folio.rest.impl.MockServer.addMockEntry; +import static org.folio.rest.impl.PiecesClaimingApiTest.ORGANIZATIONS_KEY; +import static org.folio.rest.impl.PiecesClaimingApiTest.PIECES_KEY; +import static org.folio.rest.impl.PiecesClaimingApiTest.PO_LINES_KEY; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Log4j2 +@ExtendWith(VertxExtension.class) +public class WrapperPiecesAPITest { + + private static final Headers HEADERS = prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10_CLAIMS); + private static final String EXPECTED_VENDOR_ID = "e0fb5df2-cdf1-11e8-a8d5-f2801f1b9fd1"; + private static final String EXPECTED_PIECE_ID = "dcd0ba36-b660-4751-b9fe-c8ac61ff6f99"; + private static final int ORGANIZATION_IDX = 0; + private static final int PO_LINE_IDX = 17; + private static final int PIECE_IDX = 69; + private static boolean runningOnOwn; + + @BeforeAll + static void before() throws InterruptedException, ExecutionException, TimeoutException { + if (isVerticleNotDeployed()) { + ApiTestSuite.before(); + runningOnOwn = true; + } + initSpringContext(ApplicationConfig.class); + } + + @BeforeEach + void beforeEach() { + var organization = getMockAsJson(ORGANIZATION_COLLECTION) + .getJsonArray(ORGANIZATIONS_KEY).getJsonObject(ORGANIZATION_IDX) + .mapTo(Organization.class); + var poLine = getMockAsJson(PO_LINES_COLLECTION) + .getJsonArray(PO_LINES_KEY) + .getJsonObject(PO_LINE_IDX) + .mapTo(CompositePoLine.class); + var purchaseOrder = getMinimalOrder(poLine) + .withVendor(EXPECTED_VENDOR_ID); + var piece = getMockAsJson(PIECES_COLLECTION) + .getJsonArray(PIECES_KEY) + .getJsonObject(PIECE_IDX) + .mapTo(Piece.class); + + addMockEntry(ORGANIZATION_STORAGE, organization); + addMockEntry(PURCHASE_ORDER_STORAGE, purchaseOrder); + addMockEntry(PO_LINES_STORAGE, poLine); + addMockEntry(PIECES_STORAGE, piece); + } + + @AfterEach + void afterEach() throws Exception { + clearServiceInteractions(); + } + + @AfterAll + static void after() { + if (runningOnOwn) { + ApiTestSuite.after(); + } + } + + @Test + void testGetOrdersWrapperPieces() { + log.info("Testing testGetOrdersWrapperPieces"); + + var response = verifyGet(WRAPPER_PIECES_ENDPOINT, HEADERS, APPLICATION_JSON, HttpStatus.HTTP_OK.toInt()); + var wrapperPieces = response.as(WrapperPieceCollection.class); + + assertEquals(1, wrapperPieces.getTotalRecords()); + assertEquals(EXPECTED_VENDOR_ID, wrapperPieces.getWrapperPieces().get(0).getVendorId()); + assertEquals(EXPECTED_PIECE_ID, wrapperPieces.getWrapperPieces().get(0).getPiece().getId()); + } + + @Test + void testGetOrdersWrapperPiecesById() { + log.info("Testing testGetOrdersWrapperPiecesById"); + + var response = verifyGet(WRAPPER_PIECES_ENDPOINT + "/" + EXPECTED_PIECE_ID, HEADERS, APPLICATION_JSON, HttpStatus.HTTP_OK.toInt()); + var wrapperPiece = response.as(WrapperPiece.class); + + assertEquals(EXPECTED_VENDOR_ID, wrapperPiece.getVendorId()); + assertEquals(EXPECTED_PIECE_ID, wrapperPiece.getPiece().getId()); + } +} diff --git a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java index 2ccb90b4f..54776c758 100644 --- a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java +++ b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java @@ -4,7 +4,6 @@ import io.vertx.core.json.JsonObject; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; -import lombok.extern.slf4j.Slf4j; import org.folio.CopilotGenerated; import org.folio.rest.acq.model.Organization; import org.folio.rest.core.exceptions.HttpException; @@ -48,7 +47,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -@Slf4j @ExtendWith(VertxExtension.class) @CopilotGenerated(partiallyGenerated = true) public class PiecesClaimingServiceTest { diff --git a/src/test/java/org/folio/service/pieces/WrapperPieceStorageServiceTest.java b/src/test/java/org/folio/service/pieces/WrapperPieceStorageServiceTest.java new file mode 100644 index 000000000..4b1afe372 --- /dev/null +++ b/src/test/java/org/folio/service/pieces/WrapperPieceStorageServiceTest.java @@ -0,0 +1,131 @@ +package org.folio.service.pieces; + +import io.vertx.core.Future; +import io.vertx.junit5.VertxExtension; +import io.vertx.junit5.VertxTestContext; +import org.folio.models.consortium.ConsortiumConfiguration; +import org.folio.rest.acq.model.Setting; +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.Piece; +import org.folio.rest.jaxrs.model.WrapperPiece; +import org.folio.rest.jaxrs.model.WrapperPieceCollection; +import org.folio.service.consortium.ConsortiumConfigurationService; +import org.folio.service.consortium.ConsortiumUserTenantsRetriever; +import org.folio.service.settings.SettingsRetriever; +import org.folio.service.settings.util.SettingKey; +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 java.util.List; +import java.util.Optional; +import java.util.UUID; + +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.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(VertxExtension.class) +public class WrapperPieceStorageServiceTest { + + @Mock private ConsortiumConfigurationService consortiumConfigurationService; + @Mock private ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever; + @Mock private SettingsRetriever settingsRetriever; + @Mock private RestClient restClient; + @InjectMocks private WrapperPieceStorageService wrapperPieceStorageService; + + private AutoCloseable openedMocks; + + @BeforeEach + void setUp() { + openedMocks = MockitoAnnotations.openMocks(this); + + when(consortiumConfigurationService.getConsortiumConfiguration(any())) + .thenReturn(Future.succeededFuture(Optional.of(new ConsortiumConfiguration("diku", UUID.randomUUID().toString())))); + when(settingsRetriever.getSettingByKey(eq(SettingKey.CENTRAL_ORDERING_ENABLED), any())) + .thenReturn(Future.succeededFuture(Optional.of(new Setting().withValue("0")))); + when(consortiumUserTenantsRetriever.getUserTenants(anyString(), anyString(), any())) + .thenReturn(Future.succeededFuture()); + } + + @AfterEach + void tearDown() throws Exception { + openedMocks.close(); + } + + @Test + void testGetWrapperPieces_success(VertxTestContext testContext) { + var vendorId = UUID.randomUUID().toString(); + var pieceId = UUID.randomUUID().toString(); + var wrapperPieceCollection = new WrapperPieceCollection().withWrapperPieces( + List.of(new WrapperPiece().withVendorId(vendorId).withPiece(new Piece().withId(pieceId)))); + var requestContext = mock(RequestContext.class); + + when(restClient.get(any(RequestEntry.class), any(), any())) + .thenReturn(Future.succeededFuture(wrapperPieceCollection)); + + wrapperPieceStorageService.getWrapperPieces(10, 0, "", requestContext) + .onComplete(testContext.succeeding(result -> testContext.verify(() -> { + assertEquals(1, result.getWrapperPieces().size()); + assertEquals(vendorId, result.getWrapperPieces().get(0).getVendorId()); + assertEquals(pieceId, result.getWrapperPieces().get(0).getPiece().getId()); + testContext.completeNow(); + }))); + } + + @Test + void testGetWrapperPieces3Pieces_success(VertxTestContext testContext) { + var vendorId = UUID.randomUUID().toString(); + var pieceId1 = UUID.randomUUID().toString(); + var pieceId2 = UUID.randomUUID().toString(); + var pieceId3 = UUID.randomUUID().toString(); + var wrapperPieceCollection = new WrapperPieceCollection().withWrapperPieces(List.of( + new WrapperPiece().withVendorId(vendorId).withPiece(new Piece().withId(pieceId1)), + new WrapperPiece().withVendorId(vendorId).withPiece(new Piece().withId(pieceId2)), + new WrapperPiece().withVendorId(vendorId).withPiece(new Piece().withId(pieceId3)) + )); + var requestContext = mock(RequestContext.class); + + when(restClient.get(any(RequestEntry.class), any(), any())) + .thenReturn(Future.succeededFuture(wrapperPieceCollection)); + + wrapperPieceStorageService.getWrapperPieces(10, 0, "", requestContext) + .onComplete(testContext.succeeding(result -> testContext.verify(() -> { + assertEquals(3, result.getWrapperPieces().size()); + assertTrue(result.getWrapperPieces().stream() + .allMatch(wrapperPiece -> wrapperPiece.getVendorId().equals(vendorId))); + assertEquals(pieceId1, result.getWrapperPieces().get(0).getPiece().getId()); + assertEquals(pieceId2, result.getWrapperPieces().get(1).getPiece().getId()); + assertEquals(pieceId3, result.getWrapperPieces().get(2).getPiece().getId()); + testContext.completeNow(); + }))); + } + + @Test + void testGetWrapperPieceById_success(VertxTestContext testContext) { + var vendorId = UUID.randomUUID().toString(); + var pieceId = UUID.randomUUID().toString(); + var wrapperPiece = new WrapperPiece().withVendorId(vendorId).withPiece(new Piece().withId(pieceId)); + var requestContext = mock(RequestContext.class); + + when(restClient.get(any(RequestEntry.class), any(), any())) + .thenReturn(Future.succeededFuture(wrapperPiece)); + + wrapperPieceStorageService.getWrapperPieceById(pieceId, requestContext) + .onComplete(testContext.succeeding(result -> testContext.verify(() -> { + assertEquals(vendorId, result.getVendorId()); + assertEquals(pieceId, result.getPiece().getId()); + testContext.completeNow(); + }))); + } +} diff --git a/src/test/resources/mockdata/wrapper-pieces/wrapper-pieces-by-id.json b/src/test/resources/mockdata/wrapper-pieces/wrapper-pieces-by-id.json new file mode 100644 index 000000000..39f929445 --- /dev/null +++ b/src/test/resources/mockdata/wrapper-pieces/wrapper-pieces-by-id.json @@ -0,0 +1,136 @@ +{ + "vendorId": "e0fb5df2-cdf1-11e8-a8d5-f2801f1b9fd1", + "piece": { + "id": "dcd0ba36-b660-4751-b9fe-c8ac61ff6f99", + "format": "Physical", + "itemId": "db335665-5359-41ab-b530-f36c0967852f", + "poLineId": "d27efda0-0b01-4bc5-bf96-018598dc9a61", + "titleId": "07618e59-d9d9-4be0-b05b-7e471bca870e", + "holdingId": "4ae98814-da19-4f38-8483-6571970707d7", + "displayOnHolding": false, + "displayToPublic": false, + "receivingStatus": "Expected", + "isBound": false, + "statusUpdatedDate": "2025-01-21T08:08:52.883+00:00", + "metadata": { + "createdDate": "2025-01-21T08:08:52.877+00:00", + "createdByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "updatedDate": "2025-01-21T08:08:52.877+00:00", + "updatedByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10" + } + }, + "purchaseOrder": { + "id": "347aba4a-cdab-411d-880b-a63f8b696ec3", + "tags": { + "tagList": [ + "amazon" + ] + }, + "notes": [ + "Check credit card statement to make sure payment shows up" + ], + "billTo": "5f8a321e-6b38-4d90-92d4-bf08f91a2242", + "shipTo": "f7c36792-05f7-4c8c-969d-103ac6763187", + "vendor": "0b5e9284-fe9f-419d-b14e-319c7659165f", + "approved": true, + "manualPo": false, + "metadata": { + "createdDate": "2025-01-21T08:06:30.998Z", + "updatedDate": "2025-01-21T08:08:53.155Z", + "createdByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "updatedByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10" + }, + "poNumber": "10000", + "template": "4dee318b-f5b3-40dc-be93-cc89b8c45b6f", + "orderType": "One-Time", + "acqUnitIds": [], + "reEncumber": false, + "dateOrdered": "2025-01-21T08:08:50.849+00:00", + "approvalDate": "2025-01-21T08:08:50.848+00:00", + "approvedById": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "workflowStatus": "Open" + }, + "title": { + "id": "07618e59-d9d9-4be0-b05b-7e471bca870e", + "title": "Test 1", + "metadata": { + "createdDate": "2025-01-21T08:06:50.677Z", + "updatedDate": "2025-01-21T08:08:53.122Z", + "createdByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "updatedByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10" + }, + "poLineId": "d27efda0-0b01-4bc5-bf96-018598dc9a61", + "acqUnitIds": [], + "instanceId": "7068d630-3809-423f-9f6a-6742812dddfb", + "productIds": [], + "bindItemIds": [], + "contributors": [], + "poLineNumber": "10000-1", + "claimingActive": false, + "isAcknowledged": false + }, + "poLine": { + "id": "d27efda0-0b01-4bc5-bf96-018598dc9a61", + "cost": { + "currency": "USD", + "discountType": "percentage", + "listUnitPrice": 100.0, + "quantityPhysical": 1, + "poLineEstimatedPrice": 100.0 + }, + "rush": false, + "alerts": [], + "claims": [], + "source": "User", + "details": { + "productIds": [], + "isAcknowledged": false, + "isBinderyActive": false + }, + "metadata": { + "createdDate": "2025-01-21T08:06:50.595Z", + "updatedDate": "2025-01-21T08:08:53.096Z", + "createdByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "updatedByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10" + }, + "physical": { + "volumes": [], + "materialType": "1a54b431-2e4f-452d-9cae-9cee66c9a892", + "createInventory": "Instance, Holding, Item", + "materialSupplier": "e0fb5df2-cdf1-11e8-a8d5-f2801f1b9fd1" + }, + "isPackage": false, + "locations": [ + { + "quantity": 1, + "holdingId": "4ae98814-da19-4f38-8483-6571970707d7", + "quantityPhysical": 1 + } + ], + "collection": false, + "instanceId": "7068d630-3809-423f-9f6a-6742812dddfb", + "orderFormat": "Physical Resource", + "checkinItems": false, + "contributors": [], + "poLineNumber": "10000-1", + "vendorDetail": { + "instructions": "", + "vendorAccount": "1", + "referenceNumbers": [] + }, + "paymentStatus": "Awaiting Payment", + "receiptStatus": "Awaiting Receipt", + "claimingActive": false, + "reportingCodes": [], + "titleOrPackage": "Test 1", + "automaticExport": false, + "purchaseOrderId": "347aba4a-cdab-411d-880b-a63f8b696ec3", + "fundDistribution": [], + "acquisitionMethod": "306489dd-0053-49ee-a068-c316444a8f55", + "searchLocationIds": [ + "fcd64ce1-6995-48f0-840e-89ffa2288371" + ], + "donorOrganizationIds": [], + "cancellationRestriction": true + } +} diff --git a/src/test/resources/mockdata/wrapper-pieces/wrapper-pieces.json b/src/test/resources/mockdata/wrapper-pieces/wrapper-pieces.json new file mode 100644 index 000000000..4d570800a --- /dev/null +++ b/src/test/resources/mockdata/wrapper-pieces/wrapper-pieces.json @@ -0,0 +1,141 @@ +{ + "wrapperPieces": [ + { + "vendorId": "e0fb5df2-cdf1-11e8-a8d5-f2801f1b9fd1", + "piece": { + "id": "dcd0ba36-b660-4751-b9fe-c8ac61ff6f99", + "format": "Physical", + "itemId": "db335665-5359-41ab-b530-f36c0967852f", + "poLineId": "d27efda0-0b01-4bc5-bf96-018598dc9a61", + "titleId": "07618e59-d9d9-4be0-b05b-7e471bca870e", + "holdingId": "4ae98814-da19-4f38-8483-6571970707d7", + "displayOnHolding": false, + "displayToPublic": false, + "receivingStatus": "Expected", + "isBound": false, + "statusUpdatedDate": "2025-01-21T08:08:52.883+00:00", + "metadata": { + "createdDate": "2025-01-21T08:08:52.877+00:00", + "createdByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "updatedDate": "2025-01-21T08:08:52.877+00:00", + "updatedByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10" + } + }, + "purchaseOrder": { + "id": "347aba4a-cdab-411d-880b-a63f8b696ec3", + "tags": { + "tagList": [ + "amazon" + ] + }, + "notes": [ + "Check credit card statement to make sure payment shows up" + ], + "billTo": "5f8a321e-6b38-4d90-92d4-bf08f91a2242", + "shipTo": "f7c36792-05f7-4c8c-969d-103ac6763187", + "vendor": "0b5e9284-fe9f-419d-b14e-319c7659165f", + "approved": true, + "manualPo": false, + "metadata": { + "createdDate": "2025-01-21T08:06:30.998Z", + "updatedDate": "2025-01-21T08:08:53.155Z", + "createdByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "updatedByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10" + }, + "poNumber": "10000", + "template": "4dee318b-f5b3-40dc-be93-cc89b8c45b6f", + "orderType": "One-Time", + "acqUnitIds": [], + "reEncumber": false, + "dateOrdered": "2025-01-21T08:08:50.849+00:00", + "approvalDate": "2025-01-21T08:08:50.848+00:00", + "approvedById": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "workflowStatus": "Open" + }, + "title": { + "id": "07618e59-d9d9-4be0-b05b-7e471bca870e", + "title": "Test 1", + "metadata": { + "createdDate": "2025-01-21T08:06:50.677Z", + "updatedDate": "2025-01-21T08:08:53.122Z", + "createdByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "updatedByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10" + }, + "poLineId": "d27efda0-0b01-4bc5-bf96-018598dc9a61", + "acqUnitIds": [], + "instanceId": "7068d630-3809-423f-9f6a-6742812dddfb", + "productIds": [], + "bindItemIds": [], + "contributors": [], + "poLineNumber": "10000-1", + "claimingActive": false, + "isAcknowledged": false + }, + "poLine": { + "id": "d27efda0-0b01-4bc5-bf96-018598dc9a61", + "cost": { + "currency": "USD", + "discountType": "percentage", + "listUnitPrice": 100.0, + "quantityPhysical": 1, + "poLineEstimatedPrice": 100.0 + }, + "rush": false, + "alerts": [], + "claims": [], + "source": "User", + "details": { + "productIds": [], + "isAcknowledged": false, + "isBinderyActive": false + }, + "metadata": { + "createdDate": "2025-01-21T08:06:50.595Z", + "updatedDate": "2025-01-21T08:08:53.096Z", + "createdByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10", + "updatedByUserId": "13cf3da2-e87b-4719-aa19-9e751dee6c10" + }, + "physical": { + "volumes": [], + "materialType": "1a54b431-2e4f-452d-9cae-9cee66c9a892", + "createInventory": "Instance, Holding, Item", + "materialSupplier": "e0fb5df2-cdf1-11e8-a8d5-f2801f1b9fd1" + }, + "isPackage": false, + "locations": [ + { + "quantity": 1, + "holdingId": "4ae98814-da19-4f38-8483-6571970707d7", + "quantityPhysical": 1 + } + ], + "collection": false, + "instanceId": "7068d630-3809-423f-9f6a-6742812dddfb", + "orderFormat": "Physical Resource", + "checkinItems": false, + "contributors": [], + "poLineNumber": "10000-1", + "vendorDetail": { + "instructions": "", + "vendorAccount": "1", + "referenceNumbers": [] + }, + "paymentStatus": "Awaiting Payment", + "receiptStatus": "Awaiting Receipt", + "claimingActive": false, + "reportingCodes": [], + "titleOrPackage": "Test 1", + "automaticExport": false, + "purchaseOrderId": "347aba4a-cdab-411d-880b-a63f8b696ec3", + "fundDistribution": [], + "acquisitionMethod": "306489dd-0053-49ee-a068-c316444a8f55", + "searchLocationIds": [ + "fcd64ce1-6995-48f0-840e-89ffa2288371" + ], + "donorOrganizationIds": [], + "cancellationRestriction": true + } + } + ], + "totalRecords": 1 +}