From 08fe624d7e3d165f95e2a58fea03d80124aba944 Mon Sep 17 00:00:00 2001
From: Chris Delis <cedelis@uillinois.edu>
Date: Mon, 11 Apr 2016 11:18:07 -0500
Subject: [PATCH] Lots of bug fixes. Add consortial support to Renew and Cancel
 requests.

---
 .../VuFind/src/VuFind/ILS/Driver/XCNCIP2.php  | 237 +++++++++++++-----
 1 file changed, 173 insertions(+), 64 deletions(-)

diff --git a/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php b/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php
index c08dead67b7..ad82f9983a9 100644
--- a/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php
+++ b/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php
@@ -123,8 +123,9 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         }
         if (($handle = fopen($pickupLocationsFile, "r")) !== false) {
             while (($data = fgetcsv($handle)) !== false) {
-                $this->pickupLocations[$data[0]][] = [
-                    'locationID' => $data[1],
+                $agency_ID = $data[0] . '|' . $data[1];
+                $this->pickupLocations[$agency_ID] = [
+                    'locationID' => $agency_ID,
                     'locationDisplay' => $data[2]
                 ];
             }
@@ -268,6 +269,12 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         );
         $itemCallNo = (string)$itemCallNo[0];
 
+        $number = $current->xpath(
+            'ns1:ItemOptionalFields/ns1:ItemDescription/' .
+            'ns1:CopyNumber'
+        );
+        $number = (string)$number[0];
+
         $volume = $current->xpath(
             'ns1:ItemOptionalFields/ns1:ItemDescription/' .
             'ns1:HoldingsInformation/ns1:UnstructuredHoldingsData'
@@ -295,7 +302,8 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             'callnumber' => $itemCallNo,
             'duedate' => '',        // not supported
             //'number' => $number++,
-            'number' => $volume,
+            '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,
@@ -337,15 +345,17 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     protected function getStatusRequest($idList, $resumption = null, $agency = null)
     {
-        // cedelis:
-        //if (is_null($agency)) $agency = $this->agency[0];
+        $agencyList = [];
 
-        // pzurek:
-        //if (is_null($agency)) $agency = array_keys($this->agency)[0];
-        // The above does not work on older versions of php
-        $keys = array_keys($this->agency);
         if (is_null($agency)) {
-            $agency = $keys[0];
+            $keys = array_keys($this->agency);
+            $agencyList[] = $keys[0];
+        }
+
+        if (! is_array($agency)) {
+            $agencyList[] = $agency[0];
+        } else {
+            $agencyList = $agency;
         }
 
         // Build a list of the types of information we want to retrieve:
@@ -365,15 +375,14 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             'ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/xsd/' .
             'ncip_v2_0.xsd"><ns1:Ext><ns1:LookupItemSet>';
 
-        // Add the ID list:
-        foreach ($idList as $id) {
+        for ($i = 0, $size = count($idList); $i < $size; $i++) {
             $xml .= '<ns1:BibliographicId>' .
                     '<ns1:BibliographicRecordId>' .
                         '<ns1:BibliographicRecordIdentifier>' .
-                            htmlspecialchars($id) .
+                            htmlspecialchars($idList[$i]) .
                         '</ns1:BibliographicRecordIdentifier>' .
                         '<ns1:AgencyId>' .
-                            htmlspecialchars($agency) .
+                            htmlspecialchars($agencyList[$i]) .
                         '</ns1:AgencyId>' .
                     '</ns1:BibliographicRecordId>' .
                 '</ns1:BibliographicId>';
@@ -493,49 +502,58 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     ) {
         $aggregate_id = $id;
 
-        $item_agency_id = [];
+        $agencyList = [];
+        $idList = [];
         if (! is_null($ids)) {
             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 ($this->agency[$matched_agency]) {
-                        $item_agency_id[$matched_agency] = $matched_id;
+                    if (array_key_exists($matched_agency, $this->agency)) {
+                        $agencyList[] = $matched_agency;
+                        $idList[] = $matched_id;
                     }
                 }
             }
         }
 
         $holdings = [];
-        foreach ($item_agency_id as $_agency => $_id) {
-            $request = $this->getStatusRequest([$_id], null, $_agency);
-            $response = $this->sendRequest($request);
+        $request = $this->getStatusRequest($idList, null, $agencyList);
+        $response = $this->sendRequest($request);
 
-            $bib_id = $response->xpath(
-                'ns1:Ext/ns1:LookupItemSetResponse/ns1:BibInformation/' .
+        $bibs = $response->xpath(
+            'ns1:Ext/ns1:LookupItemSetResponse/ns1:BibInformation'
+        );
+
+        foreach ($bibs as $bib) {
+            $bib_ids = $bib->xpath(
                 'ns1:BibliographicId/ns1:BibliographicRecordId/' .
                 'ns1:BibliographicRecordIdentifier'
             );
+            $bib_id = (string)$bib_ids[0];
 
-            $holdingSets = $response->xpath('//ns1:HoldingsSet');
-
+            $holdingSets = $bib->xpath('ns1:HoldingsSet');
             foreach ($holdingSets as $holding) {
                 $holdCallNo = $holding->xpath('ns1:CallNumber');
                 $holdCallNo = (string)$holdCallNo[0];
                 $avail = $holding->xpath('ns1:ItemInformation');
+                $eResource = $holding->xpath(
+                    'ns1:ElectronicResource/ns1:ReferenceToResource'
+                );
+                $eResource = (string)$eResource[0];
 
                 // Build the array of holdings:
                 foreach ($avail as $current) {
                     $chunk = $this->getHoldingsForChunk(
-                        $current, $aggregate_id, (string)$bib_id[0]
+                        $current, $aggregate_id, $bib_id
                     );
                     $chunk['callnumber'] = empty($chunk['callnumber']) ?
                         $holdCallNo : $chunk['callnumber'];
+                    $chunk['eresource'] = $eResource;
                     $holdings[] = $chunk;
                 }
             }
-
         }
 
         return $holdings;
@@ -612,7 +630,10 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         //    $username, $password, 'patron_agency_id'
         //);
         $request = $this->getLookupUserRequest($username, $password);
+
         $response = $this->sendRequest($request);
+        $this->checkResponseForError($response);
+
         $id = $response->xpath(
             'ns1:LookupUserResponse/ns1:UserId/ns1:UserIdentifierValue'
         );
@@ -663,6 +684,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             $patron['patron_agency_id'], $extras
         );
         $response = $this->sendRequest($request);
+        $this->checkResponseForError($response);
 
         $retVal = [];
         $list = $response->xpath('ns1:LookupUserResponse/ns1:LoanedItem');
@@ -678,6 +700,10 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                 'ns1:Ext/ns1:BibliographicDescription/' .
                 'ns1:BibliographicRecordId/ns1:BibliographicRecordIdentifier'
             );
+            $itemAgencyId = $current->xpath(
+                'ns1:Ext/ns1:BibliographicDescription/' .
+                'ns1:BibliographicRecordId/ns1:AgencyId'
+            );
             // Hack to account for bibs from other non-local institutions
             // temporarily until consortial functionality is enabled.
             if ((string)$bib_id[0]) {
@@ -687,6 +713,8 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             }
             $retVal[] = [
                 'id' => $tmp,
+                'item_agency_id' => (string)$itemAgencyId[0],
+                'patron_agency_id' => $patron['patron_agency_id'],
                 'duedate' => $due,
                 'title' => (string)$title[0],
                 'item_id' => (string)$item_id[0],
@@ -716,6 +744,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             $patron['patron_agency_id'], $extras
         );
         $response = $this->sendRequest($request);
+        $this->checkResponseForError($response);
 
         $list = $response->xpath(
             'ns1:LookupUserResponse/ns1:UserFiscalAccount/ns1:AccountDetails'
@@ -778,6 +807,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             $patron['patron_agency_id'], $extras
         );
         $response = $this->sendRequest($request);
+        $this->checkResponseForError($response);
 
         $retVal = [];
         $list = $response->xpath('ns1:LookupUserResponse/ns1:RequestedItem');
@@ -843,6 +873,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             $patron['patron_agency_id'], $extras
         );
         $response = $this->sendRequest($request);
+        $this->checkResponseForError($response);
 
         $first = $response->xpath(
             'ns1:LookupUserResponse/ns1:UserOptionalFields/ns1:NameInformation/' .
@@ -1065,14 +1096,12 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     public function getPickUpLocations($patron, $holdDetails = null)
     {
         $locations = [];
-        foreach (array_keys($this->agency) as $agency) {
-            foreach ($this->pickupLocations[$agency] as $thisAgency) {
-                $locations[]
-                    = [
-                        'locationID' => $thisAgency['locationID'],
-                        'locationDisplay' => $thisAgency['locationDisplay'],
-                    ];
-            }
+        foreach ($this->pickupLocations as $thisAgency) {
+            $locations[]
+                = [
+                    'locationID' => $thisAgency['locationID'],
+                    'locationDisplay' => $thisAgency['locationDisplay'],
+                ];
         }
         return $locations;
     }
@@ -1094,6 +1123,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
             $patron['patron_agency_id'], $extras
         );
         $response = $this->sendRequest($request);
+        $this->checkResponseForError($response);
 
         $retVal = [];
         $list = $response->xpath('ns1:LookupUserResponse/ns1:RequestedItem');
@@ -1103,6 +1133,10 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                 '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');
@@ -1125,6 +1159,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                     '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],
@@ -1172,6 +1207,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $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);
@@ -1180,7 +1216,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $request = $this->getRequest(
             $username, $password, $bibId, $itemId,
             $details['patron']['patron_agency_id'],
-            $details['patron']['patron_agency_id'],
+            $pickUpAgency,
             $details['item_agency_id'],
             "Stack Retrieval", "Item", $lastInterestDate, $pickUpLocation
         );
@@ -1216,8 +1252,11 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getRenewDetails($checkOutDetails)
     {
-        $renewDetails = $checkOutDetails['item_id'];
-        return $renewDetails;
+        return $checkOutDetails['item_agency_id'] .
+                                "|" .
+                                $checkOutDetails['item_id'] .
+                                "|" .
+                                $checkOutDetails['id'];
     }
 
     /**
@@ -1240,6 +1279,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $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) . '-'
@@ -1249,11 +1289,13 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $request = $this->getRequest(
             $username, $password, $bibId, $itemId,
             $details['patron']['patron_agency_id'],
-            $details['patron']['patron_agency_id'],
+            $pickUpAgency,
             $details['item_agency_id'],
             $holdType, "Item", $lastInterestDate, $pickUpLocation
         );
         $response = $this->sendRequest($request);
+        $this->checkResponseForError($response);
+
         $success = $response->xpath(
             'ns1:RequestItemResponse/ns1:ItemId/ns1:ItemIdentifierValue'
         );
@@ -1287,13 +1329,15 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $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($itemId, $requestId) = explode("|", $cancelDetails);
+            list($itemAgencyId, $requestId, $bibId) = explode("|", $cancelDetails);
             $request = $this->getCancelRequest(
-                $username, $password, $requestId, "Hold"
+                $username, $password, $patronAgency,
+                $itemAgencyId, $requestId, "Hold"
             );
             $cancelRequestResponse = $this->sendRequest($request);
             $userId = $cancelRequestResponse->xpath(
@@ -1332,7 +1376,11 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getCancelHoldDetails($holdDetails)
     {
-        $cancelDetails = $holdDetails['id'] . "|" . $holdDetails['requestId'];
+        $cancelDetails = $holdDetails['item_agency_id'] .
+                         "|" .
+                         $holdDetails['requestId'] .
+                         "|" .
+                         $holdDetails['id'];
         return $cancelDetails;
     }
 
@@ -1353,13 +1401,19 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         $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($itemId, $requestId) = explode("|", $cancelDetails);
+            list($itemAgencyId, $requestId, $bibId) = explode("|", $cancelDetails);
             $request = $this->getCancelRequest(
-                $username, $password, $requestId, "Stack Retrieval"
+                $username,
+                $password,
+                $patronAgency,
+                $itemAgencyId,
+                $requestId,
+                "Stack Retrieval"
             );
             $cancelRequestResponse = $this->sendRequest($request);
             $userId = $cancelRequestResponse->xpath(
@@ -1398,7 +1452,11 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
      */
     public function getCancelStorageRetrievalRequestDetails($callslipDetails)
     {
-        return $callslipDetails['id'] . "|" . $callslipDetails['requestId'];
+        return $callslipDetails['item_agency_id'] .
+                                "|" .
+                                $callslipDetails['requestId'] .
+                                "|" .
+                                $callslipDetails['id'];
     }
 
     /**
@@ -1415,10 +1473,13 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     public function renewMyItems($renewDetails)
     {
         $details = [];
-        foreach ($renewDetails['details'] as $renewId) {
+        foreach ($renewDetails['details'] as $agencyId_renewId) {
+            list($agencyId, $renewId, $bibId) = explode("|", $agencyId_renewId);
             $request = $this->getRenewRequest(
                 $renewDetails['patron']['cat_username'],
-                $renewDetails['patron']['cat_password'], $renewId
+                $renewDetails['patron']['cat_password'], $renewId,
+                $agencyId,
+                $renewDetails['patron']['patron_agency_id']
             );
             $response = $this->sendRequest($request);
             $dueDate = $response->xpath('ns1:RenewItemResponse/ns1:DateDue');
@@ -1449,20 +1510,35 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     /**
      * Helper function to build the request XML to cancel a request:
      *
-     * @param string $username  Username for login
-     * @param string $password  Password for login
-     * @param string $requestId Id of the request to cancel
-     * @param string $type      The type of request to cancel (Hold, etc)
+     * @param string $username     Username for login
+     * @param string $password     Password for login
+     * @param string $patronAgency Agency for patron
+     * @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)
      *
      * @return string           NCIP request XML
      */
-    protected function getCancelRequest($username, $password, $requestId, $type)
-    {
+    protected function getCancelRequest($username,
+        $password,
+        $patronAgency,
+        $itemAgencyId,
+        $requestId,
+        $type
+    ) {
+    
         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) .
@@ -1486,6 +1562,9 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                         '</ns1:AuthenticationInputType>' .
                     '</ns1:AuthenticationInput>' .
                     '<ns1:RequestId>' .
+                        '<ns1:AgencyId>' .
+                            htmlspecialchars($itemAgencyId) .
+                        '</ns1:AgencyId>' .
                         '<ns1:RequestIdentifierValue>' .
                             htmlspecialchars($requestId) .
                         '</ns1:RequestIdentifierValue>' .
@@ -1527,12 +1606,12 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                    '<ns1:InitiationHeader>' .
                         '<ns1:FromAgencyId>' .
                             '<ns1:AgencyId>' .
-                                htmlspecialchars($patron_agency_id) .
+                                htmlspecialchars($pickup_agency_id) .
                             '</ns1:AgencyId>' .
                         '</ns1:FromAgencyId>' .
                         '<ns1:ToAgencyId>' .
                             '<ns1:AgencyId>' .
-                                htmlspecialchars($pickup_agency_id) .
+                                htmlspecialchars($patron_agency_id) .
                             '</ns1:AgencyId>' .
                         '</ns1:ToAgencyId>' .
                     '</ns1:InitiationHeader>' .
@@ -1594,19 +1673,33 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
     /**
      * Helper function to build the request XML to renew an item:
      *
-     * @param string $username Username for login
-     * @param string $password Password for login
-     * @param string $itemId   Id of item to renew
+     * @param string $username       Username for login
+     * @param string $password       Password for login
+     * @param string $itemId         Id of item to renew
+     * @param string $itemAgencyId   Agency of Item Id to renew
+     * @param string $patronAgencyId Agency of patron
      *
      * @return string          NCIP request XML
      */
-    protected function getRenewRequest($username, $password, $itemId)
-    {
+    protected function getRenewRequest($username,
+        $password,
+        $itemId,
+        $itemAgencyId,
+        $patronAgencyId
+    ) {
+    
         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) .
@@ -1630,6 +1723,9 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
                         '</ns1:AuthenticationInputType>' .
                     '</ns1:AuthenticationInput>' .
                     '<ns1:ItemId>' .
+                        '<ns1:AgencyId>' .
+                            htmlspecialchars($itemAgencyId) .
+                        '</ns1:AgencyId>' .
                         '<ns1:ItemIdentifierValue>' .
                             htmlspecialchars($itemId) .
                         '</ns1:ItemIdentifierValue>' .
@@ -1661,11 +1757,6 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
         if (!is_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) .
@@ -1703,4 +1794,22 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf
 
         return $ret;
     }
+
+    /**
+     * Throw an exception if an NCIP error is found
+     *
+     * @param XML $response from NCIP call
+     *
+     * @throws ILSException
+     * @return void
+     */
+    protected function checkResponseForError($response)
+    {
+        $error = $response->xpath(
+            '//ns1:Problem/ns1:ProblemDetail'
+        );
+        if (!empty($error)) {
+            throw new ILSException($error[0]);
+        }
+    }
 }
-- 
GitLab