From c0096a748923be076bf2c8a852ff1fd42519c2e2 Mon Sep 17 00:00:00 2001
From: Josef Moravec <josef.moravec@gmail.com>
Date: Thu, 10 Sep 2020 13:17:00 +0200
Subject: [PATCH] Significant improvements to XCNCIP2 ILS driver (#1672)

- Adds tests and fixes issues revealed by testing
- Improves code style
- Adds new functionality, including configuration settings for "fromAgency" and optional location loading via "Lookup Agency" service.
---
 config/vufind/XCNCIP2.ini                     |    9 +
 .../src/VuFind/ILS/Driver/PluginManager.php   |    2 +-
 .../VuFind/src/VuFind/ILS/Driver/XCNCIP2.php  | 1614 ++++++++++-------
 .../request/CancelRequestItemRequest.xml      |    1 +
 .../CancelRequestItemRequestAuthInput.xml     |    1 +
 .../xcncip2/request/LookupAgencyRequest.xml   |    1 +
 .../xcncip2/request/LookupItemRequest.xml     |    1 +
 .../xcncip2/request/LookupItemSetRequest.xml  |    1 +
 .../LookupItemSetRequestWithoutHeader.xml     |    1 +
 .../xcncip2/request/LookupUserRequest.xml     |    1 +
 .../xcncip2/request/RenewItemRequest.xml      |    1 +
 .../xcncip2/request/RequestItemRequest.xml    |    1 +
 .../CancelRequestItemResponseAccepted.xml     |   19 +
 .../CancelRequestItemResponseDenied.xml       |   14 +
 ...celRequestItemResponseDeniedWithUserId.xml |   39 +
 .../xcncip2/response/LookupAgencyResponse.xml |   55 +
 .../LookupAgencyResponseWithoutLocations.xml  |   40 +
 .../fixtures/xcncip2/response/LookupItem.xml  |   56 +
 ...UserResponseWithoutNamespaceDefinition.xml |  383 ++++
 ...okupUserResponseWithoutNamespacePrefix.xml |  383 ++++
 .../response/RenewItemResponseAccepted.xml    |   24 +
 ...mResponseAcceptedAlternativeDateFormat.xml |   23 +
 .../response/RenewItemResponseDenied.xml      |   14 +
 .../RenewItemResponseDeniedInvalidMessage.xml |   11 +
 .../RequestItemResponseAcceptedWithItemId.xml |   21 +
 ...questItemResponseAcceptedWithRequestId.xml |   22 +
 .../response/RequestItemResponseDenied.xml    |   14 +
 .../RequestItemResponseDeniedEmpty.xml        |    5 +
 ...temResponseDeniedNotFullProblemElement.xml |   12 +
 ...questItemResponseDeniedWithIdentifiers.xml |   38 +
 .../xcncip2/response/lookupItemSet.xml        |  174 ++
 .../lookupItemSetWithoutNamespacePrefix.xml   |  173 ++
 .../xcncip2/response/lookupUserResponse.xml   |  401 ++++
 .../src/VuFindTest/ILS/Driver/XCNCIP2Test.php | 1265 ++++++++++++-
 34 files changed, 4202 insertions(+), 618 deletions(-)
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/CancelRequestItemRequest.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/CancelRequestItemRequestAuthInput.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/LookupAgencyRequest.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/LookupItemRequest.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/LookupItemSetRequest.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/LookupItemSetRequestWithoutHeader.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/LookupUserRequest.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/RenewItemRequest.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/request/RequestItemRequest.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseAccepted.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseDenied.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseDeniedWithUserId.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/LookupAgencyResponse.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/LookupAgencyResponseWithoutLocations.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/LookupItem.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/LookupUserResponseWithoutNamespaceDefinition.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/LookupUserResponseWithoutNamespacePrefix.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseAccepted.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseAcceptedAlternativeDateFormat.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseDenied.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseDeniedInvalidMessage.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseAcceptedWithItemId.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseAcceptedWithRequestId.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDenied.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedEmpty.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedNotFullProblemElement.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedWithIdentifiers.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/lookupItemSet.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/lookupItemSetWithoutNamespacePrefix.xml
 create mode 100644 module/VuFind/tests/fixtures/xcncip2/response/lookupUserResponse.xml

diff --git a/config/vufind/XCNCIP2.ini b/config/vufind/XCNCIP2.ini
index f94e3f5ce61..1a3fc880d01 100644
--- a/config/vufind/XCNCIP2.ini
+++ b/config/vufind/XCNCIP2.ini
@@ -5,6 +5,9 @@ url         = http://myuniversity.edu:8080/ncipv2/NCIPResponder
 ; Your library's Agency ID (ILSDefaultAgency setting in driver_config.properties):
 agency      = "My University"
 
+; Agency associated with your VuFind instance
+;fromAgency = 'My discovery portal'
+
 ; Pickup location definitions: CSV file
 ;
 ;     Format: [agency],[locationID],[locationDisplay]
@@ -22,6 +25,12 @@ agency      = "My University"
 ;         Agency2,12,Agency2 - Stacks
 pickupLocationsFile = "XCNCIP2_locations.txt"
 
+; If the NCIP responder is able to return pickup locations through the Lookup Agency
+; service, you could comment out the pickupLocationsFile and set
+; pickLocationFromNCIP to true. Pickup locations will be loaded by NCIP call then.
+;pickupLocationsFromNCIP = true
+
+
 ;-----------------------------------------------------------------
 ; Consortium settings below:
 ;-----------------------------------------------------------------
diff --git a/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php b/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php
index 2a79aa5cbad..19c7ab7d93a 100644
--- a/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php
+++ b/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php
@@ -109,7 +109,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         Virtua::class => InvokableFactory::class,
         Voyager::class => DriverWithDateConverterFactory::class,
         VoyagerRestful::class => VoyagerRestfulFactory::class,
-        XCNCIP2::class => InvokableFactory::class,
+        XCNCIP2::class => DriverWithDateConverterFactory::class,
     ];
 
     /**
diff --git a/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php b/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php
index ccb27882659..fa6333d5657 100644
--- a/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php
+++ b/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php
@@ -28,6 +28,7 @@
 namespace VuFind\ILS\Driver;
 
 use VuFind\Config\Locator as ConfigLocator;
+use VuFind\Date\DateException;
 use VuFind\Exception\ILS as ILSException;
 
 /**
@@ -42,6 +43,7 @@ use VuFind\Exception\ILS as ILSException;
 class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface
 {
     use \VuFindHttp\HttpServiceAwareTrait;
+    use \VuFind\Log\LoggerAwareTrait;
 
     /**
      * Is this a consortium? Default: false
@@ -69,7 +71,68 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      *
      * @var array
      */
-    protected $pickupLocations = [];
+    protected $pickupLocations = null;
+
+    /**
+     * Date converter object
+     *
+     * @var \VuFind\Date\Converter
+     */
+    protected $dateConverter;
+
+    /**
+     * From agency id
+     *
+     * @var string
+     */
+    protected $fromAgency = null;
+
+    /**
+     * Statuses of available items lowercased status string from CirculationStatus
+     * NCIP element
+     *
+     * @var string[]
+     */
+    protected $availableStatuses = ['not charged', 'available on shelf'];
+
+    /**
+     * Statuses of active requests, lowercased status strings from RequestStatusType
+     * NCIP element
+     *
+     * @var string[]
+     */
+    protected $activeRequestStatuses = ['available for pickup', 'in process'];
+
+    /**
+     * Lowercased status string for requests available for pickup by patron
+     *
+     * @var string
+     */
+    protected $requestAvailableStatus = 'available for pickup';
+
+    /**
+     * Lowercased request type strings identifying holds
+     *
+     * @var string[]
+     */
+    protected $holdRequestTypes = ['hold', 'recall'];
+
+    /**
+     * Lowercased request type strings identifying storage retrievals
+     *
+     * @var string[]
+     */
+    protected $storageRetrievalRequestTypes = ['stack retrieval'];
+
+    /**
+     * Constructor
+     *
+     * @param \VuFind\Date\Converter $dateConverter Date converter object
+     */
+    public function __construct(\VuFind\Date\Converter $dateConverter)
+    {
+        $this->dateConverter = $dateConverter;
+    }
 
     /**
      * Initialize the driver.
@@ -87,6 +150,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         }
 
         $this->url = $this->config['Catalog']['url'];
+        $this->fromAgency = $this->config['Catalog']['fromAgency'] ?? null;
         if ($this->config['Catalog']['consortium']) {
             $this->consortium = true;
             foreach ($this->config['Catalog']['agency'] as $agency) {
@@ -100,8 +164,29 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                 $this->agency[$this->config['Catalog']['agency']] = 1;
             }
         }
+    }
 
-        $this->loadPickupLocations($this->config['Catalog']['pickupLocationsFile']);
+    /**
+     * Load pickup locations from file or from NCIP responder - it depends on
+     * configuration
+     *
+     * @throws ILSException
+     * @return void
+     */
+    public function loadPickUpLocations()
+    {
+        $filename = $this->config['Catalog']['pickupLocationsFile'] ?? null;
+        if ($filename) {
+            $this->loadPickUpLocationsFromFile($filename);
+        } elseif ($this->config['Catalog']['pickupLocationsFromNCIP'] ?? false) {
+            $this->loadPickUpLocationsFromNcip();
+        } else {
+            throw new ILSException(
+                'XCNCIP2 ILS driver bad configuration. You should set up ' .
+                'one of these options: "pickupLocationsFile" or ' .
+                '"pickupLocationsFromNCIP"'
+            );
+        }
     }
 
     /**
@@ -112,7 +197,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      * @throws ILSException
      * @return void
      */
-    protected function loadPickupLocations($filename)
+    protected function loadPickUpLocationsFromFile($filename)
     {
         // Load pickup locations file:
         $pickupLocationsFile
@@ -124,9 +209,9 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         }
         if (($handle = fopen($pickupLocationsFile, "r")) !== false) {
             while (($data = fgetcsv($handle)) !== false) {
-                $agency_ID = $data[0] . '|' . $data[1];
-                $this->pickupLocations[$agency_ID] = [
-                    'locationID' => $agency_ID,
+                $agencyId = $data[0] . '|' . $data[1];
+                $this->pickupLocations[$agencyId] = [
+                    'locationId' => $agencyId,
                     'locationDisplay' => $data[2]
                 ];
             }
@@ -134,6 +219,40 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         }
     }
 
+    /**
+     * Loads pickup location information from LookupAgency NCIP service.
+     *
+     * @return void
+     */
+    public function loadPickUpLocationsFromNcip()
+    {
+        $request = $this->getLookupAgencyRequest();
+        $response = $this->sendRequest($request);
+
+        $return = [];
+
+        $agencyId = $response->xpath('ns1:LookupAgencyResponse/ns1:AgencyId');
+        $agencyId = (string)($agencyId[0] ?? '');
+        $locations = $response->xpath(
+            'ns1:LookupAgencyResponse/ns1:Ext/ns1:LocationName/' .
+            'ns1:LocationNameInstance'
+        );
+        foreach ($locations as $loc) {
+            $this->registerNamespaceFor($loc);
+            $id = $loc->xpath('ns1:LocationNameLevel');
+            $name = $loc->xpath('ns1:LocationNameValue');
+            if (empty($id) || empty($name)) {
+                continue;
+            }
+            $location = [
+                'locationId' => $agencyId . '|' . (string)$id[0],
+                'locationDisplay' => (string)$name[0],
+            ];
+            $return[] = $location;
+        }
+        $this->pickupLocations = $return;
+    }
+
     /**
      * Send an NCIP request.
      *
@@ -143,15 +262,16 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     protected function sendRequest($xml)
     {
+        $this->debug('Sendig NCIP request: ' . $xml);
         // Make the NCIP request:
         try {
             $client = $this->httpService->createClient($this->url);
             // Set timeout value
             $timeout = isset($this->config['Catalog']['http_timeout'])
-            ? $this->config['Catalog']['http_timeout'] : 30;
+                ? $this->config['Catalog']['http_timeout'] : 30;
             $client->setOptions(['timeout' => $timeout]);
             $client->setRawBody($xml);
-            $client->setEncType('application/xml; "charset=utf-8"');
+            $client->setEncType('application/xml; charset=UTF-8');
             $result = $client->setMethod('POST')->send();
         } catch (\Exception $e) {
             throw new ILSException($e->getMessage());
@@ -163,9 +283,19 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
 
         // Process the NCIP response:
         $response = $result->getBody();
+        $this->debug('Got NCIP response: ' . $response);
         $result = @simplexml_load_string($response);
         if (is_a($result, 'SimpleXMLElement')) {
-            $result->registerXPathNamespace('ns1', 'http://www.niso.org/2008/ncip');
+            // If no namespaces are used, add default one and reload the document
+            if (empty($result->getNamespaces())) {
+                $result->addAttribute('xmlns', 'http://www.niso.org/2008/ncip');
+                $xml = $result->asXML();
+                $result = @simplexml_load_string($xml);
+                if ($result === false) {
+                    throw new ILSException('Problem parsing XML: ' . $xml);
+                }
+            }
+            $this->registerNamespaceFor($result);
             return $result;
         } else {
             throw new ILSException("Problem parsing XML");
@@ -182,65 +312,61 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     protected function getStatusForChunk($current)
     {
+        $this->registerNamespaceFor($current);
         $status = $current->xpath(
             'ns1:ItemOptionalFields/ns1:CirculationStatus'
         );
-        $status = empty($status) ? '' : (string)$status[0];
-
-        /* unused variable -- can we remove?
-        $itemId = $current->xpath(
-            'ns1:ItemId/ns1:ItemIdentifierValue'
-        );
-        $item_id = (string)$itemId[0];
-         */
+        $status = (string)($status[0] ?? '');
 
         $itemCallNo = $current->xpath(
             'ns1:ItemOptionalFields/ns1:ItemDescription/ns1:CallNumber'
         );
-        $itemCallNo = (string)$itemCallNo[0];
+        $itemCallNo = !empty($itemCallNo) ? (string)$itemCallNo[0] : null;
 
         $location = $current->xpath(
             'ns1:ItemOptionalFields/ns1:Location/ns1:LocationName/' .
             'ns1:LocationNameInstance/ns1:LocationNameValue'
         );
-        $location = (string)$location[0];
+        $location = !empty($location) ? (string)$location[0] : null;
 
-        return [
-            //'id' => ...
+        $return = [
             'status' => $status,
             'location' => $location,
             'callnumber' => $itemCallNo,
-            'availability' => ($status == "Not Charged"),
+            'availability' => $this->isAvailable($status),
             'reserve' => 'N',       // not supported
         ];
+        if (strtolower($status) === 'circulation status undefined') {
+            $return['use_unknown_message'] = true;
+        }
+        return $return;
     }
 
     /**
      * Given a chunk of the availability response, extract the values needed
      * by VuFind.
      *
-     * @param array  $current      Current XCItemAvailability chunk.
-     * @param string $aggregate_id (Aggregate) ID of the consortial record
-     * @param string $bib_id       Bib ID of one of the consortial record's source
+     * @param array  $current     Current XCItemAvailability chunk.
+     * @param string $aggregateId (Aggregate) ID of the consortial record
+     * @param string $bibId       Bib ID of one of the consortial record's source
      * record(s)
      *
      * @return array
      */
-    protected function getHoldingsForChunk($current, $aggregate_id = null,
-        $bib_id = null
+    protected function getHoldingsForChunk($current, $aggregateId = null,
+        $bibId = null
     ) {
-        // Maintain an internal static count of line numbers:
-        static $number = 1;
-
-        $current->registerXPathNamespace('ns1', 'http://www.niso.org/2008/ncip');
+        $this->registerNamespaceFor($current);
 
         // Extract details from the XML:
         $status = $current->xpath(
             'ns1:ItemOptionalFields/ns1:CirculationStatus'
         );
-        $status = empty($status) ? '' : (string)$status[0];
+        $status = (string)($status[0] ?? '');
 
         $itemId = $current->xpath('ns1:ItemId/ns1:ItemIdentifierValue');
+        $itemType = $current->xpath('ns1:ItemId/ns1:ItemIdentifierType');
+        $itemType = (string)($itemType[0] ?? '');
 
         $itemAgencyId = $current->xpath('ns1:ItemId/ns1:AgencyId');
 
@@ -263,57 +389,51 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             'ns1:ItemOptionalFields/ns1:Location/' .
             'ns1:LocationName/ns1:LocationNameInstance/ns1:LocationNameValue'
         );
-        $location = (string)$tmp[0];
+        $location = !empty($tmp) ? (string)$tmp[0] : null;
 
         $itemCallNo = $current->xpath(
             'ns1:ItemOptionalFields/ns1:ItemDescription/ns1:CallNumber'
         );
-        $itemCallNo = (string)$itemCallNo[0];
+        $itemCallNo = (string)($itemCallNo[0] ?? '');
 
         $number = $current->xpath(
             'ns1:ItemOptionalFields/ns1:ItemDescription/' .
             'ns1:CopyNumber'
         );
-        $number = (string)$number[0];
+        $number = (string)($number[0] ?? '');
 
         $volume = $current->xpath(
             'ns1:ItemOptionalFields/ns1:ItemDescription/' .
             'ns1:HoldingsInformation/ns1:UnstructuredHoldingsData'
         );
-        $volume = (string)$volume[0];
-
-        if ($status === "Not Charged") {
-            $holdType = "hold";
-        } else {
-            $holdType = "recall";
-        }
+        $volume = (string)($volume[0] ?? '');
 
         // Build return array:
-        return [
-            'id' => empty($aggregate_id) ?
-                (empty($bib_id) ? '' : $bib_id) : $aggregate_id,
-            'availability' => ($status == 'Not Charged'),
+        $return = [
+            'id' => $aggregateId,
+            'availability' =>  $this->isAvailable($status),
             'status' => $status,
             'item_id' => (string)$itemId[0],
-            'bib_id' => $bib_id,
-            'item_agency_id' => (string)$itemAgencyId[0],
-            'aggregate_id' => $aggregate_id,
+            'bib_id' => $bibId,
+            'item_agency_id' => (string)($itemAgencyId[0] ?? ''),
             'location' => $location,
             'reserve' => 'N',       // not supported
             'callnumber' => $itemCallNo,
             'duedate' => '',        // not supported
-            //'number' => $number++,
             'volume' => $volume,
             'number' => $number,
-            // XC NCIP does not support barcode, but we need a placeholder here
-            // to display anything on the record screen:
-            'barcode' => 'placeholder' . $number,
+            'barcode' => ($itemType === 'Barcode')
+                ? (string)$itemId[0] : 'Unknown barcode',
             'is_holdable'  => true,
             'addLink' => true,
-            'holdtype' => $holdType,
+            'holdtype' => $this->getHoldType($status),
             'storageRetrievalRequest' => 'auto',
             'addStorageRetrievalRequestLink' => 'true',
         ];
+        if (strtolower($status) === 'circulation status undefined') {
+            $return['use_unknown_message'] = true;
+        }
+        return $return;
     }
 
     /**
@@ -346,18 +466,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     protected function getStatusRequest($idList, $resumption = null, $agency = null)
     {
-        $agencyList = [];
-
-        if (null === $agency) {
-            $keys = array_keys($this->agency);
-            $agencyList[] = $keys[0];
-        }
-
-        if (! is_array($agency)) {
-            $agencyList[] = $agency[0];
-        } else {
-            $agencyList = $agency;
-        }
+        $agency = $this->determineToAgencyId($agency);
 
         // Build a list of the types of information we want to retrieve:
         $desiredParts = [
@@ -367,32 +476,21 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             'Hold Queue Length',
             'Item Description',
             'Item Use Restriction Type',
-            'Location'
+            'Location',
         ];
 
         // Start the XML:
-        $xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' .
-            '<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ' .
-            'ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/' .
-            'ncip_v2_0.xsd"><ns1:Ext><ns1:LookupItemSet>';
-
-        for ($i = 0, $size = count($idList); $i < $size; $i++) {
-            $xml .= '<ns1:BibliographicId>' .
-                    '<ns1:BibliographicRecordId>' .
-                        '<ns1:BibliographicRecordIdentifier>' .
-                            htmlspecialchars($idList[$i]) .
-                        '</ns1:BibliographicRecordIdentifier>' .
-                        '<ns1:AgencyId>' .
-                            htmlspecialchars($agencyList[$i]) .
-                        '</ns1:AgencyId>' .
-                    '</ns1:BibliographicRecordId>' .
-                '</ns1:BibliographicId>';
+        $xml = $this->getNCIPMessageStart() . '<ns1:LookupItemSet>';
+        $xml .= $this->getInitiationHeaderXml($agency);
+
+        foreach ($idList as $id) {
+            $xml .= $this->getBibliographicId($id);
         }
 
         // Add the desired data list:
         foreach ($desiredParts as $current) {
             $xml .= '<ns1:ItemElementType ' .
-                'ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/' .
+                'ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/' .
                 'itemelementtype/itemelementtype.scm">' .
                 htmlspecialchars($current) . '</ns1:ItemElementType>';
         }
@@ -404,7 +502,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         }
 
         // Close the XML and send it to the caller:
-        $xml .= '</ns1:LookupItemSet></ns1:Ext></ns1:NCIPMessage>';
+        $xml .= '</ns1:LookupItemSet></ns1:NCIPMessage>';
         return $xml;
     }
 
@@ -426,30 +524,48 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         if ($this->consortium) {
             return $status; // (empty) TODO: add support for consortial statuses.
         }
-
         $resumption = null;
         do {
             $request = $this->getStatusRequest($idList, $resumption);
             $response = $this->sendRequest($request);
-            $avail = $response->xpath(
-                'ns1:Ext/ns1:LookupItemSetResponse/ns1:BibInformation'
+            $bibInfo = $response->xpath(
+                'ns1:LookupItemSetResponse/ns1:BibInformation'
             );
 
             // Build the array of statuses:
-            foreach ($avail as $current) {
-                $bib_id = $current->xpath(
+            foreach ($bibInfo as $bib) {
+                $this->registerNamespaceFor($bib);
+                $bibId = $bib->xpath(
                     'ns1:BibliographicId/ns1:BibliographicRecordId/' .
-                    'ns1:BibliographicRecordIdentifier'
+                    'ns1:BibliographicRecordIdentifier' .
+                    ' | ' .
+                    'ns1:BibliographicId/ns1:BibliographicItemId/' .
+                    'ns1:BibliographicItemIdentifier'
                 );
-                $bib_id = (string)$bib_id[0];
+                if (empty($bibId)) {
+                    throw new ILSException(
+                        'Bibliographic record/item identifier missing in lookup ' .
+                        'item set response'
+                    );
+                }
+                $bibId = (string)$bibId[0];
+
+                $holdings = $bib->xpath('ns1:HoldingsSet');
 
-                $holdings = $current->xpath('ns1:HoldingsSet');
+                foreach ($holdings as $holding) {
+                    $this->registerNamespaceFor($holding);
+                    $holdCallNo = $holding->xpath('ns1:CallNumber');
+                    $holdCallNo = !empty($holdCallNo) ? (string)$holdCallNo[0]
+                        : null;
 
-                foreach ($holdings as $current) {
-                    $holdCallNo = $current->xpath('ns1:CallNumber');
-                    $holdCallNo = (string)$holdCallNo[0];
+                    $items = $holding->xpath('ns1:ItemInformation');
 
-                    $items = $current->xpath('ns1:ItemInformation');
+                    $holdingLocation = $holding->xpath(
+                        'ns1:Location/ns1:LocationName/ns1:LocationNameInstance/' .
+                        'ns1:LocationNameValue'
+                    );
+                    $holdingLocation = !empty($holdingLocation)
+                        ? (string)$holdingLocation[0] : null;
 
                     foreach ($items as $item) {
                         // Get data on the current chunk of data:
@@ -461,18 +577,20 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                         // Each bibliographic ID has its own key in the $status
                         // array; make sure we initialize new arrays when necessary
                         // and then add the current chunk to the right place:
-                        $chunk['id'] = $bib_id;
-                        if (!isset($status[$id])) {
-                            $status[$id] = [];
+                        $chunk['id'] = $bibId;
+                        if (!isset($status[$bibId])) {
+                            $status[$bibId] = [];
                         }
-                        $status[$bib_id][] = $chunk;
+                        $chunk['location'] = $chunk['location']
+                            ?? $holdingLocation ?? null;
+                        $status[$bibId][] = $chunk;
                     }
                 }
             }
 
             // Check for resumption token:
             $resumption = $response->xpath(
-                'ns1:Ext/ns1:LookupItemSetResponse/ns1:NextItemToken'
+                'ns1:LookupItemSetResponse/ns1:NextItemToken'
             );
             $resumption = count($resumption) > 0 ? (string)$resumption[0] : null;
         } while (!empty($resumption));
@@ -500,19 +618,19 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     public function getConsortialHoldings($id, array $patron = null,
         array $ids = null
     ) {
-        $aggregate_id = $id;
+        $aggregateId = $id;
 
         $agencyList = [];
         $idList = [];
         if (null !== $ids) {
-            foreach ($ids as $_id) {
+            foreach ($ids as $id) {
                 // Need to parse out the 035$a format, e.g., "(Agency) 123"
-                if (preg_match('/\(([^\)]+)\)\s*([0-9]+)/', $_id, $matches)) {
-                    $matched_agency = $matches[1];
-                    $matched_id = $matches[2];
-                    if (array_key_exists($matched_agency, $this->agency)) {
-                        $agencyList[] = $matched_agency;
-                        $idList[] = $matched_id;
+                if (preg_match('/\(([^\)]+)\)\s*(.+)/', $id, $matches)) {
+                    $matchedAgency = $matches[1];
+                    $matchedId = $matches[2];
+                    if (array_key_exists($matchedAgency, $this->agency)) {
+                        $agencyList[] = $matchedAgency;
+                        $idList[] = $matchedId;
                     }
                 }
             }
@@ -523,34 +641,47 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $response = $this->sendRequest($request);
 
         $bibs = $response->xpath(
-            'ns1:Ext/ns1:LookupItemSetResponse/ns1:BibInformation'
+            'ns1:LookupItemSetResponse/ns1:BibInformation'
         );
 
         foreach ($bibs as $bib) {
-            $bib_ids = $bib->xpath(
+            $this->registerNamespaceFor($bib);
+            $bibIds = $bib->xpath(
                 'ns1:BibliographicId/ns1:BibliographicRecordId/' .
-                'ns1:BibliographicRecordIdentifier'
+                'ns1:BibliographicRecordIdentifier' .
+                ' | ' .
+                'ns1:BibliographicId/ns1:BibliographicItemId/' .
+                'ns1:BibliographicItemIdentifier'
             );
-            $bib_id = (string)$bib_ids[0];
+            $bibId = (string)$bibIds[0];
 
             $holdingSets = $bib->xpath('ns1:HoldingsSet');
             foreach ($holdingSets as $holding) {
+                $this->registerNamespaceFor($holding);
                 $holdCallNo = $holding->xpath('ns1:CallNumber');
-                $holdCallNo = (string)$holdCallNo[0];
+                $holdCallNo = (string)($holdCallNo[0] ?? '');
                 $avail = $holding->xpath('ns1:ItemInformation');
                 $eResource = $holding->xpath(
                     'ns1:ElectronicResource/ns1:ReferenceToResource'
                 );
-                $eResource = (string)$eResource[0];
+                $eResource = (string)($eResource[0] ?? '');
+                $holdingLocation = $holding->xpath(
+                    'ns1:Location/ns1:LocationName/ns1:LocationNameInstance/' .
+                    'ns1:LocationNameValue'
+                );
+                $holdingLocation = !empty($holdingLocation)
+                    ? (string)$holdingLocation[0] : null;
 
                 // Build the array of holdings:
                 foreach ($avail as $current) {
                     $chunk = $this->getHoldingsForChunk(
-                        $current, $aggregate_id, $bib_id
+                        $current, $aggregateId, $bibId
                     );
                     $chunk['callnumber'] = empty($chunk['callnumber']) ?
                         $holdCallNo : $chunk['callnumber'];
                     $chunk['eresource'] = $eResource;
+                    $chunk['location'] = $chunk['location']
+                        ?? $holdingLocation ?? null;
                     $holdings[] = $chunk;
                 }
             }
@@ -584,8 +715,8 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             // Translate $id into consortial (035$a) format,
             // e.g., "123" -> "(Agency) 123"
             $sourceRecord = '';
-            foreach (array_keys($this->agency) as $_agency) {
-                $sourceRecord = '(' . $_agency . ') ';
+            foreach (array_keys($this->agency) as $Agency) {
+                $sourceRecord = '(' . $Agency . ') ';
             }
             $sourceRecord .= $id;
             $ids = [$sourceRecord];
@@ -609,7 +740,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getPurchaseHistory($id)
     {
-        // TODO
+        // NCIP is not able to send acquisition data
         return [];
     }
 
@@ -627,12 +758,24 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function patronLogin($username, $password)
     {
-        // TODO: we somehow need to figure out 'patron_agency_id' in the
+        // TODO: we somehow need to figure out 'patronAgencyId' in the
         // consortium=true case
         //$request = $this->getLookupUserRequest(
-        //    $username, $password, 'patron_agency_id'
+        //    $username, $password, 'patronAgencyId'
         //);
-        $request = $this->getLookupUserRequest($username, $password);
+
+        $extras = [
+            '<ns1:UserElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/' .
+            'schemes/userelementtype/userelementtype.scm">' .
+            'User Address Information' .
+            '</ns1:UserElementType>',
+            '<ns1:UserElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/' .
+            'schemes/userelementtype/userelementtype.scm">' .
+            'Name Information' .
+            '</ns1:UserElementType>'
+        ];
+
+        $request = $this->getLookupUserRequest($username, $password, null, $extras);
 
         $response = $this->sendRequest($request);
         $this->checkResponseForError($response);
@@ -640,31 +783,41 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $id = $response->xpath(
             'ns1:LookupUserResponse/ns1:UserId/ns1:UserIdentifierValue'
         );
-        $patron_agency_id = $response->xpath(
+        $patronAgencyId = $response->xpath(
             'ns1:LookupUserResponse/ns1:UserId/ns1:AgencyId'
         );
+        $first = $response->xpath(
+            'ns1:LookupUserResponse/ns1:UserOptionalFields/ns1:NameInformation/' .
+            'ns1:PersonalNameInformation/ns1:StructuredPersonalUserName/' .
+            'ns1:GivenName'
+        );
+        $last = $response->xpath(
+            'ns1:LookupUserResponse/ns1:UserOptionalFields/ns1:NameInformation/' .
+            'ns1:PersonalNameInformation/ns1:StructuredPersonalUserName/' .
+            'ns1:Surname'
+        );
+        $email = $response->xpath(
+            'ns1:LookupUserResponse/ns1:UserOptionalFields/' .
+            'ns1:UserAddressInformation/ns1:ElectronicAddress/' .
+                'ns1:ElectronicAddressData'
+        );
+
+        $patron = null;
         if (!empty($id)) {
             // Fill in basic patron details:
             $patron = [
                 'id' => (string)$id[0],
-                'patron_agency_id' => (string)$patron_agency_id[0],
+                'patronAgencyId' => (string)$patronAgencyId[0],
                 'cat_username' => $username,
                 'cat_password' => $password,
-                'email' => null,
+                'email' => !empty($email) ? (string)$email[0] : null,
                 'major' => null,
-                'college' => null
+                'college' => null,
+                'firstname' => (string)$first[0],
+                'lastname' => (string)$last[0],
             ];
-
-            // Look up additional details:
-            $details = $this->getMyProfile($patron);
-            if (!empty($details)) {
-                $patron['firstname'] = $details['firstname'];
-                $patron['lastname'] = $details['lastname'];
-                return $patron;
-            }
         }
-
-        return null;
+        return $patron;
     }
 
     /**
@@ -684,44 +837,81 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $extras = ['<ns1:LoanedItemsDesired/>'];
         $request = $this->getLookupUserRequest(
             $patron['cat_username'], $patron['cat_password'],
-            $patron['patron_agency_id'], $extras
+            $patron['patronAgencyId'], $extras
         );
         $response = $this->sendRequest($request);
         $this->checkResponseForError($response);
 
         $retVal = [];
         $list = $response->xpath('ns1:LookupUserResponse/ns1:LoanedItem');
-
         foreach ($list as $current) {
-            $current->registerXPathNamespace('ns1', 'http://www.niso.org/2008/ncip');
+            $this->registerNamespaceFor($current);
             $tmp = $current->xpath('ns1:DateDue');
-            $due = strtotime((string)$tmp[0]);
-            $due = date("l, d-M-y h:i a", $due);
+            // DateDue could be omitted in response
+            $due = $this->displayDate(!empty($tmp) ? (string)$tmp[0] : null);
             $title = $current->xpath('ns1:Title');
-            $item_id = $current->xpath('ns1:ItemId/ns1:ItemIdentifierValue');
-            $bib_id = $current->xpath(
+            $itemId = $current->xpath('ns1:ItemId/ns1:ItemIdentifierValue');
+            $itemId = (string)$itemId[0];
+            $bibId = $current->xpath(
+                'ns1:Ext/ns1:BibliographicDescription/' .
+                'ns1:BibliographicRecordId/ns1:BibliographicRecordIdentifier' .
+                ' | ' .
                 'ns1:Ext/ns1:BibliographicDescription/' .
-                'ns1:BibliographicRecordId/ns1:BibliographicRecordIdentifier'
+                'ns1:BibliographicItemId/ns1:BibliographicItemIdentifier'
             );
             $itemAgencyId = $current->xpath(
                 'ns1:Ext/ns1:BibliographicDescription/' .
-                'ns1:BibliographicRecordId/ns1:AgencyId'
+                'ns1:BibliographicRecordId/ns1:AgencyId' .
+                ' | ' .
+                'ns1:ItemId/ns1:AgencyId'
             );
-            // Hack to account for bibs from other non-local institutions
-            // temporarily until consortial functionality is enabled.
-            if ((string)$bib_id[0]) {
-                $tmp = (string)$bib_id[0];
-            } else {
-                $tmp = "1";
+
+            $notRenewable = $current->xpath(
+                'ns1:Ext/ns1:RenewalNotPermitted'
+            );
+
+            $itemAgencyId = !empty($itemAgencyId) ? (string)$itemAgencyId[0] : null;
+            $bibId = !empty($bibId) ? (string)$bibId[0] : null;
+            if ($bibId === null || $itemAgencyId === null) {
+                $itemType = $current->xpath('ns1:ItemId/ns1:ItemIdentifierType');
+                $itemType = !empty($itemType) ? (string)$itemType[0] : null;
+                $itemRequest = $this->getLookupItemRequest($itemId, $itemType);
+                $itemResponse = $this->sendRequest($itemRequest);
+            }
+            if ($bibId === null) {
+                $bibId = $itemResponse->xpath(
+                    'ns1:LookupItemResponse/ns1:ItemOptionalFields/' .
+                    'ns1:BibliographicDescription/ns1:BibliographicItemId/' .
+                    'ns1:BibliographicItemIdentifier' .
+                    ' | ' .
+                    'ns1:LookupItemResponse/ns1:ItemOptionalFields/' .
+                    'ns1:BibliographicDescription/ns1:BibliographicRecordId/' .
+                    'ns1:BibliographicRecordIdentifier'
+                );
+                // Hack to account for bibs from other non-local institutions
+                // temporarily until consortial functionality is enabled.
+                $bibId = !empty($bibId) ? (string)$bibId[0] : "1";
             }
+            if ($itemAgencyId === null) {
+                $itemAgencyId = $itemResponse->xpath(
+                    'ns1:LookupItemResponse/ns1:ItemOptionalFields/' .
+                    'ns1:BibliographicDescription/ns1:BibliographicRecordId/' .
+                    'ns1:AgencyId' .
+                    ' | ' .
+                    'ns1:LookupItemResponse/ns1:ItemId/ns1:AgencyId'
+                );
+                $itemAgencyId = !empty($itemAgencyId)
+                    ? (string)$itemAgencyId[0] : null;
+            }
+
             $retVal[] = [
-                'id' => $tmp,
-                'item_agency_id' => (string)$itemAgencyId[0],
-                'patron_agency_id' => $patron['patron_agency_id'],
+                'id' => $bibId,
+                'item_agency_id' => $itemAgencyId,
+                'patronAgencyId' => $patron['patronAgencyId'],
                 'duedate' => $due,
                 'title' => (string)$title[0],
-                'item_id' => (string)$item_id[0],
-                'renewable' => true,
+                'item_id' => $itemId,
+                'renewable' => empty($notRenewable),
             ];
         }
 
@@ -744,7 +934,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $extras = ['<ns1:UserFiscalAccountDesired/>'];
         $request = $this->getLookupUserRequest(
             $patron['cat_username'], $patron['cat_password'],
-            $patron['patron_agency_id'], $extras
+            $patron['patronAgencyId'], $extras
         );
         $response = $this->sendRequest($request);
         $this->checkResponseForError($response);
@@ -754,32 +944,33 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         );
 
         $fines = [];
-        $balance = 0;
         foreach ($list as $current) {
-            $current->registerXPathNamespace('ns1', 'http://www.niso.org/2008/ncip');
+            $this->registerNamespaceFor($current);
 
             $tmp = $current->xpath(
                 'ns1:FiscalTransactionInformation/ns1:Amount/ns1:MonetaryValue'
             );
             $amount = (string)$tmp[0];
             $tmp = $current->xpath('ns1:AccrualDate');
-            $date = (string)$tmp[0];
+            $date = $this->displayDate(!empty($tmp) ? (string)$tmp[0] : null);
             $tmp = $current->xpath(
                 'ns1:FiscalTransactionInformation/ns1:FiscalTransactionType'
             );
             $desc = (string)$tmp[0];
-            /* This is an item ID, not a bib ID, so it's not actually useful:
-            $tmp = $current->xpath(
+
+            $bibId = $current->xpath(
+                'ns1:FiscalTransactionInformation/ns1:ItemDetails/' .
+                'ns1:BibliographicDescription/ns1:BibliographicRecordId/' .
+                'ns1:BibliographicRecordIdentifier' .
+                ' | ' .
                 'ns1:FiscalTransactionInformation/ns1:ItemDetails/' .
-                'ns1:ItemId/ns1:ItemIdentifierValue'
+                'ns1:BibliographicDescription/ns1:BibliographicItemId/' .
+                'ns1:BibliographicItemIdentifier'
             );
-            $id = (string)$tmp[0];
-             */
-            $id = '';
-            $balance += $amount;
+            $id = (string)($bibId[0] ?? '');
             $fines[] = [
                 'amount' => $amount,
-                'balance' => $balance,
+                'balance' => $amount,
                 'checkout' => '',
                 'fine' => $desc,
                 'duedate' => '',
@@ -791,63 +982,100 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     }
 
     /**
-     * Get Patron Holds
+     * Get Patron requests by type
      *
      * This is responsible for retrieving all holds by a specific patron.
      *
      * @param array $patron The patron array from patronLogin
+     * @param array $types  Request types
      *
      * @throws VuFind\Date\DateException;
      * @throws ILSException
      * @return array        Array of the patron's holds on success.
      */
-    public function getMyHolds($patron)
+    protected function getMyRequests(array $patron, array $types)
     {
         $extras = ['<ns1:RequestedItemsDesired/>'];
         $request = $this->getLookupUserRequest(
             $patron['cat_username'], $patron['cat_password'],
-            $patron['patron_agency_id'], $extras
+            $patron['patronAgencyId'], $extras
         );
         $response = $this->sendRequest($request);
         $this->checkResponseForError($response);
 
         $retVal = [];
-        $list = $response->xpath('ns1:LookupUserResponse/ns1:RequestedItem');
-        foreach ($list as $current) {
+        $requests = $response->xpath('ns1:LookupUserResponse/ns1:RequestedItem');
+
+        foreach ($requests as $current) {
+            $this->registerNamespaceFor($current);
             $id = $current->xpath(
                 'ns1:Ext/ns1:BibliographicDescription/' .
-                'ns1:BibliographicRecordId/ns1:BibliographicRecordIdentifier'
+                'ns1:BibliographicRecordId/ns1:BibliographicRecordIdentifier' .
+                ' | ' .
+                'ns1:Ext/ns1:BibliographicDescription/' .
+                'ns1:BibliographicItemId/ns1:BibliographicItemIdentifier'
+            );
+            $itemAgencyId = $current->xpath(
+                'ns1:Ext/ns1:BibliographicDescription/' .
+                'ns1:BibliographicRecordId/ns1:AgencyId'
             );
-            // (unused variable): $created = $current->xpath('ns1:DatePlaced');
+
             $title = $current->xpath('ns1:Title');
             $pos = $current->xpath('ns1:HoldQueuePosition');
-            $requestType = $current->xpath('ns1:RequestType');
             $requestId = $current->xpath('ns1:RequestId/ns1:RequestIdentifierValue');
             $itemId = $current->xpath('ns1:ItemId/ns1:ItemIdentifierValue');
             $pickupLocation = $current->xpath('ns1:PickupLocation');
+            $created = $current->xpath('ns1:DatePlaced');
+            $created = $this->displayDate(
+                !empty($created) ? (string)$created[0] : null
+            );
             $expireDate = $current->xpath('ns1:PickupExpiryDate');
-            $expireDate = strtotime((string)$expireDate[0]);
-            $expireDate = date("l, d-M-y", $expireDate);
-            $requestType = (string)$requestType[0];
-            // Only return requests of type Hold or Recall. Callslips/Stack
-            // Retrieval requests are fetched using getMyStorageRetrievalRequests
-            if ($requestType === "Hold" or $requestType === "Recall") {
+            $expireDate = $this->displayDate(
+                !empty($expireDate) ? (string)$expireDate[0] : null
+            );
+
+            $requestStatusType = $current->xpath('ns1:RequestStatusType');
+            $status = !empty($requestStatusType) ? (string)$requestStatusType[0]
+                : null;
+            $available = strtolower($status) === $this->requestAvailableStatus;
+
+            // Only return requests of desired type
+            if ($this->checkRequestType($current, $types)) {
                 $retVal[] = [
                     'id' => (string)$id[0],
-                    'create' => '',
+                    'create' => $created,
                     'expire' => $expireDate,
                     'title' => (string)$title[0],
-                    'position' => (string)$pos[0],
-                    'requestId' => (string)$requestId[0],
+                    'position' => !empty($pos) ? (string)$pos[0] : null,
+                    'requestId' => !empty($requestId) ? (string)$requestId[0] : null,
+                    'item_agency_id' => !empty($itemAgencyId)
+                        ? (string)$itemAgencyId[0] : null,
+                    'canceled' => $this->isRequestCancelled($status),
                     'item_id' => (string)$itemId[0],
                     'location' => (string)$pickupLocation[0],
+                    'available' => $available,
                 ];
             }
         }
-
         return $retVal;
     }
 
+    /**
+     * Get Patron Holds
+     *
+     * This is responsible for retrieving all holds by a specific patron.
+     *
+     * @param array $patron The patron array from patronLogin
+     *
+     * @throws VuFind\Date\DateException;
+     * @throws ILSException
+     * @return array        Array of the patron's holds on success.
+     */
+    public function getMyHolds($patron)
+    {
+        return $this->getMyRequests($patron, $this->holdRequestTypes);
+    }
+
     /**
      * Get Patron Profile
      *
@@ -872,7 +1100,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         ];
         $request = $this->getLookupUserRequest(
             $patron['cat_username'], $patron['cat_password'],
-            $patron['patron_agency_id'], $extras
+            $patron['patronAgencyId'], $extras
         );
         $response = $this->sendRequest($request);
         $this->checkResponseForError($response);
@@ -930,7 +1158,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getNewItems($page, $limit, $daysOld, $fundId = null)
     {
-        // TODO
+        // NCIP is not able to send acquisition data
         return [];
     }
 
@@ -944,7 +1172,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getFunds()
     {
-        // TODO
+        // NCIP is not able to send acquisition data, so we don't need getFunds
         return [];
     }
 
@@ -972,7 +1200,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getInstructors()
     {
-        // TODO
+        // NCIP does not support course reserves
         return [];
     }
 
@@ -986,7 +1214,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getCourses()
     {
-        // TODO
+        // NCIP does not support course reserves
         return [];
     }
 
@@ -1006,7 +1234,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function findReserves($course, $inst, $dept)
     {
-        // TODO
+        // NCIP does not support course reserves
         return [];
     }
 
@@ -1037,7 +1265,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     {
         if ($function == 'Holds') {
             return [
-                'HMACKeys' => 'item_id:holdtype:item_agency_id:aggregate_id:bib_id',
+                'HMACKeys' => 'item_id:holdtype:item_agency_id:id:bib_id',
                 'extraHoldFields' => 'comments:pickUpLocation:requiredByDate',
                 'defaultRequiredDate' => '0:2:0',
                 'consortium' => $this->consortium,
@@ -1045,10 +1273,8 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         }
         if ($function == 'StorageRetrievalRequests') {
             return [
-                'HMACKeys' => 'id:item_id:item_agency_id:aggregate_id:bib_id',
+                'HMACKeys' => 'id:item_id:item_agency_id:id:bib_id',
                 'extraFields' => 'comments:pickUpLocation:requiredByDate:item-issue',
-                'helpText' => 'This is a storage retrieval request help text' .
-                    ' with some <span style="color: red">styling</span>.',
                 'defaultRequiredDate' => '0:2:0',
             ];
         }
@@ -1073,7 +1299,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getDefaultPickUpLocation($patron, $holdDetails = null)
     {
-        return $this->pickupLocations[$patron['patron_agency_id']][0]['locationID'];
+        return $this->pickupLocations[$patron['patronAgencyId']][0]['locationId'];
     }
 
     /**
@@ -1090,22 +1316,17 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      * or may be ignored.  The driver must not add new options to the return array
      * based on this data or other areas of VuFind may behave incorrectly.
      *
-     * @return array        An array of associative arrays with locationID and
+     * @return array        An array of associative arrays with locationId and
      * locationDisplay keys
      *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
     public function getPickUpLocations($patron, $holdDetails = null)
     {
-        $locations = [];
-        foreach ($this->pickupLocations as $thisAgency) {
-            $locations[]
-                = [
-                    'locationID' => $thisAgency['locationID'],
-                    'locationDisplay' => $thisAgency['locationDisplay'],
-                ];
+        if (!isset($this->pickupLocations)) {
+            $this->loadPickUpLocations();
         }
-        return $locations;
+        return array_values($this->pickupLocations);
     }
 
     /**
@@ -1119,58 +1340,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getMyStorageRetrievalRequests($patron)
     {
-        $extras = ['<ns1:RequestedItemsDesired/>'];
-        $request = $this->getLookupUserRequest(
-            $patron['cat_username'], $patron['cat_password'],
-            $patron['patron_agency_id'], $extras
-        );
-        $response = $this->sendRequest($request);
-        $this->checkResponseForError($response);
-
-        $retVal = [];
-        $list = $response->xpath('ns1:LookupUserResponse/ns1:RequestedItem');
-        foreach ($list as $current) {
-            $cancelled = true;
-            $id = $current->xpath(
-                'ns1:Ext/ns1:BibliographicDescription/' .
-                'ns1:BibliographicRecordId/ns1:BibliographicRecordIdentifier'
-            );
-            $itemAgencyId = $current->xpath(
-                'ns1:Ext/ns1:BibliographicDescription/' .
-                'ns1:BibliographicRecordId/ns1:AgencyId'
-            );
-            //$created = $current->xpath('ns1:DatePlaced');
-            $title = $current->xpath('ns1:Title');
-            $pos = $current->xpath('ns1:HoldQueuePosition');
-            $pickupLocation = $current->xpath('ns1:PickupLocation');
-            $requestId = $current->xpath('ns1:RequestId/ns1:RequestIdentifierValue');
-            $requestType = $current->xpath('ns1:RequestType');
-            $requestType = (string)$requestType[0];
-            $tmpStatus = $current->xpath('ns1:RequestStatusType');
-            list($status, $created) = explode(" ", (string)$tmpStatus[0], 2);
-            if ($status === "Accepted") {
-                $cancelled = false;
-            }
-            // Only return requests of type Stack Retrieval/Callslip. Hold
-            // and Recall requests are fetched using getMyHolds
-            if ($requestType === 'Stack Retrieval') {
-                $retVal[] = [
-                    'id' => (string)$id[0],
-                    'create' => $created,
-                    'expire' => '',
-                    'title' => (string)$title[0],
-                    'position' => (string)$pos[0],
-                    'requestId' => (string)$requestId[0],
-                    'item_agency_id' => (string)$itemAgencyId[0],
-                    'location' => 'test',
-                    'canceled' => $cancelled,
-                    'location' => (string)$pickupLocation[0],
-                    'processed' => false,
-                ];
-            }
-        }
-
-        return $retVal;
+        return $this->getMyRequests($patron, $this->storageRetrievalRequestTypes);
     }
 
     /**
@@ -1204,41 +1374,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function placeStorageRetrievalRequest($details)
     {
-        $username = $details['patron']['cat_username'];
-        $password = $details['patron']['cat_password'];
-        $bibId = $details['bib_id'];
-        $itemId = $details['item_id'];
-        $pickUpLocation = $details['pickUpLocation'];
-        list($pickUpAgency, $pickUpLocation) = explode("|", $pickUpLocation);
-        $lastInterestDate = $details['requiredBy'];
-        $lastInterestDate = substr($lastInterestDate, 6, 10) . '-' .
-                substr($lastInterestDate, 0, 5);
-        $lastInterestDate = $lastInterestDate . "T00:00:00.000Z";
-
-        $request = $this->getRequest(
-            $username, $password, $bibId, $itemId,
-            $details['patron']['patron_agency_id'],
-            $pickUpAgency,
-            $details['item_agency_id'],
-            "Stack Retrieval", "Item", $lastInterestDate, $pickUpLocation
-        );
-
-        $response = $this->sendRequest($request);
-        $success = $response->xpath(
-            'ns1:RequestItemResponse/ns1:ItemId/ns1:ItemIdentifierValue'
-        );
-
-        if ($success) {
-            return [
-                'success' => true,
-                "sysMessage" => 'Storage Retrieval Request Successful.'
-            ];
-        } else {
-            return [
-                'success' => false,
-                "sysMessage" => 'Storage Retrieval Request Not Successful.'
-            ];
-        }
+        return $this->placeRequest($details, 'Stack Retrieval');
     }
 
     /**
@@ -1255,10 +1391,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     public function getRenewDetails($checkOutDetails)
     {
         return $checkOutDetails['item_agency_id'] .
-                                "|" .
-                                $checkOutDetails['item_id'] .
-                                "|" .
-                                $checkOutDetails['id'];
+            "|" . $checkOutDetails['item_id'];
     }
 
     /**
@@ -1276,94 +1409,162 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function placeHold($details)
     {
+        return $this->placeRequest($details, $details['holdtype']);
+    }
+
+    /**
+     * Place a general request
+     *
+     * Attempts to place a hold or recall on a particular item and returns
+     * an array with result details or throws an exception on failure of support
+     * classes
+     *
+     * @param array  $details An array of item and patron data
+     * @param string $type    Type of request, could be 'Hold' or 'Stack Retrieval'
+     *
+     * @throws ILSException
+     * @return mixed An array of data on the request including
+     * whether or not it was successful
+     */
+    public function placeRequest($details, $type = 'Hold')
+    {
+        $msgPrefix = ($type == 'Stack Retrieval') ? 'Storage Retrieval ' : '';
         $username = $details['patron']['cat_username'];
         $password = $details['patron']['cat_password'];
         $bibId = $details['bib_id'];
         $itemId = $details['item_id'];
         $pickUpLocation = $details['pickUpLocation'];
         list($pickUpAgency, $pickUpLocation) = explode("|", $pickUpLocation);
-        $holdType = $details['holdtype'];
         $lastInterestDate = $details['requiredBy'];
         $lastInterestDate = substr($lastInterestDate, 6, 10) . '-'
             . substr($lastInterestDate, 0, 5);
         $lastInterestDate = $lastInterestDate . "T00:00:00.000Z";
+        $successReturn = [
+            'success' => true,
+            'sysMessage' => $msgPrefix . 'Request Successful.'
+        ];
+        $failureReturn = [
+            'success' => false,
+            'sysMessage' => $msgPrefix . 'Request Not Successful.'
+        ];
 
         $request = $this->getRequest(
             $username, $password, $bibId, $itemId,
-            $details['patron']['patron_agency_id'],
-            $pickUpAgency,
-            $details['item_agency_id'],
-            $holdType, "Item", $lastInterestDate, $pickUpLocation
+            $details['patron']['patronAgencyId'], $details['item_agency_id'],
+            $type, "Item", $lastInterestDate, $pickUpLocation
         );
         $response = $this->sendRequest($request);
-        $this->checkResponseForError($response);
 
         $success = $response->xpath(
-            'ns1:RequestItemResponse/ns1:ItemId/ns1:ItemIdentifierValue'
+            'ns1:RequestItemResponse/ns1:ItemId/ns1:ItemIdentifierValue' .
+            ' | ' .
+            'ns1:RequestItemResponse/ns1:RequestId/ns1:RequestIdentifierValue'
         );
 
-        if ($success) {
-            return [
-                    'success' => true,
-                    "sysMessage" => 'Request Successful.'
-            ];
-        } else {
-            return [
-                    'success' => false,
-                    "sysMessage" => 'Request Not Successful.'
-            ];
+        try {
+            $this->checkResponseForError($response);
+        } catch (ILSException $exception) {
+            return $failureReturn;
         }
+
+        return !empty($success) ? $successReturn : $failureReturn;
     }
 
     /**
-     * Cancel Holds
+     * General cancel request method
      *
-     * Attempts to Cancel a hold or recall on a particular item. The
-     * data in $cancelDetails['details'] is determined by getCancelHoldDetails().
+     * Attempts to Cancel a request on a particular item. The data in
+     * $cancelDetails['details'] is determined by getCancel*Details().
      *
-     * @param array $cancelDetails An array of item and patron data
+     * @param array  $cancelDetails An array of item and patron data
+     * @param string $type          Type of request, could be: 'Hold',
+     * 'Stack Retrieval'
      *
      * @return array               An array of data on each request including
      * whether or not it was successful.
      */
-    public function cancelHolds($cancelDetails)
+    public function handleCancelRequest($cancelDetails, $type = 'Hold')
     {
+        $msgPrefix = ($type === 'Stack Retrieval')
+            ? 'storage_retrieval_request_cancel_'
+            : 'hold_cancel_';
         $count = 0;
         $username = $cancelDetails['patron']['cat_username'];
         $password = $cancelDetails['patron']['cat_password'];
-        $patronAgency = $cancelDetails['patron']['patron_agency_id'];
+        $patronAgency = $cancelDetails['patron']['patronAgencyId'];
         $details = $cancelDetails['details'];
         $response = [];
+        $failureReturn = [
+            'success' => false,
+            'status' => $msgPrefix . 'fail',
+        ];
+        $successReturn = [
+            'success' => true,
+            'status' => $msgPrefix . 'success',
+        ];
 
-        foreach ($details as $cancelDetails) {
-            list($itemAgencyId, $requestId) = explode("|", $cancelDetails);
+        foreach ($details as $detail) {
+            list($itemAgencyId, $requestId, $itemId) = explode("|", $detail);
             $request = $this->getCancelRequest(
                 $username, $password, $patronAgency,
-                $itemAgencyId, $requestId, "Hold"
+                $itemAgencyId, $requestId, $type,
+                $itemId
             );
             $cancelRequestResponse = $this->sendRequest($request);
             $userId = $cancelRequestResponse->xpath(
                 'ns1:CancelRequestItemResponse/' .
                 'ns1:UserId/ns1:UserIdentifierValue'
             );
-            $itemId = (string)$itemId;
+            $itemId = $itemId ?? $requestId;
+            try {
+                $this->checkResponseForError($cancelRequestResponse);
+            } catch (ILSException $exception) {
+                $response[$itemId] = $failureReturn;
+                continue;
+            }
             if ($userId) {
                 $count++;
-                $response[$itemId] = [
-                        'success' => true,
-                        'status' => 'hold_cancel_success',
-                ];
+                $response[$itemId] = $successReturn;
             } else {
-                $response[$itemId] = [
-                        'success' => false,
-                        'status' => 'hold_cancel_fail',
-                ];
+                $response[$itemId] = $failureReturn;
             }
         }
         $result = ['count' => $count, 'items' => $response];
         return $result;
     }
 
+    /**
+     * Cancel Holds
+     *
+     * Attempts to Cancel a hold or recall on a particular item. The
+     * data in $cancelDetails['details'] is determined by getCancelHoldDetails().
+     *
+     * @param array $cancelDetails An array of item and patron data
+     *
+     * @return array               An array of data on each request including
+     * whether or not it was successful.
+     */
+    public function cancelHolds($cancelDetails)
+    {
+        return $this->handleCancelRequest($cancelDetails, 'Hold');
+    }
+
+    /**
+     * Get Cancel Request Details
+     *
+     * General method for getting details for cancel requests
+     *
+     * @param array $details An array of item data
+     *
+     * @return string Data for use in a form field
+     */
+    public function getCancelRequestDetails($details)
+    {
+        return $details['item_agency_id'] .
+            "|" . $details['requestId'] .
+            "|" . $details['item_id'];
+    }
+
     /**
      * Get Cancel Hold Details
      *
@@ -1378,12 +1579,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getCancelHoldDetails($holdDetails)
     {
-        $cancelDetails = $holdDetails['item_agency_id'] .
-                         "|" .
-                         $holdDetails['requestId'] .
-                         "|" .
-                         $holdDetails['id'];
-        return $cancelDetails;
+        return $this->getCancelRequestDetails($holdDetails);
     }
 
     /**
@@ -1400,44 +1596,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function cancelStorageRetrievalRequests($cancelDetails)
     {
-        $count = 0;
-        $username = $cancelDetails['patron']['cat_username'];
-        $password = $cancelDetails['patron']['cat_password'];
-        $patronAgency = $cancelDetails['patron']['patron_agency_id'];
-        $details = $cancelDetails['details'];
-        $response = [];
-
-        foreach ($details as $cancelDetails) {
-            list($itemAgencyId, $requestId) = explode("|", $cancelDetails);
-            $request = $this->getCancelRequest(
-                $username,
-                $password,
-                $patronAgency,
-                $itemAgencyId,
-                $requestId,
-                "Stack Retrieval"
-            );
-            $cancelRequestResponse = $this->sendRequest($request);
-            $userId = $cancelRequestResponse->xpath(
-                'ns1:CancelRequestItemResponse/' .
-                'ns1:UserId/ns1:UserIdentifierValue'
-            );
-            $itemId = (string)$itemId;
-            if ($userId) {
-                $count++;
-                $response[$itemId] = [
-                    'success' => true,
-                    'status' => 'storage_retrieval_request_cancel_success',
-                ];
-            } else {
-                $response[$itemId] = [
-                    'success' => false,
-                    'status' => 'storage_retrieval_request_cancel_fail',
-                ];
-            }
-        }
-        $result = ['count' => $count, 'items' => $response];
-        return $result;
+        return $this->handleCancelRequest($cancelDetails, 'Stack Retrieval');
     }
 
     /**
@@ -1448,17 +1607,13 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      * value is then extracted by the CancelStorageRetrievalRequests function.
      * The item id is used as the key in the return value.
      *
-     * @param array $callslipDetails An array of item data
+     * @param array $details An array of item data
      *
      * @return string Data for use in a form field
      */
-    public function getCancelStorageRetrievalRequestDetails($callslipDetails)
+    public function getCancelStorageRetrievalRequestDetails($details)
     {
-        return $callslipDetails['item_agency_id'] .
-                                "|" .
-                                $callslipDetails['requestId'] .
-                                "|" .
-                                $callslipDetails['id'];
+        return $this->getCancelRequestDetails($details);
     }
 
     /**
@@ -1475,37 +1630,41 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     public function renewMyItems($renewDetails)
     {
         $details = [];
-        foreach ($renewDetails['details'] as $agencyId_renewId) {
-            list($agencyId, $renewId) = explode("|", $agencyId_renewId);
+        foreach ($renewDetails['details'] as $detail) {
+            list($agencyId, $itemId) = explode("|", $detail);
+            $failureReturn = [
+                "success" => false,
+                "item_id" => $itemId,
+            ];
             $request = $this->getRenewRequest(
                 $renewDetails['patron']['cat_username'],
-                $renewDetails['patron']['cat_password'], $renewId,
+                $renewDetails['patron']['cat_password'], $itemId,
                 $agencyId,
-                $renewDetails['patron']['patron_agency_id']
+                $renewDetails['patron']['patronAgencyId']
             );
             $response = $this->sendRequest($request);
-            $dueDate = $response->xpath('ns1:RenewItemResponse/ns1:DateDue');
-            if ($dueDate) {
-                $tmp = $dueDate;
-                $newDueDate = (string)$tmp[0];
-                $tmp = explode('T', $newDueDate);
-                $splitDate = $tmp[0];
-                $splitTime = $tmp[1];
-                $details[$renewId] = [
+            $dueDateXml = $response->xpath('ns1:RenewItemResponse/ns1:DateDue');
+            $dueDate = '';
+            $dueTime = '';
+            if (!empty($dueDateXml)) {
+                $dueDateString = (string)$dueDateXml[0];
+                $dueDate = $this->displayDate($dueDateString);
+                $dueTime = $this->displayTime($dueDateString);
+            }
+
+            if ($dueDate !== '') {
+                $details[$itemId] = [
                     "success" => true,
-                    "new_date" => $splitDate,
-                    "new_time" => rtrim($splitTime, "Z"),
-                    "item_id" => $renewId,
+                    "new_date" => $dueDate,
+                    "new_time" => $dueTime,
+                    "item_id" => $itemId,
                 ];
             } else {
-                $details[$renewId] = [
-                    "success" => false,
-                    "item_id" => $renewId,
-                ];
+                $details[$itemId] = $failureReturn;
             }
         }
 
-        return [null, "details" => $details];
+        return [ 'blocks' => false, 'details' => $details];
     }
 
     /**
@@ -1517,6 +1676,8 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      * @param string $itemAgencyId Agency ID for item
      * @param string $requestId    Id of the request to cancel
      * @param string $type         The type of request to cancel (Hold, etc)
+     * @param string $itemId       Item identifier
+     * @param string $patronId     Patron identifier
      *
      * @return string           NCIP request XML
      */
@@ -1525,42 +1686,23 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $patronAgency,
         $itemAgencyId,
         $requestId,
-        $type
+        $type,
+        $itemId,
+        $patronId = null
     ) {
-        return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' .
-            '<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ' .
-            'ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/' .
-            'xsd/ncip_v2_0.xsd">' .
-                '<ns1:CancelRequestItem>' .
-                   '<ns1:InitiationHeader>' .
-                        '<ns1:ToAgencyId>' .
-                            '<ns1:AgencyId>' .
-                                htmlspecialchars($patronAgency) .
-                            '</ns1:AgencyId>' .
-                        '</ns1:ToAgencyId>' .
-                    '</ns1:InitiationHeader>' .
-                    '<ns1:AuthenticationInput>' .
-                        '<ns1:AuthenticationInputData>' .
-                            htmlspecialchars($username) .
-                        '</ns1:AuthenticationInputData>' .
-                        '<ns1:AuthenticationDataFormatType>' .
-                            'text' .
-                        '</ns1:AuthenticationDataFormatType>' .
-                        '<ns1:AuthenticationInputType>' .
-                            'Username' .
-                        '</ns1:AuthenticationInputType>' .
-                    '</ns1:AuthenticationInput>' .
-                    '<ns1:AuthenticationInput>' .
-                        '<ns1:AuthenticationInputData>' .
-                            htmlspecialchars($password) .
-                        '</ns1:AuthenticationInputData>' .
-                        '<ns1:AuthenticationDataFormatType>' .
-                            'text' .
-                        '</ns1:AuthenticationDataFormatType>' .
-                        '<ns1:AuthenticationInputType>' .
-                            'Password' .
-                        '</ns1:AuthenticationInputType>' .
-                    '</ns1:AuthenticationInput>' .
+        if ($requestId === null && $itemId === null) {
+            throw new ILSException('No identifiers for CancelRequest');
+        }
+
+        $ret = $this->getNCIPMessageStart() .
+            '<ns1:CancelRequestItem>' .
+            $this->getInitiationHeaderXml($patronAgency) .
+            $this->getAuthenticationInputXml($username, $password);
+
+        $ret .= $this->getUserIdXml($patronAgency, $patronId);
+
+        if ($requestId !== null) {
+            $ret .=
                     '<ns1:RequestId>' .
                         '<ns1:AgencyId>' .
                             htmlspecialchars($itemAgencyId) .
@@ -1568,12 +1710,14 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                         '<ns1:RequestIdentifierValue>' .
                             htmlspecialchars($requestId) .
                         '</ns1:RequestIdentifierValue>' .
-                    '</ns1:RequestId>' .
-                    '<ns1:RequestType>' .
-                        htmlspecialchars($type) .
-                    '</ns1:RequestType>' .
-                '</ns1:CancelRequestItem>' .
-            '</ns1:NCIPMessage>';
+                    '</ns1:RequestId>';
+        }
+        if ($itemId !== null) {
+            $ret .= $this->getItemIdXml($itemAgencyId, $itemId);
+        }
+        $ret .= $this->getRequestTypeXml($type) .
+            '</ns1:CancelRequestItem></ns1:NCIPMessage>';
+        return $ret;
     }
 
     /**
@@ -1584,90 +1728,41 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      * @param string $password         Password for login
      * @param string $bibId            Bib Id of item to request
      * @param string $itemId           Id of item to request
-     * @param string $patron_agency_id Patron agency ID
-     * @param string $pickup_agency_id Pickup agency ID
-     * @param string $item_agency_id   Item agency ID
+     * @param string $patronAgencyId   Patron agency ID
+     * @param string $itemAgencyId     Item agency ID
      * @param string $requestType      Type of the request (Hold, Callslip, etc)
      * @param string $requestScope     Level of request (title, item, etc)
      * @param string $lastInterestDate Last date interested in item
      * @param string $pickupLocation   Code of location to pickup request
+     * @param string $patronId         Patron internal identifier
      *
      * @return string          NCIP request XML
      */
     protected function getRequest($username, $password, $bibId, $itemId,
-        $patron_agency_id, $pickup_agency_id, $item_agency_id,
-        $requestType, $requestScope, $lastInterestDate, $pickupLocation = null
+        $patronAgencyId, $itemAgencyId, $requestType, $requestScope,
+        $lastInterestDate, $pickupLocation = null, $patronId = null
     ) {
-        return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' .
-            '<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ' .
-            'ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/' .
-            'xsd/ncip_v2_0.xsd">' .
-                '<ns1:RequestItem>' .
-                   '<ns1:InitiationHeader>' .
-                        '<ns1:FromAgencyId>' .
-                            '<ns1:AgencyId>' .
-                                htmlspecialchars($pickup_agency_id) .
-                            '</ns1:AgencyId>' .
-                        '</ns1:FromAgencyId>' .
-                        '<ns1:ToAgencyId>' .
-                            '<ns1:AgencyId>' .
-                                htmlspecialchars($patron_agency_id) .
-                            '</ns1:AgencyId>' .
-                        '</ns1:ToAgencyId>' .
-                    '</ns1:InitiationHeader>' .
-                    '<ns1:AuthenticationInput>' .
-                        '<ns1:AuthenticationInputData>' .
-                            htmlspecialchars($username) .
-                        '</ns1:AuthenticationInputData>' .
-                        '<ns1:AuthenticationDataFormatType>' .
-                            'text' .
-                        '</ns1:AuthenticationDataFormatType>' .
-                        '<ns1:AuthenticationInputType>' .
-                            'Username' .
-                        '</ns1:AuthenticationInputType>' .
-                    '</ns1:AuthenticationInput>' .
-                    '<ns1:AuthenticationInput>' .
-                        '<ns1:AuthenticationInputData>' .
-                            htmlspecialchars($password) .
-                        '</ns1:AuthenticationInputData>' .
-                        '<ns1:AuthenticationDataFormatType>' .
-                            'text' .
-                        '</ns1:AuthenticationDataFormatType>' .
-                        '<ns1:AuthenticationInputType>' .
-                            'Password' .
-                        '</ns1:AuthenticationInputType>' .
-                    '</ns1:AuthenticationInput>' .
-                    '<ns1:BibliographicId>' .
-                        '<ns1:BibliographicRecordId>' .
-                            '<ns1:AgencyId>' .
-                                htmlspecialchars($item_agency_id) .
-                            '</ns1:AgencyId>' .
-                            '<ns1:BibliographicRecordIdentifier>' .
-                                htmlspecialchars($bibId) .
-                            '</ns1:BibliographicRecordIdentifier>' .
-                        '</ns1:BibliographicRecordId>' .
-                    '</ns1:BibliographicId>' .
-                    '<ns1:ItemId>' .
-                        '<ns1:ItemIdentifierValue>' .
-                            htmlspecialchars($itemId) .
-                        '</ns1:ItemIdentifierValue>' .
-                    '</ns1:ItemId>' .
-                    '<ns1:RequestType>' .
-                            htmlspecialchars($requestType) .
-                    '</ns1:RequestType>' .
-                    '<ns1:RequestScopeType ' .
-                        'ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes' .
-                        '/requestscopetype/requestscopetype.scm">' .
-                            htmlspecialchars($requestScope) .
-                    '</ns1:RequestScopeType>' .
-                    '<ns1:PickupLocation>' .
-                        htmlspecialchars($pickupLocation) .
-                    '</ns1:PickupLocation>' .
-                    '<ns1:PickupExpiryDate>' .
-                        htmlspecialchars($lastInterestDate) .
-                    '</ns1:PickupExpiryDate>' .
-                '</ns1:RequestItem>' .
-            '</ns1:NCIPMessage>';
+        $ret = $this->getNCIPMessageStart() .
+            '<ns1:RequestItem>' .
+            $this->getInitiationHeaderXml($patronAgencyId) .
+            $this->getAuthenticationInputXml($username, $password) .
+            $this->getUserIdXml($patronAgencyId, $patronId) .
+            $this->getBibliographicId($bibId) .
+            $this->getItemIdXml($itemAgencyId, $itemId) .
+            $this->getRequestTypeXml($requestType, $requestScope);
+
+        if (!empty($pickupLocation)) {
+            $ret .= '<ns1:PickupLocation>' .
+                htmlspecialchars($pickupLocation) .
+            '</ns1:PickupLocation>';
+        }
+        if (!empty($lastInterestDate)) {
+            $ret .= '<ns1:NeedBeforeDate>' .
+                htmlspecialchars($lastInterestDate) .
+            '</ns1:NeedBeforeDate>';
+        }
+        $ret .= '</ns1:RequestItem></ns1:NCIPMessage>';
+        return $ret;
     }
 
     /**
@@ -1678,6 +1773,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      * @param string $itemId         Id of item to renew
      * @param string $itemAgencyId   Agency of Item Id to renew
      * @param string $patronAgencyId Agency of patron
+     * @param string $patronId       Internal patron id
      *
      * @return string          NCIP request XML
      */
@@ -1685,124 +1781,271 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $password,
         $itemId,
         $itemAgencyId,
-        $patronAgencyId
+        $patronAgencyId,
+        $patronId = null
     ) {
-        return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' .
-            '<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ' .
-            'ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/' .
-            'xsd/ncip_v2_0.xsd">' .
-                '<ns1:RenewItem>' .
-                   '<ns1:InitiationHeader>' .
-                        '<ns1:ToAgencyId>' .
-                            '<ns1:AgencyId>' .
-                                htmlspecialchars($patronAgencyId) .
-                            '</ns1:AgencyId>' .
-                        '</ns1:ToAgencyId>' .
-                    '</ns1:InitiationHeader>' .
-                    '<ns1:AuthenticationInput>' .
-                        '<ns1:AuthenticationInputData>' .
-                            htmlspecialchars($username) .
-                        '</ns1:AuthenticationInputData>' .
-                        '<ns1:AuthenticationDataFormatType>' .
-                            'text' .
-                        '</ns1:AuthenticationDataFormatType>' .
-                        '<ns1:AuthenticationInputType>' .
-                            'Username' .
-                        '</ns1:AuthenticationInputType>' .
-                    '</ns1:AuthenticationInput>' .
-                    '<ns1:AuthenticationInput>' .
-                        '<ns1:AuthenticationInputData>' .
-                            htmlspecialchars($password) .
-                        '</ns1:AuthenticationInputData>' .
-                        '<ns1:AuthenticationDataFormatType>' .
-                            'text' .
-                        '</ns1:AuthenticationDataFormatType>' .
-                        '<ns1:AuthenticationInputType>' .
-                            'Password' .
-                        '</ns1:AuthenticationInputType>' .
-                    '</ns1:AuthenticationInput>' .
-                    '<ns1:ItemId>' .
-                        '<ns1:AgencyId>' .
-                            htmlspecialchars($itemAgencyId) .
-                        '</ns1:AgencyId>' .
-                        '<ns1:ItemIdentifierValue>' .
-                            htmlspecialchars($itemId) .
-                        '</ns1:ItemIdentifierValue>' .
-                    '</ns1:ItemId>' .
-                '</ns1:RenewItem>' .
-            '</ns1:NCIPMessage>';
+        return $this->getNCIPMessageStart() .
+            '<ns1:RenewItem>' .
+            $this->getInitiationHeaderXml($patronAgencyId) .
+            $this->getAuthenticationInputXml($username, $password) .
+            $this->getUserIdXml($patronAgencyId, $patronId) .
+            $this->getItemIdXml($itemAgencyId, $itemId) .
+            '</ns1:RenewItem></ns1:NCIPMessage>';
     }
 
     /**
      * Helper function to build the request XML to log in a user
      * and/or retrieve loaned items / request information
      *
-     * @param string $username         Username for login
-     * @param string $password         Password for login
-     * @param string $patron_agency_id Patron agency ID (optional)
-     * @param string $extras           Extra elements to include in the request
+     * @param string $username       Username for login
+     * @param string $password       Password for login
+     * @param string $patronAgencyId Patron agency ID (optional)
+     * @param string $extras         Extra elements to include in the request
+     * @param string $patronId       Patron internal identifier
      *
      * @return string          NCIP request XML
      */
     protected function getLookupUserRequest($username, $password,
-        $patron_agency_id = null, $extras = []
+        $patronAgencyId = null, $extras = [], $patronId = null
     ) {
-        $ret = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' .
-            '<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ' .
-            'ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/' .
-            'xsd/ncip_v2_0.xsd">' .
-                '<ns1:LookupUser>';
+        return $this->getNCIPMessageStart() .
+            '<ns1:LookupUser>' .
+            $this->getInitiationHeaderXml($patronAgencyId) .
+            $this->getAuthenticationInputXml($username, $password) .
+            $this->getUserIdXml($patronAgencyId, $patronId) .
+            implode('', $extras) .
+            '</ns1:LookupUser></ns1:NCIPMessage>';
+    }
 
-        if (null !== $patron_agency_id) {
-            $ret .=
-                   '<ns1:InitiationHeader>' .
-                        '<ns1:FromAgencyId>' .
-                            '<ns1:AgencyId>' .
-                                htmlspecialchars($patron_agency_id) .
-                            '</ns1:AgencyId>' .
-                        '</ns1:FromAgencyId>' .
-                        '<ns1:ToAgencyId>' .
-                            '<ns1:AgencyId>' .
-                                htmlspecialchars($patron_agency_id) .
-                            '</ns1:AgencyId>' .
-                        '</ns1:ToAgencyId>' .
-                    '</ns1:InitiationHeader>';
+    /**
+     * Get LookupAgency Request XML message
+     *
+     * @param string|null $agency Agency Id
+     *
+     * @return string XML Document
+     */
+    public function getLookupAgencyRequest($agency = null)
+    {
+        $agency = $this->determineToAgencyId($agency);
+
+        $ret = $this->getNCIPMessageStart() .
+            '<ns1:LookupAgency>' .
+             $this->getInitiationHeaderXml($agency) .
+            '<ns1:AgencyId>' . htmlspecialchars($agency) . '</ns1:AgencyId>';
+
+        $desiredElementTypes = [
+            'Agency Address Information', 'Agency User Privilege Type',
+            'Application Profile Supported Type', 'Authentication Prompt',
+            'Consortium Agreement', 'Organization Name Information'
+        ];
+        foreach ($desiredElementTypes as $elementType) {
+            $ret .= '<ns1:AgencyElementType ' .
+                'ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/' .
+                'agencyelementtype/agencyelementtype.scm">' .
+                    $elementType .
+                '</ns1:AgencyElementType>';
         }
+        $ret .= '</ns1:LookupAgency></ns1:NCIPMessage>';
+        return $ret;
+    }
 
-        $ret .=
-                    '<ns1:AuthenticationInput>' .
-                        '<ns1:AuthenticationInputData>' .
-                            htmlspecialchars($username) .
-                        '</ns1:AuthenticationInputData>' .
-                        '<ns1:AuthenticationDataFormatType>' .
-                            'text' .
-                        '</ns1:AuthenticationDataFormatType>' .
-                        '<ns1:AuthenticationInputType>' .
-                            'Username' .
-                        '</ns1:AuthenticationInputType>' .
-                    '</ns1:AuthenticationInput>' .
-                    '<ns1:AuthenticationInput>' .
-                        '<ns1:AuthenticationInputData>' .
-                            htmlspecialchars($password) .
-                        '</ns1:AuthenticationInputData>' .
-                        '<ns1:AuthenticationDataFormatType>' .
-                            'text' .
-                        '</ns1:AuthenticationDataFormatType>' .
-                        '<ns1:AuthenticationInputType>' .
-                            'Password' .
-                        '</ns1:AuthenticationInputType>' .
-                    '</ns1:AuthenticationInput>' .
-                    implode('', $extras) .
-                '</ns1:LookupUser>' .
-            '</ns1:NCIPMessage>';
+    /**
+     * Create Lookup Item Request
+     *
+     * @param string $itemId Item identifier
+     * @param string $idType Item identifier type
+     *
+     * @return string XML document
+     */
+    protected function getLookupItemRequest($itemId, $idType = null)
+    {
+        $keys = array_keys($this->agency);
+        $agency = $keys[0];
+
+        $ret = $this->getNCIPMessageStart() .
+            '<ns1:LookupItem>' .
+            $this->getInitiationHeaderXml($agency) .
+            $this->getItemIdXml($agency, $itemId, $idType) .
+            '<ns1:ItemElementType ' .
+                'ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/' .
+                'itemelementtype/itemelementtype.scm">' .
+                'Bibliographic Description</ns1:ItemElementType>' .
+        '</ns1:LookupItem></ns1:NCIPMessage>';
+        return $ret;
+    }
 
+    /**
+     * Get InitiationHeader element XML string
+     *
+     * @param string $agency Agency of NCIP responder
+     *
+     * @return string
+     */
+    protected function getInitiationHeaderXml($agency = '')
+    {
+        if (empty($agency) || empty($this->fromAgency)) {
+            return '';
+        }
+        return '<ns1:InitiationHeader>' .
+                '<ns1:FromAgencyId>' .
+                    '<ns1:AgencyId>' .
+                        htmlspecialchars($this->fromAgency) .
+                    '</ns1:AgencyId>' .
+                '</ns1:FromAgencyId>' .
+                '<ns1:ToAgencyId>' .
+                    '<ns1:AgencyId>' .
+                        htmlspecialchars($agency) .
+                    '</ns1:AgencyId>' .
+                '</ns1:ToAgencyId>' .
+            '</ns1:InitiationHeader>';
+    }
+
+    /**
+     * Helper method for creating XML header and main element start
+     *
+     * @return string
+     */
+    protected function getNCIPMessageStart()
+    {
+        return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' .
+            '<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ' .
+            'ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">';
+    }
+
+    /**
+     * Get XML string for AuthenticationInput element
+     *
+     * @param string $username User login
+     * @param string $password User password
+     *
+     * @return string XML string for AuthenticationInput element
+     */
+    protected function getAuthenticationInputXml($username, $password)
+    {
+        return (!empty($username) && !empty($password))
+            ? '<ns1:AuthenticationInput>' .
+                '<ns1:AuthenticationInputData>' .
+                    htmlspecialchars($username) .
+                '</ns1:AuthenticationInputData>' .
+                '<ns1:AuthenticationDataFormatType>' .
+                    'text' .
+                '</ns1:AuthenticationDataFormatType>' .
+                '<ns1:AuthenticationInputType>' .
+                    'Username' .
+                '</ns1:AuthenticationInputType>' .
+            '</ns1:AuthenticationInput>' .
+            '<ns1:AuthenticationInput>' .
+                '<ns1:AuthenticationInputData>' .
+                    htmlspecialchars($password) .
+                '</ns1:AuthenticationInputData>' .
+                '<ns1:AuthenticationDataFormatType>' .
+                    'text' .
+                '</ns1:AuthenticationDataFormatType>' .
+                '<ns1:AuthenticationInputType>' .
+                    'Password' .
+                '</ns1:AuthenticationInputType>' .
+            '</ns1:AuthenticationInput>'
+            : '';
+    }
+
+    /**
+     * Get ItemId element XML
+     *
+     * @param string      $agency Agency id
+     * @param string      $itemId Item id
+     * @param null|string $idType Item id type
+     *
+     * @return string ItemId element XML string
+     */
+    protected function getItemIdXml($agency, $itemId, $idType = null)
+    {
+        $ret = '<ns1:ItemId><ns1:AgencyId>' .
+            htmlspecialchars($agency) . '</ns1:AgencyId>';
+        if ($idType !== null) {
+            $ret .= '<ns1:ItemIdentifierType>' .
+                htmlspecialchars($idType) . '</ns1:ItemIdentifierType>';
+        }
+        $ret .= '<ns1:ItemIdentifierValue>' .
+            htmlspecialchars($itemId) . '</ns1:ItemIdentifierValue>' .
+            '</ns1:ItemId>';
         return $ret;
     }
 
+    /**
+     * Get UserId element XML
+     *
+     * @param string $patronAgency Patron agency id
+     * @param string $patronId     Internal patron identifier
+     *
+     * @return string Get UserId element XML string
+     */
+    protected function getUserIdXml($patronAgency, $patronId = null)
+    {
+        if ($patronId !== null) {
+            return '<ns1:UserId>' .
+                '<ns1:AgencyId>' .
+                    htmlspecialchars($patronAgency) .
+                '</ns1:AgencyId>' .
+                '<ns1:UserIdentifierType>Institution Id Number' .
+                '</ns1:UserIdentifierType>' .
+                '<ns1:UserIdentifierValue>' .
+                    htmlspecialchars($patronId) .
+                '</ns1:UserIdentifierValue>' .
+            '</ns1:UserId>';
+        }
+        return '';
+    }
+
+    /**
+     * Get request type elements XML
+     *
+     * @param string $type  Request type
+     * @param string $scope Request type scope (defaults to 'Bibliographic Item')
+     *
+     * @return string RequestType and RequestScopeType element XML string
+     */
+    protected function getRequestTypeXml($type, $scope = 'Bibliographic Item')
+    {
+        return '<ns1:RequestType ' .
+                'ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/' .
+                'requesttype/requesttype.scm">' .
+                htmlspecialchars($type) .
+            '</ns1:RequestType>' .
+            '<ns1:RequestScopeType ' .
+                'ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/' .
+                'requestscopetype/requestscopetype.scm">' .
+                htmlspecialchars($scope) .
+            '</ns1:RequestScopeType>';
+    }
+
+    /**
+     * Get BibliographicId element
+     *
+     * @param string $id Bibliographic item id
+     *
+     * @return string Get BibiographicId XML element string
+     */
+    protected function getBibliographicId($id)
+    {
+        return '<ns1:BibliographicId>' .
+            '<ns1:BibliographicItemId>' .
+                '<ns1:BibliographicItemIdentifier>' .
+                    htmlspecialchars($id) .
+                '</ns1:BibliographicItemIdentifier>' .
+                '<ns1:BibliographicItemIdentifierCode ' .
+                    'ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/' .
+                    'schemes/bibliographicitemidentifiercode/' .
+                    'bibliographicitemidentifiercode.scm">' .
+                    'Legal Deposit Number' .
+                '</ns1:BibliographicItemIdentifierCode>' .
+            '</ns1:BibliographicItemId>' .
+        '</ns1:BibliographicId>';
+    }
+
     /**
      * Throw an exception if an NCIP error is found
      *
-     * @param XML $response from NCIP call
+     * @param \SimpleXMLElement $response from NCIP call
      *
      * @throws ILSException
      * @return void
@@ -1816,4 +2059,143 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             throw new ILSException($error[0]);
         }
     }
+
+    /**
+     * Register namespace(s) for an XML element/tree
+     *
+     * @param \SimpleXMLElement $element Element to register namespace for
+     *
+     * @return void
+     */
+    protected function registerNamespaceFor(\SimpleXMLElement $element)
+    {
+        $element->registerXPathNamespace('ns1', 'http://www.niso.org/2008/ncip');
+    }
+
+    /**
+     * Convert a date to display format
+     *
+     * @param string $date Date and time string
+     *
+     * @return string
+     */
+    protected function displayDate($date)
+    {
+        return $this->convertDateOrTime($date);
+    }
+
+    /**
+     * Convert a time to display format
+     *
+     * @param string $date Date and time string
+     *
+     * @return string
+     */
+    protected function displayTime($date)
+    {
+        return $this->convertDateOrTime($date, 'time');
+    }
+
+    /**
+     * Convert datetime to display format
+     *
+     * @param string $dateString Datetime string
+     * @param string $dateOrTime Desired datetime part, could be 'date' or 'time'
+     *
+     * @return string
+     */
+    protected function convertDateOrTime($dateString, $dateOrTime = 'date')
+    {
+        if (!$dateString) {
+            return '';
+        }
+        $createFormats = ['Y-m-d\TH:i:s.uP', 'Y-m-d\TH:i:sP'];
+        $formatted = '';
+        foreach ($createFormats as $format) {
+            try {
+                $formatted = ($dateOrTime === 'time')
+                    ? $this->dateConverter->convertToDisplayTime(
+                        $format, $dateString
+                    )
+                    : $this->dateConverter->convertToDisplayDate(
+                        $format, $dateString
+                    );
+            } catch (DateException $exception) {
+                continue;
+            }
+        }
+        return $formatted;
+    }
+
+    /**
+     * Get Hold Type
+     *
+     * @param string $status Status string from CirculationStatus NCIP element
+     *
+     * @return string Hold type, could be 'Hold' or 'Recall'
+     */
+    protected function getHoldType(string $status)
+    {
+        return in_array(strtolower($status), $this->availableStatuses)
+            ? 'Hold' : 'Recall';
+    }
+
+    /**
+     * Is an item available?
+     *
+     * @param string $status Status string from CirculationStatus NCIP element
+     *
+     * @return bool Return true if item is available
+     */
+    protected function isAvailable(string $status)
+    {
+        return in_array(strtolower($status), $this->availableStatuses);
+    }
+
+    /**
+     * Is request cancelled?
+     *
+     * @param string $status Status string from RequestStatusType NCIP element
+     *
+     * @return bool Return true if a request was cancelled
+     */
+    protected function isRequestCancelled(string $status)
+    {
+        return !in_array(strtolower($status), $this->activeRequestStatuses);
+    }
+
+    /**
+     * Is request of desired type?
+     *
+     * @param \SimpleXMLElement $request RequestedItem NCIP Element
+     * @param array             $types   Array of types to check against
+     *
+     * @return bool Return true if request is of desired type
+     */
+    protected function checkRequestType(\SimpleXMLElement $request, array $types)
+    {
+        $requestType = $request->xpath('ns1:RequestType');
+        $requestType = (string)$requestType[0];
+        return in_array(strtolower($requestType), $types);
+    }
+
+    /**
+     * Determine ToAgencyId
+     *
+     * @param array|string|null $agency List of available (configured) agencies or
+     * Agency Id
+     *
+     * @return string|null First Agency Id found
+     */
+    protected function determineToAgencyId($agency = null)
+    {
+        // FIXME: We are using the first defined agency, it will probably not work in
+        // consortium scenario
+        if (null === $agency) {
+            $keys = array_keys($this->agency);
+            $agency = $keys[0];
+        }
+
+        return is_array($agency) ? $agency[0] : $agency;
+    }
 }
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/CancelRequestItemRequest.xml b/module/VuFind/tests/fixtures/xcncip2/request/CancelRequestItemRequest.xml
new file mode 100644
index 00000000000..1a789639730
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/CancelRequestItemRequest.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:CancelRequestItem><ns1:UserId><ns1:AgencyId>patron agency</ns1:AgencyId><ns1:UserIdentifierType>Institution Id Number</ns1:UserIdentifierType><ns1:UserIdentifierValue>12345</ns1:UserIdentifierValue></ns1:UserId><ns1:RequestId><ns1:AgencyId>item agency</ns1:AgencyId><ns1:RequestIdentifierValue>rq1</ns1:RequestIdentifierValue></ns1:RequestId><ns1:ItemId><ns1:AgencyId>item agency</ns1:AgencyId><ns1:ItemIdentifierValue>item1</ns1:ItemIdentifierValue></ns1:ItemId><ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Hold</ns1:RequestType><ns1:RequestScopeType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requestscopetype/requestscopetype.scm">Bibliographic Item</ns1:RequestScopeType></ns1:CancelRequestItem></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/CancelRequestItemRequestAuthInput.xml b/module/VuFind/tests/fixtures/xcncip2/request/CancelRequestItemRequestAuthInput.xml
new file mode 100644
index 00000000000..4ef53888d77
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/CancelRequestItemRequestAuthInput.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:CancelRequestItem><ns1:AuthenticationInput><ns1:AuthenticationInputData>username</ns1:AuthenticationInputData><ns1:AuthenticationDataFormatType>text</ns1:AuthenticationDataFormatType><ns1:AuthenticationInputType>Username</ns1:AuthenticationInputType></ns1:AuthenticationInput><ns1:AuthenticationInput><ns1:AuthenticationInputData>password</ns1:AuthenticationInputData><ns1:AuthenticationDataFormatType>text</ns1:AuthenticationDataFormatType><ns1:AuthenticationInputType>Password</ns1:AuthenticationInputType></ns1:AuthenticationInput><ns1:UserId><ns1:AgencyId>patron agency</ns1:AgencyId><ns1:UserIdentifierType>Institution Id Number</ns1:UserIdentifierType><ns1:UserIdentifierValue>12345</ns1:UserIdentifierValue></ns1:UserId><ns1:RequestId><ns1:AgencyId>item agency</ns1:AgencyId><ns1:RequestIdentifierValue>rq1</ns1:RequestIdentifierValue></ns1:RequestId><ns1:ItemId><ns1:AgencyId>item agency</ns1:AgencyId><ns1:ItemIdentifierValue>item1</ns1:ItemIdentifierValue></ns1:ItemId><ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Hold</ns1:RequestType><ns1:RequestScopeType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requestscopetype/requestscopetype.scm">Bibliographic Item</ns1:RequestScopeType></ns1:CancelRequestItem></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/LookupAgencyRequest.xml b/module/VuFind/tests/fixtures/xcncip2/request/LookupAgencyRequest.xml
new file mode 100644
index 00000000000..f9da6e79cd6
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/LookupAgencyRequest.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:LookupAgency><ns1:AgencyId>Test agency</ns1:AgencyId><ns1:AgencyElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyelementtype/agencyelementtype.scm">Agency Address Information</ns1:AgencyElementType><ns1:AgencyElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyelementtype/agencyelementtype.scm">Agency User Privilege Type</ns1:AgencyElementType><ns1:AgencyElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyelementtype/agencyelementtype.scm">Application Profile Supported Type</ns1:AgencyElementType><ns1:AgencyElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyelementtype/agencyelementtype.scm">Authentication Prompt</ns1:AgencyElementType><ns1:AgencyElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyelementtype/agencyelementtype.scm">Consortium Agreement</ns1:AgencyElementType><ns1:AgencyElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyelementtype/agencyelementtype.scm">Organization Name Information</ns1:AgencyElementType></ns1:LookupAgency></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/LookupItemRequest.xml b/module/VuFind/tests/fixtures/xcncip2/request/LookupItemRequest.xml
new file mode 100644
index 00000000000..b71b2c7cda7
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/LookupItemRequest.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:LookupItem><ns1:InitiationHeader><ns1:FromAgencyId><ns1:AgencyId>My portal</ns1:AgencyId></ns1:FromAgencyId><ns1:ToAgencyId><ns1:AgencyId>Test agency</ns1:AgencyId></ns1:ToAgencyId></ns1:InitiationHeader><ns1:ItemId><ns1:AgencyId>Test agency</ns1:AgencyId><ns1:ItemIdentifierType>Accession Number</ns1:ItemIdentifierType><ns1:ItemIdentifierValue>item1</ns1:ItemIdentifierValue></ns1:ItemId><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Bibliographic Description</ns1:ItemElementType></ns1:LookupItem></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/LookupItemSetRequest.xml b/module/VuFind/tests/fixtures/xcncip2/request/LookupItemSetRequest.xml
new file mode 100644
index 00000000000..b68b2b4d916
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/LookupItemSetRequest.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:LookupItemSet><ns1:InitiationHeader><ns1:FromAgencyId><ns1:AgencyId>My portal</ns1:AgencyId></ns1:FromAgencyId><ns1:ToAgencyId><ns1:AgencyId>Test agency</ns1:AgencyId></ns1:ToAgencyId></ns1:InitiationHeader><ns1:BibliographicId><ns1:BibliographicItemId><ns1:BibliographicItemIdentifier>1</ns1:BibliographicItemIdentifier><ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</ns1:BibliographicItemIdentifierCode></ns1:BibliographicItemId></ns1:BibliographicId><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Bibliographic Description</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Circulation Status</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Electronic Resource</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Hold Queue Length</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Item Description</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Item Use Restriction Type</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Location</ns1:ItemElementType></ns1:LookupItemSet></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/LookupItemSetRequestWithoutHeader.xml b/module/VuFind/tests/fixtures/xcncip2/request/LookupItemSetRequestWithoutHeader.xml
new file mode 100644
index 00000000000..a4232ee4c48
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/LookupItemSetRequestWithoutHeader.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:LookupItemSet><ns1:BibliographicId><ns1:BibliographicItemId><ns1:BibliographicItemIdentifier>1</ns1:BibliographicItemIdentifier><ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</ns1:BibliographicItemIdentifierCode></ns1:BibliographicItemId></ns1:BibliographicId><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Bibliographic Description</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Circulation Status</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Electronic Resource</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Hold Queue Length</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Item Description</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Item Use Restriction Type</ns1:ItemElementType><ns1:ItemElementType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemelementtype/itemelementtype.scm">Location</ns1:ItemElementType></ns1:LookupItemSet></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/LookupUserRequest.xml b/module/VuFind/tests/fixtures/xcncip2/request/LookupUserRequest.xml
new file mode 100644
index 00000000000..b5e53a6015f
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/LookupUserRequest.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:LookupUser><ns1:UserId><ns1:AgencyId>patron agency</ns1:AgencyId><ns1:UserIdentifierType>Institution Id Number</ns1:UserIdentifierType><ns1:UserIdentifierValue>patron1</ns1:UserIdentifierValue></ns1:UserId><ns1:LoanedItemsDesired /></ns1:LookupUser></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/RenewItemRequest.xml b/module/VuFind/tests/fixtures/xcncip2/request/RenewItemRequest.xml
new file mode 100644
index 00000000000..2df40283391
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/RenewItemRequest.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:RenewItem><ns1:AuthenticationInput><ns1:AuthenticationInputData>username</ns1:AuthenticationInputData><ns1:AuthenticationDataFormatType>text</ns1:AuthenticationDataFormatType><ns1:AuthenticationInputType>Username</ns1:AuthenticationInputType></ns1:AuthenticationInput><ns1:AuthenticationInput><ns1:AuthenticationInputData>password</ns1:AuthenticationInputData><ns1:AuthenticationDataFormatType>text</ns1:AuthenticationDataFormatType><ns1:AuthenticationInputType>Password</ns1:AuthenticationInputType></ns1:AuthenticationInput><ns1:ItemId><ns1:AgencyId>item agency</ns1:AgencyId><ns1:ItemIdentifierValue>item1</ns1:ItemIdentifierValue></ns1:ItemId></ns1:RenewItem></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/request/RequestItemRequest.xml b/module/VuFind/tests/fixtures/xcncip2/request/RequestItemRequest.xml
new file mode 100644
index 00000000000..1d657e73176
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/request/RequestItemRequest.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd"><ns1:RequestItem><ns1:InitiationHeader><ns1:FromAgencyId><ns1:AgencyId>My portal</ns1:AgencyId></ns1:FromAgencyId><ns1:ToAgencyId><ns1:AgencyId>patron agency</ns1:AgencyId></ns1:ToAgencyId></ns1:InitiationHeader><ns1:UserId><ns1:AgencyId>patron agency</ns1:AgencyId><ns1:UserIdentifierType>Institution Id Number</ns1:UserIdentifierType><ns1:UserIdentifierValue>patron1</ns1:UserIdentifierValue></ns1:UserId><ns1:BibliographicId><ns1:BibliographicItemId><ns1:BibliographicItemIdentifier>bib1</ns1:BibliographicItemIdentifier><ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</ns1:BibliographicItemIdentifierCode></ns1:BibliographicItemId></ns1:BibliographicId><ns1:ItemId><ns1:AgencyId>item agency</ns1:AgencyId><ns1:ItemIdentifierValue>item1</ns1:ItemIdentifierValue></ns1:ItemId><ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Hold</ns1:RequestType><ns1:RequestScopeType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requestscopetype/requestscopetype.scm">Item</ns1:RequestScopeType><ns1:NeedBeforeDate>2020-12-20T00:00:00.000Z</ns1:NeedBeforeDate></ns1:RequestItem></ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseAccepted.xml b/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseAccepted.xml
new file mode 100644
index 00000000000..db21a43393f
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseAccepted.xml
@@ -0,0 +1,19 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">
+    <ns1:CancelRequestItemResponse>
+        <ns1:RequestId>
+            <ns1:AgencyId ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">CBA001</ns1:AgencyId>
+            <ns1:RequestIdentifierType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">IDX</ns1:RequestIdentifierType>
+            <ns1:RequestIdentifierValue>cbvk_trx*13512707</ns1:RequestIdentifierValue>
+        </ns1:RequestId>
+        <ns1:UserId>
+            <ns1:AgencyId ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">CBA001</ns1:AgencyId>
+            <ns1:UserIdentifierType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">INSTITUTION_ID_NUMBER</ns1:UserIdentifierType>
+            <ns1:UserIdentifierValue>0059085</ns1:UserIdentifierValue>
+        </ns1:UserId>
+    </ns1:CancelRequestItemResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseDenied.xml b/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseDenied.xml
new file mode 100644
index 00000000000..c7b27f681c1
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseDenied.xml
@@ -0,0 +1,14 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">
+    <ns1:CancelRequestItemResponse>
+        <ns1:Problem>
+            <ns1:ProblemType ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/processingerrortype/cancelrequestitemprocessingerror.scm">Element Rule Violated</ns1:ProblemType>
+            <ns1:ProblemDetail>Error ARL WS CancelRequestItem request retcode = ERRNCIP215, retmsg = Error ARL WS NCIP CancelRequestItem, CHYBA - TRX záznam cbvk_trx*13502448 není možno smazat s modulu IPAC. code:ERRX003I#cbvk_trx*13502448</ns1:ProblemDetail>
+            <ns1:ProblemElement>CancelRequestItem</ns1:ProblemElement>
+        </ns1:Problem>
+    </ns1:CancelRequestItemResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseDeniedWithUserId.xml b/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseDeniedWithUserId.xml
new file mode 100644
index 00000000000..ba35cb0fbe7
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/CancelRequestItemResponseDeniedWithUserId.xml
@@ -0,0 +1,39 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8"?>
+<NCIPMessage version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd">
+    <CancelRequestItemResponse>
+        <ItemId>
+            <AgencyId Scheme="http://www.niso.org/ncip/v1_0/schemes/agencyidtype/agencyidtype.scm">ABA008</AgencyId>
+            <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+            <ItemIdentifierValue>T015480</ItemIdentifierValue>
+        </ItemId>
+        <ResponseHeader>
+            <FromAgencyId>
+                <AgencyId>ABA008</AgencyId>
+            </FromAgencyId>
+            <ToAgencyId>
+                <AgencyId>CPK</AgencyId>
+            </ToAgencyId>
+        </ResponseHeader>
+        <UserId>
+            <AgencyId>ABA008</AgencyId>
+            <UserIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Institution Id Number</UserIdentifierType>
+            <UserIdentifierValue>C5452001</UserIdentifierValue>
+        </UserId>
+        <Problem>
+            <ProblemType Scheme="http://www.niso.org/ncip/v1_0/schemes/processingerrortype/generalprocessingerror.scm">Temporary Processing Failure</ProblemType>
+            <ProblemDetail>CancelRequestItem Error</ProblemDetail>
+            <ProblemElement>ItemIdentifier</ProblemElement>
+            <ProblemValue>Všeobecná operácia nad databázou
+                OpacModifyDataRec -&gt;
+                Získanie údajov výpožičky
+                [ID=-1?]
+                Neznáma chyba [-1]
+                []
+            </ProblemValue>
+        </Problem>
+    </CancelRequestItemResponse>
+</NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/LookupAgencyResponse.xml b/module/VuFind/tests/fixtures/xcncip2/response/LookupAgencyResponse.xml
new file mode 100644
index 00000000000..dcdf6c1e6dd
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/LookupAgencyResponse.xml
@@ -0,0 +1,55 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8"?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd">
+    <ns1:LookupAgencyResponse>
+        <ns1:AgencyId ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/agencyelementtype/agencyelementtype.scm">My library</ns1:AgencyId>
+        <ns1:OrganizationNameInformation>
+            <ns1:OrganizationNameType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/organizationnametype/organizationnametype.scm">Translated Name</ns1:OrganizationNameType>
+            <ns1:OrganizationName>My personal library</ns1:OrganizationName>
+        </ns1:OrganizationNameInformation>
+        <ns1:AgencyAddressInformation>
+            <ns1:AgencyAddressRoleType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyaddressroletype/agencyaddressroletype.scm">Official</ns1:AgencyAddressRoleType>
+            <ns1:PhysicalAddress>
+                <ns1:UnstructuredAddress>
+                    <ns1:UnstructuredAddressType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/unstructuredaddresstype/unstructuredaddresstype.scm">Newline-Delimited Text</ns1:UnstructuredAddressType>
+                    <ns1:UnstructuredAddressData>Some street 123, City 1</ns1:UnstructuredAddressData>
+                </ns1:UnstructuredAddress>
+                <ns1:PhysicalAddressType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/physicaladdresstype/physicaladdresstype.scm">Street Address</ns1:PhysicalAddressType>
+            </ns1:PhysicalAddress>
+        </ns1:AgencyAddressInformation>
+        <ns1:Ext>
+            <ns1:LocationName>
+                <ns1:LocationNameInstance>
+                    <ns1:LocationNameLevel>1</ns1:LocationNameLevel>
+                    <ns1:LocationNameValue>Main library</ns1:LocationNameValue>
+                    <ns1:Ext>
+                        <ns1:PhysicalAddress>
+                            <ns1:UnstructuredAddress>
+                                <ns1:UnstructuredAddressType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/unstructuredaddresstype/unstructuredaddresstype.scm">Newline-Delimited Text</ns1:UnstructuredAddressType>
+                                <ns1:UnstructuredAddressData>Some street 123, City 1</ns1:UnstructuredAddressData>
+                            </ns1:UnstructuredAddress>
+                            <ns1:PhysicalAddressType>Street Address</ns1:PhysicalAddressType>
+                        </ns1:PhysicalAddress>
+                    </ns1:Ext>
+                </ns1:LocationNameInstance>
+                <ns1:LocationNameInstance>
+                    <ns1:LocationNameLevel>2</ns1:LocationNameLevel>
+                    <ns1:LocationNameValue>Stacks</ns1:LocationNameValue>
+                    <ns1:Ext>
+                        <ns1:PhysicalAddress>
+                            <ns1:UnstructuredAddress>
+                                <ns1:UnstructuredAddressType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/unstructuredaddresstype/unstructuredaddresstype.scm">Newline-Delimited Text</ns1:UnstructuredAddressType>
+                                <ns1:UnstructuredAddressData>Some street 123, City 1</ns1:UnstructuredAddressData>
+                            </ns1:UnstructuredAddress>
+                            <ns1:PhysicalAddressType>Street Address</ns1:PhysicalAddressType>
+                        </ns1:PhysicalAddress>
+                    </ns1:Ext>
+                </ns1:LocationNameInstance>
+            </ns1:LocationName>
+        </ns1:Ext>
+    </ns1:LookupAgencyResponse>
+</ns1:NCIPMessage>
+
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/LookupAgencyResponseWithoutLocations.xml b/module/VuFind/tests/fixtures/xcncip2/response/LookupAgencyResponseWithoutLocations.xml
new file mode 100644
index 00000000000..284f6d431e2
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/LookupAgencyResponseWithoutLocations.xml
@@ -0,0 +1,40 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8"?>
+<NCIPMessage xmlns="http://www.niso.org/2008/ncip">
+    <LookupAgencyResponse>
+        <AgencyId>My University</AgencyId>
+        <OrganizationNameInformation>
+            <OrganizationNameType>Official Name</OrganizationNameType>
+            <OrganizationName>My University library</OrganizationName>
+        </OrganizationNameInformation>
+        <AgencyAddressInformation>
+            <physicalAddress>
+                <UnstructuredAddress>
+                    <UnstructuredAddressType>Newline-Delimited Text</UnstructuredAddressType>
+                    <UnstructuredAddressData>Some Street 58, Some City</UnstructuredAddressData>
+                </UnstructuredAddress>
+            </physicalAddress>
+        </AgencyAddressInformation>
+        <AgencyAddressInformation>
+            <electronicAddress>
+                <ElectronicAddressType>tel</ElectronicAddressType>
+                <ElectronicAddressData>475 209 126</ElectronicAddressData>
+            </electronicAddress>
+        </AgencyAddressInformation>
+        <AgencyAddressInformation>
+            <electronicAddress>
+                <ElectronicAddressType>http</ElectronicAddressType>
+                <ElectronicAddressData>library.myuniverity.edu</ElectronicAddressData>
+            </electronicAddress>
+        </AgencyAddressInformation>
+        <AgencyAddressInformation>
+            <electronicAddress>
+                <ElectronicAddressType>mailto</ElectronicAddressType>
+                <ElectronicAddressData>library@myuniversity.edu</ElectronicAddressData>
+            </electronicAddress>
+        </AgencyAddressInformation>
+    </LookupAgencyResponse>
+</NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/LookupItem.xml b/module/VuFind/tests/fixtures/xcncip2/response/LookupItem.xml
new file mode 100644
index 00000000000..8f3e21076f4
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/LookupItem.xml
@@ -0,0 +1,56 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8"?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" xmlns:ns2="https://ncip.knihovny.cz/ILSDI/ncip/2015/extensions" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">
+    <ns1:LookupItemResponse>
+        <ns1:ItemId>
+            <ns1:AgencyId>Agency from lookup item</ns1:AgencyId>
+            <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Barcode</ns1:ItemIdentifierType>
+            <ns1:ItemIdentifierValue>318300321322</ns1:ItemIdentifierValue>
+        </ns1:ItemId>
+        <ns1:ItemTransaction>
+            <ns1:CurrentBorrower>
+                <ns1:UserId>
+                    <ns1:AgencyId>OPG001</ns1:AgencyId>
+                    <ns1:UserIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Institution Id Number</ns1:UserIdentifierType>
+                    <ns1:UserIdentifierValue>99046</ns1:UserIdentifierValue>
+                </ns1:UserId>
+            </ns1:CurrentBorrower>
+        </ns1:ItemTransaction>
+        <ns1:ItemOptionalFields>
+            <ns1:BibliographicDescription>
+                <ns1:Author>Prichard, Katharine Susannah, 1883-1969</ns1:Author>
+                <ns1:BibliographicItemId>
+                    <ns1:BibliographicItemIdentifier>KN3183000000046386</ns1:BibliographicItemIdentifier>
+                </ns1:BibliographicItemId>
+                <ns1:PlaceOfPublication>Praha</ns1:PlaceOfPublication>
+                <ns1:PublicationDate>1975</ns1:PublicationDate>
+                <ns1:Publisher>Svoboda</ns1:Publisher>
+                <ns1:Title>Sharn, má naděje a láska</ns1:Title>
+                <ns1:BibliographicLevel ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographiclevel/bibliographiclevel.scm">Monograph</ns1:BibliographicLevel>
+                <ns1:Language ns1:Scheme="http://lcweb.loc.gov/standards/iso639-2/bibcodes.html">cze</ns1:Language>
+                <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+            </ns1:BibliographicDescription>
+            <ns1:ItemUseRestrictionType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">Limited Circulation, Normal Loan Period</ns1:ItemUseRestrictionType>
+            <ns1:CirculationStatus ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">On Loan</ns1:CirculationStatus>
+            <ns1:HoldQueueLength>0</ns1:HoldQueueLength>
+            <ns1:ItemDescription>
+                <ns1:CallNumber>Q</ns1:CallNumber>
+                <ns1:ItemDescriptionLevel ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemdescriptionlevel/itemdescriptionlevel.scm">Bibliographic Item</ns1:ItemDescriptionLevel>
+            </ns1:ItemDescription>
+            <ns1:Location>
+                <ns1:LocationType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/locationtype/locationtype.scm">Permanent Location</ns1:LocationType>
+                <ns1:LocationName>
+                    <ns1:LocationNameInstance>
+                        <ns1:LocationNameLevel>1</ns1:LocationNameLevel>
+                        <ns1:LocationNameValue>Středisko Opava VF</ns1:LocationNameValue>
+                    </ns1:LocationNameInstance>
+                </ns1:LocationName>
+            </ns1:Location>
+            <ns1:DateDue>2019-11-17T23:00:00Z</ns1:DateDue>
+        </ns1:ItemOptionalFields>
+    </ns1:LookupItemResponse>
+</ns1:NCIPMessage>
+
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/LookupUserResponseWithoutNamespaceDefinition.xml b/module/VuFind/tests/fixtures/xcncip2/response/LookupUserResponseWithoutNamespaceDefinition.xml
new file mode 100644
index 00000000000..b0542df4ecb
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/LookupUserResponseWithoutNamespaceDefinition.xml
@@ -0,0 +1,383 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="utf-8" ?>
+<NCIPMessage version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd">
+    <LookupUserResponse>
+        <UserId>
+            <AgencyId Scheme="http://www.niso.org/ncip/v1_0/schemes/agencyidtype/agencyidtype.scm">MZK</AgencyId>
+            <UserIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Institution Id Number</UserIdentifierType>
+            <UserIdentifierValue>700</UserIdentifierValue>
+        </UserId>
+        <UserFiscalAccount>
+            <AccountBalance>
+                <CurrencyCode>CZK</CurrencyCode>
+                <MonetaryValue>105</MonetaryValue>
+            </AccountBalance>
+            <AccountDetails>
+                <AccrualDate>2014-11-14T16:22:46.016Z</AccrualDate>
+                <FiscalTransactionInformation>
+                    <FiscalActionType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/fiscalactiontype/fiscalactiontype.scm">Penalty</FiscalActionType>
+                    <FiscalTransactionType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/fiscaltransactiontype/fiscaltransactiontype.scm">Service Charge</FiscalTransactionType>
+                    <Amount>
+                        <CurrencyCode>CZK</CurrencyCode>
+                        <MonetaryValue>25</MonetaryValue>
+                    </Amount>
+                    <FiscalTransactionDescription>Please note that there is an additional accrued overdue items fine of: 0.00.</FiscalTransactionDescription>
+                </FiscalTransactionInformation>
+            </AccountDetails>
+        </UserFiscalAccount>
+        <LoanedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>104</ItemIdentifierValue>
+            </ItemId>
+            <DateDue>2014-11-20T01:00:00Z</DateDue>
+            <Title>Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...</Title>
+            <Ext>
+                <DateCheckedOut>2014-03-26T01:00:00Z</DateCheckedOut>
+                <BibliographicDescription>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>MZK01000847602-MZK50000847602000090</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                        <AgencyId>My Agency</AgencyId>
+                    </BibliographicRecordId>
+                    <Publisher>Joh. Alt,</Publisher>
+                    <Title>Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Magazine</MediumType>
+                </BibliographicDescription>
+            </Ext>
+        </LoanedItem>
+        <LoanedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>105</ItemIdentifierValue>
+            </ItemId>
+            <DateDue>2014-11-27T01:00:00Z</DateDue>
+            <Title>Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz</Title>
+            <Ext>
+                <DateCheckedOut>2014-08-27T00:00:00Z</DateCheckedOut>
+                <BibliographicDescription>
+                    <Author>Saathen, Friedrich,</Author>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>MZK01000000456-MZK50000000456000440</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                        <AgencyId>My Agency</AgencyId>
+                    </BibliographicRecordId>
+                    <Publisher>Akropolis,</Publisher>
+                    <Title>Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+            </Ext>
+        </LoanedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000974548-MZK50000974548000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-02T16:42:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Draganov, Nedjalko Ivanov,</Author>
+                    <Publisher>Bulharská tvorba,</Publisher>
+                    <Title>Česko-bulharské rozhovory / sestavil Nedjalko Draganov</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-02T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-02T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000353880-MZK50000353880000040</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Hold</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-10T13:14:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Title>Ahoj, Blanko! : dívčí román / Eva Bernardinová</Title>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Bernardinová, Eva,</Author>
+                    <Publisher>Erika,</Publisher>
+                    <Title>Ahoj, Blanko! : dívčí román / Eva Bernardinová</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>111</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                    </BibliographicRecordId>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-10T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2014-11-20T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000702881-MZK50000702881000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-17T09:41:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Title>Veselé prasátko [objekt]</Title>
+            <Ext>
+                <BibliographicDescription>
+                    <Publisher>Svojtka </Publisher>
+                    <Title>Veselé prasátko [objekt]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-17T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-17T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000065021-MZK50000065021000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Recall</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-23T16:44:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Title>Aktiv revizních techniků elektrických zařízení</Title>
+            <Ext>
+                <BibliographicDescription>
+                    <Publisher>Dům techniky ČSVTS,</Publisher>
+                    <Title>Aktiv revizních techniků elektrických zařízení</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>112</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                    </BibliographicRecordId>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-23T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-23T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01001276830-MZK50001311815000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-24T13:21:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>James, E. L.,</Author>
+                    <Publisher>XYZ,</Publisher>
+                    <Title>Padesát odstínů temnoty = Fifty shades darker / E.L. James ; [z anglického originálu ... přelo</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-24T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-24T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01001422752-MZK50001457754000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-24T18:36:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Teplan, Vladimír,</Author>
+                    <Publisher>Grada,</Publisher>
+                    <Title>Praktická nefrologie / Vladimír Teplan</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-24T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-24T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000053496-MZK50000053496000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-30T14:33:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Himl, Pavel,</Author>
+                    <Publisher>Parta,</Publisher>
+                    <Title>Zlato Šumavy / Pavel Himl ; [ilustroval Miloslav Havlíček]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-30T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-30T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000053495-MZK50000053495000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-30T15:19:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Rausch, Antonín,</Author>
+                    <Publisher>Erika,</Publisher>
+                    <Title>Příběhy ze staré Šumavy / Antonín Rausch</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-30T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-30T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000003911-MZK50000003911000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-30T18:50:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Petržela, Zdeněk</Author>
+                    <Publisher>Fantisk,</Publisher>
+                    <Title>Hrajeme si II</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-30T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-30T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000602324-MZK50000602324000020</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-11-09T13:19:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Šámalová, Lenka</Author>
+                    <Publisher>Grada,</Publisher>
+                    <Title>Klinické použití krve : příručka / [odpovědná redaktorka Lenka Šámalová]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-11-09T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-11-09T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01001333770-MZK50001370317000020</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Stack Retrieval</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-11-09T19:29:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</Title>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Štiková, Věra</Author>
+                    <Publisher>Nová škola,</Publisher>
+                    <Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>155</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                    </BibliographicRecordId>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-11-09T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-11-09T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01001333770-MZK50001370317000030</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-11-09T19:29:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Štiková, Věra</Author>
+                    <Publisher>Nová škola,</Publisher>
+                    <Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-11-09T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-11-09T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <UserOptionalFields>
+            <NameInformation>
+                <PersonalNameInformation>
+                    <StructuredPersonalUserName>
+                        <GivenName>John</GivenName>
+                        <Surname>Smith</Surname>
+                    </StructuredPersonalUserName>
+                </PersonalNameInformation>
+            </NameInformation>
+            <UserAddressInformation>
+                <UserAddressRoleType Scheme="http://www.niso.org/ncip/v2_0/imp1/schemes/useraddressroletype/useraddressroletype.scm">Ship To</UserAddressRoleType>
+                <PhysicalAddress>
+                    <StructuredAddress>
+                        <Street>Trvalá ulice 123</Street>
+                        <Locality>Trvalé</Locality>
+                        <PostalCode>12345 </PostalCode>
+                    </StructuredAddress>
+                    <UnstructuredAddress>
+                        <UnstructuredAddressData>Trvalá ulice 123, Big City, 12345</UnstructuredAddressData>
+                    </UnstructuredAddress>
+                    <PhysicalAddressType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/physicaladdresstype/physicaladdresstype.scm">Postal Address</PhysicalAddressType>
+                </PhysicalAddress>
+            </UserAddressInformation>
+            <UserAddressInformation>
+                <UserAddressRoleType Scheme="http://www.niso.org/ncip/v2_0/imp1/schemes/useraddressroletype/useraddressroletype.scm">Notice</UserAddressRoleType>
+                <ElectronicAddress>
+                    <ElectronicAddressType Scheme="http://www.iana.org/assignments/uri-schemes">mailserver</ElectronicAddressType>
+                    <ElectronicAddressData>test@mzk.cz</ElectronicAddressData>
+                </ElectronicAddress>
+            </UserAddressInformation>
+            <UserPrivilege>
+                <AgencyId>MZK</AgencyId>
+                <AgencyUserPrivilegeType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyuserprivilegetype/agencyuserprivilegetype.scm">MZK type</AgencyUserPrivilegeType>
+                <ValidFromDate>2010-08-30T00:00:00Z</ValidFromDate>
+                <ValidToDate>2099-12-31T01:00:00Z</ValidToDate>
+                <UserPrivilegeDescription>04 - S</UserPrivilegeDescription>
+            </UserPrivilege>
+            <UserId>
+                <UserIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Barcode</UserIdentifierType>
+                <UserIdentifierValue>700</UserIdentifierValue>
+            </UserId>
+        </UserOptionalFields>
+    </LookupUserResponse>
+</NCIPMessage>
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/LookupUserResponseWithoutNamespacePrefix.xml b/module/VuFind/tests/fixtures/xcncip2/response/LookupUserResponseWithoutNamespacePrefix.xml
new file mode 100644
index 00000000000..593db0d9ed8
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/LookupUserResponseWithoutNamespacePrefix.xml
@@ -0,0 +1,383 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="utf-8" ?>
+<NCIPMessage xmlns="http://www.niso.org/2008/ncip" version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd">
+    <LookupUserResponse>
+        <UserId>
+            <AgencyId Scheme="http://www.niso.org/ncip/v1_0/schemes/agencyidtype/agencyidtype.scm">MZK</AgencyId>
+            <UserIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Institution Id Number</UserIdentifierType>
+            <UserIdentifierValue>700</UserIdentifierValue>
+        </UserId>
+        <UserFiscalAccount>
+            <AccountBalance>
+                <CurrencyCode>CZK</CurrencyCode>
+                <MonetaryValue>105</MonetaryValue>
+            </AccountBalance>
+            <AccountDetails>
+                <AccrualDate>2014-11-14T16:22:46.016Z</AccrualDate>
+                <FiscalTransactionInformation>
+                    <FiscalActionType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/fiscalactiontype/fiscalactiontype.scm">Penalty</FiscalActionType>
+                    <FiscalTransactionType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/fiscaltransactiontype/fiscaltransactiontype.scm">Service Charge</FiscalTransactionType>
+                    <Amount>
+                        <CurrencyCode>CZK</CurrencyCode>
+                        <MonetaryValue>25</MonetaryValue>
+                    </Amount>
+                    <FiscalTransactionDescription>Please note that there is an additional accrued overdue items fine of: 0.00.</FiscalTransactionDescription>
+                </FiscalTransactionInformation>
+            </AccountDetails>
+        </UserFiscalAccount>
+        <LoanedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>104</ItemIdentifierValue>
+            </ItemId>
+            <DateDue>2014-11-20T01:00:00Z</DateDue>
+            <Title>Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...</Title>
+            <Ext>
+                <DateCheckedOut>2014-03-26T01:00:00Z</DateCheckedOut>
+                <BibliographicDescription>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>MZK01000847602-MZK50000847602000090</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                        <AgencyId>My Agency</AgencyId>
+                    </BibliographicRecordId>
+                    <Publisher>Joh. Alt,</Publisher>
+                    <Title>Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Magazine</MediumType>
+                </BibliographicDescription>
+            </Ext>
+        </LoanedItem>
+        <LoanedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>105</ItemIdentifierValue>
+            </ItemId>
+            <DateDue>2014-11-27T01:00:00Z</DateDue>
+            <Title>Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz</Title>
+            <Ext>
+                <DateCheckedOut>2014-08-27T00:00:00Z</DateCheckedOut>
+                <BibliographicDescription>
+                    <Author>Saathen, Friedrich,</Author>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>MZK01000000456-MZK50000000456000440</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                        <AgencyId>My Agency</AgencyId>
+                    </BibliographicRecordId>
+                    <Publisher>Akropolis,</Publisher>
+                    <Title>Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+            </Ext>
+        </LoanedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000974548-MZK50000974548000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-02T16:42:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Draganov, Nedjalko Ivanov,</Author>
+                    <Publisher>Bulharská tvorba,</Publisher>
+                    <Title>Česko-bulharské rozhovory / sestavil Nedjalko Draganov</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-02T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-02T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000353880-MZK50000353880000040</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Hold</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-10T13:14:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Title>Ahoj, Blanko! : dívčí román / Eva Bernardinová</Title>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Bernardinová, Eva,</Author>
+                    <Publisher>Erika,</Publisher>
+                    <Title>Ahoj, Blanko! : dívčí román / Eva Bernardinová</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>111</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                    </BibliographicRecordId>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-10T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2014-11-20T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000702881-MZK50000702881000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-17T09:41:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Title>Veselé prasátko [objekt]</Title>
+            <Ext>
+                <BibliographicDescription>
+                    <Publisher>Svojtka </Publisher>
+                    <Title>Veselé prasátko [objekt]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-17T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-17T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000065021-MZK50000065021000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Recall</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-23T16:44:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Title>Aktiv revizních techniků elektrických zařízení</Title>
+            <Ext>
+                <BibliographicDescription>
+                    <Publisher>Dům techniky ČSVTS,</Publisher>
+                    <Title>Aktiv revizních techniků elektrických zařízení</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>112</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                    </BibliographicRecordId>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-23T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-23T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01001276830-MZK50001311815000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-24T13:21:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>James, E. L.,</Author>
+                    <Publisher>XYZ,</Publisher>
+                    <Title>Padesát odstínů temnoty = Fifty shades darker / E.L. James ; [z anglického originálu ... přelo</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-24T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-24T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01001422752-MZK50001457754000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-24T18:36:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Teplan, Vladimír,</Author>
+                    <Publisher>Grada,</Publisher>
+                    <Title>Praktická nefrologie / Vladimír Teplan</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-24T00:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-24T00:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000053496-MZK50000053496000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-30T14:33:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Himl, Pavel,</Author>
+                    <Publisher>Parta,</Publisher>
+                    <Title>Zlato Šumavy / Pavel Himl ; [ilustroval Miloslav Havlíček]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-30T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-30T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000053495-MZK50000053495000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-30T15:19:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Rausch, Antonín,</Author>
+                    <Publisher>Erika,</Publisher>
+                    <Title>Příběhy ze staré Šumavy / Antonín Rausch</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-30T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-30T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000003911-MZK50000003911000010</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-10-30T18:50:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Petržela, Zdeněk</Author>
+                    <Publisher>Fantisk,</Publisher>
+                    <Title>Hrajeme si II</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-10-30T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-10-30T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01000602324-MZK50000602324000020</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-11-09T13:19:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Šámalová, Lenka</Author>
+                    <Publisher>Grada,</Publisher>
+                    <Title>Klinické použití krve : příručka / [odpovědná redaktorka Lenka Šámalová]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-11-09T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-11-09T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01001333770-MZK50001370317000020</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Stack Retrieval</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-11-09T19:29:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</Title>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Štiková, Věra</Author>
+                    <Publisher>Nová škola,</Publisher>
+                    <Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                    <BibliographicRecordId>
+                        <BibliographicRecordIdentifier>155</BibliographicRecordIdentifier>
+                        <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</BibliographicRecordIdentifierCode>
+                    </BibliographicRecordId>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-11-09T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-11-09T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <RequestedItem>
+            <ItemId>
+                <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                <ItemIdentifierValue>MZK01001333770-MZK50001370317000030</ItemIdentifierValue>
+            </ItemId>
+            <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</RequestType>
+            <RequestStatusType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</RequestStatusType>
+            <DatePlaced>2014-11-09T19:29:00Z</DatePlaced>
+            <PickupLocation>Loan Department - Ground floor</PickupLocation>
+            <ReminderLevel>3</ReminderLevel>
+            <Ext>
+                <BibliographicDescription>
+                    <Author>Štiková, Věra</Author>
+                    <Publisher>Nová škola,</Publisher>
+                    <Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</Title>
+                    <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+                </BibliographicDescription>
+                <EarliestDateNeeded>2014-11-09T01:00:00Z</EarliestDateNeeded>
+                <NeedBeforeDate>2015-11-09T01:00:00Z</NeedBeforeDate>
+            </Ext>
+        </RequestedItem>
+        <UserOptionalFields>
+            <NameInformation>
+                <PersonalNameInformation>
+                    <StructuredPersonalUserName>
+                        <GivenName>John</GivenName>
+                        <Surname>Smith</Surname>
+                    </StructuredPersonalUserName>
+                </PersonalNameInformation>
+            </NameInformation>
+            <UserAddressInformation>
+                <UserAddressRoleType Scheme="http://www.niso.org/ncip/v2_0/imp1/schemes/useraddressroletype/useraddressroletype.scm">Ship To</UserAddressRoleType>
+                <PhysicalAddress>
+                    <StructuredAddress>
+                        <Street>Trvalá ulice 123</Street>
+                        <Locality>Trvalé</Locality>
+                        <PostalCode>12345 </PostalCode>
+                    </StructuredAddress>
+                    <UnstructuredAddress>
+                        <UnstructuredAddressData>Trvalá ulice 123, Big City, 12345</UnstructuredAddressData>
+                    </UnstructuredAddress>
+                    <PhysicalAddressType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/physicaladdresstype/physicaladdresstype.scm">Postal Address</PhysicalAddressType>
+                </PhysicalAddress>
+            </UserAddressInformation>
+            <UserAddressInformation>
+                <UserAddressRoleType Scheme="http://www.niso.org/ncip/v2_0/imp1/schemes/useraddressroletype/useraddressroletype.scm">Notice</UserAddressRoleType>
+                <ElectronicAddress>
+                    <ElectronicAddressType Scheme="http://www.iana.org/assignments/uri-schemes">mailserver</ElectronicAddressType>
+                    <ElectronicAddressData>test@mzk.cz</ElectronicAddressData>
+                </ElectronicAddress>
+            </UserAddressInformation>
+            <UserPrivilege>
+                <AgencyId>MZK</AgencyId>
+                <AgencyUserPrivilegeType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyuserprivilegetype/agencyuserprivilegetype.scm">MZK type</AgencyUserPrivilegeType>
+                <ValidFromDate>2010-08-30T00:00:00Z</ValidFromDate>
+                <ValidToDate>2099-12-31T01:00:00Z</ValidToDate>
+                <UserPrivilegeDescription>04 - S</UserPrivilegeDescription>
+            </UserPrivilege>
+            <UserId>
+                <UserIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Barcode</UserIdentifierType>
+                <UserIdentifierValue>700</UserIdentifierValue>
+            </UserId>
+        </UserOptionalFields>
+    </LookupUserResponse>
+</NCIPMessage>
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseAccepted.xml b/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseAccepted.xml
new file mode 100644
index 00000000000..d7520004965
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseAccepted.xml
@@ -0,0 +1,24 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">
+    <ns1:RenewItemResponse>
+        <ns1:ItemId>
+            <ns1:AgencyId ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">CBA001</ns1:AgencyId>
+            <ns1:ItemIdentifierType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">BARCODE</ns1:ItemIdentifierType>
+            <ns1:ItemIdentifierValue>2680372392</ns1:ItemIdentifierValue>
+        </ns1:ItemId>
+        <ns1:UserId>
+            <ns1:AgencyId ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">CBA001</ns1:AgencyId>
+            <ns1:UserIdentifierType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">INSTITUTION_ID_NUMBER</ns1:UserIdentifierType>
+            <ns1:UserIdentifierValue>0059085</ns1:UserIdentifierValue>
+        </ns1:UserId>
+        <ns1:DateDue>2020-09-09T00:00:00Z</ns1:DateDue>
+        <ns1:RenewalCount>1</ns1:RenewalCount>
+        <ns1:ItemOptionalFields>
+            <ns1:DateDue>2020-09-09T00:00:00Z</ns1:DateDue>
+        </ns1:ItemOptionalFields>
+    </ns1:RenewItemResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseAcceptedAlternativeDateFormat.xml b/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseAcceptedAlternativeDateFormat.xml
new file mode 100644
index 00000000000..9b8ea0d7776
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseAcceptedAlternativeDateFormat.xml
@@ -0,0 +1,23 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<NCIPMessage xmlns="http://www.niso.org/2008/ncip">
+    <RenewItemResponse>
+        <ItemId>
+            <ItemIdentifierType>Barcode</ItemIdentifierType>
+            <ItemIdentifierValue>421170101658</ItemIdentifierValue>
+        </ItemId>
+        <UserId>
+            <AgencyId>SOG504</AgencyId>
+            <UserIdentifierType>Institution Id Number</UserIdentifierType>
+            <UserIdentifierValue>19404</UserIdentifierValue>
+        </UserId>
+        <DateDue>2020-08-31T23:59:59.999+02:00</DateDue>
+        <ItemOptionalFields>
+            <DateDue>2020-08-31T23:59:59.999+02:00</DateDue>
+        </ItemOptionalFields>
+        <UserOptionalFields/>
+    </RenewItemResponse>
+</NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseDenied.xml b/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseDenied.xml
new file mode 100644
index 00000000000..fe2cf1f3d46
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseDenied.xml
@@ -0,0 +1,14 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">
+    <ns1:RenewItemResponse>
+        <ns1:Problem>
+            <ns1:ProblemType ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/processingerrortype/generalprocessingerror.scm">Temporary Processing Failure</ns1:ProblemType>
+            <ns1:ProblemDetail>Error ARL WS RenewItem request retcode = ERRNCIP179, retmsg = Error ARL WS NCIP RenewItem, Prolongace je možná až za 6 dní (tedy 35 dní před aktuálním koncem platnosti operace). code:ERRP008#6#35</ns1:ProblemDetail>
+            <ns1:ProblemElement>RenewItem</ns1:ProblemElement>
+        </ns1:Problem>
+    </ns1:RenewItemResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseDeniedInvalidMessage.xml b/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseDeniedInvalidMessage.xml
new file mode 100644
index 00000000000..995528a5f2f
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RenewItemResponseDeniedInvalidMessage.xml
@@ -0,0 +1,11 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<NCIPMessage xmlns="http://www.niso.org/2008/ncip">
+    <Problem>
+        <ProblemType>Item Not Renewable</ProblemType>
+        <ProblemDetail>No active registration.</ProblemDetail>
+    </Problem>
+</NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseAcceptedWithItemId.xml b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseAcceptedWithItemId.xml
new file mode 100644
index 00000000000..ba547ec753f
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseAcceptedWithItemId.xml
@@ -0,0 +1,21 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">
+    <ns1:RequestItemResponse>
+        <ns1:ItemId>
+            <ns1:AgencyId ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">CBA001</ns1:AgencyId>
+            <ns1:ItemIdentifierType ns1:Scheme="">Barcode</ns1:ItemIdentifierType>
+            <ns1:ItemIdentifierValue>3800123456</ns1:ItemIdentifierValue>
+        </ns1:ItemId>
+        <ns1:UserId>
+            <ns1:AgencyId ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">CBA001</ns1:AgencyId>
+            <ns1:UserIdentifierType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">INSTITUTION_ID_NUMBER</ns1:UserIdentifierType>
+            <ns1:UserIdentifierValue>0059085</ns1:UserIdentifierValue>
+        </ns1:UserId>
+        <ns1:RequestType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">hold</ns1:RequestType>
+        <ns1:RequestScopeType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">item</ns1:RequestScopeType>
+    </ns1:RequestItemResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseAcceptedWithRequestId.xml b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseAcceptedWithRequestId.xml
new file mode 100644
index 00000000000..dd12653dae5
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseAcceptedWithRequestId.xml
@@ -0,0 +1,22 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns1:NCIPMessage  xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">
+    <ns1:RequestItemResponse>
+        <ns1:RequestId>
+            <ns1:RequestIdentifierValue>14112472</ns1:RequestIdentifierValue>
+        </ns1:RequestId>
+        <ns1:UserId>
+            <ns1:AgencyId>OPG001</ns1:AgencyId>
+            <ns1:UserIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Institution Id Number</ns1:UserIdentifierType>
+            <ns1:UserIdentifierValue>99045</ns1:UserIdentifierValue>
+        </ns1:UserId>
+        <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Hold</ns1:RequestType>
+        <ns1:RequestScopeType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requestscopetype/requestscopetype.scm">Item</ns1:RequestScopeType>
+        <ns1:ItemOptionalFields>
+            <ns1:DateDue>2020-08-16T22:00:00Z</ns1:DateDue>
+        </ns1:ItemOptionalFields>
+    </ns1:RequestItemResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDenied.xml b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDenied.xml
new file mode 100644
index 00000000000..152934caf63
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDenied.xml
@@ -0,0 +1,14 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_02/ncip_v2_02.xsd">
+    <ns1:RequestItemResponse>
+        <ns1:Problem>
+            <ns1:ProblemType ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/processingerrortype/generalprocessingerror.scm">Temporary Processing Failure</ns1:ProblemType>
+            <ns1:ProblemDetail>Error ARL WS RequestItem request retcode = ERRNCIP188, retmsg = Error ARL WS NCIP RequestItem, Čtenář již má dokument tohoto titulu na kontě, další transakce na stejný titul není povolena. code:ERRU003|</ns1:ProblemDetail>
+            <ns1:ProblemElement>RequestItem</ns1:ProblemElement>
+        </ns1:Problem>
+    </ns1:RequestItemResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedEmpty.xml b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedEmpty.xml
new file mode 100644
index 00000000000..138887ec45d
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedEmpty.xml
@@ -0,0 +1,5 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><NCIPMessage xmlns="http://www.niso.org/2008/ncip"/>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedNotFullProblemElement.xml b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedNotFullProblemElement.xml
new file mode 100644
index 00000000000..01b5a060e8c
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedNotFullProblemElement.xml
@@ -0,0 +1,12 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8"?>
+<ns1:NCIPMessage ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd" xmlns:ns1="http://www.niso.org/2008/ncip">
+    <ns1:RequestItemResponse>
+        <ns1:Problem>
+            <ns1:ProblemType ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/processingerrortype/generalprocessingerror.scm">User Blocked</ns1:ProblemType>
+        </ns1:Problem>
+    </ns1:RequestItemResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedWithIdentifiers.xml b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedWithIdentifiers.xml
new file mode 100644
index 00000000000..8eca5d5d328
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/RequestItemResponseDeniedWithIdentifiers.xml
@@ -0,0 +1,38 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="UTF-8"?>
+<NCIPMessage version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd">
+    <RequestItemResponse>
+        <RequestId>
+            <RequestIdentifierValue>-1</RequestIdentifierValue>
+        </RequestId>
+        <ItemId>
+            <AgencyId Scheme="http://www.niso.org/ncip/v1_0/schemes/agencyidtype/agencyidtype.scm">ABA008</AgencyId>
+            <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+            <ItemIdentifierValue>421170118317</ItemIdentifierValue>
+        </ItemId>
+        <ResponseHeader>
+            <FromAgencyId>
+                <AgencyId>ABA008</AgencyId>
+            </FromAgencyId>
+            <ToAgencyId>
+                <AgencyId>CPK</AgencyId>
+            </ToAgencyId>
+        </ResponseHeader>
+        <RequestType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Hold</RequestType>
+        <RequestScopeType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requestscopetype/requestscopetype.scm">Item</RequestScopeType>
+        <UserId>
+            <AgencyId>ABA008</AgencyId>
+            <UserIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Institution Id Number</UserIdentifierType>
+            <UserIdentifierValue>C5452001</UserIdentifierValue>
+        </UserId>
+        <Problem>
+            <ProblemType Scheme="http://www.niso.org/ncip/v1_0/schemes/processingerrortype/generalprocessingerror.scm">Temporary Processing Failure</ProblemType>
+            <ProblemDetail>RequestItem Error</ProblemDetail>
+            <ProblemElement>Can not identify item</ProblemElement>
+            <ProblemValue></ProblemValue>
+        </Problem>
+    </RequestItemResponse>
+</NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/lookupItemSet.xml b/module/VuFind/tests/fixtures/xcncip2/response/lookupItemSet.xml
new file mode 100644
index 00000000000..a6925b4dafc
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/lookupItemSet.xml
@@ -0,0 +1,174 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="utf-8" ?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd">
+   <ns1:LookupItemSetResponse>
+      <ns1:BibInformation>
+         <ns1:BibliographicId>
+            <ns1:BibliographicRecordId>
+               <ns1:BibliographicRecordIdentifier>MZK01000000421</ns1:BibliographicRecordIdentifier>
+               <ns1:AgencyId ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/agencyidtype/agencyidtype.scm">MZK</ns1:AgencyId>
+            </ns1:BibliographicRecordId>
+         </ns1:BibliographicId>
+         <ns1:HoldingsSet>
+            <ns1:BibliographicDescription>
+               <ns1:Author>Malinová, Libuše</ns1:Author>
+               <ns1:BibliographicItemId>
+                  <ns1:BibliographicItemIdentifier>80-900870-1-9</ns1:BibliographicItemIdentifier>
+                  <ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">ISBN</ns1:BibliographicItemIdentifierCode>
+               </ns1:BibliographicItemId>
+               <ns1:Publisher>EPA,</ns1:Publisher>
+               <ns1:Title>Anglicko-český a česko-anglický elektrotechnický a elektronický slovník / Sest. aut. kol. pod</ns1:Title>
+               <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+            </ns1:BibliographicDescription>
+            <ns1:ItemInformation>
+               <ns1:ItemId>
+                  <ns1:AgencyId>My university</ns1:AgencyId>
+                  <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Barcode</ns1:ItemIdentifierType>
+                  <ns1:ItemIdentifierValue>MZK01000000421-MZK50000000421000010</ns1:ItemIdentifierValue>
+               </ns1:ItemId>
+               <ns1:ItemOptionalFields>
+                  <ns1:BibliographicDescription>
+                     <ns1:BibliographicItemId>
+                        <ns1:BibliographicItemIdentifier>2610002885</ns1:BibliographicItemIdentifier>
+                        <ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</ns1:BibliographicItemIdentifierCode>
+                     </ns1:BibliographicItemId>
+                  </ns1:BibliographicDescription>
+                  <ns1:ItemUseRestrictionType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">In Library Use Only</ns1:ItemUseRestrictionType>
+                  <ns1:CirculationStatus ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">Available on shelf</ns1:CirculationStatus>
+                  <ns1:HoldQueueLength>0</ns1:HoldQueueLength>
+                  <ns1:ItemDescription>
+                     <ns1:CallNumber>621.3 ANG</ns1:CallNumber>
+                     <ns1:NumberOfPieces>1</ns1:NumberOfPieces>
+                  </ns1:ItemDescription>
+               </ns1:ItemOptionalFields>
+            </ns1:ItemInformation>
+         </ns1:HoldingsSet>
+      </ns1:BibInformation>
+      <ns1:BibInformation>
+         <ns1:BibliographicId>
+            <ns1:BibliographicItemId>
+               <ns1:BibliographicItemIdentifier>MZK01000062021</ns1:BibliographicItemIdentifier>
+               <ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">Accession Number</ns1:BibliographicItemIdentifierCode>
+            </ns1:BibliographicItemId>
+         </ns1:BibliographicId>
+         <ns1:HoldingsSet>
+            <ns1:BibliographicDescription>
+               <ns1:Author>Brammer, Lawrence M.</ns1:Author>
+               <ns1:BibliographicItemId>
+                  <ns1:BibliographicItemIdentifier>0-13-914622-9</ns1:BibliographicItemIdentifier>
+                  <ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">ISBN</ns1:BibliographicItemIdentifierCode>
+               </ns1:BibliographicItemId>
+               <ns1:Publisher>Prentice-Hall,</ns1:Publisher>
+               <ns1:Title>Therapeutic psychology : fundamentals of counseling and psychotherapy / Lawrence M. Brammer, Everett</ns1:Title>
+               <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+            </ns1:BibliographicDescription>
+            <ns1:ItemInformation>
+               <ns1:ItemId>
+                  <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                  <ns1:ItemIdentifierValue>MZK01000062021-MZK50000062021000010</ns1:ItemIdentifierValue>
+               </ns1:ItemId>
+               <ns1:ItemOptionalFields>
+                  <ns1:BibliographicDescription>
+                     <ns1:BibliographicItemId>
+                        <ns1:BibliographicItemIdentifier>3119972468</ns1:BibliographicItemIdentifier>
+                        <ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</ns1:BibliographicItemIdentifierCode>
+                     </ns1:BibliographicItemId>
+                  </ns1:BibliographicDescription>
+                  <ns1:ItemUseRestrictionType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">Limited Circulation, Normal Loan Period</ns1:ItemUseRestrictionType>
+                  <ns1:CirculationStatus ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">Available On Shelf</ns1:CirculationStatus>
+                  <ns1:HoldQueueLength>0</ns1:HoldQueueLength>
+                  <ns1:ItemDescription>
+                     <ns1:CallNumber>PK-0083.568</ns1:CallNumber>
+                     <ns1:NumberOfPieces>1</ns1:NumberOfPieces>
+                  </ns1:ItemDescription>
+               </ns1:ItemOptionalFields>
+            </ns1:ItemInformation>
+         </ns1:HoldingsSet>
+      </ns1:BibInformation>
+      <ns1:BibInformation>
+         <ns1:BibliographicId>
+            <ns1:BibliographicRecordId>
+               <ns1:BibliographicRecordIdentifier>MZK01000000425</ns1:BibliographicRecordIdentifier>
+               <ns1:BibliographicRecordIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">Accession Number</ns1:BibliographicRecordIdentifierCode>
+            </ns1:BibliographicRecordId>
+         </ns1:BibliographicId>
+         <ns1:HoldingsSet>
+            <ns1:Location>
+               <ns1:LocationType Scheme="http://www.niso.org/ncip/;1_0/imp1/schemes/locationtype/locationtype.scm">Permanent Location</ns1:LocationType>
+               <ns1:LocationName>
+                  <ns1:LocationNameInstance>
+                     <ns1:LocationNameLevel>1</ns1:LocationNameLevel>
+                     <ns1:LocationNameValue>Some holding location</ns1:LocationNameValue>
+                  </ns1:LocationNameInstance>
+               </ns1:LocationName>
+            </ns1:Location>
+            <ns1:BibliographicDescription>
+               <ns1:Author>Elman, Jiří</ns1:Author>
+               <ns1:BibliographicItemId>
+                  <ns1:BibliographicItemIdentifier>80-85605-77-5</ns1:BibliographicItemIdentifier>
+                  <ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">ISBN</ns1:BibliographicItemIdentifierCode>
+               </ns1:BibliographicItemId>
+               <ns1:Publisher>Victoria Publishing,</ns1:Publisher>
+               <ns1:Title>Anglicko-český ekonomický slovník : Ekonomie, právo, výpočetní technika. [2.] M-Z / ..., Kam</ns1:Title>
+               <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+            </ns1:BibliographicDescription>
+            <ns1:ItemInformation>
+               <ns1:ItemId>
+                  <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                  <ns1:ItemIdentifierValue>MZK01000000425-MZK50000000425000020</ns1:ItemIdentifierValue>
+               </ns1:ItemId>
+               <ns1:ItemOptionalFields>
+                  <ns1:BibliographicDescription>
+                     <ns1:BibliographicItemId>
+                        <ns1:BibliographicItemIdentifier>2610007443</ns1:BibliographicItemIdentifier>
+                        <ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</ns1:BibliographicItemIdentifierCode>
+                     </ns1:BibliographicItemId>
+                  </ns1:BibliographicDescription>
+                  <ns1:ItemUseRestrictionType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">In Library Use Only</ns1:ItemUseRestrictionType>
+                  <ns1:CirculationStatus ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">Available On Shelf</ns1:CirculationStatus>
+                  <ns1:HoldQueueLength>0</ns1:HoldQueueLength>
+                  <ns1:ItemDescription>
+                     <ns1:CallNumber>2-0997.767,2</ns1:CallNumber>
+                     <ns1:NumberOfPieces>2</ns1:NumberOfPieces>
+                  </ns1:ItemDescription>
+               </ns1:ItemOptionalFields>
+            </ns1:ItemInformation>
+            <ns1:ItemInformation>
+               <ns1:ItemId>
+                  <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                  <ns1:ItemIdentifierValue>MZK01000000425-MZK50000000425000030</ns1:ItemIdentifierValue>
+               </ns1:ItemId>
+               <ns1:ItemOptionalFields>
+                  <ns1:BibliographicDescription>
+                     <ns1:BibliographicItemId>
+                        <ns1:BibliographicItemIdentifier>2610008987</ns1:BibliographicItemIdentifier>
+                        <ns1:BibliographicItemIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</ns1:BibliographicItemIdentifierCode>
+                     </ns1:BibliographicItemId>
+                  </ns1:BibliographicDescription>
+                  <ns1:ItemUseRestrictionType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">Limited Circulation, Normal Loan Period</ns1:ItemUseRestrictionType>
+                  <ns1:CirculationStatus ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">Circulation Status Undefined</ns1:CirculationStatus>
+                  <ns1:HoldQueueLength>0</ns1:HoldQueueLength>
+                  <ns1:ItemDescription>
+                     <ns1:NumberOfPieces>2</ns1:NumberOfPieces>
+                  </ns1:ItemDescription>
+               </ns1:ItemOptionalFields>
+            </ns1:ItemInformation>
+         </ns1:HoldingsSet>
+      </ns1:BibInformation>
+      <ns1:BibInformation>
+         <ns1:BibliographicId>
+            <ns1:BibliographicRecordId>
+               <ns1:BibliographicRecordIdentifier>MZK01000062052</ns1:BibliographicRecordIdentifier>
+               <ns1:BibliographicRecordIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">Accession Number</ns1:BibliographicRecordIdentifierCode>
+            </ns1:BibliographicRecordId>
+         </ns1:BibliographicId>
+         <ns1:Problem>
+            <ns1:ProblemType ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/processingerrortype/lookupitemprocessingerror.scm">Unknown Item</ns1:ProblemType>
+            <ns1:ProblemValue>Item MZK01000062052, you are searching for, does not exists.</ns1:ProblemValue>
+         </ns1:Problem>
+      </ns1:BibInformation>
+   </ns1:LookupItemSetResponse>
+</ns1:NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/lookupItemSetWithoutNamespacePrefix.xml b/module/VuFind/tests/fixtures/xcncip2/response/lookupItemSetWithoutNamespacePrefix.xml
new file mode 100644
index 00000000000..ace0bd4ee71
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/lookupItemSetWithoutNamespacePrefix.xml
@@ -0,0 +1,173 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="utf-8" ?>
+<NCIPMessage xmlns="http://www.niso.org/2008/ncip" version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd">
+   <LookupItemSetResponse>
+      <BibInformation>
+         <BibliographicId>
+            <BibliographicRecordId>
+               <BibliographicRecordIdentifier>MZK01000000421</BibliographicRecordIdentifier>
+               <AgencyId Scheme="http://www.niso.org/ncip/v1_0/schemes/agencyidtype/agencyidtype.scm">MZK</AgencyId>
+            </BibliographicRecordId>
+         </BibliographicId>
+         <HoldingsSet>
+            <BibliographicDescription>
+               <Author>Malinová, Libuše</Author>
+               <BibliographicItemId>
+                  <BibliographicItemIdentifier>80-900870-1-9</BibliographicItemIdentifier>
+                  <BibliographicItemIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">ISBN</BibliographicItemIdentifierCode>
+               </BibliographicItemId>
+               <Publisher>EPA,</Publisher>
+               <Title>Anglicko-český a česko-anglický elektrotechnický a elektronický slovník / Sest. aut. kol. pod</Title>
+               <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+            </BibliographicDescription>
+            <ItemInformation>
+               <ItemId>
+                  <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                  <ItemIdentifierValue>MZK01000000421-MZK50000000421000010</ItemIdentifierValue>
+               </ItemId>
+               <ItemOptionalFields>
+                  <BibliographicDescription>
+                     <BibliographicItemId>
+                        <BibliographicItemIdentifier>2610002885</BibliographicItemIdentifier>
+                        <BibliographicItemIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</BibliographicItemIdentifierCode>
+                     </BibliographicItemId>
+                  </BibliographicDescription>
+                  <ItemUseRestrictionType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">In Library Use Only</ItemUseRestrictionType>
+                  <CirculationStatus Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">Available on shelf</CirculationStatus>
+                  <HoldQueueLength>0</HoldQueueLength>
+                  <ItemDescription>
+                     <CallNumber>621.3 ANG</CallNumber>
+                     <NumberOfPieces>1</NumberOfPieces>
+                  </ItemDescription>
+               </ItemOptionalFields>
+            </ItemInformation>
+         </HoldingsSet>
+      </BibInformation>
+      <BibInformation>
+         <BibliographicId>
+            <BibliographicItemId>
+               <BibliographicItemIdentifier>MZK01000062021</BibliographicItemIdentifier>
+               <BibliographicItemIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">Accession Number</BibliographicItemIdentifierCode>
+            </BibliographicItemId>
+         </BibliographicId>
+         <HoldingsSet>
+            <BibliographicDescription>
+               <Author>Brammer, Lawrence M.</Author>
+               <BibliographicItemId>
+                  <BibliographicItemIdentifier>0-13-914622-9</BibliographicItemIdentifier>
+                  <BibliographicItemIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">ISBN</BibliographicItemIdentifierCode>
+               </BibliographicItemId>
+               <Publisher>Prentice-Hall,</Publisher>
+               <Title>Therapeutic psychology : fundamentals of counseling and psychotherapy / Lawrence M. Brammer, Everett</Title>
+               <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+            </BibliographicDescription>
+            <ItemInformation>
+               <ItemId>
+                  <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                  <ItemIdentifierValue>MZK01000062021-MZK50000062021000010</ItemIdentifierValue>
+               </ItemId>
+               <ItemOptionalFields>
+                  <BibliographicDescription>
+                     <BibliographicItemId>
+                        <BibliographicItemIdentifier>3119972468</BibliographicItemIdentifier>
+                        <BibliographicItemIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</BibliographicItemIdentifierCode>
+                     </BibliographicItemId>
+                  </BibliographicDescription>
+                  <ItemUseRestrictionType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">Limited Circulation, Normal Loan Period</ItemUseRestrictionType>
+                  <CirculationStatus Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">Available On Shelf</CirculationStatus>
+                  <HoldQueueLength>0</HoldQueueLength>
+                  <ItemDescription>
+                     <CallNumber>PK-0083.568</CallNumber>
+                     <NumberOfPieces>1</NumberOfPieces>
+                  </ItemDescription>
+               </ItemOptionalFields>
+            </ItemInformation>
+         </HoldingsSet>
+      </BibInformation>
+      <BibInformation>
+         <BibliographicId>
+            <BibliographicRecordId>
+               <BibliographicRecordIdentifier>MZK01000000425</BibliographicRecordIdentifier>
+               <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">Accession Number</BibliographicRecordIdentifierCode>
+            </BibliographicRecordId>
+         </BibliographicId>
+         <HoldingsSet>
+            <Location>
+               <LocationType Scheme="http://www.niso.org/ncip/;1_0/imp1/schemes/locationtype/locationtype.scm">Permanent Location</LocationType>
+               <LocationName>
+                  <LocationNameInstance>
+                     <LocationNameLevel>1</LocationNameLevel>
+                     <LocationNameValue>Some holding location</LocationNameValue>
+                  </LocationNameInstance>
+               </LocationName>
+            </Location>
+            <BibliographicDescription>
+               <Author>Elman, Jiří</Author>
+               <BibliographicItemId>
+                  <BibliographicItemIdentifier>80-85605-77-5</BibliographicItemIdentifier>
+                  <BibliographicItemIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">ISBN</BibliographicItemIdentifierCode>
+               </BibliographicItemId>
+               <Publisher>Victoria Publishing,</Publisher>
+               <Title>Anglicko-český ekonomický slovník : Ekonomie, právo, výpočetní technika. [2.] M-Z / ..., Kam</Title>
+               <MediumType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</MediumType>
+            </BibliographicDescription>
+            <ItemInformation>
+               <ItemId>
+                  <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                  <ItemIdentifierValue>MZK01000000425-MZK50000000425000020</ItemIdentifierValue>
+               </ItemId>
+               <ItemOptionalFields>
+                  <BibliographicDescription>
+                     <BibliographicItemId>
+                        <BibliographicItemIdentifier>2610007443</BibliographicItemIdentifier>
+                        <BibliographicItemIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</BibliographicItemIdentifierCode>
+                     </BibliographicItemId>
+                  </BibliographicDescription>
+                  <ItemUseRestrictionType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">In Library Use Only</ItemUseRestrictionType>
+                  <CirculationStatus Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">Available On Shelf</CirculationStatus>
+                  <HoldQueueLength>0</HoldQueueLength>
+                  <ItemDescription>
+                     <CallNumber>2-0997.767,2</CallNumber>
+                     <NumberOfPieces>2</NumberOfPieces>
+                  </ItemDescription>
+               </ItemOptionalFields>
+            </ItemInformation>
+            <ItemInformation>
+               <ItemId>
+                  <ItemIdentifierType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ItemIdentifierType>
+                  <ItemIdentifierValue>MZK01000000425-MZK50000000425000030</ItemIdentifierValue>
+               </ItemId>
+               <ItemOptionalFields>
+                  <BibliographicDescription>
+                     <BibliographicItemId>
+                        <BibliographicItemIdentifier>2610008987</BibliographicItemIdentifier>
+                        <BibliographicItemIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicitemidentifiercode/bibliographicitemidentifiercode.scm">Legal Deposit Number</BibliographicItemIdentifierCode>
+                     </BibliographicItemId>
+                  </BibliographicDescription>
+                  <ItemUseRestrictionType Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/itemuserestrictiontype/itemuserestrictiontype.scm">Limited Circulation, Normal Loan Period</ItemUseRestrictionType>
+                  <CirculationStatus Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/circulationstatus/circulationstatus.scm">Available On Shelf</CirculationStatus>
+                  <HoldQueueLength>0</HoldQueueLength>
+                  <ItemDescription>
+                     <NumberOfPieces>2</NumberOfPieces>
+                  </ItemDescription>
+               </ItemOptionalFields>
+            </ItemInformation>
+         </HoldingsSet>
+      </BibInformation>
+      <BibInformation>
+         <BibliographicId>
+            <BibliographicRecordId>
+               <BibliographicRecordIdentifier>MZK01000062052</BibliographicRecordIdentifier>
+               <BibliographicRecordIdentifierCode Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">Accession Number</BibliographicRecordIdentifierCode>
+            </BibliographicRecordId>
+         </BibliographicId>
+         <Problem>
+            <ProblemType Scheme="http://www.niso.org/ncip/v1_0/schemes/processingerrortype/lookupitemprocessingerror.scm">Unknown Item</ProblemType>
+            <ProblemValue>Item MZK01000062052, you are searching for, does not exists.</ProblemValue>
+         </Problem>
+      </BibInformation>
+   </LookupItemSetResponse>
+</NCIPMessage>
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/xcncip2/response/lookupUserResponse.xml b/module/VuFind/tests/fixtures/xcncip2/response/lookupUserResponse.xml
new file mode 100644
index 00000000000..198092b56ea
--- /dev/null
+++ b/module/VuFind/tests/fixtures/xcncip2/response/lookupUserResponse.xml
@@ -0,0 +1,401 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml
+Date: Wed, 18 Mar 2015 11:49:40 GMT
+
+<?xml version="1.0" encoding="utf-8" ?>
+<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/ncip_v2_0.xsd">
+    <ns1:LookupUserResponse>
+        <ns1:UserId>
+            <ns1:AgencyId ns1:Scheme="http://www.niso.org/ncip/v1_0/schemes/agencyidtype/agencyidtype.scm">MZK</ns1:AgencyId>
+            <ns1:UserIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Institution Id Number</ns1:UserIdentifierType>
+            <ns1:UserIdentifierValue>700</ns1:UserIdentifierValue>
+        </ns1:UserId>
+        <ns1:UserFiscalAccount>
+            <ns1:AccountBalance>
+                <ns1:CurrencyCode>CZK</ns1:CurrencyCode>
+                <ns1:MonetaryValue>105</ns1:MonetaryValue>
+            </ns1:AccountBalance>
+            <ns1:AccountDetails>
+                <ns1:AccrualDate>2014-11-14T16:22:46.016Z</ns1:AccrualDate>
+                <ns1:FiscalTransactionInformation>
+                    <ns1:FiscalActionType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/fiscalactiontype/fiscalactiontype.scm">Penalty</ns1:FiscalActionType>
+                    <ns1:FiscalTransactionType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/fiscaltransactiontype/fiscaltransactiontype.scm">Service Charge</ns1:FiscalTransactionType>
+                    <ns1:Amount>
+                        <ns1:CurrencyCode>CZK</ns1:CurrencyCode>
+                        <ns1:MonetaryValue>25</ns1:MonetaryValue>
+                    </ns1:Amount>
+                    <ns1:FiscalTransactionDescription>Please note that there is an additional accrued overdue items fine of: 0.00.</ns1:FiscalTransactionDescription>
+                    <ns1:ItemDetails>
+                        <ns1:ItemId>
+                            <ns1:ItemIdentifierType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">BARCODE</ns1:ItemIdentifierType>
+                            <ns1:ItemIdentifierValue>2681016402</ns1:ItemIdentifierValue>
+                        </ns1:ItemId>
+                        <ns1:BibliographicDescription>
+                            <ns1:Author>Běhan, Petr</ns1:Author>
+                            <ns1:BibliographicItemId>
+                                <ns1:BibliographicItemIdentifier>8071750247</ns1:BibliographicItemIdentifier>
+                                <ns1:BibliographicItemIdentifierCode ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">ISBN</ns1:BibliographicItemIdentifierCode>
+                            </ns1:BibliographicItemId>
+                            <ns1:ComponentId>
+                                <ns1:ComponentIdentifierType ns1:Scheme="https://www.library.sk/ncip/v2_02/schemes.scm">ACCESSION_NUMBER</ns1:ComponentIdentifierType>
+                                <ns1:ComponentIdentifier>2681016402</ns1:ComponentIdentifier>
+                            </ns1:ComponentId>
+                            <ns1:Edition />
+                            <ns1:PlaceOfPublication>Praha :</ns1:PlaceOfPublication>
+                            <ns1:PublicationDate>1995</ns1:PublicationDate>
+                            <ns1:Publisher>Prospektrum,</ns1:Publisher>
+                            <ns1:Title>Daňová soustava. Díl 3a, Daně z příjmů fyzické osoby, právnické osoby</ns1:Title>
+                        </ns1:BibliographicDescription>
+                    </ns1:ItemDetails>
+                </ns1:FiscalTransactionInformation>
+            </ns1:AccountDetails>
+        </ns1:UserFiscalAccount>
+        <ns1:LoanedItem>
+            <ns1:ItemId>
+                <ns1:AgencyId>My Agency</ns1:AgencyId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>104</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:DateDue>2014-11-20T01:00:00Z</ns1:DateDue>
+            <ns1:Title>Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...</ns1:Title>
+            <ns1:Ext>
+                <ns1:DateCheckedOut>2014-03-26T01:00:00Z</ns1:DateCheckedOut>
+                <ns1:BibliographicDescription>
+                    <ns1:BibliographicRecordId>
+                        <ns1:BibliographicRecordIdentifier>MZK01000847602-MZK50000847602000090</ns1:BibliographicRecordIdentifier>
+                        <ns1:BibliographicRecordIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</ns1:BibliographicRecordIdentifierCode>
+                    </ns1:BibliographicRecordId>
+                    <ns1:Publisher>Joh. Alt,</ns1:Publisher>
+                    <ns1:Title>Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Magazine</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:RenewalNotPermitted />
+            </ns1:Ext>
+        </ns1:LoanedItem>
+        <ns1:LoanedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>105</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:DateDue>2014-11-27T01:00:00Z</ns1:DateDue>
+            <ns1:Title>Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz</ns1:Title>
+            <ns1:Ext>
+                <ns1:DateCheckedOut>2014-08-27T00:00:00Z</ns1:DateCheckedOut>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Saathen, Friedrich,</ns1:Author>
+                    <ns1:Publisher>Akropolis,</ns1:Publisher>
+                    <ns1:Title>Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+            </ns1:Ext>
+        </ns1:LoanedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01000974548-MZK50000974548000010</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-02T16:42:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Draganov, Nedjalko Ivanov,</ns1:Author>
+                    <ns1:Publisher>Bulharská tvorba,</ns1:Publisher>
+                    <ns1:Title>Česko-bulharské rozhovory / sestavil Nedjalko Draganov</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-02T00:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-10-02T00:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01000353880-MZK50000353880000040</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Hold</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-10T13:14:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Title>Ahoj, Blanko! : dívčí román / Eva Bernardinová</ns1:Title>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Bernardinová, Eva,</ns1:Author>
+                    <ns1:Publisher>Erika,</ns1:Publisher>
+                    <ns1:Title>Ahoj, Blanko! : dívčí román / Eva Bernardinová</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                    <ns1:BibliographicRecordId>
+                        <ns1:BibliographicRecordIdentifier>111</ns1:BibliographicRecordIdentifier>
+                        <ns1:BibliographicRecordIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</ns1:BibliographicRecordIdentifierCode>
+                    </ns1:BibliographicRecordId>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-10T00:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2014-11-20T01:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01000702881-MZK50000702881000010</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-17T09:41:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Title>Veselé prasátko [objekt]</ns1:Title>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Publisher>Svojtka </ns1:Publisher>
+                    <ns1:Title>Veselé prasátko [objekt]</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-17T00:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-10-17T00:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01000065021-MZK50000065021000010</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Recall</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-23T16:44:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Title>Aktiv revizních techniků elektrických zařízení</ns1:Title>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Publisher>Dům techniky ČSVTS,</ns1:Publisher>
+                    <ns1:Title>Aktiv revizních techniků elektrických zařízení</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                    <ns1:BibliographicRecordId>
+                        <ns1:BibliographicRecordIdentifier>112</ns1:BibliographicRecordIdentifier>
+                        <ns1:BibliographicRecordIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</ns1:BibliographicRecordIdentifierCode>
+                    </ns1:BibliographicRecordId>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-23T00:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-10-23T00:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01001276830-MZK50001311815000010</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-24T13:21:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>James, E. L.,</ns1:Author>
+                    <ns1:Publisher>XYZ,</ns1:Publisher>
+                    <ns1:Title>Padesát odstínů temnoty = Fifty shades darker / E.L. James ; [z anglického originálu ... přelo</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-24T00:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-10-24T00:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01001422752-MZK50001457754000010</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-24T18:36:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Teplan, Vladimír,</ns1:Author>
+                    <ns1:Publisher>Grada,</ns1:Publisher>
+                    <ns1:Title>Praktická nefrologie / Vladimír Teplan</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-24T00:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-10-24T00:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01000053496-MZK50000053496000010</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-30T14:33:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Himl, Pavel,</ns1:Author>
+                    <ns1:Publisher>Parta,</ns1:Publisher>
+                    <ns1:Title>Zlato Šumavy / Pavel Himl ; [ilustroval Miloslav Havlíček]</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-30T01:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-10-30T01:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01000053495-MZK50000053495000010</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-30T15:19:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Rausch, Antonín,</ns1:Author>
+                    <ns1:Publisher>Erika,</ns1:Publisher>
+                    <ns1:Title>Příběhy ze staré Šumavy / Antonín Rausch</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-30T01:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-10-30T01:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01000003911-MZK50000003911000010</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-10-30T18:50:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Petržela, Zdeněk</ns1:Author>
+                    <ns1:Publisher>Fantisk,</ns1:Publisher>
+                    <ns1:Title>Hrajeme si II</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-10-30T01:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-10-30T01:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01000602324-MZK50000602324000020</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-11-09T13:19:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Šámalová, Lenka</ns1:Author>
+                    <ns1:Publisher>Grada,</ns1:Publisher>
+                    <ns1:Title>Klinické použití krve : příručka / [odpovědná redaktorka Lenka Šámalová]</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-11-09T01:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-11-09T01:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01001333770-MZK50001370317000020</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Stack Retrieval</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-11-09T19:29:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</ns1:Title>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Štiková, Věra</ns1:Author>
+                    <ns1:Publisher>Nová škola,</ns1:Publisher>
+                    <ns1:Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                    <ns1:BibliographicRecordId>
+                        <ns1:BibliographicRecordIdentifier>155</ns1:BibliographicRecordIdentifier>
+                        <ns1:BibliographicRecordIdentifierCode ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/bibliographicrecordidentifiercode/bibliographicrecordidentifiercode.scm">URI</ns1:BibliographicRecordIdentifierCode>
+                    </ns1:BibliographicRecordId>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-11-09T01:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-11-09T01:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:RequestedItem>
+            <ns1:ItemId>
+                <ns1:ItemIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleitemidentifiertype/visibleitemidentifiertype.scm">Accession Number</ns1:ItemIdentifierType>
+                <ns1:ItemIdentifierValue>MZK01001333770-MZK50001370317000030</ns1:ItemIdentifierValue>
+            </ns1:ItemId>
+            <ns1:RequestType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requesttype/requesttype.scm">Estimate</ns1:RequestType>
+            <ns1:RequestStatusType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/requeststatustype/requeststatustype.scm">In Process</ns1:RequestStatusType>
+            <ns1:DatePlaced>2014-11-09T19:29:00Z</ns1:DatePlaced>
+            <ns1:PickupLocation>Loan Department - Ground floor</ns1:PickupLocation>
+            <ns1:ReminderLevel>3</ns1:ReminderLevel>
+            <ns1:Ext>
+                <ns1:BibliographicDescription>
+                    <ns1:Author>Štiková, Věra</ns1:Author>
+                    <ns1:Publisher>Nová škola,</ns1:Publisher>
+                    <ns1:Title>Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]</ns1:Title>
+                    <ns1:MediumType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/mediumtype/mediumtype.scm">Book</ns1:MediumType>
+                </ns1:BibliographicDescription>
+                <ns1:EarliestDateNeeded>2014-11-09T01:00:00Z</ns1:EarliestDateNeeded>
+                <ns1:NeedBeforeDate>2015-11-09T01:00:00Z</ns1:NeedBeforeDate>
+            </ns1:Ext>
+        </ns1:RequestedItem>
+        <ns1:UserOptionalFields>
+            <ns1:NameInformation>
+                <ns1:PersonalNameInformation>
+                    <ns1:StructuredPersonalUserName>
+                        <ns1:GivenName>John</ns1:GivenName>
+                        <ns1:Surname>Smith</ns1:Surname>
+                    </ns1:StructuredPersonalUserName>
+                </ns1:PersonalNameInformation>
+            </ns1:NameInformation>
+            <ns1:UserAddressInformation>
+                <ns1:UserAddressRoleType ns1:Scheme="http://www.niso.org/ncip/v2_0/imp1/schemes/useraddressroletype/useraddressroletype.scm">Ship To</ns1:UserAddressRoleType>
+                <ns1:PhysicalAddress>
+                    <ns1:StructuredAddress>
+                        <ns1:Street>Trvalá ulice 123</ns1:Street>
+                        <ns1:Locality>Trvalé</ns1:Locality>
+                        <ns1:PostalCode>12345 </ns1:PostalCode>
+                    </ns1:StructuredAddress>
+                    <ns1:UnstructuredAddress>
+                        <ns1:UnstructuredAddressData>Trvalá ulice 123, Big City, 12345</ns1:UnstructuredAddressData>
+                    </ns1:UnstructuredAddress>
+                    <ns1:PhysicalAddressType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/physicaladdresstype/physicaladdresstype.scm">Postal Address</ns1:PhysicalAddressType>
+                </ns1:PhysicalAddress>
+            </ns1:UserAddressInformation>
+            <ns1:UserAddressInformation>
+                <ns1:UserAddressRoleType ns1:Scheme="http://www.niso.org/ncip/v2_0/imp1/schemes/useraddressroletype/useraddressroletype.scm">Notice</ns1:UserAddressRoleType>
+                <ns1:ElectronicAddress>
+                    <ns1:ElectronicAddressType ns1:Scheme="http://www.iana.org/assignments/uri-schemes">mailserver</ns1:ElectronicAddressType>
+                    <ns1:ElectronicAddressData>test@mzk.cz</ns1:ElectronicAddressData>
+                </ns1:ElectronicAddress>
+            </ns1:UserAddressInformation>
+            <ns1:UserPrivilege>
+                <ns1:AgencyId>MZK</ns1:AgencyId>
+                <ns1:AgencyUserPrivilegeType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/agencyuserprivilegetype/agencyuserprivilegetype.scm">MZK type</ns1:AgencyUserPrivilegeType>
+                <ns1:ValidFromDate>2010-08-30T00:00:00Z</ns1:ValidFromDate>
+                <ns1:ValidToDate>2099-12-31T01:00:00Z</ns1:ValidToDate>
+                <ns1:UserPrivilegeDescription>04 - S</ns1:UserPrivilegeDescription>
+            </ns1:UserPrivilege>
+            <ns1:UserId>
+                <ns1:UserIdentifierType ns1:Scheme="http://www.niso.org/ncip/v1_0/imp1/schemes/visibleuseridentifiertype/visibleuseridentifiertype.scm">Barcode</ns1:UserIdentifierType>
+                <ns1:UserIdentifierValue>700</ns1:UserIdentifierValue>
+            </ns1:UserId>
+        </ns1:UserOptionalFields>
+    </ns1:LookupUserResponse>
+</ns1:NCIPMessage>
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/XCNCIP2Test.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/XCNCIP2Test.php
index 328cdb5cdf7..0e74dd84771 100644
--- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/XCNCIP2Test.php
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/XCNCIP2Test.php
@@ -27,6 +27,9 @@
  */
 namespace VuFindTest\ILS\Driver;
 
+use InvalidArgumentException;
+use Laminas\Http\Client\Adapter\Test as TestAdapter;
+use Laminas\Http\Response as HttpResponse;
 use VuFind\ILS\Driver\XCNCIP2;
 
 /**
@@ -47,6 +50,1266 @@ class XCNCIP2Test extends \VuFindTest\Unit\ILSDriverTestCase
      */
     public function setUp(): void
     {
-        $this->driver = new XCNCIP2();
+        $this->driver = new XCNCIP2(new \VuFind\Date\Converter());
+    }
+
+    /**
+     * Test definition for testGetMyTransactions
+     *
+     * @var array[]
+     */
+    protected $transactionsTests = [
+        [
+            'file' => [
+                'lookupUserResponse.xml',
+                'LookupItem.xml',
+            ],
+            'result' => [
+                [
+                    'id' => 'MZK01000847602-MZK50000847602000090',
+                    'item_agency_id' => 'My Agency',
+                    'patronAgencyId' => 'Test agency',
+                    'duedate' => '11-19-2014',
+                    'title' => 'Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...',
+                    'item_id' => '104',
+                    'renewable' => false,
+                ],
+                [
+                    'id' => 'KN3183000000046386',
+                    'item_agency_id' => 'Agency from lookup item',
+                    'patronAgencyId' => 'Test agency',
+                    'duedate' => '11-26-2014',
+                    'title' => 'Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz',
+                    'item_id' => '105',
+                    'renewable' => true,
+                ],
+            ],
+        ],
+        [
+            'file' => [
+                'LookupUserResponseWithoutNamespacePrefix.xml',
+            ],
+            'result' => [
+                [
+                    'id' => 'MZK01000847602-MZK50000847602000090',
+                    'item_agency_id' => 'My Agency',
+                    'patronAgencyId' => 'Test agency',
+                    'duedate' => '11-19-2014',
+                    'title' => 'Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...',
+                    'item_id' => '104',
+                    'renewable' => true,
+                ],
+                [
+                    'id' => 'MZK01000000456-MZK50000000456000440',
+                    'item_agency_id' => 'My Agency',
+                    'patronAgencyId' => 'Test agency',
+                    'duedate' => '11-26-2014',
+                    'title' => 'Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz',
+                    'item_id' => '105',
+                    'renewable' => true,
+                ],
+            ],
+        ],
+        [
+            'file' => [
+                'LookupUserResponseWithoutNamespaceDefinition.xml',
+            ],
+            'result' => [
+                [
+                    'id' => 'MZK01000847602-MZK50000847602000090',
+                    'item_agency_id' => 'My Agency',
+                    'patronAgencyId' => 'Test agency',
+                    'duedate' => '11-19-2014',
+                    'title' => 'Jahrbücher der Deutschen Malakozoologischen Gesellschaft ...',
+                    'item_id' => '104',
+                    'renewable' => true,
+                ],
+                [
+                    'id' => 'MZK01000000456-MZK50000000456000440',
+                    'item_agency_id' => 'My Agency',
+                    'patronAgencyId' => 'Test agency',
+                    'duedate' => '11-26-2014',
+                    'title' => 'Anna Nahowská a císař František Josef : zápisky / Friedrich Saathen ; z něm. přel. Ivana Víz',
+                    'item_id' => '105',
+                    'renewable' => true,
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testGetMyFines
+     *
+     * @var array[]
+     */
+    protected $finesTests = [
+        [
+            'file' => 'lookupUserResponse.xml',
+            'result' => [
+                [
+                    'id' => '8071750247',
+                    'duedate' => '',
+                    'amount' => 25,
+                    'balance' => 25,
+                    'checkout' => '',
+                    'fine' => 'Service Charge',
+                    'createdate' => '11-14-2014',
+                ],
+            ],
+        ],
+        [
+            'file' => 'LookupUserResponseWithoutNamespacePrefix.xml',
+            'result' => [
+                [
+                    'id' => '',
+                    'duedate' => '',
+                    'amount' => 25,
+                    'balance' => 25,
+                    'checkout' => '',
+                    'fine' => 'Service Charge',
+                    'createdate' => '11-14-2014',
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testPatronLogin
+     *
+     * @var array[]
+     */
+    protected $loginTests = [
+        [
+            'file' => 'lookupUserResponse.xml',
+            'result' => [
+                'id' => '700',
+                'patronAgencyId' => 'MZK',
+                'cat_username' => 'my_login',
+                'cat_password' => 'my_password',
+                'email' => 'test@mzk.cz',
+                'major' => null,
+                'college' => null,
+                'firstname' => 'John',
+                'lastname' => 'Smith',
+            ],
+        ],
+        [
+            'file' => 'LookupUserResponseWithoutNamespacePrefix.xml',
+            'result' => [
+                'id' => '700',
+                'patronAgencyId' => 'MZK',
+                'cat_username' => 'my_login',
+                'cat_password' => 'my_password',
+                'email' => 'test@mzk.cz',
+                'major' => null,
+                'college' => null,
+                'firstname' => 'John',
+                'lastname' => 'Smith',
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testGetMyHolds
+     *
+     * @var array[]
+     */
+    protected $holdsTests = [
+        [
+            'file' => 'lookupUserResponse.xml',
+            'result' => [
+                [
+                    'id' => '111',
+                    'title' => 'Ahoj, Blanko! : dívčí román / Eva Bernardinová',
+                    'item_id' => 'MZK01000353880-MZK50000353880000040',
+                    'create' => '10-10-2014',
+                    'expire' => null,
+                    'position' => null,
+                    'requestId' => null,
+                    'location' => 'Loan Department - Ground floor',
+                    'item_agency_id' => null,
+                    'canceled' => false,
+                    'available' => false,
+
+                ],
+                [
+                    'id' => '112',
+                    'title' => 'Aktiv revizních techniků elektrických zařízení',
+                    'item_id' => 'MZK01000065021-MZK50000065021000010',
+                    'create' => '10-23-2014',
+                    'expire' => null,
+                    'position' => null,
+                    'requestId' => null,
+                    'location' => 'Loan Department - Ground floor',
+                    'item_agency_id' => null,
+                    'canceled' => false,
+                    'available' => false,
+                ],
+            ],
+        ],
+        [
+            'file' => 'LookupUserResponseWithoutNamespacePrefix.xml',
+            'result' => [
+                [
+                    'id' => '111',
+                    'title' => 'Ahoj, Blanko! : dívčí román / Eva Bernardinová',
+                    'item_id' => 'MZK01000353880-MZK50000353880000040',
+                    'create' => '10-10-2014',
+                    'expire' => null,
+                    'position' => null,
+                    'requestId' => null,
+                    'location' => 'Loan Department - Ground floor',
+                    'item_agency_id' => null,
+                    'canceled' => false,
+                    'available' => false,
+
+                ],
+                [
+                    'id' => '112',
+                    'title' => 'Aktiv revizních techniků elektrických zařízení',
+                    'item_id' => 'MZK01000065021-MZK50000065021000010',
+                    'create' => '10-23-2014',
+                    'expire' => null,
+                    'position' => null,
+                    'requestId' => null,
+                    'location' => 'Loan Department - Ground floor',
+                    'item_agency_id' => null,
+                    'canceled' => false,
+                    'available' => false,
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testGetMyProfile
+     *
+     * @var array[]
+     */
+    protected $profileTests = [
+        [
+            'file' => 'lookupUserResponse.xml',
+            'result' => [
+                'firstname' => 'John',
+                'lastname' => 'Smith',
+                'address1' => 'Trvalá ulice 123, Big City, 12345',
+                'address2' => '',
+                'zip' => '',
+                'phone' => '',
+                'group' => '',
+            ],
+        ],
+        [
+            'file' => 'LookupUserResponseWithoutNamespacePrefix.xml',
+            'result' => [
+                'firstname' => 'John',
+                'lastname' => 'Smith',
+                'address1' => 'Trvalá ulice 123, Big City, 12345',
+                'address2' => '',
+                'zip' => '',
+                'phone' => '',
+                'group' => '',
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testGetMyStorageRetrievalRequests
+     *
+     * @var array[]
+     */
+    protected $storageRetrievalTests = [
+        [
+            'file' => 'lookupUserResponse.xml',
+            'result' => [
+                [
+                    'id' => '155',
+                    'title' => 'Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]',
+                    'create' => '11-09-2014',
+                    'expire' => null,
+                    'position' => null,
+                    'requestId' => null,
+                    'location' => 'Loan Department - Ground floor',
+                    'item_agency_id' => null,
+                    'canceled' => false,
+                    'item_id' => 'MZK01001333770-MZK50001370317000020',
+                    'available' => false,
+                ],
+            ],
+        ],
+        [
+            'file' => 'LookupUserResponseWithoutNamespacePrefix.xml',
+            'result' => [
+                [
+                    'id' => '155',
+                    'title' => 'Listen and play : with magicians! : 3. ročník / Věra Štiková ; [ilustrace Andrea Schindlerová]',
+                    'create' => '11-09-2014',
+                    'expire' => null,
+                    'position' => null,
+                    'requestId' => null,
+                    'location' => 'Loan Department - Ground floor',
+                    'item_agency_id' => null,
+                    'canceled' => false,
+                    'item_id' => 'MZK01001333770-MZK50001370317000020',
+                    'available' => false,
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testGetStatuses
+     *
+     * @var array[]
+     */
+    protected $statusesTests = [
+        [
+            'file' => 'lookupItemSet.xml',
+            'result' => [
+                'MZK01000000421' => [
+                    [
+                        'status' => 'Available on shelf',
+                        'location' => null,
+                        'callnumber' => '621.3 ANG',
+                        'availability' => true,
+                        'reserve' => 'N',
+                        'id' => 'MZK01000000421',
+                    ],
+                ],
+                'MZK01000062021' => [
+                    [
+                        'status' => 'Available On Shelf',
+                        'location' => null,
+                        'callnumber' => 'PK-0083.568',
+                        'availability' => true,
+                        'reserve' => 'N',
+                        'id' => 'MZK01000062021',
+                    ],
+                ],
+                'MZK01000000425' => [
+                    [
+                        'status' => 'Available On Shelf',
+                        'location' => 'Some holding location',
+                        'callnumber' => '2-0997.767,2',
+                        'availability' => true,
+                        'reserve' => 'N',
+                        'id' => 'MZK01000000425',
+                    ],
+                    [
+                        'status' => 'Circulation Status Undefined',
+                        'location' => 'Some holding location',
+                        'callnumber' => null,
+                        'availability' => false,
+                        'reserve' => 'N',
+                        'id' => 'MZK01000000425',
+                        'use_unknown_message' => true,
+                    ],
+                ],
+            ],
+        ],
+        [
+            'file' => 'lookupItemSetWithoutNamespacePrefix.xml',
+            'result' => [
+                'MZK01000000421' => [
+                    [
+                        'status' => 'Available on shelf',
+                        'location' => null,
+                        'callnumber' => '621.3 ANG',
+                        'availability' => true,
+                        'reserve' => 'N',
+                        'id' => 'MZK01000000421',
+                    ],
+                ],
+                'MZK01000062021' => [
+                    [
+                        'status' => 'Available On Shelf',
+                        'location' => null,
+                        'callnumber' => 'PK-0083.568',
+                        'availability' => true,
+                        'reserve' => 'N',
+                        'id' => 'MZK01000062021',
+                    ],
+                ],
+                'MZK01000000425' => [
+                    [
+                        'status' => 'Available On Shelf',
+                        'location' => 'Some holding location',
+                        'callnumber' => '2-0997.767,2',
+                        'availability' => true,
+                        'reserve' => 'N',
+                        'id' => 'MZK01000000425',
+                    ],
+                    [
+                        'status' => 'Available On Shelf',
+                        'location' => 'Some holding location',
+                        'callnumber' => null,
+                        'availability' => true,
+                        'reserve' => 'N',
+                        'id' => 'MZK01000000425',
+                    ],
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testGetHolding
+     *
+     * @var array[]
+     */
+    protected $holdingTests = [
+        [
+            'file' => 'lookupItemSet.xml',
+            'result' => [
+                [
+                    'status' => 'Available on shelf',
+                    'location' => null,
+                    'callnumber' => '621.3 ANG',
+                    'availability' => true,
+                    'reserve' => 'N',
+                    'id' => '123456',
+                    'item_id' => 'MZK01000000421-MZK50000000421000010',
+                    'bib_id' => 'MZK01000000421',
+                    'duedate' => '',
+                    'volume' => '',
+                    'number' => '',
+                    'is_holdable' => true,
+                    'addLink' => true,
+                    'storageRetrievalRequest' => 'auto',
+                    'addStorageRetrievalRequestLink' => 'true',
+                    'eresource' => '',
+                    'item_agency_id' => 'My university',
+                    'holdtype' => 'Hold',
+                    'barcode' => 'MZK01000000421-MZK50000000421000010',
+                ],
+                [
+                    'status' => 'Available On Shelf',
+                    'location' => null,
+                    'callnumber' => 'PK-0083.568',
+                    'availability' => true,
+                    'reserve' => 'N',
+                    'id' => '123456',
+                    'bib_id' => 'MZK01000062021',
+                    'item_id' => 'MZK01000062021-MZK50000062021000010',
+                    'item_agency_id' => '',
+                    'duedate' => '',
+                    'volume' => '',
+                    'number' => '',
+                    'barcode' => 'Unknown barcode',
+                    'is_holdable' => true,
+                    'addLink' => true,
+                    'holdtype' => 'Hold',
+                    'storageRetrievalRequest' => 'auto',
+                    'addStorageRetrievalRequestLink' => 'true',
+                    'eresource' => '',
+                ],
+                [
+                    'status' => 'Available On Shelf',
+                    'location' => 'Some holding location',
+                    'callnumber' => '2-0997.767,2',
+                    'availability' => true,
+                    'reserve' => 'N',
+                    'id' => '123456',
+                    'item_id' => 'MZK01000000425-MZK50000000425000020',
+                    'bib_id' => 'MZK01000000425',
+                    'item_agency_id' => '',
+                    'duedate' => '',
+                    'volume' => '',
+                    'number' => '',
+                    'barcode' => 'Unknown barcode',
+                    'is_holdable' => true,
+                    'addLink' => true,
+                    'holdtype' => 'Hold',
+                    'storageRetrievalRequest' => 'auto',
+                    'addStorageRetrievalRequestLink' => 'true',
+                    'eresource' => '',
+                ],
+                [
+                    'status' => 'Circulation Status Undefined',
+                    'location' => 'Some holding location',
+                    'callnumber' => '',
+                    'availability' => false,
+                    'reserve' => 'N',
+                    'id' => '123456',
+                    'use_unknown_message' => true,
+                    'item_id' => 'MZK01000000425-MZK50000000425000030',
+                    'bib_id' => 'MZK01000000425',
+                    'item_agency_id' => '',
+                    'duedate' => '',
+                    'volume' => '',
+                    'number' => '',
+                    'barcode' => 'Unknown barcode',
+                    'is_holdable' => true,
+                    'addLink' => true,
+                    'holdtype' => 'Recall',
+                    'storageRetrievalRequest' => 'auto',
+                    'addStorageRetrievalRequestLink' => 'true',
+                    'eresource' => '',
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testPlaceHold
+     *
+     * @var array[]
+     */
+    protected $placeHoldTests = [
+        [
+            'file' => 'RequestItemResponseAcceptedWithItemId.xml',
+            'result' => [
+                'success' => true,
+                'sysMessage' => 'Request Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseAcceptedWithRequestId.xml',
+            'result' => [
+                'success' => true,
+                'sysMessage' => 'Request Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseDenied.xml',
+            'result' => [
+                'success' => false,
+                'sysMessage' => 'Request Not Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseDeniedWithIdentifiers.xml',
+            'result' => [
+                'success' => false,
+                'sysMessage' => 'Request Not Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseDeniedNotFullProblemElement.xml',
+            'result' => [
+                'success' => false,
+                'sysMessage' => 'Request Not Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseDeniedEmpty.xml',
+            'result' => [
+                'success' => false,
+                'sysMessage' => 'Request Not Successful.'
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testPlaceStorageRetrievalRequest
+     *
+     * @var array[]
+     */
+    protected $placeStorageRetrievalRequestTests = [
+        [
+            'file' => 'RequestItemResponseAcceptedWithItemId.xml',
+            'result' => [
+                'success' => true,
+                'sysMessage' => 'Storage Retrieval Request Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseAcceptedWithRequestId.xml',
+            'result' => [
+                'success' => true,
+                'sysMessage' => 'Storage Retrieval Request Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseDenied.xml',
+            'result' => [
+                'success' => false,
+                'sysMessage' => 'Storage Retrieval Request Not Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseDeniedWithIdentifiers.xml',
+            'result' => [
+                'success' => false,
+                'sysMessage' => 'Storage Retrieval Request Not Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseDeniedNotFullProblemElement.xml',
+            'result' => [
+                'success' => false,
+                'sysMessage' => 'Storage Retrieval Request Not Successful.'
+            ],
+        ],
+        [
+            'file' => 'RequestItemResponseDeniedEmpty.xml',
+            'result' => [
+                'success' => false,
+                'sysMessage' => 'Storage Retrieval Request Not Successful.'
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testCancelHolds
+     *
+     * @var array[]
+     */
+    protected $cancelHoldsTests = [
+        [
+            'file' => 'CancelRequestItemResponseAccepted.xml',
+            'result' => [
+                'count' => 1,
+                'items' => [
+                    'Item1' => [
+                        'success' => true,
+                        'status' => 'hold_cancel_success',
+                    ],
+                ],
+            ],
+        ],
+        [
+            'file' => 'CancelRequestItemResponseDenied.xml',
+            'result' => [
+                'count' => 0,
+                'items' => [
+                    'Item1' => [
+                        'success' => false,
+                        'status' => 'hold_cancel_fail',
+                    ],
+                ],
+            ],
+        ],
+        [
+            'file' => 'CancelRequestItemResponseDeniedWithUserId.xml',
+            'result' => [
+                'count' => 0,
+                'items' => [
+                    'Item1' => [
+                        'success' => false,
+                        'status' => 'hold_cancel_fail',
+                    ],
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testCancelStorageRetrievalRequests
+     *
+     * @var array[]
+     */
+    protected $cancelStorageRetrievalTests = [
+        [
+            'file' => 'CancelRequestItemResponseAccepted.xml',
+            'result' => [
+                'count' => 1,
+                'items' => [
+                    'Item1' => [
+                        'success' => true,
+                        'status' => 'storage_retrieval_request_cancel_success',
+                    ],
+                ],
+            ],
+        ],
+        [
+            'file' => 'CancelRequestItemResponseDenied.xml',
+            'result' => [
+                'count' => 0,
+                'items' => [
+                    'Item1' => [
+                        'success' => false,
+                        'status' => 'storage_retrieval_request_cancel_fail',
+                    ],
+                ],
+            ],
+        ],
+        [
+            'file' => 'CancelRequestItemResponseDeniedWithUserId.xml',
+            'result' => [
+                'count' => 0,
+                'items' => [
+                    'Item1' => [
+                        'success' => false,
+                        'status' => 'storage_retrieval_request_cancel_fail',
+                    ],
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test definition for testRenewMyItems
+     *
+     * @var array[]
+     */
+    protected $renewMyItemsTests = [
+        [
+            'file' => 'RenewItemResponseAccepted.xml',
+            'result' => [
+                'blocks' => false,
+                'details' => [
+                    'Item1' => [
+                        'success' => true,
+                        'new_date' => '09-08-2020',
+                        'new_time' => '20:00',
+                        'item_id' => 'Item1'
+                    ],
+                ],
+            ],
+        ],
+        [
+            'file' => 'RenewItemResponseAcceptedAlternativeDateFormat.xml',
+            'result' => [
+                'blocks' => false,
+                'details' => [
+                    'Item1' => [
+                        'success' => true,
+                        'new_date' => '08-31-2020',
+                        'new_time' => '17:59',
+                        'item_id' => 'Item1'
+                    ],
+                ],
+            ],
+        ],
+        [
+            'file' => 'RenewItemResponseDenied.xml',
+            'result' => [
+                'blocks' => false,
+                'details' => [
+                    'Item1' => [
+                        'success' => false,
+                        'item_id' => 'Item1'
+                    ],
+                ],
+            ],
+        ],
+        [
+            'file' => 'RenewItemResponseDeniedInvalidMessage.xml',
+            'result' => [
+                'blocks' => false,
+                'details' => [
+                    'Item1' => [
+                        'success' => false,
+                        'item_id' => 'Item1'
+                    ],
+                ],
+            ],
+        ],
+    ];
+
+    /**
+     * Test getMyTransactions
+     *
+     * @return void
+     */
+    public function testGetMyTransactions()
+    {
+        $this->configureDriver();
+        foreach ($this->transactionsTests as $test) {
+            $this->mockResponse($test['file']);
+            $transactions = $this->driver->getMyTransactions([
+                'cat_username' => 'my_login',
+                'cat_password' => 'my_password',
+                'patronAgencyId' => 'Test agency',
+            ]);
+            $this->assertEquals(
+                $test['result'], $transactions, 'Fixture file: ' . implode(', ', (array)$test['file'])
+            );
+        }
+    }
+
+    /**
+     * Test getMyFines
+     *
+     * @return void
+     */
+    public function testGetMyFines()
+    {
+        $this->configureDriver();
+        foreach ($this->finesTests as $test) {
+            $this->mockResponse($test['file']);
+            $fines = $this->driver->getMyFines(
+                [
+                    'cat_username' => 'my_login', 'cat_password' => 'my_password',
+                    'patronAgencyId' => 'Test agency',
+                ]
+            );
+            $this->assertEquals($test['result'], $fines, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test patronLogin
+     *
+     * @return void
+     */
+    public function testPatronLogin()
+    {
+        $this->configureDriver();
+        foreach ($this->loginTests as $test) {
+            $this->mockResponse($test['file']);
+            $patron = $this->driver->patronLogin('my_login', 'my_password');
+            $this->assertEquals($test['result'], $patron, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test getMyHolds
+     *
+     * @return void
+     */
+    public function testGetMyHolds()
+    {
+        $this->configureDriver();
+        foreach ($this->holdsTests as $test) {
+            $this->mockResponse($test['file']);
+            $holds = $this->driver->getMyHolds([
+                'cat_username' => 'my_login',
+                'cat_password' => 'my_password',
+                'patronAgencyId' => 'Test agency',
+             ]);
+            $this->assertEquals($test['result'], $holds, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test getMyProfile
+     *
+     * @return void
+     */
+    public function testGetMyProfile()
+    {
+        $this->configureDriver();
+        foreach ($this->profileTests as $test) {
+            $this->mockResponse($test['file']);
+            $profile = $this->driver->getMyProfile(
+                [
+                    'cat_username' => 'my_login',
+                    'cat_password' => 'my_password',
+                    'patronAgencyId' => 'Test agency',
+                ]
+            );
+            $this->assertEquals($test['result'], $profile, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test getMyStorageRetrievalRequests
+     *
+     * @return void
+     */
+    public function testGetMyStorageRetrievalRequests()
+    {
+        $this->configureDriver();
+        foreach ($this->storageRetrievalTests as $test) {
+            $this->mockResponse($test['file']);
+            $storageRetrievals = $this->driver->getMyStorageRetrievalRequests([
+                'cat_username' => 'my_login',
+                'cat_password' => 'my_password',
+                'patronAgencyId' => 'Test agency',
+            ]);
+            $this->assertEquals($test['result'], $storageRetrievals, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test getStatuses
+     *
+     * @return void
+     */
+    public function testGetStatuses()
+    {
+        $this->configureDriver();
+        foreach ($this->statusesTests as $test) {
+            $this->mockResponse($test['file']);
+            $status = $this->driver->getStatuses(['Some Id']);
+            $this->assertEquals($test['result'], $status, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test getHolding
+     *
+     * @return void
+     */
+    public function testGetHolding()
+    {
+        $this->configureDriver();
+        foreach ($this->holdingTests as $test) {
+            $this->mockResponse($test['file']);
+            $holdings = $this->driver->getHolding('123456');
+            $this->assertEquals($test['result'], $holdings, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test getPickUpLocations
+     *
+     * @return void
+     */
+    public function testGetPickupLocations()
+    {
+        // Test reading pickup locations from file
+        $this->configureDriver();
+        $locations = $this->driver->getPickUpLocations([]);
+        $this->assertEquals([
+            [
+                'locationId' => 'My University|1',
+                'locationDisplay' => 'Main Circulation Desk',
+            ],
+            [
+                'locationId' => 'My University|2',
+                'locationDisplay' => 'Stacks',
+            ]
+        ], $locations);
+
+        // Test reading pickup locations from NCIP responder
+        $this->configureDriver([
+                'Catalog' => [
+                    'url' => 'https://test.ncip.example',
+                    'consortium' => false,
+                    'agency' => ['Test agency'],
+                    'pickupLocationsFromNCIP' => true,
+                ],
+                'NCIP' => [],
+            ]);
+        $this->mockResponse('LookupAgencyResponse.xml');
+        $locations = $this->driver->getPickUpLocations([]);
+        $this->assertEquals([
+            [
+                'locationId' => 'My library|1',
+                'locationDisplay' => 'Main library',
+            ],
+            [
+                'locationId' => 'My library|2',
+                'locationDisplay' => 'Stacks',
+            ]
+        ], $locations);
+
+        // Test reading pickup locations from NCIP, but response is without locations
+        $this->configureDriver([
+            'Catalog' => [
+                'url' => 'https://test.ncip.example',
+                'consortium' => false,
+                'agency' => ['Test agency'],
+                'pickupLocationsFromNCIP' => true,
+            ],
+            'NCIP' => [],
+        ]);
+        $this->mockResponse('LookupAgencyResponseWithoutLocations.xml');
+        $locations = $this->driver->getPickUpLocations([]);
+        $this->assertEquals([], $locations);
+    }
+
+    /**
+     * Test placeHold
+     *
+     * @return void
+     */
+    public function testPlaceHold()
+    {
+        $this->configureDriver();
+        foreach ($this->placeHoldTests as $test) {
+            $this->mockResponse($test['file']);
+            $hold = $this->driver->placeHold(
+                [
+                    'patron' => [
+                        'cat_username' => 'my_login',
+                        'cat_password' => 'my_password',
+                        'patronAgencyId' => 'Test agency',
+                    ],
+                    'bib_id' => '1',
+                    'item_id' => '1',
+                    'pickUpLocation' => 'My University|1',
+                    'holdtype' => 'title',
+                    'requiredBy' => '2020-12-30',
+                    'item_agency_id' => 'My University',
+                ]
+            );
+            $this->assertEquals($test['result'], $hold, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test placeStorageRetrievalRequest
+     *
+     * @return void
+     */
+    public function testPlaceStorageRetrievalRequest()
+    {
+        $this->configureDriver();
+        foreach ($this->placeStorageRetrievalRequestTests as $test) {
+            $this->mockResponse($test['file']);
+            $result = $this->driver->placeStorageRetrievalRequest(
+                [
+                    'patron' => [
+                        'cat_username' => 'my_login',
+                        'cat_password' => 'my_password',
+                        'patronAgencyId' => 'Test agency',
+                    ],
+                    'bib_id' => '1',
+                    'item_id' => '1',
+                    'pickUpLocation' => 'My University|1',
+                    'holdtype' => 'title',
+                    'requiredBy' => '2020-12-30',
+                    'item_agency_id' => 'My University',
+                ]
+            );
+            $this->assertEquals($test['result'], $result, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test cancelHolds
+     *
+     * @return void
+     */
+    public function testCancelHolds()
+    {
+        $this->configureDriver();
+        foreach ($this->cancelHoldsTests as $test) {
+            $this->mockResponse($test['file']);
+            $result = $this->driver->cancelHolds(
+                [
+                    'patron' => [
+                        'cat_username' => 'my_login',
+                        'cat_password' => 'my_password',
+                        'patronAgencyId' => 'Test agency',
+                    ],
+                    'details' => [
+                        'My University|Request1|Item1',
+                    ],
+                ]
+            );
+            $this->assertEquals($test['result'], $result, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test cancelHolds
+     *
+     * @return void
+     */
+    public function testCancelStorageRetrievalRequests()
+    {
+        $this->configureDriver();
+        foreach ($this->cancelStorageRetrievalTests as $test) {
+            $this->mockResponse($test['file']);
+            $result = $this->driver->cancelStorageRetrievalRequests(
+                [
+                    'patron' => [
+                        'cat_username' => 'my_login',
+                        'cat_password' => 'my_password',
+                        'patronAgencyId' => 'Test agency',
+                    ],
+                    'details' => [
+                        'My University|Request1|Item1',
+                    ],
+                ]
+            );
+            $this->assertEquals($test['result'], $result, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test renewMyItems
+     *
+     * @return void
+     */
+    public function testRenewMyItems()
+    {
+        $this->configureDriver();
+        foreach ($this->renewMyItemsTests as $test) {
+            $this->mockResponse($test['file']);
+            $result = $this->driver->renewMyItems(
+                [
+                    'patron' => [
+                        'cat_username' => 'my_login',
+                        'cat_password' => 'my_password',
+                        'patronAgencyId' => 'Test agency',
+                    ],
+                    'details' => [
+                        'My University|Item1',
+                    ],
+                ]
+            );
+            $this->assertEquals($test['result'], $result, 'Fixture file: ' . implode(', ', (array)$test['file']));
+        }
+    }
+
+    /**
+     * Test definition for testGetRequestMethods
+     *
+     * @var array[]
+     */
+    protected $requestTests = [
+        '1' => [
+            'method' => 'getStatusRequest',
+            'config' => [
+                'Catalog' => [
+                    'url' => 'https://test.ncip.example',
+                    'consortium' => false,
+                    'agency' => ['Test agency'],
+                    'pickupLocationsFile' => 'XCNCIP2_locations.txt',
+                    'fromAgency' => 'My portal',
+                ],
+                'NCIP' => [],
+            ],
+            'params' => [['1'], null, 'Test agency'],
+            'result' => 'LookupItemSetRequest.xml',
+        ],
+        '2' => [
+            'method' => 'getStatusRequest',
+            'params' => [['1'], null, 'Test agency'],
+            'result' => 'LookupItemSetRequestWithoutHeader.xml',
+        ],
+        '3' => [
+            'method' => 'getCancelRequest',
+            'params' => ['', '', 'patron agency', 'item agency', 'rq1', 'Hold', 'item1', '12345'],
+            'result' => 'CancelRequestItemRequest.xml'
+        ],
+        '4' => [
+            'method' => 'getCancelRequest',
+            'params' => ['username', 'password', 'patron agency', 'item agency', 'rq1', 'Hold', 'item1', '12345'],
+            'result' => 'CancelRequestItemRequestAuthInput.xml'
+        ],
+        '5' => [
+            'method' => 'getRenewRequest',
+            'params' => ['username', 'password', 'item1', 'item agency', 'patron agency'],
+            'result' => 'RenewItemRequest.xml'
+        ],
+        '6' => [
+            'method' => 'getRequest',
+            'config' => [
+                'Catalog' => [
+                    'url' => 'https://test.ncip.example',
+                    'consortium' => false,
+                    'agency' => ['Test agency'],
+                    'pickupLocationsFile' => 'XCNCIP2_locations.txt',
+                    'fromAgency' => 'My portal',
+                ],
+                'NCIP' => [],
+            ],
+            'params' => ['username', '', 'bib1', 'item1', 'patron agency', 'item agency', 'Hold', 'Item', '2020-12-20T00:00:00.000Z', null, 'patron1'],
+            'result' => 'RequestItemRequest.xml'
+        ],
+        '7' => [
+            'method' => 'getLookupUserRequest',
+            'params' => [null, 'password', 'patron agency', ['<ns1:LoanedItemsDesired />'], 'patron1'],
+            'result' => 'LookupUserRequest.xml'
+        ],
+        '8' => [
+            'method' => 'getLookupAgencyRequest',
+            'params' => [null],
+            'result' => 'LookupAgencyRequest.xml'
+        ],
+        '9' => [
+            'method' => 'getLookupItemRequest',
+            'config' => [
+                'Catalog' => [
+                    'url' => 'https://test.ncip.example',
+                    'consortium' => false,
+                    'agency' => ['Test agency'],
+                    'pickupLocationsFile' => 'XCNCIP2_locations.txt',
+                    'fromAgency' => 'My portal',
+                ],
+                'NCIP' => [],
+            ],
+            'params' => ['item1', 'Accession Number'],
+            'result' => 'LookupItemRequest.xml'
+        ],
+    ];
+
+    /**
+     * Test methods for creating NCIP requests
+     *
+     * @return void
+     */
+    public function testGetRequestMethods()
+    {
+        foreach ($this->requestTests as $id => $test) {
+            $this->configureDriver($test['config'] ?? null);
+            $method = new \ReflectionMethod('\VuFind\ILS\Driver\XCNCIP2', $test['method']);
+            $method->setAccessible(true);
+            $request = $method->invokeArgs($this->driver, $test['params'] ?? []);
+            $file = realpath(
+                __DIR__ .
+                '/../../../../../../tests/fixtures/xcncip2/request/' .
+                $test['result']
+            );
+            $expected = file_get_contents($file);
+            $this->assertEquals($expected, $request, 'Test identifier: ' . $id);
+        }
+    }
+
+    /**
+     * Mock fixture as HTTP client response
+     *
+     * @param string|array|null $fixture Fixture file
+     **
+     * @throws InvalidArgumentException Fixture file does not exist
+     */
+    protected function mockResponse($fixture = null)
+    {
+        $adapter = new TestAdapter();
+        if (!empty($fixture)) {
+            $fixture = (array)$fixture;
+            $responseObj = $this->loadResponse($fixture[0]);
+            $adapter->setResponse($responseObj);
+            array_shift($fixture);
+            foreach ($fixture as $f) {
+                $responseObj = $this->loadResponse($f);
+                $adapter->addResponse($responseObj);
+            }
+        }
+
+        $service = new \VuFindHttp\HttpService();
+        $service->setDefaultAdapter($adapter);
+        $this->driver->setHttpService($service);
+    }
+
+    /**
+     * Load response from file
+     *
+     * @param string $filename File name of raw HTTP response
+     *
+     * @return HttpResponse Response object
+     */
+    protected function loadResponse($filename)
+    {
+        $file = realpath(
+            __DIR__ .
+            '/../../../../../../tests/fixtures/xcncip2/response/' . $filename
+        );
+        if (!is_string($file) || !file_exists($file) || !is_readable($file)) {
+            throw new InvalidArgumentException(
+                sprintf('Unable to load fixture file: %s ', $file)
+            );
+        }
+        $response = file_get_contents($file);
+        return HttpResponse::fromString($response);
+    }
+
+    /**
+     * Configure driver for test case
+     *
+     * @param array|null $config ILS driver configuration
+     *
+     * @return void
+     */
+    protected function configureDriver($config = null)
+    {
+        $this->driver = new XCNCIP2(new \VuFind\Date\Converter());
+        $this->driver->setConfig($config ?? [
+            'Catalog' => [
+                'url' => 'https://test.ncip.example',
+                'consortium' => false,
+                'agency' => 'Test agency',
+                'pickupLocationsFile' => 'XCNCIP2_locations.txt',
+            ],
+            'NCIP' => [],
+        ]);
+        $this->driver->init();
     }
 }
-- 
GitLab