From dd464b5ee64b6e98e4ebed8bae3301bccbff7c2a Mon Sep 17 00:00:00 2001 From: Janis Saldabols Date: Tue, 20 Aug 2024 13:52:03 +0300 Subject: [PATCH] PR-1906 Add ILL request id to ILS request and loan --- README.md | 38 +++--- src/main/java/org/folio/ncip/Constants.java | 8 ++ .../folio/ncip/FolioRemoteServiceManager.java | 122 +++++++++++++----- .../services/FolioRequestItemService.java | 18 +-- src/main/resources/init-values.properties | 7 + src/main/resources/ncip.properties | 2 + src/main/resources/toolkit.properties | 1 - .../java/org/folio/ncip/CheckOutItem.java | 21 +++ src/test/java/org/folio/ncip/MockServer.java | 14 ++ .../java/org/folio/ncip/NcipTestSuite.java | 2 +- src/test/resources/mockdata/loan-get.json | 57 ++++++++ .../resources/mockdata/ncip-checkout-full.xml | 26 ++++ src/test/resources/mockdata/ncip-configs.json | 28 ++++ .../mockdata/ncip-requestitem-title.xml | 2 +- .../resources/mockdata/noteTypes-get.json | 18 +++ utils/setNcipProperties_diku.py | 20 +++ 16 files changed, 317 insertions(+), 67 deletions(-) create mode 100644 src/test/java/org/folio/ncip/CheckOutItem.java create mode 100644 src/test/resources/mockdata/loan-get.json create mode 100644 src/test/resources/mockdata/ncip-checkout-full.xml create mode 100644 src/test/resources/mockdata/noteTypes-get.json diff --git a/README.md b/README.md index ec7e1f3..eebc77d 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ NISO Circulation Interchange Protocol (NCIP) support in FOLIO * (15) user.email.type (optional - will default to "electronic mail address" For LookupUser response) * (16) cancel.request.reason.name Reason for request cancellation when different item is checkout (Settings -> Circulation -> Request cancellation reasons) * (17) cancel.request.reason.patron.name Reason for request cancellation when patron did not checkout item (Settings -> Circulation -> Request cancellation reasons) + * (18) request.note.name Request note name. Default value "ILL note" + * (19) request.note.enabled Request note enabled. Will add ILL request ID to loan and ILS request. Default value "false" Notes * You can assign different values to these settings per Agency ID used in the NCIP requests. This approach lets you setup different values for different Agency IDs. For example, if Relais calls your NCIP server with the Agency ID of 'Relais' you can configure values for that agency. If ReShare calls your NCIP server using a different Agency ID, you can set up different configuration values to be used for ReShare requests. These settings have to exist for each Agency ID that will be used in the NCIP requests. @@ -137,23 +139,25 @@ There are three types of settings that can exist in mod-configuration for the NC #### NCIP Properties -| MODULE | configName (the AgencyID) | code | value (examples) | -| ------------- |:-------------:|:-------------------------------------|------------------:| -| NCIP | Relais | instance.type.name | RESHARE | -| NCIP | Relais | instance.source | RESHARE | -| NCIP | Relais | item.material.type.name | RESHARE | -| NCIP | Relais | item.perm.loan.type.name | RESHARE | -| NCIP | Relais | item.status.name | Available | -| NCIP | Relais | item.perm.location.code | RESHARE_DATALOGISK | -| NCIP | Relais | holdings.perm.location.code | RESHARE_DATALOGISK | -| NCIP | Relais | instance.custom.identifier.name | ReShare Request ID | -| NCIP | Relais | checkout.service.point.code | online | -| NCIP | Relais | checkin.service.point.code | online | -| NCIP | Relais | response.includes.physical.address | false | -| NCIP | Relais | user.priv.ok.status | OK | -| NCIP | Relais | user.priv.blocked.status | BLOCKED | -| NCIP | Relais | cancel.request.reason.name | Item Not Available | -| NCIP | Relais | cancel.request.reason.patron.name | Item Not Available | +| MODULE | configName (the AgencyID) | code | value (examples) | +|-----------|:-------------------------:|:-----------------------------------|-------------------:| +| NCIP | Relais | instance.type.name | RESHARE | +| NCIP | Relais | instance.source | RESHARE | +| NCIP | Relais | item.material.type.name | RESHARE | +| NCIP | Relais | item.perm.loan.type.name | RESHARE | +| NCIP | Relais | item.status.name | Available | +| NCIP | Relais | item.perm.location.code | RESHARE_DATALOGISK | +| NCIP | Relais | holdings.perm.location.code | RESHARE_DATALOGISK | +| NCIP | Relais | instance.custom.identifier.nam | ReShare Request ID | +| NCIP | Relais | checkout.service.point.code | online | +| NCIP | Relais | checkin.service.point.code | online | +| NCIP | Relais | response.includes.physical.address | false | +| NCIP | Relais | user.priv.ok.status | OK | +| NCIP | Relais | user.priv.blocked.status | BLOCKED | +| NCIP | Relais | cancel.request.reason.name | Item Not Available | +| NCIP | Relais | cancel.request.reason.patron.name | Item Not Available | +| NCIP | Relais | request.note.name | ILL note | +| NCIP | Relais | request.note.enabled | false | You will need a set of these settings in mod-configuration for each individual Agency ID making NCIP requests. Example of an AgencyID in an NCIP request: diff --git a/src/main/java/org/folio/ncip/Constants.java b/src/main/java/org/folio/ncip/Constants.java index e796cfb..565682f 100644 --- a/src/main/java/org/folio/ncip/Constants.java +++ b/src/main/java/org/folio/ncip/Constants.java @@ -3,6 +3,7 @@ public class Constants { public static final String BLOCKED = "BLOCKED"; public static final String OK = "OK"; + public static final String ID = "id"; public static final String AUTH_UID = "Username"; public static final String AUTH_BARCODE = "Barcode Id"; public static final String ACTIVE = "ACTIVE"; @@ -100,11 +101,18 @@ public class Constants { public static final String FEE_FINE_BY_OWNER_AND_TYPE = "/feefines?query=ownerId==$ownerId$%20and%20feeFineType=%22$feeType$%22&limit=1"; public static final String PATRON_GROUP_BY_ID = "/groups/"; public static final String ACCOUNT_URL = "/accounts"; + public static final String NOTES_URL = "/notes"; + public static final String ADD_STAFF_INFO_URL = "/circulation/loans/%s/add-info"; public static final String DEFAULT_TIMEOUT = "30000"; public static final String SERVICE_MGR_TIMEOUT = "service_manager_timeout_ms"; public static final String CHARGE_DEFAULT_PATRON_FEE = "charge-default-patron-fee"; public static final String DEFAULT_PAYMENT_STATUS = "Outstanding"; public static final String DEFAULT_FEE_STATUS = "Open"; + public static final String BOOLEAN_TRUE = "true"; + public static final String NOTE_DOMAIN_REQUESTS = "requests"; + public static final String NOTE_TITLE_TEMPLATE = "ILL Request id: %s"; + public static final String NOTE_LINK_TYPE_REQUEST = "request"; + public static final String STAFF_INFO_TYPE = "staffInfoAdded"; } diff --git a/src/main/java/org/folio/ncip/FolioRemoteServiceManager.java b/src/main/java/org/folio/ncip/FolioRemoteServiceManager.java index 39d8253..e52dc29 100644 --- a/src/main/java/org/folio/ncip/FolioRemoteServiceManager.java +++ b/src/main/java/org/folio/ncip/FolioRemoteServiceManager.java @@ -8,6 +8,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.concurrent.CompletionService; @@ -16,12 +17,15 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +import org.apache.commons.lang.StringUtils; import org.apache.http.entity.ContentType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.extensiblecatalog.ncip.v2.service.FiscalTransactionInformation; import org.extensiblecatalog.ncip.v2.service.RemoteServiceManager; +import org.extensiblecatalog.ncip.v2.service.RequestId; +import org.extensiblecatalog.ncip.v2.service.RequestItemInitiationData; import org.extensiblecatalog.ncip.v2.service.UserId; import org.folio.util.StringUtil; import org.folio.util.PercentCodec; @@ -53,7 +57,9 @@ public class FolioRemoteServiceManager implements RemoteServiceManager { private static final Logger logger = LogManager.getLogger(FolioRemoteServiceManager.class); - + private static final Map REQUEST_TYPE = Map.of("page", "Page", + "hold", "Hold", + "recall", "Recall"); private MultiMap okapiHeaders; private Properties ncipProperties; @@ -347,7 +353,7 @@ public JsonObject checkIn(CheckInItemInitiationData initData, String agencyId) t jsonObject.put("servicePointId", servicePoint); jsonObject.put("checkInDate", returnDate); jsonObject.put("itemBarcode", itemBarcode); - jsonObject.put("id", id.toString()); + jsonObject.put(Constants.ID, id.toString()); String url = baseUrl + Constants.CHECK_IN_BY_BARCODE; String checkInResponse = callApiPost(url, jsonObject); @@ -378,7 +384,7 @@ public JsonObject checkOut(CheckOutItemInitiationData initData, String agencyId) if (user == null) throw new FolioNcipException(Constants.USER_NOT_FOUND); - user = gatherPatronData(user, user.getString("id")); + user = gatherPatronData(user, user.getString(Constants.ID)); // DO MANUAL BLOCKS EXIST? JsonArray blocks = user.getJsonArray("manualblocks"); @@ -406,7 +412,7 @@ public JsonObject checkOut(CheckOutItemInitiationData initData, String agencyId) JsonObject jsonObject = new JsonObject(); jsonObject.put("itemBarcode", itemBarcode); jsonObject.put("userBarcode", user.getString("barcode")); - jsonObject.put("id", id.toString()); + jsonObject.put(Constants.ID, id.toString()); //jsonObject.put("loanDate", loanDate); //use default - current date/time jsonObject.put("servicePointId", servicePoint); @@ -414,6 +420,7 @@ public JsonObject checkOut(CheckOutItemInitiationData initData, String agencyId) try { String checkoutResponse = callApiPost(url, jsonObject); JsonObject checkoutResponseAsJson = new JsonObject(checkoutResponse); + addStaffInfoIfNeeded(agencyId, initData.getRequestId(), checkoutResponseAsJson.getString(Constants.ID), baseUrl); return checkoutResponseAsJson; } catch(Exception e) { @@ -438,6 +445,22 @@ public JsonObject checkOut(CheckOutItemInitiationData initData, String agencyId) } } + private void addStaffInfoIfNeeded(String agencyId, RequestId requestId, String loanUuid, String baseUrl){ + String noteEnabled = ncipProperties.getProperty(agencyId + ".request.note.enabled"); + if (Constants.BOOLEAN_TRUE.equalsIgnoreCase(noteEnabled) && requestId != null && + requestId.getRequestIdentifierValue() != null) { + JsonObject staffInfo = new JsonObject(); + staffInfo.put("action", Constants.STAFF_INFO_TYPE); + staffInfo.put("actionComment", String.format(Constants.NOTE_TITLE_TEMPLATE, requestId.getRequestIdentifierValue())); + try { + callApiPost(baseUrl + String.format(Constants.ADD_STAFF_INFO_URL, loanUuid), staffInfo); + } catch (Exception e) { + logger.error("Unable to add staff info to loan: {}", loanUuid); + logger.error(e.getMessage()); + } + } + } + /** * The acceptItem method: 1) creates an instance (bib) 2) creates a holding * record 3) creates an item 4) places the item on hold @@ -488,7 +511,7 @@ public JsonObject acceptItem(AcceptItemInitiationData initData, UserId userId, S JsonObject instance = new JsonObject(); instance.put("title", retreiveItemTitle(initData)); instance.put("instanceTypeId", ncipProperties.get(requesterAgencyId + ".instance.type.id")); - instance.put("id", instanceUuid.toString()); + instance.put(Constants.ID, instanceUuid.toString()); instance.put("source", ncipProperties.get(requesterAgencyId + ".instance.source")); instance.put("discoverySuppress", true); JsonArray identifiersArray = new JsonArray(); @@ -506,7 +529,7 @@ public JsonObject acceptItem(AcceptItemInitiationData initData, UserId userId, S // CALL HOLDINGS API: JsonObject holdings = new JsonObject(); String holdingsPermLocation = ncipProperties.getProperty(requesterAgencyId + ".holdings.perm.location.id"); - holdings.put("id", holdingsUuid.toString()); + holdings.put(Constants.ID, holdingsUuid.toString()); holdings.put("sourceId", ncipProperties.get(requesterAgencyId + ".holdings.source.id")); holdings.put("instanceId", instanceUuid.toString()); holdings.put("discoverySuppress", true); @@ -520,19 +543,19 @@ public JsonObject acceptItem(AcceptItemInitiationData initData, UserId userId, S String itemLocation = ncipProperties.getProperty(requesterAgencyId + ".item.perm.location.id"); ItemId itemId = initData.getItemId(); JsonObject item = new JsonObject(); - item.put("id", itemUuid.toString()); + item.put(Constants.ID, itemUuid.toString()); item.put("holdingsRecordId", holdingsUuid.toString()); item.put("discoverySuppress", true); item.put("itemLevelCallNumber", itemId.getItemIdentifierValue()); // PLACE HOLD DOES NOT WORK UNLESS THE ITEM HAS A PERM LOCATION JsonObject permLocation = new JsonObject(); - permLocation.put("id", itemLocation); + permLocation.put(Constants.ID, itemLocation); JsonObject materialType = new JsonObject(); - materialType.put("id", ncipProperties.getProperty(requesterAgencyId + ".item.material.type.id")); + materialType.put(Constants.ID, ncipProperties.getProperty(requesterAgencyId + ".item.material.type.id")); JsonObject status = new JsonObject(); status.put("name", itemStatusName); JsonObject permLoanType = new JsonObject(); - permLoanType.put("id", ncipProperties.getProperty(requesterAgencyId + ".item.perm.loan.type.id")); + permLoanType.put(Constants.ID, ncipProperties.getProperty(requesterAgencyId + ".item.perm.loan.type.id")); item.put("status", status); item.put("materialType", materialType); @@ -549,7 +572,7 @@ public JsonObject acceptItem(AcceptItemInitiationData initData, UserId userId, S // FOR EXPLAINATION ABOUT HARDCODE FULFILLMENT // SEE NOTES.TXT request.put("fulfillmentPreference", "Hold Shelf"); - String uid = user.getString("id"); + String uid = user.getString(Constants.ID); request.put("requesterId", uid); request.put("itemId", itemUuid.toString()); request.put("instanceId", holdings.getString("instanceId")); @@ -566,7 +589,8 @@ public JsonObject acceptItem(AcceptItemInitiationData initData, UserId userId, S returnValues.mergeIn(new JsonObject(requestRespone)).put("item", new JsonObject(itemResponse)) .put("holdings", new JsonObject(holdingsResponse)); - addDefaultPatronFee(initData.getFiscalTransactionInformation(), user.getString("id"), user.getString(Constants.PATRON_GROUP), baseUrl); + addDefaultPatronFee(initData.getFiscalTransactionInformation(), user.getString(Constants.ID), user.getString(Constants.PATRON_GROUP), baseUrl); + addNoteIfNeeded(requesterAgencyId, returnValues.getString(Constants.ID), initData.getRequestId().getRequestIdentifierValue(), baseUrl, false); } catch (Exception e) { // IF ANY OF THE ABOVE FAILED - ATTEMPT TO DELETE THE INSTANCE, HOLDINGS ITEM // THAT MAY HAVE BEEN CREATED ALONG THE WAY @@ -586,7 +610,7 @@ protected JsonObject addDefaultPatronFee(FiscalTransactionInformation fiscalTran if (ownersArray.isEmpty()) { throw new FolioNcipException("Failed to find fee owner Reshare-ILL"); } - String ownerId = ownersArray.getJsonObject(0).getString("id"); + String ownerId = ownersArray.getJsonObject(0).getString(Constants.ID); String patronGroup = new JsonObject(callApiGet(baseUrl + Constants.PATRON_GROUP_BY_ID + patronGroupId)) .getString("group"); if (patronGroup == null) { @@ -600,7 +624,7 @@ protected JsonObject addDefaultPatronFee(FiscalTransactionInformation fiscalTran JsonObject fee = fees.getJsonArray("feefines").getJsonObject(0); JsonObject charge = new JsonObject(); charge.put("ownerId", ownerId); - charge.put("feeFineId", fee.getString("id")); + charge.put("feeFineId", fee.getString(Constants.ID)); charge.put("amount", fee.getValue("defaultAmount")); JsonObject paymentStatus = new JsonObject(); paymentStatus.put("name", Constants.DEFAULT_PAYMENT_STATUS); @@ -612,7 +636,7 @@ protected JsonObject addDefaultPatronFee(FiscalTransactionInformation fiscalTran charge.put("feeFineType", fee.getString("feeFineType")); charge.put("feeFineOwner", ownersArray.getJsonObject(0).getString("owner")); charge.put("userId", userId); - charge.put("id", UUID.randomUUID().toString()); + charge.put(Constants.ID, UUID.randomUUID().toString()); if (fiscalTransactionInformation.getItemDetails() != null && fiscalTransactionInformation.getItemDetails().getItemId() != null && fiscalTransactionInformation.getItemDetails().getItemId().getItemIdentifierValue() != null) { charge.put("barcode", fiscalTransactionInformation.getItemDetails().getItemId().getItemIdentifierValue()); @@ -661,13 +685,20 @@ private String getServicePointId(String pickUpLocationCode, String baseUrl) thro JsonObject servicePoints = new JsonObject(servicePointResponse); if (servicePoints.getJsonArray("servicepoints").size() == 0) throw new FolioNcipException("pickup location code note found: " + pickUpLocationCode); - return servicePoints.getJsonArray("servicepoints").getJsonObject(0).getString("id"); + return servicePoints.getJsonArray("servicepoints").getJsonObject(0).getString(Constants.ID); } + public JsonObject requestItem(RequestItemInitiationData initData) throws Exception { + String hrid = initData.getBibliographicId(0).getBibliographicRecordId().getBibliographicRecordIdentifier(); + final boolean titleRequest = initData.getRequestScopeType() != null && initData.getRequestScopeType().getValue().toLowerCase().contains("title"); + final String requestType = REQUEST_TYPE.getOrDefault(initData.getRequestType().getValue().toLowerCase(), "Page"); + String pickUpLocationCode = null; + if (initData.getPickupLocation() != null && StringUtils.isNotBlank(initData.getPickupLocation().getValue())) { + pickUpLocationCode = initData.getPickupLocation().getValue(); + + } - public JsonObject requestItem(String hrid, UserId userId, boolean titleRequest, String requestType, - String pickUpLocationCode) throws Exception { JsonObject returnValues = new JsonObject(); - JsonObject user = lookupPatronRecord(userId); + JsonObject user = lookupPatronRecord(initData.getUserId()); if (user == null) throw new FolioNcipException(Constants.USER_NOT_FOUND); try { @@ -683,21 +714,21 @@ public JsonObject requestItem(String hrid, UserId userId, boolean titleRequest, JsonObject request = new JsonObject(); if (titleRequest) { JsonObject instanceObject = response.getJsonArray("instances").getJsonObject(0); - request.put("instanceId", instanceObject.getString("id")); + request.put("instanceId", instanceObject.getString(Constants.ID)); request.put("requestLevel", "Title"); } else { JsonObject itemObject = response.getJsonArray("items").getJsonObject(0); String holdingsUrl = baseUrl + Constants.HOLDINGS_URL + "/" + itemObject.getString("holdingsRecordId"); String holdingResponseString = callApiGet(holdingsUrl); JsonObject holdingResponse = new JsonObject(holdingResponseString); - request.put("itemId", itemObject.getString("id")); + request.put("itemId", itemObject.getString(Constants.ID)); request.put("instanceId", holdingResponse.getString("instanceId")); - request.put("holdingsRecordId", holdingResponse.getString("id")); + request.put("holdingsRecordId", holdingResponse.getString(Constants.ID)); request.put("requestLevel", "Item"); } request.put("requestType", requestType); request.put("fulfillmentPreference", "Delivery"); - request.put("requesterId", user.getString("id")); + request.put("requesterId", user.getString(Constants.ID)); request.put("requestDate", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now())); if (pickUpLocationCode != null) { String servicePointId = getServicePointId(pickUpLocationCode, baseUrl); @@ -708,6 +739,8 @@ public JsonObject requestItem(String hrid, UserId userId, boolean titleRequest, String requestResponse = callApiPost(requestUrl, request); returnValues.mergeIn(new JsonObject(requestResponse)); + addNoteIfNeeded(initData.getInitiationHeader().getFromAgencyId().getAgencyId().getValue(), + returnValues.getString(Constants.ID), initData.getRequestId().getRequestIdentifierValue(), baseUrl, true); } else { logger.error("Found total of " + totalRecords + " items by hrid " + hrid); throw new FolioNcipException(Constants.REQUEST_ITEM_MISSING_PROBLEM); @@ -720,6 +753,33 @@ public JsonObject requestItem(String hrid, UserId userId, boolean titleRequest, return returnValues; } + private void addNoteIfNeeded(String agencyId, String requestUuid, String illRequestId, String baseUrl, boolean initProperties) { + try { + agencyId = agencyId == null ? agencyId : agencyId.toLowerCase(); + if (initProperties) { + initProperties(agencyId, baseUrl); + } + + String noteEnabled = ncipProperties.getProperty(agencyId + ".request.note.enabled"); + if (Constants.BOOLEAN_TRUE.equalsIgnoreCase(noteEnabled)) { + JsonObject note = new JsonObject(); + note.put("domain", Constants.NOTE_DOMAIN_REQUESTS); + note.put("typeId", ncipProperties.getProperty(agencyId + ".request.note.id")); + note.put("title", String.format(Constants.NOTE_TITLE_TEMPLATE, illRequestId)); + JsonArray links = new JsonArray(); + JsonObject link = new JsonObject(); + link.put("type", Constants.NOTE_LINK_TYPE_REQUEST); + link.put(Constants.ID, requestUuid); + links.add(link); + note.put("links", links); + callApiPost(baseUrl + Constants.NOTES_URL, note); + } + } catch (Exception e) { + logger.error("Failed to add request note to request: {}", requestUuid); + logger.error(e.getMessage()); + } + } + private String retreiveItemTitle(AcceptItemInitiationData initData) { String title = ""; try { @@ -839,7 +899,7 @@ private void initProperties(String requesterAgencyId, String baseUrl) throws Exc while (i.hasNext()) { JSONObject setting = (JSONObject) i.next(); String lookup = (String) setting.get("lookup"); - String id = (String) setting.get("id"); + String id = (String) setting.get(Constants.ID); String url = (String) setting.get("url"); String returnArray = (String) setting.get("returnArray"); String identifier = (String) setting.get("identifier"); @@ -980,7 +1040,7 @@ public JsonObject lookupUser(UserId userid) throws Exception { JsonObject user = lookupPatronRecord(userid); if (user == null) return user; - String id = user.getString("id"); + String id = user.getString(Constants.ID); user = gatherPatronData(user, id); return user; } @@ -990,7 +1050,7 @@ public void checkUserPin(String userId, String pin) throws FolioNcipException { String baseUrl = okapiHeaders.get(Constants.X_OKAPI_URL); String pinApiUri = baseUrl + Constants.PATRON_PIN_VERIFY; JsonObject request = new JsonObject(); - request.put("id", userId); + request.put(Constants.ID, userId); request.put("pin", pin); callApiPost(pinApiUri, request); } catch (IOException | FolioNcipException e) { @@ -1021,7 +1081,7 @@ public JsonObject cancelRequestItem(String requestId, UserId userId, String agen try { JsonObject requestResponse = new JsonObject(callApiGet(url)); requestResponse.put("status", Constants.REQUEST_CANCELLED_STATUS); - requestResponse.put("cancelledByUserId", user.getString("id")); + requestResponse.put("cancelledByUserId", user.getString(Constants.ID)); requestResponse.put("cancellationReasonId", reasonId); requestResponse.put("cancellationAdditionalInformation", Constants.REQUEST_CANCEL_ADDITIONAL_INFO); requestResponse.put("cancelledDate", date); @@ -1051,7 +1111,7 @@ public void deleteItem(String itemId, String agencyId) throws Exception { Integer totalRecords = itemResponse.getInteger("totalRecords"); if (totalRecords == 1) { // There is item JsonObject itemObject = itemResponse.getJsonArray("items").getJsonObject(0); - String itemUuid = itemObject.getString("id"); + String itemUuid = itemObject.getString(Constants.ID); String holdingsRecordId = itemObject.getString("holdingsRecordId"); // Search open requests @@ -1069,14 +1129,14 @@ public void deleteItem(String itemId, String agencyId) throws Exception { requests.forEach(r -> { JsonObject requestObject = (JsonObject) r; try { - String url = baseUrl + Constants.REQUEST_URL + "/" + requestObject.getString("id"); + String url = baseUrl + Constants.REQUEST_URL + "/" + requestObject.getString(Constants.ID); requestObject.put("status", Constants.REQUEST_CANCELLED_STATUS); requestObject.put("cancellationReasonId", reasonId); requestObject.put("cancellationAdditionalInformation", Constants.REQUEST_CANCEL_PATRON_ADDITIONAL_INFO); requestObject.put("cancelledDate", getDateTimeNowString()); callApiPut(url, requestObject); } catch (Exception e){ - logger.error("Could not cancel request {}", requestObject.getString("id")); + logger.error("Could not cancel request {}", requestObject.getString(Constants.ID)); } }); } @@ -1097,7 +1157,7 @@ public JsonObject createUserFiscalTransaction(UserId userId, FiscalTransactionIn try { String baseUrl = okapiHeaders.get(Constants.X_OKAPI_URL); JsonObject user = lookupPatronRecord(userId); - return addDefaultPatronFee(fiscalTransactionInformation, user.getString("id"), user.getString(Constants.PATRON_GROUP), baseUrl); + return addDefaultPatronFee(fiscalTransactionInformation, user.getString(Constants.ID), user.getString(Constants.PATRON_GROUP), baseUrl); } catch (Exception exception) { logger.error("Exception occurred during CreateUserFiscalTransaction"); throw exception; diff --git a/src/main/java/org/folio/ncip/services/FolioRequestItemService.java b/src/main/java/org/folio/ncip/services/FolioRequestItemService.java index b36f552..3a796f3 100644 --- a/src/main/java/org/folio/ncip/services/FolioRequestItemService.java +++ b/src/main/java/org/folio/ncip/services/FolioRequestItemService.java @@ -1,7 +1,6 @@ package org.folio.ncip.services; import io.vertx.core.json.JsonObject; -import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.extensiblecatalog.ncip.v2.service.BibliographicId; import org.extensiblecatalog.ncip.v2.service.ItemDescription; @@ -33,16 +32,11 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; -import java.util.Map; public class FolioRequestItemService extends FolioNcipService implements RequestItemService { private static final Logger logger = Logger.getLogger(FolioRequestItemService.class); - private static final Map REQUEST_TYPE = Map.of("page", "Page", - "hold", "Hold", - "recall", "Recall"); - @Override public RequestItemResponseData performService(RequestItemInitiationData initData, ServiceContext serviceContext, RemoteServiceManager serviceManager) { @@ -56,6 +50,7 @@ public RequestItemResponseData performService(RequestItemInitiationData initData validateUserId(userId); validateBibliographicIdIsPresent(bibliographicId); initData.getRequestType().getValue(); + validateRequestIdIsPresent(initData.getRequestId()); } catch(Exception exception) { logger.error("Failed validating userId and itemId. " + exception.getLocalizedMessage()); @@ -63,22 +58,13 @@ public RequestItemResponseData performService(RequestItemInitiationData initData exception.getMessage(),exception.getMessage())); } - String pickUpLocationCode = null; - if (initData.getPickupLocation() != null && StringUtils.isNotBlank(initData.getPickupLocation().getValue())) { - pickUpLocationCode = initData.getPickupLocation().getValue(); - - } - - final boolean titleRequest = initData.getRequestScopeType() != null && initData.getRequestScopeType().getValue().toLowerCase().contains("title"); - final String requestType = REQUEST_TYPE.getOrDefault(initData.getRequestType().getValue().toLowerCase(), "Page"); ItemId itemId = new ItemId(); RequestId ncipRequestId = new RequestId(); ItemDescription itemDescription = new ItemDescription(); LocationNameInstance locationNameInstance = new LocationNameInstance(); UserId optionalUserId = new UserId(); try { - JsonObject requestItemResponseDetails = ((FolioRemoteServiceManager)serviceManager) - .requestItem(bibliographicId.getBibliographicRecordId().getBibliographicRecordIdentifier(), userId, titleRequest, requestType, pickUpLocationCode); + JsonObject requestItemResponseDetails = ((FolioRemoteServiceManager)serviceManager).requestItem(initData); String assignedRequestId = requestItemResponseDetails.getString("id"); String requesterId = requestItemResponseDetails.getString("requesterId"); String barcode = null; diff --git a/src/main/resources/init-values.properties b/src/main/resources/init-values.properties index 7e3b685..08641d3 100644 --- a/src/main/resources/init-values.properties +++ b/src/main/resources/init-values.properties @@ -79,6 +79,13 @@ "url": "/cancellation-reason-storage/cancellation-reasons?query=(name=%22{lookup}%22)&limit=1", "returnArray": "cancellationReasons", "identifier": "id" + }, + { + "lookup": "request.note.name", + "id": "request.note.id", + "url": "/note-types?query=(name=%22{lookup}%22)&limit=1", + "returnArray": "noteTypes", + "identifier": "id" } ] } \ No newline at end of file diff --git a/src/main/resources/ncip.properties b/src/main/resources/ncip.properties index 0e567e8..8e510d5 100644 --- a/src/main/resources/ncip.properties +++ b/src/main/resources/ncip.properties @@ -20,3 +20,5 @@ user.email.type=electronic mail address # Cancel request cancel.request.reason.name cancel.request.reason.patron.name +request.note.name=ILL note +request.note.enabled=false diff --git a/src/main/resources/toolkit.properties b/src/main/resources/toolkit.properties index 5028a2d..59ea4f6 100644 --- a/src/main/resources/toolkit.properties +++ b/src/main/resources/toolkit.properties @@ -1,5 +1,4 @@ ToolkitConfiguration.PropertiesFileTitle=ExamplesMain -TranslatorConfiguration.LogMessages=false NCIPServiceValidatorConfiguration.SchemaURLs= NCIPServiceValidatorConfiguration.AddDefaultNamespaceURI=true ToolkitConfiguration.AppName=FolioNcipResponder diff --git a/src/test/java/org/folio/ncip/CheckOutItem.java b/src/test/java/org/folio/ncip/CheckOutItem.java new file mode 100644 index 0000000..13c035f --- /dev/null +++ b/src/test/java/org/folio/ncip/CheckOutItem.java @@ -0,0 +1,21 @@ +package org.folio.ncip; + +import io.restassured.response.Response; +import org.junit.Test; + +import java.net.MalformedURLException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class CheckOutItem extends TestBase { + + private static final String ITEM_ID = "c7369b8e-a573-45e4-b2ab-9c39e7fe04a4"; + @Test + public void callCheckOutItem() throws MalformedURLException { + Response response = postData("src/test/resources/mockdata/ncip-checkout-full.xml"); + String body = response.getBody().prettyPrint(); + assertEquals(200, response.getStatusCode()); + assertTrue(body.contains(ITEM_ID)); + } +} diff --git a/src/test/java/org/folio/ncip/MockServer.java b/src/test/java/org/folio/ncip/MockServer.java index 1faac23..240959e 100644 --- a/src/test/java/org/folio/ncip/MockServer.java +++ b/src/test/java/org/folio/ncip/MockServer.java @@ -130,6 +130,8 @@ private Router defineRoutes() { router.get("/owners").handler(this::getFeeOwner); router.get("/feefines").handler(this::findFee); router.post("/accounts").handler(this::postAccounts); + router.get("/note-types").handler(this::getNoteTypes); + router.post("/circulation/check-out-by-barcode").handler(this::postLoan); return router; } @@ -338,6 +340,18 @@ private void postAccounts(RoutingContext ctx) { serverResponse(ctx, 200, APPLICATION_JSON, body); } + private void getNoteTypes(RoutingContext ctx) { + String mockFileName = TestConstants.PATH_TO_MOCK_FILES + "noteTypes-get.json"; + String body = readLineByLine(mockFileName); + serverResponse(ctx, 200, APPLICATION_JSON, body); + } + + private void postLoan(RoutingContext ctx) { + String mockFileName = TestConstants.PATH_TO_MOCK_FILES + "loan-get.json"; + String body = readLineByLine(mockFileName); + serverResponse(ctx, 200, APPLICATION_JSON, body); + } + private void serverResponse(RoutingContext ctx, int statusCode, String contentType, String body) { ctx.response() .setStatusCode(statusCode) diff --git a/src/test/java/org/folio/ncip/NcipTestSuite.java b/src/test/java/org/folio/ncip/NcipTestSuite.java index 080c841..04c56bb 100644 --- a/src/test/java/org/folio/ncip/NcipTestSuite.java +++ b/src/test/java/org/folio/ncip/NcipTestSuite.java @@ -14,7 +14,7 @@ @RunWith(Suite.class) @Suite.SuiteClasses({ - LookupUser.class, RequestItem.class, CancelRequestItem.class, DeleteItem.class, AcceptItem.class, CreateUserFiscalTransaction.class + LookupUser.class, RequestItem.class, CancelRequestItem.class, DeleteItem.class, AcceptItem.class, CreateUserFiscalTransaction.class, CheckOutItem.class }) public class NcipTestSuite { diff --git a/src/test/resources/mockdata/loan-get.json b/src/test/resources/mockdata/loan-get.json new file mode 100644 index 0000000..59f7c8f --- /dev/null +++ b/src/test/resources/mockdata/loan-get.json @@ -0,0 +1,57 @@ +{ + "id" : "c7369b8e-a573-45e4-b2ab-9c39e7fe04a4", + "userId" : "99999999-9999-1999-8999-999999999901", + "itemId" : "b195771a-6c54-43dc-802e-b20cbc5b34ff", + "status" : { + "name" : "Open" + }, + "loanDate" : "2019-10-07T20:16:19Z", + "dueDate" : "2020-01-14T23:59:59.000+0000", + "action" : "checkedout", + "loanPolicyId" : "b3d3e18a-9bf4-401c-a5b3-b88dd5653858", + "checkoutServicePointId" : "79faacf1-4ba4-42c7-8b2a-566b259e4641", + "metadata" : { + "createdDate" : "2019-10-07T20:16:20.501+0000", + "createdByUserId" : "99999999-9999-1999-8999-999999999901", + "updatedDate" : "2019-10-07T20:16:20.501+0000", + "updatedByUserId" : "99999999-9999-1999-8999-999999999901" + }, + "patronGroupAtCheckout" : { + "id" : "dac3aab4-7a86-422b-b5fc-6fbf7afd7aeb", + "name" : "GRAD" + }, + "item" : { + "id" : "b195771a-6c54-43dc-802e-b20cbc5b34ff", + "holdingsRecordId" : "09065bab-34dd-438e-95be-79eccc1f95aa", + "instanceId" : "97e40763-b77c-4fe5-baf5-4d2655e92add", + "title" : "TEST ONE", + "barcode" : "LEH-20191003225", + "callNumber" : "LEH-20191003225", + "status" : { + "name" : "Checked out" + }, + "location" : { + "name" : "PALCI Shelf" + }, + "materialType" : { + "name" : "PALCI" + } + }, + "checkoutServicePoint" : { + "name" : "Linderman", + "code" : "LINDERMAN", + "discoveryDisplayName" : "Linderman", + "description" : null, + "shelvingLagTime" : 10, + "pickupLocation" : true + }, + "borrower" : { + "firstName" : "Doe", + "lastName" : "Jane", + "middleName" : null, + "barcode" : "8377630" + }, + "loanPolicy" : { + "name" : "Example Loan Policy" + } +} \ No newline at end of file diff --git a/src/test/resources/mockdata/ncip-checkout-full.xml b/src/test/resources/mockdata/ncip-checkout-full.xml new file mode 100644 index 0000000..2cc6cdc --- /dev/null +++ b/src/test/resources/mockdata/ncip-checkout-full.xml @@ -0,0 +1,26 @@ + + + + + + relais + + + other + + EZBORROW + + + relais + slnp_one_inst_user + + + relais + tl-a11 + + + relais + js-test-0015 + + + \ No newline at end of file diff --git a/src/test/resources/mockdata/ncip-configs.json b/src/test/resources/mockdata/ncip-configs.json index cfe2089..8f59960 100644 --- a/src/test/resources/mockdata/ncip-configs.json +++ b/src/test/resources/mockdata/ncip-configs.json @@ -182,6 +182,34 @@ "updatedDate": "2020-03-06T20:24:55.479+0000", "updatedByUserId": "99999999-9999-1999-8999-999999999901" } + }, + { + "id": "a07bc5a3-7e26-485d-a09a-e72ea4dbdbe7", + "module": "NCIP", + "configName": "Relais", + "code": "request.note.name", + "enabled": true, + "value": "ILL Note", + "metadata": { + "createdDate": "2020-03-06T20:24:55.479+0000", + "createdByUserId": "99999999-9999-1999-8999-999999999901", + "updatedDate": "2020-03-06T20:24:55.479+0000", + "updatedByUserId": "99999999-9999-1999-8999-999999999901" + } + }, + { + "id": "a07bc5a3-7e26-485d-a09a-e72ea4dbdbe8", + "module": "NCIP", + "configName": "Relais", + "code": "request.note.enabled", + "enabled": true, + "value": "true", + "metadata": { + "createdDate": "2020-03-06T20:24:55.479+0000", + "createdByUserId": "99999999-9999-1999-8999-999999999901", + "updatedDate": "2020-03-06T20:24:55.479+0000", + "updatedByUserId": "99999999-9999-1999-8999-999999999901" + } } ], "totalRecords": 14, diff --git a/src/test/resources/mockdata/ncip-requestitem-title.xml b/src/test/resources/mockdata/ncip-requestitem-title.xml index 529437a..ca98acf 100644 --- a/src/test/resources/mockdata/ncip-requestitem-title.xml +++ b/src/test/resources/mockdata/ncip-requestitem-title.xml @@ -2,7 +2,7 @@ - Relais + relais Lehigh University diff --git a/src/test/resources/mockdata/noteTypes-get.json b/src/test/resources/mockdata/noteTypes-get.json new file mode 100644 index 0000000..8a00260 --- /dev/null +++ b/src/test/resources/mockdata/noteTypes-get.json @@ -0,0 +1,18 @@ +{ + "noteTypes": [ + { + "id": "2dc9d2a3-0247-4c19-bdea-604dc5fecc30", + "name": "ILL note", + "usage": { + "isAssigned": false + }, + "metadata": { + "createdDate": "2024-08-20T07:36:44.931073Z", + "createdByUserId": "7d265701-9b80-5501-bc85-0758b4667022", + "updatedDate": "2024-08-20T07:36:44.931073Z", + "updatedByUserId": "7d265701-9b80-5501-bc85-0758b4667022" + } + } + ], + "totalRecords": 1 +} \ No newline at end of file diff --git a/utils/setNcipProperties_diku.py b/utils/setNcipProperties_diku.py index 839c5fb..cc5e442 100644 --- a/utils/setNcipProperties_diku.py +++ b/utils/setNcipProperties_diku.py @@ -195,3 +195,23 @@ response = requests.post(url + "/configurations/entries",the_data,headers=headers) print(response) +configuration = {} +configuration['configName'] = "ReShare" #AGENCY ID +configuration['code'] = "request.note.name" +configuration['value'] = "ILL note" +configuration['module'] = "NCIP" +the_data = json.dumps(configuration) +print(the_data) +response = requests.post(url + "/configurations/entries",the_data,headers=headers) +print(response) + +configuration = {} +configuration['configName'] = "ReShare" #AGENCY ID +configuration['code'] = "request.note.enabled" +configuration['value'] = "false" +configuration['module'] = "NCIP" +the_data = json.dumps(configuration) +print(the_data) +response = requests.post(url + "/configurations/entries",the_data,headers=headers) +print(response) +