Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.49% |
2 / 409 |
|
0.00% |
0 / 20 |
CRAP | |
0.00% |
0 / 1 |
Amicus | |
0.49% |
2 / 409 |
|
0.00% |
0 / 20 |
7204.53 | |
0.00% |
0 / 1 |
init | |
9.09% |
2 / 22 |
|
0.00% |
0 / 1 |
9.76 | |||
pickStatus | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
42 | |||
determineAvailability | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
sacaStatus | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
20 | |||
sacaFecha | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
20 | |||
sacaReservas | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
getStatus | |
0.00% |
0 / 79 |
|
0.00% |
0 / 1 |
240 | |||
getStatuses | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getHolding | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
56 | |||
getPurchaseHistory | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
patronLogin | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
20 | |||
getMyTransactions | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
12 | |||
getMyFines | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
12 | |||
getMyHolds | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
12 | |||
getMyProfile | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
12 | |||
getHoldLink | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNewItems | |
0.00% |
0 / 37 |
|
0.00% |
0 / 1 |
56 | |||
getFunds | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
findReserves | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
12 | |||
getSuppressedRecords | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | /** |
4 | * Amicus ILS Driver |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Scanbit 2011. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package ILS_Drivers |
25 | * @author Josu Moreno <jmoreno@scanbit.net> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki |
28 | */ |
29 | |
30 | namespace VuFind\ILS\Driver; |
31 | |
32 | use PDO; |
33 | use PDOException; |
34 | use VuFind\Date\DateException; |
35 | use VuFind\Exception\ILS as ILSException; |
36 | use VuFind\I18n\Translator\TranslatorAwareInterface; |
37 | |
38 | use function count; |
39 | use function in_array; |
40 | |
41 | /** |
42 | * Amicus ILS Driver |
43 | * |
44 | * @category VuFind |
45 | * @package ILS_Drivers |
46 | * @author Josu Moreno <jmoreno@scanbit.net> |
47 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
48 | * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki |
49 | */ |
50 | class Amicus extends AbstractBase implements TranslatorAwareInterface |
51 | { |
52 | use \VuFind\I18n\Translator\TranslatorAwareTrait; |
53 | |
54 | /** |
55 | * Database connection |
56 | * |
57 | * @var PDO |
58 | */ |
59 | protected $db; |
60 | |
61 | /** |
62 | * Stored status rankings from the database; initialized to false but populated |
63 | * by the pickStatus() method. |
64 | * |
65 | * @var array|bool |
66 | */ |
67 | protected $statusRankings = false; |
68 | |
69 | /** |
70 | * Initialize the driver. |
71 | * |
72 | * Validate configuration and perform all resource-intensive tasks needed to |
73 | * make the driver active. |
74 | * |
75 | * @throws ILSException |
76 | * @return void |
77 | */ |
78 | public function init() |
79 | { |
80 | if (empty($this->config)) { |
81 | throw new ILSException('Configuration needs to be set.'); |
82 | } |
83 | |
84 | // Define Database Name |
85 | $tns = '(DESCRIPTION=' . |
86 | '(ADDRESS_LIST=' . |
87 | '(ADDRESS=' . |
88 | '(PROTOCOL=TCP)' . |
89 | '(HOST=' . $this->config['Catalog']['host'] . ')' . |
90 | '(PORT=' . $this->config['Catalog']['port'] . ')' . |
91 | ')' . |
92 | ')' . |
93 | '(CONNECT_DATA=' . |
94 | '(SERVICE_NAME=' . $this->config['Catalog']['service'] . ')' . |
95 | ')' . |
96 | ')'; |
97 | try { |
98 | $this->db = new PDO( |
99 | "oci:dbname=$tns", |
100 | $this->config['Catalog']['user'], |
101 | $this->config['Catalog']['password'] |
102 | ); |
103 | $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
104 | } catch (PDOException $e) { |
105 | throw $e; |
106 | } |
107 | } |
108 | |
109 | /** |
110 | * Support method to pick which status message to display when multiple |
111 | * options are present. |
112 | * |
113 | * @param array $statusArray Array of status messages to choose from. |
114 | * |
115 | * @return string The best status message to display. |
116 | */ |
117 | protected function pickStatus($statusArray) |
118 | { |
119 | // This array controls the rankings of possible status messages. The lower |
120 | // the ID in the ITEM_STATUS_TYPE table, the higher the priority of the |
121 | // message. We only need to load it once -- after that, it's cached in the |
122 | // driver. |
123 | if ($this->statusRankings == false) { |
124 | // Execute SQL |
125 | $sql = 'SELECT * FROM T_HLDG_STUS_TYP'; |
126 | try { |
127 | $sqlStmt = $this->db->prepare($sql); |
128 | $sqlStmt->execute(); |
129 | } catch (PDOException $e) { |
130 | $this->throwAsIlsException($e); |
131 | } |
132 | |
133 | // Read results |
134 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
135 | $this->statusRankings[$row['TBL_LNG_FR_TXT']] = $row['TBL_VLU_CDE']; |
136 | } |
137 | } |
138 | |
139 | // Pick the first entry by default, then see if we can find a better match: |
140 | $status = $statusArray[0]; |
141 | $rank = $this->statusRankings[$status]; |
142 | for ($x = 1; $x < count($statusArray); $x++) { |
143 | if ($this->statusRankings[$statusArray[$x]] < $rank) { |
144 | $status = $statusArray[$x]; |
145 | } |
146 | } |
147 | return $status; |
148 | } |
149 | |
150 | /** |
151 | * Support method to take an array of status strings and determine |
152 | * whether or not this indicates an available item. Returns an array with |
153 | * two keys: 'available', the boolean availability status, and 'otherStatuses', |
154 | * every status code found other than "Not Charged" - for use with _pickStatus(). |
155 | * |
156 | * @param array $statusArray The status codes to analyze. |
157 | * |
158 | * @return array Availability and other status information. |
159 | */ |
160 | protected function determineAvailability($statusArray) |
161 | { |
162 | // It's possible for a record to have multiple status codes. We |
163 | // need to loop through in search of the "Not Charged" (i.e. on |
164 | // shelf) status, collecting any other statuses we find along the |
165 | // way... |
166 | $notCharged = false; |
167 | $otherStatuses = []; |
168 | foreach ($statusArray as $status) { |
169 | switch ($status) { |
170 | case 'Disponible': |
171 | $notCharged = true; |
172 | break; |
173 | default: |
174 | $otherStatuses[] = $status; |
175 | break; |
176 | } |
177 | } |
178 | |
179 | // If we found other statuses or if we failed to find "Not Charged," |
180 | // the item is not available! |
181 | $available = (count($otherStatuses) == 0 && $notCharged); |
182 | return ['available' => $available, 'otherStatuses' => $otherStatuses]; |
183 | } |
184 | |
185 | /** |
186 | * Function that returns the number or on loan items for a given copy number. |
187 | * If there is no on loan items it returns 0. |
188 | * Used in getHolding and getStatus functions |
189 | * |
190 | * @param int $copyId The copy id number to check. |
191 | * |
192 | * @return int Number of on loan items. |
193 | */ |
194 | protected function sacaStatus($copyId) |
195 | { |
196 | $circulacion = 'SELECT COUNT(*) AS PRESTADO ' . |
197 | 'FROM CIRT_ITM ' . |
198 | "WHERE CPY_ID_NBR = '$copyId'"; |
199 | |
200 | //$holds = "SELECT COUNT(*) AS PRESTADO FROM CIRTN_HLD " . |
201 | //"WHERE CPY_ID_NBR = '$copyId'"; |
202 | |
203 | $prestados = 0; |
204 | try { |
205 | $sqlStmt = $this->db->prepare($circulacion); |
206 | $sqlStmt->execute(); |
207 | //$sqlStmt2 = $this->db->prepare($holds); |
208 | //$sqlStmt2->execute(); |
209 | } catch (PDOException $e) { |
210 | $this->throwAsIlsException($e); |
211 | } |
212 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
213 | $prestados = $row['PRESTADO']; |
214 | if ($row['PRESTADO'] == 0) { |
215 | $prestados = 'Disponible'; |
216 | } else { |
217 | $prestados = 'No disponible'; |
218 | } |
219 | } |
220 | return $prestados; |
221 | } |
222 | |
223 | /** |
224 | * Function that returns the due date or a special message. |
225 | * If the difference is greater than 50 days it will return one special message |
226 | * If not it returns the due date |
227 | * |
228 | * @param int $copyId The copy id number to check. |
229 | * |
230 | * @return string String with special message or due date. |
231 | */ |
232 | protected function sacaFecha($copyId) |
233 | { |
234 | $circulacion = "SELECT to_char(CIRT_ITM_DUE_DTE,'dd-mm-yyyy') AS FECHADEV, " |
235 | . 'ROUND(CIRT_ITM_DUE_DTE - SYSDATE) AS DIFERENCIA ' |
236 | . 'FROM CIRT_ITM ' |
237 | . "WHERE CPY_ID_NBR = '$copyId'"; |
238 | $fecha = 0; |
239 | $diferencia = 0; |
240 | try { |
241 | $sqlStmt = $this->db->prepare($circulacion); |
242 | $sqlStmt->execute(); |
243 | } catch (PDOException $e) { |
244 | $this->throwAsIlsException($e); |
245 | } |
246 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
247 | $diferencia = $row['DIFERENCIA']; |
248 | if ($diferencia > 50) { |
249 | $fecha = 'SIN DETERMINAR'; |
250 | } else { |
251 | $fecha = $row['FECHADEV']; |
252 | } |
253 | } |
254 | return $fecha; |
255 | } |
256 | |
257 | /** |
258 | * Function that returns the numbers of holds for a copy id number given. |
259 | * If there is no holds it returns 0. |
260 | * |
261 | * @param int $holdingId The copy id number to check. |
262 | * |
263 | * @return int Integer with the number of holds. |
264 | */ |
265 | protected function sacaReservas($holdingId) |
266 | { |
267 | $reservas = 'SELECT COUNT(*) as reservados ' . |
268 | 'FROM CIRTN_HLD ' . |
269 | "WHERE CPY_ID_NBR = '$holdingId'"; |
270 | |
271 | $reservados = 0; |
272 | try { |
273 | $sqlStmt = $this->db->prepare($reservas); |
274 | $sqlStmt->execute(); |
275 | } catch (PDOException $e) { |
276 | $this->throwAsIlsException($e); |
277 | } |
278 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
279 | $reservados = $row['RESERVADOS']; |
280 | } |
281 | return $reservados; |
282 | } |
283 | |
284 | /** |
285 | * Get Status |
286 | * |
287 | * This is responsible for retrieving the status information of a certain |
288 | * record. |
289 | * |
290 | * @param string $id The record id to retrieve the holdings for |
291 | * |
292 | * @throws ILSException |
293 | * @return mixed On success, an associative array with the following keys: |
294 | * id, availability (boolean), status, location, reserve, callnumber. |
295 | */ |
296 | public function getStatus($id) |
297 | { |
298 | // There are two possible queries we can use to obtain status information. |
299 | // The first (and most common) obtains information from a combination of |
300 | // items and holdings records. The second (a rare case) obtains |
301 | // information from the holdings record when no items are available. |
302 | |
303 | $items = 'select BIB_ITM_NBR, ILL_CDE as ON_RESERVE, ' . |
304 | 'T_LCTN_NME_BUO.TBL_LNG_ENG_TXT ' . |
305 | 'as LOCATION, SHLF_LIST_SRT_FORM as CALLNUMBER, CPY_ID_NBR as ' . |
306 | 'CPY_ID_NBR ' . |
307 | 'from CPY_ID, SHLF_LIST, T_LCTN_NME_BUO ' . |
308 | 'where CPY_ID.SHLF_LIST_KEY_NBR = SHLF_LIST.SHLF_LIST_KEY_NBR ' . |
309 | 'and CPY_ID.LCTN_NME_CDE = T_LCTN_NME_BUO.TBL_VLU_CDE ' . |
310 | "and CPY_ID.BIB_ITM_NBR = '$id'"; |
311 | |
312 | $multipleLoc = 'SELECT COUNT(DISTINCT(SHLF_LIST_KEY_NBR)) AS multiple ' . |
313 | 'FROM CPY_ID ' . |
314 | "WHERE CPY_ID.BIB_ITM_NBR = '$id'"; |
315 | |
316 | try { |
317 | $sqlStmt = $this->db->prepare($multipleLoc); |
318 | $sqlStmt->execute(); |
319 | } catch (PDOException $e) { |
320 | $this->throwAsIlsException($e); |
321 | } |
322 | |
323 | $multiple = ''; |
324 | // Read results |
325 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
326 | $multiple = $row['MULTIPLE']; |
327 | } |
328 | $prestados = 0; |
329 | $reservados = 0; |
330 | $possibleQueries = [$items]; |
331 | |
332 | // Loop through the possible queries and try each in turn -- the first one |
333 | // that yields results will cause the function to return. |
334 | foreach ($possibleQueries as $sql) { |
335 | // Execute SQL |
336 | |
337 | try { |
338 | $sqlStmt = $this->db->prepare($sql); |
339 | $sqlStmt->execute(); |
340 | } catch (PDOException $e) { |
341 | $this->throwAsIlsException($e); |
342 | } |
343 | |
344 | // Build Array of Item Information |
345 | $data = []; |
346 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
347 | $prestados = $this->sacaStatus($row['CPY_ID_NBR']); |
348 | $reservados = $this->sacaReservas($row['CPY_ID_NBR']); |
349 | if (!isset($data[$row['BIB_ITM_NBR']])) { |
350 | if ($multiple != 1) { |
351 | $multiple = $this->translate('Multiple Locations'); |
352 | $textoLoc = $this->translate('Multiple'); |
353 | $textoSign = $this->translate('Multiple Locations'); |
354 | $data[$row['BIB_ITM_NBR']] = [ |
355 | 'id' => $id, |
356 | 'status' => $prestados, |
357 | 'status_array' => [$prestados], |
358 | 'location' => $textoLoc, |
359 | 'reserve' => $reservados, |
360 | 'callnumber' => $textoSign, |
361 | ]; |
362 | } else { |
363 | $multiple = $row['LOCATION']; |
364 | if ($multiple == 'Deposito2') { |
365 | $multiple = 'Depósito2'; |
366 | } |
367 | if ($multiple == 'Deposito') { |
368 | $multiple = 'Depósito'; |
369 | } |
370 | $data[$row['BIB_ITM_NBR']] = [ |
371 | 'id' => $id, |
372 | 'status' => $prestados, |
373 | 'status_array' => [$prestados], |
374 | 'location' => $multiple, |
375 | 'reserve' => $reservados, |
376 | 'callnumber' => $row['CALLNUMBER'], |
377 | ]; |
378 | } |
379 | } else { |
380 | $status_array = & $data[$row['BIB_ITM_NBR']]['status_array']; |
381 | if (!in_array($prestados, $status_array)) { |
382 | $status_array[] = $prestados; |
383 | } |
384 | } |
385 | } |
386 | // If we found any information, break out of the foreach loop; |
387 | // we don't need to try any more queries. |
388 | if (count($data) == 0) { |
389 | $data[$id] = [ |
390 | 'id' => $id, |
391 | 'status' => $prestados, |
392 | 'status_array' => [$prestados], |
393 | 'location' => $this->translate('No copies'), |
394 | 'reserve' => $reservados, |
395 | 'callnumber' => $this->translate('No copies'), |
396 | ]; |
397 | break; |
398 | } |
399 | if (count($data) > 0) { |
400 | break; |
401 | } |
402 | } |
403 | // Process the raw data into final status information: |
404 | $status = []; |
405 | foreach ($data as $current) { |
406 | // Get availability/status info based on the array of status codes: |
407 | $availability = $this->determineAvailability($current['status_array']); |
408 | // If we found other statuses, we should override the display value |
409 | // appropriately: |
410 | if (count($availability['otherStatuses']) > 0) { |
411 | $current['status'] |
412 | = $this->pickStatus($availability['otherStatuses']); |
413 | } |
414 | $current['availability'] = $availability['available']; |
415 | $status[] = $current; |
416 | } |
417 | return $status; |
418 | } |
419 | |
420 | /** |
421 | * Get Statuses |
422 | * |
423 | * This is responsible for retrieving the status information for a |
424 | * collection of records. |
425 | * |
426 | * @param array $idList The array of record ids to retrieve the status for |
427 | * |
428 | * @throws ILSException |
429 | * @return array An array of getStatus() return values on success. |
430 | */ |
431 | public function getStatuses($idList) |
432 | { |
433 | $status = []; |
434 | |
435 | foreach ($idList as $id) { |
436 | $status[] = $this->getStatus($id); |
437 | } |
438 | return $status; |
439 | } |
440 | |
441 | /** |
442 | * Get Holding |
443 | * |
444 | * This is responsible for retrieving the holding information of a certain |
445 | * record. |
446 | * |
447 | * @param string $id The record id to retrieve the holdings for |
448 | * @param array $patron Patron data |
449 | * @param array $options Extra options (not currently used) |
450 | * |
451 | * @throws DateException |
452 | * @throws ILSException |
453 | * @return array On success, an associative array with the following |
454 | * keys: id, availability (boolean), status, location, reserve, callnumber, |
455 | * duedate, number, barcode. |
456 | * |
457 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
458 | */ |
459 | public function getHolding($id, array $patron = null, array $options = []) |
460 | { |
461 | $items = 'select CPY_ID.BRCDE_NBR, CPY_ID.BIB_ITM_NBR, ' . |
462 | 'T_LCTN_NME_BUO.TBL_LNG_ENG_TXT ' . |
463 | 'as LOCATION, SHLF_LIST_SRT_FORM as CALLNUMBER, CPY_ID.CPY_ID_NBR as ' . |
464 | 'CPY_ID_NBR ' . |
465 | 'from CPY_ID, SHLF_LIST, T_LCTN_NME_BUO ' . |
466 | 'where CPY_ID.SHLF_LIST_KEY_NBR = SHLF_LIST.SHLF_LIST_KEY_NBR ' . |
467 | 'AND CPY_ID.LCTN_NME_CDE = T_LCTN_NME_BUO.TBL_VLU_CDE ' . |
468 | "and CPY_ID.BIB_ITM_NBR = '$id' " . |
469 | 'order by SHLF_LIST_SRT_FORM ASC, CPY_ID.CPY_ID_NBR ASC'; |
470 | |
471 | $possibleQueries = [$items]; |
472 | |
473 | // Loop through the possible queries and try each in turn -- the first one |
474 | // that yields results will cause us to break out of the loop. |
475 | foreach ($possibleQueries as $sql) { |
476 | // Execute SQL |
477 | try { |
478 | $sqlStmt = $this->db->prepare($sql); |
479 | $sqlStmt->execute(); |
480 | } catch (PDOException $e) { |
481 | $this->throwAsIlsException($e); |
482 | } |
483 | |
484 | // Build Holdings Array |
485 | $data = []; |
486 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
487 | // Determine Location |
488 | $loc = $row['LOCATION']; |
489 | if ($loc == 'Deposito2') { |
490 | $loc = 'Depósito2'; |
491 | } |
492 | if ($loc == 'Deposito') { |
493 | $loc = 'Depósito'; |
494 | } |
495 | |
496 | $status = $this->sacaStatus($row['CPY_ID_NBR']); |
497 | $availability = $this->determineAvailability([$status]); |
498 | $currentItem = [ |
499 | 'id' => $id, |
500 | 'availability' => $availability['available'], |
501 | 'status' => $status, |
502 | 'location' => $loc, |
503 | // TODO: make this smarter if you use course reserves: |
504 | 'reserve' => 'N', |
505 | 'callnumber' => $row['CALLNUMBER'], |
506 | 'duedate' => $this->sacaFecha($row['CPY_ID_NBR']), |
507 | // TODO: fill this in if you want "recently returned" support: |
508 | 'returnDate' => false, |
509 | 'number' => count($data) + 1, |
510 | 'item_id' => $row['CPY_ID_NBR'], |
511 | 'barcode' => $row['BRCDE_NBR'], |
512 | ]; |
513 | $data[] = $currentItem; |
514 | } |
515 | // If we found data, we can leave the foreach loop -- we don't need to |
516 | // try any more queries. |
517 | if (count($data) > 0) { |
518 | break; |
519 | } |
520 | } |
521 | return $data; |
522 | } |
523 | |
524 | /** |
525 | * Get Purchase History |
526 | * |
527 | * This is responsible for retrieving the acquisitions history data for the |
528 | * specific record (usually recently received issues of a serial). |
529 | * |
530 | * @param string $id The record id to retrieve the info for |
531 | * |
532 | * @throws ILSException |
533 | * @return array An array with the acquisitions data on success. |
534 | */ |
535 | public function getPurchaseHistory($id) |
536 | { |
537 | $sql = "select REPLACE(REPLACE(CPY_STMT_TXT,'a',''),'Fondos: ','') as " . |
538 | 'ENUMCHRON ' . |
539 | 'from CPY_ID ' . |
540 | "WHERE CPY_ID.BIB_ITM_NBR = '$id' " . |
541 | 'order by CPY_ID.SHLF_LIST_KEY_NBR ASC, CPY_ID.CPY_ID_NBR ASC'; |
542 | $data = []; |
543 | try { |
544 | $sqlStmt = $this->db->prepare($sql); |
545 | $sqlStmt->execute(); |
546 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
547 | $data[] = ['issue' => $row['ENUMCHRON']]; |
548 | } |
549 | } catch (PDOException $e) { |
550 | $this->throwAsIlsException($e); |
551 | } |
552 | return $data; |
553 | } |
554 | |
555 | /** |
556 | * Patron Login |
557 | * |
558 | * This is responsible for authenticating a patron against the catalog. |
559 | * |
560 | * @param string $barcode The patron username |
561 | * @param string $lname The patron's password |
562 | * |
563 | * @throws ILSException |
564 | * @return mixed Associative array of patron info on successful login, |
565 | * null on unsuccessful login. |
566 | */ |
567 | public function patronLogin($barcode, $lname) |
568 | { |
569 | $sql = 'SELECT LOGIN , PASSWORD AS FIRST_NAME ' . |
570 | 'FROM LV_USER ' . |
571 | "WHERE PASSWORD = '$lname' AND LOGIN = '$barcode'"; |
572 | |
573 | try { |
574 | $sqlStmt = $this->db->prepare($sql); |
575 | $sqlStmt->execute(); |
576 | $row = $sqlStmt->fetch(PDO::FETCH_ASSOC); |
577 | if (isset($row['LOGIN']) && ($row['LOGIN'] != '')) { |
578 | return [ |
579 | 'id' => $row['LOGIN'], |
580 | 'firstname' => $row['FIRST_NAME'], |
581 | 'lastname' => $lname, |
582 | 'cat_username' => $barcode, |
583 | 'cat_password' => $lname, |
584 | // There's supposed to be a getPatronEmailAddress stored |
585 | // procedure in Oracle, but I couldn't get it to work here; |
586 | // might be worth investigating further if needed later. |
587 | 'email' => null, |
588 | 'major' => null, |
589 | 'college' => null]; |
590 | } else { |
591 | return null; |
592 | } |
593 | } catch (PDOException $e) { |
594 | $this->throwAsIlsException($e); |
595 | } |
596 | } |
597 | |
598 | /** |
599 | * Get Patron Transactions |
600 | * |
601 | * This is responsible for retrieving all transactions (i.e. checked out items) |
602 | * by a specific patron. |
603 | * |
604 | * @param array $patron The patron array from patronLogin |
605 | * |
606 | * @throws DateException |
607 | * @throws ILSException |
608 | * @return array Array of the patron's transactions on success. |
609 | */ |
610 | public function getMyTransactions($patron) |
611 | { |
612 | $transList = []; |
613 | |
614 | $sql = "SELECT TO_CHAR(CIRT_ITM.CIRT_ITM_DUE_DTE,'DD/MM/YYYY') " . |
615 | 'AS DUEDATE, CIRT_ITM.BIB_ITM_NBR AS BIB_ID ' . |
616 | 'FROM LV_USER, CIRT_ITM ' . |
617 | 'WHERE LV_USER.PRSN_NBR = CIRT_ITM.PRSN_NBR ' . |
618 | "AND LV_USER.LOGIN = '" . $patron['id'] . "'"; |
619 | try { |
620 | $sqlStmt = $this->db->prepare($sql); |
621 | $sqlStmt->execute(); |
622 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
623 | $transList[] = ['duedate' => $row['DUEDATE'], |
624 | 'id' => $row['BIB_ID']]; |
625 | } |
626 | } catch (PDOException $e) { |
627 | $this->throwAsIlsException($e); |
628 | } |
629 | return $transList; |
630 | } |
631 | |
632 | /** |
633 | * Get Patron Fines |
634 | * |
635 | * This is responsible for retrieving all fines by a specific patron. |
636 | * |
637 | * @param array $patron The patron array from patronLogin |
638 | * |
639 | * @throws DateException |
640 | * @throws ILSException |
641 | * @return mixed Array of the patron's fines on success. |
642 | */ |
643 | public function getMyFines($patron) |
644 | { |
645 | $fineList = []; |
646 | |
647 | $sql = "SELECT UNIQUE TO_CHAR(CIRT_ITM.CIRT_ITM_CHRG_OUT_DTE,'DD/MM/YYYY') " |
648 | . 'AS ORIG_CHARGE_DATE, ' |
649 | . "TO_CHAR(CIRT_ITM.CIRT_ITM_DUE_DTE,'DD/MM/YYYY') AS DUE_DATE, " |
650 | . 'CIRT_ITM.BIB_ITM_NBR AS BIB_ID ' |
651 | . 'FROM CIRT_ITM, LV_USER ' |
652 | . 'WHERE CIRT_ITM.PRSN_NBR = LV_USER.PRSN_NBR ' |
653 | . 'AND CIRT_ITM_DUE_DTE < SYSDATE ' |
654 | . "AND LV_USER.LOGIN='" . $patron['id'] . "'"; |
655 | try { |
656 | $sqlStmt = $this->db->prepare($sql); |
657 | $sqlStmt->execute(); |
658 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
659 | $fineList[] = ['checkout' => $row['ORIG_CHARGE_DATE'], |
660 | 'duedate' => $row['DUE_DATE'], |
661 | 'id' => $row['BIB_ID']]; |
662 | } |
663 | return $fineList; |
664 | } catch (PDOException $e) { |
665 | $this->throwAsIlsException($e); |
666 | } |
667 | } |
668 | |
669 | /** |
670 | * Get Patron Holds |
671 | * |
672 | * This is responsible for retrieving all holds by a specific patron. |
673 | * |
674 | * @param array $patron The patron array from patronLogin |
675 | * |
676 | * @throws DateException |
677 | * @throws ILSException |
678 | * @return array Array of the patron's holds on success. |
679 | */ |
680 | public function getMyHolds($patron) |
681 | { |
682 | $holdList = []; |
683 | |
684 | $sql = 'SELECT CIRTN_HLD.BIB_ITM_NBR AS BIB_ID, ' . |
685 | 'CIRTN_HLD.CIRTN_HLD_LCTN_ORG_NBR AS PICKUP_LOCATION, ' . |
686 | 'CIRTN_HLD.CIRTN_HLD_TYP_CDE AS HOLD_RECALL_TYPE, ' . |
687 | "TO_CHAR(CIRTN_HLD.TME_HLD_END_DTE,'DD/MM/YYYY') AS EXPIRE_DATE, " . |
688 | "TO_CHAR(CIRTN_HLD.CIRTN_HLD_CRTE_DTE,'DD/MM/YYYY') AS " . |
689 | 'CREATE_DATE FROM CIRTN_HLD, LV_USER ' . |
690 | 'WHERE CIRTN_HLD.PRSN_NBR = LV_USER.PRSN_NBR ' . |
691 | "AND LV_USER.LOGIN = '" . $patron['id'] . "'"; |
692 | try { |
693 | $sqlStmt = $this->db->prepare($sql); |
694 | $sqlStmt->execute(); |
695 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
696 | $holdList[] = ['type' => $row['HOLD_RECALL_TYPE'], |
697 | 'id' => $row['BIB_ID'], |
698 | 'location' => $row['PICKUP_LOCATION'], |
699 | 'expire' => $row['EXPIRE_DATE'], |
700 | 'create' => $row['CREATE_DATE']]; |
701 | } |
702 | } catch (PDOException $e) { |
703 | $this->throwAsIlsException($e); |
704 | } |
705 | return $holdList; |
706 | } |
707 | |
708 | /** |
709 | * Get Patron Profile |
710 | * |
711 | * This is responsible for retrieving the profile for a specific patron. |
712 | * |
713 | * @param array $patron The patron array |
714 | * |
715 | * @throws ILSException |
716 | * @return array Array of the patron's profile data on success. |
717 | */ |
718 | public function getMyProfile($patron) |
719 | { |
720 | $sql = 'SELECT DISTINCT PRSN.PRSN_NBR AS UNO, (SELECT p1.PRSN_CMCTN_NBR ' . |
721 | 'FROM PRSN_CMCTN p1 ' . |
722 | 'WHERE p1.prsn_nbr = PRSN.prsn_nbr AND PRSN_CMCTN_TYP_CDE = 7) tfno, ' . |
723 | '(SELECT p1.PRSN_CMCTN_NBR FROM PRSN_CMCTN p1 WHERE p1.prsn_nbr = ' . |
724 | 'PRSN.prsn_nbr AND ' . |
725 | 'PRSN_CMCTN_TYP_CDE = 1) email, ' . |
726 | 'PRSN.PRSN_SRNME_SRT_FORM AS LAST_NAME, PRSN.PRSN_1ST_NME_SRT_FORM ' . |
727 | 'AS FIRST_NAME, ' . |
728 | "CONCAT(PSTL_ADR_ST_NME,CONCAT(' ',CONCAT(PSTL_ADR_ST_NBR,CONCAT(' ', " . |
729 | "CONCAT(PSTL_ADR_FLR_NBR,CONCAT(' ',PSTL_ADR_RM_NBR)))))) " . |
730 | 'AS ADDRESS_LINE1, PRSN_PSTL_ADR.PSTL_ADR_CTY_NME ' . |
731 | 'AS ADDRESS_LINE2, PRSN_PSTL_ADR.PSTL_ADR_PSTL_CDE AS ZIP_POSTAL ' . |
732 | 'FROM PRSN, PRSN_CMCTN, PRSN_PSTL_ADR, LV_USER ' . |
733 | 'WHERE PRSN_CMCTN.PRSN_nbr = PRSN.PRSN_NBR (+) ' . |
734 | 'AND PRSN.PRSN_NBR = PRSN_PSTL_ADR.PRSN_NBR (+) ' . |
735 | 'AND LV_USER.PRSN_NBR = PRSN.PRSN_NBR ' . |
736 | "AND LV_USER.LOGIN = UPPER('" . $patron['id'] . "')"; |
737 | |
738 | try { |
739 | $sqlStmt = $this->db->prepare($sql); |
740 | $sqlStmt->execute(); |
741 | $row = $sqlStmt->fetch(PDO::FETCH_ASSOC); |
742 | if ($row) { |
743 | $patron = ['firstname' => $row['FIRST_NAME'], |
744 | 'lastname' => $row['LAST_NAME'], |
745 | 'address1' => $row['ADDRESS_LINE1'], |
746 | 'address2' => $row['ADDRESS_LINE2'], |
747 | 'zip' => $row['ZIP_POSTAL'], |
748 | 'phone' => $row['TFNO'], |
749 | 'email' => $row['EMAIL'], |
750 | 'group' => $row['PATRON_GROUP_NAME']]; |
751 | return $patron; |
752 | } |
753 | } catch (PDOException $e) { |
754 | $this->throwAsIlsException($e); |
755 | } |
756 | return null; |
757 | } |
758 | |
759 | /** |
760 | * Get Hold Link |
761 | * |
762 | * The goal for this method is to return a URL to a "place hold" web page on |
763 | * the ILS OPAC. This is used for ILSs that do not support an API or method |
764 | * to place Holds. |
765 | * |
766 | * @param string $recordId The id of the bib record |
767 | * @param array $details Item details from getHoldings return array |
768 | * |
769 | * @return string URL to ILS's OPAC's place hold screen. |
770 | * |
771 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
772 | */ |
773 | public function getHoldLink($recordId, $details) |
774 | { |
775 | return $this->config['Catalog']['hold'] . $recordId; |
776 | } |
777 | |
778 | /** |
779 | * Get New Items |
780 | * |
781 | * Retrieve the IDs of items recently added to the catalog. |
782 | * |
783 | * @param int $page Page number of results to retrieve (counting starts at 1) |
784 | * @param int $limit The size of each page of results to retrieve |
785 | * @param int $daysOld The maximum age of records to retrieve in days (max. 30) |
786 | * @param int $fundId optional fund ID to use for limiting results (use a value |
787 | * returned by getFunds, or exclude for no limit); note that "fund" may be a |
788 | * misnomer - if funds are not an appropriate way to limit your new item |
789 | * results, you can return a different set of values from getFunds. The |
790 | * important thing is that this parameter supports an ID returned by getFunds, |
791 | * whatever that may mean. |
792 | * |
793 | * @throws ILSException |
794 | * @return array Associative array with 'count' and 'results' keys |
795 | * |
796 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
797 | */ |
798 | public function getNewItems($page, $limit, $daysOld, $fundId = null) |
799 | { |
800 | $items = []; |
801 | |
802 | // Prevent unnecessary load on voyager |
803 | if ($daysOld > 30) { |
804 | $daysOld = 30; |
805 | } |
806 | |
807 | $enddate = date('d-m-Y', strtotime('now')); |
808 | $startdate = date('d-m-Y', strtotime("-$daysOld day")); |
809 | |
810 | $sql = 'select count(distinct BIB_ITM_NBR) as count ' . |
811 | 'from CPY_ID ' . |
812 | "where CPY_ID.CRTN_DTE >= to_date('$startdate', 'dd-mm-yyyy') " . |
813 | "and CPY_ID.CRTN_DTE < to_date('$enddate', 'dd-mm-yyyy')"; |
814 | try { |
815 | $sqlStmt = $this->db->prepare($sql); |
816 | $sqlStmt->execute(); |
817 | $row = $sqlStmt->fetch(PDO::FETCH_ASSOC); |
818 | $items['count'] = $row['COUNT']; |
819 | } catch (PDOException $e) { |
820 | $this->throwAsIlsException($e); |
821 | } |
822 | |
823 | $page = ($page) ? $page : 1; |
824 | $limit = ($limit) ? $limit : 20; |
825 | $startRow = (($page - 1) * $limit) + 1; |
826 | $endRow = ($page * $limit); |
827 | $sql = 'select * from ' . |
828 | '(select a.*, rownum rnum from ' . |
829 | '(select CPY_ID.BIB_ITM_NBR as BIB_ID, CPY_ID.CRTN_DTE ' . |
830 | 'as CREATE_DATE ' . |
831 | 'from CPY_ID ' . |
832 | "where CPY_ID.CRTN_DTE >= to_date('$startdate', 'dd-mm-yyyy') " . |
833 | "and CPY_ID.CRTN_DTE < to_date('$enddate', 'dd-mm-yyyy') " . |
834 | 'group by CPY_ID.BIB_ITM_NBR, CPY_ID.CRTN_DTE ' . |
835 | 'order by CPY_ID.CRTN_DTE desc) a ' . |
836 | "where rownum <= $endRow) " . |
837 | "where rnum >= $startRow"; |
838 | try { |
839 | $sqlStmt = $this->db->prepare($sql); |
840 | $sqlStmt->execute(); |
841 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
842 | $items['results'][]['id'] = $row['BIB_ID']; |
843 | } |
844 | } catch (PDOException $e) { |
845 | $this->throwAsIlsException($e); |
846 | } |
847 | return $items; |
848 | } |
849 | |
850 | /** |
851 | * Get Funds |
852 | * |
853 | * Return a list of funds which may be used to limit the getNewItems list. |
854 | * |
855 | * @throws ILSException |
856 | * @return array An associative array with key = fund ID, value = fund name. |
857 | */ |
858 | public function getFunds() |
859 | { |
860 | $list = []; |
861 | |
862 | $sql = 'select distinct * from ' . |
863 | '(select initcap(lower(FUND.FUND_NME)) as name from FUND) ' . |
864 | 'order by name'; |
865 | try { |
866 | $sqlStmt = $this->db->prepare($sql); |
867 | $sqlStmt->execute(); |
868 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
869 | $list[] = $row['NAME']; |
870 | } |
871 | } catch (PDOException $e) { |
872 | $this->throwAsIlsException($e); |
873 | } |
874 | |
875 | return $list; |
876 | } |
877 | |
878 | /** |
879 | * Find Reserves |
880 | * |
881 | * Obtain information on course reserves. |
882 | * |
883 | * @param string $course ID from getCourses (empty string to match all) |
884 | * @param string $inst ID from getInstructors (empty string to match all) |
885 | * @param string $dept ID from getDepartments (empty string to match all) |
886 | * |
887 | * @throws ILSException |
888 | * @return array An array of associative arrays representing reserve items. |
889 | * |
890 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
891 | */ |
892 | public function findReserves($course, $inst, $dept) |
893 | { |
894 | $recordList = []; |
895 | |
896 | $dept = str_replace("'", '', $dept); |
897 | $dept = str_replace('"', '', $dept); |
898 | $dept = str_replace(':', '', $dept); |
899 | $dept = str_replace(',', '', $dept); |
900 | $dept = str_replace('.', '', $dept); |
901 | $dept = str_replace(';', '', $dept); |
902 | $dept = str_replace('*', '%', $dept); |
903 | |
904 | $sql = 'select distinct(BIB_ITM_NBR) as BIB_ID ' . |
905 | 'FROM CPY_ID, SHLF_LIST ' . |
906 | 'WHERE CPY_ID.SHLF_LIST_KEY_NBR = SHLF_LIST.SHLF_LIST_KEY_NBR ' . |
907 | 'AND UPPER(SUBSTR(SHLF_LIST.SHLF_LIST_STRNG_TEXT,3,20)) LIKE ' . |
908 | "UPPER('" . $dept . "%') " . |
909 | 'AND ROWNUM <= 1000'; |
910 | |
911 | try { |
912 | $sqlStmt = $this->db->prepare($sql); |
913 | $sqlStmt->execute(); |
914 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
915 | $recordList[] = $row; |
916 | } |
917 | } catch (PDOException $e) { |
918 | $this->throwAsIlsException($e); |
919 | } |
920 | |
921 | return $recordList; |
922 | } |
923 | |
924 | /* |
925 | function findReserves($course, $inst, $dept) |
926 | { |
927 | $recordList = array(); |
928 | |
929 | $reserve_subset = ""; |
930 | |
931 | $reserve_subset = "(".$reserve_subset.")"; |
932 | $sql = "select SHLF_LIST_SRT_FORM as DISPLAY_CALL_NO, CPY_ID.BIB_ITM_NBR " . |
933 | "as BIB_ID, NME_MAIN_ENTRY_STRNG_TXT as AUTHOR, TTL_HDG_MAIN_STRNG_TXT" . |
934 | " as TITLE, BIB_NTE_IPRNT_STRNG_TXT as PUBLISHER, ITM_DTE_1_DSC as " . |
935 | "PUBLISHER_DATE " . |
936 | "from CIRTN_HLD, S_CACHE_BIB_ITM_DSPLY, SHLF_LIST, CPY_ID " . |
937 | "where S_CACHE_BIB_ITM_DSPLY.BIB_ITM_NBR = CIRTN_HLD.BIB_ITM_NBR and " . |
938 | "CIRTN_HLD.CPY_ID_NBR = CPY_ID.CPY_ID_NBR and " . |
939 | "CPY_ID.SHLF_LIST_KEY_NBR = SHLF_LIST.SHLF_LIST_KEY_NBR"; |
940 | |
941 | |
942 | try { |
943 | $sqlStmt = $this->db->prepare($sql); |
944 | $sqlStmt->execute(); |
945 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
946 | $recordList[] = $row; |
947 | } |
948 | } catch (PDOException $e) { |
949 | $this->throwAsIlsException($e); |
950 | } |
951 | |
952 | return $recordList; |
953 | } |
954 | */ |
955 | |
956 | /** |
957 | * Get suppressed records. |
958 | * |
959 | * @throws ILSException |
960 | * @return array ID numbers of suppressed records in the system. |
961 | */ |
962 | public function getSuppressedRecords() |
963 | { |
964 | $list = []; |
965 | $sql = 'SELECT BIB_AUT_ITM_NBR as BIB_ID ' . |
966 | 'FROM CTLGG_TRSTN_ACTVT_LOG ' . |
967 | 'WHERE STATS_TRSTN_TYP_CDE = 4 ' . |
968 | 'AND trstn_log_tmest >= SYSDATE -30'; |
969 | try { |
970 | $sqlStmt = $this->db->prepare($sql); |
971 | $sqlStmt->execute(); |
972 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
973 | $list[] = $row['BIB_ID']; |
974 | } |
975 | } catch (PDOException $e) { |
976 | $this->throwAsIlsException($e); |
977 | } |
978 | |
979 | return $list; |
980 | } |
981 | } |