Merge pull request #4 from tadl/master
[sitka/iNCIPit.git] / iNCIPit.cgi
CommitLineData
4cdc4f67
JS
1#! /usr/bin/perl
2
3eefadf9
JG
3#
4# Copyright 2012-2013 Midwest Consortium for Library Services
5# Copyright 2013 Calvin College
6# contact Dan Wells <dbw2@calvin.edu>
7# Copyright 2013 Traverse Area District Library,
8# contact Jeff Godin <jgodin@tadl.org>
9#
10#
11# This code incorporates code (with modifications) from issa, "a small
12# command-line client to OpenILS/Evergreen". issa is licensed GPLv2 or (at your
13# option) any later version of the GPL.
14#
15# issa is copyright:
16#
17# Copyright 2011 Jason J.A. Stephenson <jason@sigio.com>
18# Portions Copyright 2012 Merrimack Valley Library Consortium
19# <jstephenson@mvlc.org>
20#
21#
1ac548f2 22# This file is part of iNCIPit
4cdc4f67
JS
23#
24# iNCIPit is free software: you can redistribute it and/or modify it
25# under the terms of the GNU General Public License as published by
26# the Free Software Foundation, either version 2 of the License, or
27# (at your option) any later version.
28#
29# iNCIPit is distributed in the hope that it will be useful, but WITHOUT
30# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
31# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
32# License for more details.
33#
34# You should have received a copy of the GNU General Public License
7a94033c 35# along with iNCIPit. If not, see <http://www.gnu.org/licenses/>.
4cdc4f67 36
4cdc4f67 37use warnings;
9061228c 38use strict;
4cdc4f67 39use XML::LibXML;
8ed2a2d4 40use CGI;
4cdc4f67
JS
41use HTML::Entities;
42use CGI::Carp;
4cdc4f67
JS
43use OpenSRF::System;
44use OpenSRF::Utils::SettingsClient;
45use Digest::MD5 qw/md5_hex/;
46use OpenILS::Utils::Fieldmapper;
47use OpenILS::Utils::CStoreEditor qw/:funcs/;
48use OpenILS::Const qw/:const/;
49use Scalar::Util qw(reftype blessed);
50use MARC::Record;
51use MARC::Field;
52use MARC::File::XML;
53use POSIX qw/strftime/;
54use DateTime;
f5e8d07e
JG
55use Config::Tiny;
56
a30f6f9e 57my $U = "OpenILS::Application::AppUtils";
4cdc4f67 58
f5e8d07e
JG
59my $conf = load_config( 'iNCIPit.ini' );
60
b9103e62
JG
61# Set some variables from config (or defaults)
62my $patron_id_type;
63
64if ($conf->{behavior}->{patron_id_as_identifier} =~ m/^yes$/i) {
65 $patron_id_type = "id";
66} else {
67 $patron_id_type = "barcode";
68}
69
60d032c1 70# reject non-https access unless configured otherwise
a8c18053 71unless ($conf->{access}->{permit_plaintext} =~ m/^yes$/i) {
99da8d8a 72 unless (defined($ENV{HTTPS}) && $ENV{HTTPS} eq 'on') {
60d032c1 73 print "Content-type: text/plain\n\n";
86fda995
JG
74 print "Access denied.\n";
75 exit 0;
60d032c1
JG
76 }
77}
78
79# TODO: support for multiple load balancer IPs
80my $lb_ip = $conf->{access}->{load_balancer_ip};
81
82# if we are behind a load balancer, check to see that the
83# actual client IP is permitted
84if ($lb_ip) {
85 my @allowed_ips = split(/ *, */, $conf->{access}->{allowed_client_ips});
86
86fda995
JG
87 my $forwarded = $ENV{HTTP_X_FORWARDED_FOR};
88 my $ok = 0;
60d032c1 89
86fda995
JG
90 foreach my $check_ip (@allowed_ips) {
91 $ok = 1 if ($check_ip eq $forwarded);
92 }
60d032c1
JG
93
94 # if we have a load balancer IP and are relying on
95 # X-Forwarded-For, deny requests other than those
96 # from the load balancer
97 # TODO: support for chained X-Forwarded-For -- ignore all but last
86fda995 98 unless ($ok && $ENV{REMOTE_ADDR} eq $lb_ip) {
60d032c1 99 print "Content-type: text/plain\n\n";
86fda995
JG
100 print "Access denied.\n";
101 exit 0;
102 }
60d032c1
JG
103}
104
8ed2a2d4
JG
105my $cgi = CGI->new();
106
107my $xml = $cgi->param('POSTDATA') || $cgi->param('XForms:Model');
4cdc4f67 108
1ac548f2
JS
109# log posted data
110# XXX: posted ncip message log filename should be in config.
4cdc4f67
JS
111open POST_DATA, ">>post_data.txt";
112print POST_DATA $xml;
113close POST_DATA;
114
115# initialize the parser
116my $parser = new XML::LibXML;
117my $doc = $parser->load_xml( string => $xml );
118
119my %session = login();
120
1ac548f2 121if ( defined( $session{authtoken} ) ) {
5f517e0a
DW
122 $doc->exists('/NCIPMessage/LookupUser') ? lookupUser() : (
123 $doc->exists('/NCIPMessage/ItemRequested') ? item_request() : (
124 $doc->exists('/NCIPMessage/ItemShipped') ? item_shipped() : (
125 $doc->exists('/NCIPMessage/ItemCheckedOut') ? item_checked_out() : (
126 $doc->exists('/NCIPMessage/CheckOutItem') ? check_out_item() : (
127 $doc->exists('/NCIPMessage/ItemCheckedIn') ? item_checked_in() : (
128 $doc->exists('/NCIPMessage/CheckInItem') ? check_in_item() : (
129 $doc->exists('/NCIPMessage/ItemReceived') ? item_received() : (
130 $doc->exists('/NCIPMessage/AcceptItem') ? accept_item() : (
131 $doc->exists('/NCIPMessage/ItemRequestCancelled') ? item_cancelled() : (
132 $doc->exists('/NCIPMessage/ItemRenewed') ? item_renew() : (
133 $doc->exists('/NCIPMessage/RenewItem') ? renew_item() :
134 fail("UNKNOWN NCIPMessage")
135 )))))))))));
136
5f517e0a 137 logout();
4cdc4f67 138} else {
5f517e0a 139 fail("Unable to perform action : Unknown Service Request");
4cdc4f67
JS
140}
141
f5e8d07e
JG
142# load and parse config file
143sub load_config {
144 my $file = shift;
145
146 my $Config = Config::Tiny->new;
147 $Config = Config::Tiny->read( $file ) ||
148 die( "Error reading config file ", $file, ": ", Config::Tiny->errstr, "\n" );
149 return $Config;
150}
151
47866eef 152# load and parse userpriv_map file, returning a hashref
3602ecf5 153sub load_map_file {
47866eef
JG
154 my $filename = shift;
155 my $map = {};
156 if (open(my $fh, "<", $filename)) {
157 while (my $entry = <$fh>) {
158 chomp($entry);
159 my ($from, $to) = split(m/:/, $entry);
160 $map->{$from} = $to;
161 }
162 close $fh;
163 }
164 return $map;
165}
166
167sub lookup_userpriv {
168 my $input = shift;
169 my $map = shift;
170 if (defined($map->{$input})) { # if we have a mapping for this profile
171 return $map->{$input}; # return value from mapping hash
172 } else {
173 return $input; # return original value
174 }
175}
176
3602ecf5
JG
177sub lookup_pickup_lib {
178 my $input = shift;
179 my $map = shift;
180 if (defined($map->{$input})) { # if we found this pickup lib
181 return $map->{$input}; # return value from mapping hash
182 } else {
183 return undef; # the original value does us no good -- return undef
184 }
185}
186
4cdc4f67 187sub logit {
5f517e0a
DW
188 my ( $msg, $func, $more_info ) = @_;
189 open RESP_DATA, ">>resp_data.txt";
190 print RESP_DATA $msg;
191 print RESP_DATA $more_info unless !$more_info;
192 close RESP_DATA;
193 print $msg || fail($func);
4cdc4f67
JS
194}
195
6e6bdfe9 196sub staff_log {
5f517e0a
DW
197 my ( $taiv, $faiv, $more_info ) = @_;
198 my $now = localtime();
199 open STAFF_LOG, ">>staff_data.csv";
200 print STAFF_LOG "$now, $faiv, $taiv, $more_info\n";
201 close STAFF_LOG;
6e6bdfe9
JS
202}
203
4cdc4f67 204sub item_renew {
5f517e0a
DW
205 my $faidSchemeX = $doc->findvalue('/NCIPMessage/ItemRenewed/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
206 my $faidScheme = HTML::Entities::encode($faidSchemeX);
207 my $faidValue = $doc->find('/NCIPMessage/ItemRenewed/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
208 my $taidSchemeX = $doc->findvalue('/NCIPMessage/ItemRenewed/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
209 my $taidScheme = HTML::Entities::encode($taidSchemeX);
210 my $taidValue = $doc->find('/NCIPMessage/ItemRenewed/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
211
212 my $pid = $doc->findvalue('/NCIPMessage/ItemRenewed/UniqueUserId/UserIdentifierValue');
213 my $visid = $doc->findvalue('/NCIPMessage/ItemRenewed/ItemOptionalFields/ItemDescription/VisibleItemId/VisibleItemIdentifier') . $faidValue;
214 my $due_date = $doc->findvalue('/NCIPMessage/ItemRenewed/DateDue');
215
216 my $r = renewal( $visid, $due_date );
217
218 my $hd = <<ITEMRENEWAL;
4cdc4f67
JS
219Content-type: text/xml
220
221
222<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
223<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
224 <ItemRenewedResponse>
225 <ResponseHeader>
226 <FromAgencyId>
227 <UniqueAgencyId>
228 <Scheme>$faidScheme</Scheme>
229 <Value>$faidValue</Value>
230 </UniqueAgencyId>
231 </FromAgencyId>
232 <ToAgencyId>
233 <UniqueAgencyId>
234 <Scheme>$taidScheme</Scheme>
235 <Value>$taidValue</Value>
236 </UniqueAgencyId>
237 </ToAgencyId>
238 </ResponseHeader>
239 <UniqueItemId>
240 <ItemIdentifierValue datatype="string">$visid</ItemIdentifierValue>
241 </UniqueItemId>
242 </ItemRenewedResponse>
4cdc4f67
JS
243</NCIPMessage>
244
245ITEMRENEWAL
246
5f517e0a 247 my $more_info = <<MOREINFO;
4cdc4f67 248
5f517e0a
DW
249VISID = $visid
250Desired Due Date = $due_date
4cdc4f67
JS
251
252MOREINFO
253
5f517e0a
DW
254 logit( $hd, ( caller(0) )[3], $more_info );
255 staff_log( $taidValue, $faidValue,
256 "ItemRenewal -> Patronid : "
257 . $pid
258 . " | Visid : "
259 . $visid
260 . " | Due Date : "
261 . $due_date );
6e6bdfe9 262}
4cdc4f67 263
6e6bdfe9 264sub renew_item {
5f517e0a
DW
265 my $faidSchemeX = $doc->findvalue('/NCIPMessage/RenewItem/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
266 my $faidScheme = HTML::Entities::encode($faidSchemeX);
267 my $faidValue = $doc->find('/NCIPMessage/RenewItem/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
268 my $taidSchemeX = $doc->findvalue('/NCIPMessage/RenewItem/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
269 my $taidScheme = HTML::Entities::encode($taidSchemeX);
270 my $taidValue = $doc->find('/NCIPMessage/RenewItem/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
271
272 my $pid = $doc->findvalue('/NCIPMessage/RenewItem/UniqueUserId/UserIdentifierValue');
fd19ff1c 273 my $unique_item_id = $doc->findvalue('/NCIPMessage/RenewItem/UniqueItemId/ItemIdentifierValue');
5f517e0a
DW
274 my $due_date = $doc->findvalue('/NCIPMessage/RenewItem/DateDue');
275
fd19ff1c
JG
276 # we are using the UniqueItemId value as a barcode here
277 my $r = renewal( $unique_item_id, $due_date );
5f517e0a
DW
278
279 my $hd = <<ITEMRENEWAL;
6e6bdfe9
JS
280Content-type: text/xml
281
282
283<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
284<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
285 <RenewItemResponse>
286 <ResponseHeader>
287 <FromAgencyId>
288 <UniqueAgencyId>
289 <Scheme>$faidScheme</Scheme>
290 <Value>$faidValue</Value>
291 </UniqueAgencyId>
292 </FromAgencyId>
293 <ToAgencyId>
294 <UniqueAgencyId>
295 <Scheme>$taidScheme</Scheme>
296 <Value>$taidValue</Value>
297 </UniqueAgencyId>
298 </ToAgencyId>
299 </ResponseHeader>
300 <UniqueItemId>
fd19ff1c 301 <ItemIdentifierValue datatype="string">$unique_item_id</ItemIdentifierValue>
5f517e0a
DW
302 </UniqueItemId>
303 </RenewItemResponse>
6e6bdfe9
JS
304</NCIPMessage>
305
306ITEMRENEWAL
307
5f517e0a 308 my $more_info = <<MOREINFO;
6e6bdfe9 309
fd19ff1c 310UNIQUEID = $unique_item_id
5f517e0a 311Desired Due Date = $due_date
6e6bdfe9
JS
312
313MOREINFO
314
5f517e0a
DW
315 logit( $hd, ( caller(0) )[3], $more_info );
316 staff_log( $taidValue, $faidValue,
317 "RenewItem -> Patronid : "
318 . $pid
fd19ff1c
JG
319 . " | Uniqueid: : "
320 . $unique_item_id
5f517e0a
DW
321 . " | Due Date : "
322 . $due_date );
4cdc4f67
JS
323}
324
325sub accept_item {
5f517e0a
DW
326 my $faidSchemeX = $doc->findvalue('/NCIPMessage/AcceptItem/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
327 my $faidScheme = HTML::Entities::encode($faidSchemeX);
328 my $faidValue = $doc->find('/NCIPMessage/AcceptItem/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
329 my $taidSchemeX = $doc->findvalue('/NCIPMessage/AcceptItem/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
330 my $taidScheme = HTML::Entities::encode($taidSchemeX);
331 my $taidValue = $doc->find('/NCIPMessage/AcceptItem/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
5f517e0a
DW
332 my $visid = $doc->findvalue('/NCIPMessage/AcceptItem/ItemOptionalFields/ItemDescription/VisibleItemId/VisibleItemIdentifier') . $faidValue;
333 my $request_id = $doc->findvalue('/NCIPMessage/AcceptItem/UniqueRequestId/RequestIdentifierValue') || "unknown";
334 my $patron = $doc->findvalue('/NCIPMessage/AcceptItem/UserOptionalFields/VisibleUserId/VisibleUserIdentifier');
335 my $copy = copy_from_barcode($visid);
bf70b77d 336 fail( "accept_item: " . $copy->{textcode} . " $visid" ) unless ( blessed $copy);
ba01d5da 337 my $r2 = update_copy( $copy, $conf->{status}->{hold} ); # put into INN-Reach Hold status
272651d1
JG
338 # We need to find the hold to know the pickup location
339 my $hold = find_hold_on_copy($visid);
67f5b308
JG
340 if (defined $hold && blessed($hold)) {
341 # Check the copy in to capture for hold -- do it at the pickup_lib
342 # so that the hold becomes Available
343 my $checkin_result = checkin_accept($copy->id, $hold->pickup_lib);
344 } else {
345 fail( "accept_item: no hold found for visid " . $visid );
346 }
5f517e0a
DW
347
348 my $hd = <<ACCEPTITEM;
4cdc4f67
JS
349Content-type: text/xml
350
351
352<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
353<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
354 <AcceptItemResponse>
355 <ResponseHeader>
356 <FromAgencyId>
357 <UniqueAgencyId>
358 <Scheme>$faidScheme</Scheme>
359 <Value>$faidValue</Value>
360 </UniqueAgencyId>
361 </FromAgencyId>
362 <ToAgencyId>
363 <UniqueAgencyId>
364 <Scheme>$taidScheme</Scheme>
365 <Value>$taidValue</Value>
366 </UniqueAgencyId>
367 </ToAgencyId>
368 </ResponseHeader>
369 <UniqueRequestId>
370 <ItemIdentifierValue datatype="string">$request_id</ItemIdentifierValue>
371 </UniqueRequestId>
372 <UniqueItemId>
373 <ItemIdentifierValue datatype="string">$visid</ItemIdentifierValue>
374 </UniqueItemId>
375 </AcceptItemResponse>
4cdc4f67
JS
376</NCIPMessage>
377
378ACCEPTITEM
379
5f517e0a
DW
380 logit( $hd, ( caller(0) )[3] );
381 staff_log( $taidValue, $faidValue,
0b0c5299 382 "AcceptItem -> Request Id : " . $request_id . " | Patron Id : " . $patron . " | Visible Id :" . $visid );
4cdc4f67
JS
383}
384
385sub item_received {
dfb584fc
JG
386 my $faidSchemeX = $doc->findvalue('/NCIPMessage/ItemReceived/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
387 my $faidScheme = HTML::Entities::encode($faidSchemeX);
5f517e0a 388 my $faidValue = $doc->find('/NCIPMessage/ItemReceived/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
dfb584fc
JG
389 my $taidSchemeX = $doc->findvalue('/NCIPMessage/ItemReceived/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
390 my $taidScheme = HTML::Entities::encode($taidSchemeX);
391 my $taidValue = $doc->find('/NCIPMessage/ItemReceived/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
0b0c5299
DW
392 my $visid = $doc->findvalue('/NCIPMessage/ItemReceived/ItemOptionalFields/ItemDescription/VisibleItemId/VisibleItemIdentifier') . $faidValue;
393 my $copy = copy_from_barcode($visid);
394 fail( $copy->{textcode} . " $visid" ) unless ( blessed $copy);
395 my $r1 = checkin($visid) if ( $copy->status == OILS_COPY_STATUS_CHECKED_OUT ); # checkin the item before delete if ItemCheckedIn step was skipped
5f517e0a
DW
396 my $r2 = delete_copy($copy);
397
398 my $hd = <<ITEMRECEIVED;
4cdc4f67
JS
399Content-type: text/xml
400
401
402<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
403<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
404 <ItemReceivedResponse>
405 <ResponseHeader>
406 <FromAgencyId>
407 <UniqueAgencyId>
408 <Scheme>$faidScheme</Scheme>
409 <Value>$faidValue</Value>
410 </UniqueAgencyId>
411 </FromAgencyId>
412 <ToAgencyId>
413 <UniqueAgencyId>
414 <Scheme>$taidScheme</Scheme>
415 <Value>$taidValue</Value>
416 </UniqueAgencyId>
417 </ToAgencyId>
418 </ResponseHeader>
419 <UniqueItemId>
0b0c5299 420 <ItemIdentifierValue datatype="string">$visid</ItemIdentifierValue>
5f517e0a
DW
421 </UniqueItemId>
422 </ItemReceivedResponse>
4cdc4f67
JS
423</NCIPMessage>
424
425ITEMRECEIVED
426
5f517e0a 427 logit( $hd, ( caller(0) )[3] );
0b0c5299 428 staff_log( $taidValue, $faidValue, "ItemReceived -> Visible ID : " . $visid );
4cdc4f67
JS
429}
430
431sub item_cancelled {
5f517e0a
DW
432 my $faidSchemeX = $doc->findvalue('/NCIPMessage/ItemRequestCancelled/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
433 my $faidScheme = HTML::Entities::encode($faidSchemeX);
434 my $faidValue = $doc->find('/NCIPMessage/ItemRequestCancelled/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
435
436 my $taidSchemeX = $doc->findvalue('/NCIPMessage/ItemRequestCancelled/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
437 my $taidScheme = HTML::Entities::encode($taidSchemeX);
438 my $taidValue = $doc->find('/NCIPMessage/ItemRequestCancelled/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
439 my $UniqueItemIdAgencyIdValue = $doc->findvalue('/NCIPMessage/ItemRequestCancelled/UniqueItemId/UniqueAgencyId/Value');
440
441 my $barcode = $doc->findvalue('/NCIPMessage/ItemRequestCancelled/UniqueItemId/ItemIdentifierValue');
442
443 if ( $barcode =~ /^i/ ) { # delete copy only if barcode is an iNUMBER
444 $barcode .= $faidValue;
445 my $copy = copy_from_barcode($barcode);
446 fail( $copy->{textcode} . " $barcode" ) unless ( blessed $copy);
447 my $r = delete_copy($copy);
448 } else {
449
450 # remove hold!
451 my $copy = copy_from_barcode($barcode);
452 fail( $copy->{textcode} . " $barcode" ) unless ( blessed $copy);
0b0c5299 453 my $r = update_copy( $copy, 0 ); # TODO: we need to actually remove the hold, not just reset to available
5f517e0a
DW
454 }
455
456 my $hd = <<ITEMREQUESTCANCELLED;
4cdc4f67
JS
457Content-type: text/xml
458
459
460<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
461<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
462 <ItemRequestCancelledResponse>
463 <ResponseHeader>
464 <FromAgencyId>
465 <UniqueAgencyId>
466 <Scheme>$faidScheme</Scheme>
467 <Value>$faidValue</Value>
468 </UniqueAgencyId>
469 </FromAgencyId>
470 <ToAgencyId>
471 <UniqueAgencyId>
472 <Scheme>$taidScheme</Scheme>
473 <Value>$taidValue</Value>
474 </UniqueAgencyId>
475 </ToAgencyId>
476 </ResponseHeader>
477 <UniqueItemId>
478 <ItemIdentifierValue datatype="string">$barcode</ItemIdentifierValue>
479 </UniqueItemId>
480 </ItemRequestCancelledResponse>
4cdc4f67
JS
481</NCIPMessage>
482
483ITEMREQUESTCANCELLED
484
5f517e0a
DW
485 logit( $hd, ( caller(0) )[3] );
486 staff_log( $taidValue, $faidValue,
487 "ItemRequestCancelled -> Barcode : " . $barcode );
4cdc4f67
JS
488}
489
490sub item_checked_in {
5f517e0a
DW
491 my $faidSchemeX = $doc->findvalue('/NCIPMessage/ItemCheckedIn/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
492 my $faidScheme = HTML::Entities::encode($faidSchemeX);
493 my $faidValue = $doc->find('/NCIPMessage/ItemCheckedIn/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
494 my $taidSchemeX = $doc->findvalue('/NCIPMessage/ItemCheckedIn/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
495 my $taidScheme = HTML::Entities::encode($taidSchemeX);
496 my $taidValue = $doc->find('/NCIPMessage/ItemCheckedIn/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
497
0b0c5299
DW
498 my $visid = $doc->findvalue('/NCIPMessage/ItemCheckedIn/ItemOptionalFields/ItemDescription/VisibleItemId/VisibleItemIdentifier') . $faidValue;
499 my $r = checkin($visid);
500 my $copy = copy_from_barcode($visid);
501 fail( $copy->{textcode} . " $visid" ) unless ( blessed $copy);
ba01d5da 502 my $r2 = update_copy( $copy, $conf->{status}->{transit_return} ); # "INN-Reach Transit Return" status
5f517e0a
DW
503
504 my $hd = <<ITEMCHECKEDIN;
4cdc4f67
JS
505Content-type: text/xml
506
507
508<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
509<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
510 <ItemCheckedInResponse>
511 <ResponseHeader>
512 <FromAgencyId>
513 <UniqueAgencyId>
514 <Scheme>$faidScheme</Scheme>
515 <Value>$faidValue</Value>
516 </UniqueAgencyId>
517 </FromAgencyId>
518 <ToAgencyId>
519 <UniqueAgencyId>
520 <Scheme>$taidScheme</Scheme>
521 <Value>$taidValue</Value>
522 </UniqueAgencyId>
523 </ToAgencyId>
524 </ResponseHeader>
525 <UniqueItemId>
0b0c5299 526 <ItemIdentifierValue datatype="string">$visid</ItemIdentifierValue>
5f517e0a
DW
527 </UniqueItemId>
528 </ItemCheckedInResponse>
4cdc4f67
JS
529</NCIPMessage>
530
531ITEMCHECKEDIN
532
5f517e0a 533 logit( $hd, ( caller(0) )[3] );
0b0c5299 534 staff_log( $taidValue, $faidValue, "ItemCheckedIn -> Visible ID : " . $visid );
4cdc4f67
JS
535}
536
537sub item_checked_out {
5f517e0a
DW
538 my $faidSchemeX = $doc->findvalue('/NCIPMessage/ItemCheckedOut/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
539 my $faidScheme = HTML::Entities::encode($faidSchemeX);
540 my $faidValue = $doc->find('/NCIPMessage/ItemCheckedOut/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
541 my $taidSchemeX = $doc->findvalue('/NCIPMessage/ItemCheckedOut/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
542 my $taidScheme = HTML::Entities::encode($taidSchemeX);
543 my $taidValue = $doc->find('/NCIPMessage/ItemCheckedOut/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
544
0b0c5299 545 my $patron_barcode = $doc->findvalue('/NCIPMessage/ItemCheckedOut/UserOptionalFields/VisibleUserId/VisibleUserIdentifier');
5f517e0a 546 my $due_date = $doc->findvalue('/NCIPMessage/ItemCheckedOut/DateDue');
0b0c5299 547 my $visid = $doc->findvalue('/NCIPMessage/ItemCheckedOut/ItemOptionalFields/ItemDescription/VisibleItemId/VisibleItemIdentifier') . $faidValue;
5f517e0a
DW
548
549 my $copy = copy_from_barcode($visid);
550 fail( $copy->{textcode} . " $visid" ) unless ( blessed $copy);
0b0c5299
DW
551 my $r = update_copy( $copy, 0 ); # seemed like copy had to be available before it could be checked out, so ...
552 my $r1 = checkin($visid) if ( $copy->status == OILS_COPY_STATUS_CHECKED_OUT ); # double posted itemcheckedout messages cause error ... trying to simplify
553 my $r2 = checkout( $visid, $patron_barcode, $due_date );
5f517e0a
DW
554
555 my $hd = <<ITEMCHECKEDOUT;
4cdc4f67
JS
556Content-type: text/xml
557
558
559<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
560<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
561 <ItemCheckedOutResponse>
562 <ResponseHeader>
563 <FromAgencyId>
564 <UniqueAgencyId>
565 <Scheme>$faidScheme</Scheme>
566 <Value>$faidValue</Value>
567 </UniqueAgencyId>
568 </FromAgencyId>
569 <ToAgencyId>
570 <UniqueAgencyId>
571 <Scheme>$taidScheme</Scheme>
572 <Value>$taidValue</Value>
573 </UniqueAgencyId>
574 </ToAgencyId>
575 </ResponseHeader>
576 <UniqueItemId>
577 <ItemIdentifierValue datatype="string">$visid</ItemIdentifierValue>
578 </UniqueItemId>
579 </ItemCheckedOutResponse>
4cdc4f67
JS
580</NCIPMessage>
581
582ITEMCHECKEDOUT
583
5f517e0a
DW
584 logit( $hd, ( caller(0) )[3] );
585 staff_log( $taidValue, $faidValue,
0b0c5299 586 "ItemCheckedOut -> Visible Id : " . $visid . " | Patron Barcode : " . $patron_barcode . " | Due Date : " . $due_date );
4cdc4f67
JS
587}
588
589sub check_out_item {
5f517e0a
DW
590 my $faidSchemeX = $doc->findvalue('/NCIPMessage/CheckOutItem/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
591 my $faidScheme = HTML::Entities::encode($faidSchemeX);
592 my $faidValue = $doc->find('/NCIPMessage/CheckOutItem/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
593 my $taidSchemeX = $doc->findvalue('/NCIPMessage/CheckOutItem/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
594 my $taidScheme = HTML::Entities::encode($taidSchemeX);
595 my $taidValue = $doc->find('/NCIPMessage/CheckOutItem/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
596
597 my $mdate = $doc->findvalue('/NCIPMessage/CheckOutItem/MandatedAction/DateEventOccurred');
119c2c2f
JG
598 # TODO: look up individual accounts for agencies based on barcode prefix + agency identifier
599 my $patron_barcode = $conf->{checkout}->{institutional_patron}; # patron id if patron_id_as_identifier = yes
5f517e0a 600
04643d4b 601 # For CheckOutItem and INN-REACH, this value will correspond with our local barcode
5f517e0a 602 my $barcode = $doc->findvalue('/NCIPMessage/CheckOutItem/UniqueItemId/ItemIdentifierValue');
0b0c5299
DW
603
604 # TODO: watch for possible real ids here?
5f517e0a
DW
605 my $due_date = $doc->findvalue('/NCIPMessage/CheckOutItem/DateDue');
606
607 my $copy = copy_from_barcode($barcode);
608 fail( $copy->{textcode} . " $barcode" ) unless ( blessed $copy);
609
0b0c5299
DW
610 my $r2 = checkout( $barcode, $patron_barcode, $due_date );
611
612 # TODO: check for checkout exception (like OPEN_CIRCULATION_EXISTS)
5f517e0a
DW
613
614 my $hd = <<CHECKOUTITEM;
4cdc4f67
JS
615Content-type: text/xml
616
617
618<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
619<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
620 <CheckOutItemResponse>
621 <ResponseHeader>
622 <FromAgencyId>
623 <UniqueAgencyId>
624 <Scheme>$faidScheme</Scheme>
625 <Value>$faidValue</Value>
626 </UniqueAgencyId>
627 </FromAgencyId>
628 <ToAgencyId>
629 <UniqueAgencyId>
630 <Scheme>$taidScheme</Scheme>
631 <Value>$taidValue</Value>
632 </UniqueAgencyId>
633 </ToAgencyId>
634 </ResponseHeader>
635 <UniqueItemId>
636 <ItemIdentifierValue datatype="string">$barcode</ItemIdentifierValue>
637 </UniqueItemId>
638 </CheckOutItemResponse>
4cdc4f67
JS
639</NCIPMessage>
640
641CHECKOUTITEM
642
5f517e0a
DW
643 logit( $hd, ( caller(0) )[3] );
644 staff_log( $taidValue, $faidValue,
0b0c5299 645 "CheckOutItem -> Barcode : " . $barcode . " | Patron Barcode : " . $patron_barcode . " | Due Date : " . $due_date );
4cdc4f67
JS
646}
647
648sub check_in_item {
5f517e0a
DW
649 my $faidSchemeX = $doc->findvalue('/NCIPMessage/CheckInItem/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
650 my $faidScheme = HTML::Entities::encode($faidSchemeX);
651 my $faidValue = $doc->find('/NCIPMessage/CheckInItem/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
652 my $taidSchemeX = $doc->findvalue('/NCIPMessage/CheckInItem/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
653 my $taidScheme = HTML::Entities::encode($taidSchemeX);
654 my $taidValue = $doc->find('/NCIPMessage/CheckInItem/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
655
04643d4b 656 # For CheckInItem and INN-REACH, this value will correspond with our local barcode
5f517e0a 657 my $barcode = $doc->findvalue('/NCIPMessage/CheckInItem/UniqueItemId/ItemIdentifierValue');
2606eada
DW
658 my $r = checkin($barcode);
659 fail($r) if $r =~ /^COPY_NOT_CHECKED_OUT/;
660 # TODO: do we need to do these next steps? checkin() should handle everything, and we want this to end up in 'reshelving'. If we are worried about transits, we should handle (abort) them, not just change the status
661 ##my $copy = copy_from_barcode($barcode);
662 ##fail($copy->{textcode}." $barcode") unless (blessed $copy);
663 ## my $r2 = update_copy($copy,0); # Available now
5f517e0a
DW
664
665 my $hd = <<CHECKINITEM;
4cdc4f67
JS
666Content-type: text/xml
667
668
669<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
670<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
671 <CheckInItemResponse>
672 <ResponseHeader>
673 <FromAgencyId>
674 <UniqueAgencyId>
675 <Scheme>$faidScheme</Scheme>
676 <Value>$faidValue</Value>
677 </UniqueAgencyId>
678 </FromAgencyId>
679 <ToAgencyId>
680 <UniqueAgencyId>
681 <Scheme>$taidScheme</Scheme>
682 <Value>$taidValue</Value>
683 </UniqueAgencyId>
684 </ToAgencyId>
685 </ResponseHeader>
686 <UniqueItemId>
687 <ItemIdentifierValue datatype="string">$barcode</ItemIdentifierValue>
688 </UniqueItemId>
689 </CheckInItemResponse>
4cdc4f67
JS
690</NCIPMessage>
691
692CHECKINITEM
693
5f517e0a
DW
694 logit( $hd, ( caller(0) )[3] );
695 staff_log( $taidValue, $faidValue, "CheckInItem -> Barcode : " . $barcode );
4cdc4f67
JS
696}
697
698sub item_shipped {
5f517e0a
DW
699 my $faidSchemeX = $doc->findvalue('/NCIPMessage/ItemShipped/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
700 my $faidScheme = HTML::Entities::encode($faidSchemeX);
701 my $faidValue = $doc->find('/NCIPMessage/ItemShipped/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
702 my $taidSchemeX = $doc->findvalue('/NCIPMessage/ItemShipped/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
703 my $taidScheme = HTML::Entities::encode($taidSchemeX);
704 my $taidValue = $doc->find('/NCIPMessage/ItemShipped/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
705
3602ecf5
JG
706 my $address = $doc->findvalue('/NCIPMessage/ItemShipped/ShippingInformation/PhysicalAddress/UnstructuredAddress/UnstructuredAddressData');
707
5f517e0a
DW
708 my $visid = $doc->findvalue('/NCIPMessage/ItemShipped/ItemOptionalFields/ItemDescription/VisibleItemId/VisibleItemIdentifier') . $faidValue;
709 my $barcode = $doc->findvalue('/NCIPMessage/ItemShipped/UniqueItemId/ItemIdentifierValue') . $faidValue;
710 my $title = $doc->findvalue('/NCIPMessage/ItemShipped/ItemOptionalFields/BibliographicDescription/Title');
711 my $callnumber = $doc->findvalue('/NCIPMessage/ItemShipped/ItemOptionalFields/ItemDescription/CallNumber');
712
713 my $copy = copy_from_barcode($barcode);
3602ecf5 714
0a9f148c 715 fail( $copy->{textcode} . " $barcode" ) unless ( blessed $copy);
b874cb1d 716
3602ecf5
JG
717 my $pickup_lib;
718
719 if ($address) {
720 my $pickup_lib_map = load_map_file( $conf->{path}->{pickup_lib_map} );
721
722 if ($pickup_lib_map) {
723 $pickup_lib = lookup_pickup_lib($address, $pickup_lib_map);
724 }
725 }
726
727 if ($pickup_lib) {
728 update_hold_pickup($barcode, $pickup_lib);
729 }
730
ba01d5da 731 my $r = update_copy_shipped( $copy, $conf->{status}->{transit}, $visid ); # put copy into INN-Reach Transit status & modify barcode = Visid != tempIIIiNumber
5f517e0a
DW
732
733 my $hd = <<ITEMSHIPPED;
4cdc4f67
JS
734Content-type: text/xml
735
736
737<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
738<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
739 <ItemShippedResponse>
740 <ResponseHeader>
741 <FromAgencyId>
742 <UniqueAgencyId>
743 <Scheme>$faidScheme</Scheme>
744 <Value>$faidValue</Value>
745 </UniqueAgencyId>
746 </FromAgencyId>
747 <ToAgencyId>
748 <UniqueAgencyId>
749 <Scheme>$taidScheme</Scheme>
750 <Value>$taidValue</Value>
751 </UniqueAgencyId>
752 </ToAgencyId>
753 </ResponseHeader>
754 <UniqueItemId>
755 <ItemIdentifierValue datatype="string">$visid</ItemIdentifierValue>
756 </UniqueItemId>
757 </ItemShippedResponse>
4cdc4f67
JS
758</NCIPMessage>
759
760ITEMSHIPPED
761
5f517e0a
DW
762 logit( $hd, ( caller(0) )[3] );
763 staff_log( $taidValue, $faidValue,
0b0c5299 764 "ItemShipped -> Visible Id : " . $visid . " | Barcode : " . $barcode . " | Title : " . $title . " | Call Number : " . $callnumber );
4cdc4f67
JS
765}
766
767sub item_request {
5f517e0a
DW
768 my $faidSchemeX = $doc->findvalue('/NCIPMessage/ItemRequested/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
769 my $faidScheme = HTML::Entities::encode($faidSchemeX);
770 my $faidValue = $doc->find('/NCIPMessage/ItemRequested/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
771
772 my $taidSchemeX = $doc->findvalue('/NCIPMessage/ItemRequested/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
773 my $taidScheme = HTML::Entities::encode($taidSchemeX);
774 my $taidValue = $doc->find('/NCIPMessage/ItemRequested/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
775 my $UniqueItemIdAgencyIdValue = $doc->findvalue('/NCIPMessage/ItemRequested/UniqueItemId/UniqueAgencyId/Value');
776
0b0c5299
DW
777 # TODO: should we use the VisibleID for item agency variation of this method call
778
779 my $pid = $doc->findvalue('/NCIPMessage/ItemRequested/UniqueUserId/UserIdentifierValue');
5f517e0a
DW
780 my $barcode = $doc->findvalue('/NCIPMessage/ItemRequested/UniqueItemId/ItemIdentifierValue');
781 my $author = $doc->findvalue('/NCIPMessage/ItemRequested/ItemOptionalFields/BibliographicDescription/Author');
782 my $title = $doc->findvalue('/NCIPMessage/ItemRequested/ItemOptionalFields/BibliographicDescription/Title');
783 my $callnumber = $doc->findvalue('/NCIPMessage/ItemRequested/ItemOptionalFields/ItemDescription/CallNumber');
784 my $medium_type = $doc->find('/NCIPMessage/ItemRequested/ItemOptionalFields/BibliographicDescription/MediumType/Value');
785
786 my $r = "default error checking response";
787
a30f6f9e 788 if ( $barcode =~ /^i/ ) { # XXX EG is User Agency # create copy only if barcode is an iNUMBER
ba01d5da 789 my $copy_status_id = $conf->{status}->{loan_requested}; # INN-Reach Loan Requested - local configured status
5f517e0a 790 $barcode .= $faidValue;
a30f6f9e
DW
791 # we want our custom status to be then end result, so create the copy with status of "Available, then hold it, then update the status
792 $r = create_copy( $title, $callnumber, $barcode, 0, $medium_type );
793 my $copy = copy_from_barcode($barcode);
794 my $r2 = place_simple_hold( $copy->id, $pid );
795 my $r3 = update_copy( $copy, $copy_status_id );
796 } else { # XXX EG is Item Agency
15d084ee
JG
797 unless ( $conf->{behavior}->{no_item_agency_holds} =~ m/^y/i ) {
798 # place hold for user UniqueUserId/UniqueAgencyId/Value = institution account
799 my $copy = copy_from_barcode($barcode);
800 my $pid2 = 1013459; # XXX CUSTOMIZATION NEEDED XXX # this is the id of a user representing your DCB system, TODO: use agency information to create and link to individual accounts per agency, if needed
801 $r = place_simple_hold( $copy->id, $pid2 );
802 my $r2 = update_copy( $copy, $conf->{status}->{hold} ); # put into INN-Reach Hold status
803 }
5f517e0a
DW
804 }
805
20bfa6cb
JG
806 # Avoid generating invalid XML responses by encoding title/author
807 # TODO: Move away from heredocs for generating XML
808 $title = HTML::Entities::encode($title);
809 $author = HTML::Entities::encode($author);
810
5f517e0a 811 my $hd = <<ITEMREQ;
4cdc4f67
JS
812Content-type: text/xml
813
814
815<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
816<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
817 <ItemRequestedResponse>
818 <ResponseHeader>
819 <FromAgencyId>
820 <UniqueAgencyId>
821 <Scheme>$faidScheme</Scheme>
822 <Value>$faidValue</Value>
823 </UniqueAgencyId>
824 </FromAgencyId>
825 <ToAgencyId>
826 <UniqueAgencyId>
827 <Scheme>$taidScheme</Scheme>
828 <Value>$taidValue</Value>
829 </UniqueAgencyId>
830 </ToAgencyId>
831 </ResponseHeader>
832 <UniqueUserId>
833 <UniqueAgencyId>
834 <Scheme datatype="string">$taidScheme</Scheme>
835 <Value datatype="string">$taidValue</Value>
836 </UniqueAgencyId>
0b0c5299 837 <UserIdentifierValue datatype="string">$pid</UserIdentifierValue>
5f517e0a
DW
838 </UniqueUserId>
839 <UniqueItemId>
840 <ItemIdentifierValue datatype="string">$barcode</ItemIdentifierValue>
841 </UniqueItemId>
842 <ItemOptionalFields>
843 <BibliographicDescription>
844 <Author datatype="string">$author</Author>
845 <Title datatype="string">$title</Title>
846 </BibliographicDescription>
847 <ItemDescription>
848 <CallNumber datatype="string">$callnumber</CallNumber>
849 </ItemDescription>
850 </ItemOptionalFields>
851 </ItemRequestedResponse>
4cdc4f67
JS
852</NCIPMessage>
853
854ITEMREQ
855
5f517e0a
DW
856 logit( $hd, ( caller(0) )[3] );
857 staff_log( $taidValue, $faidValue,
0b0c5299 858 "ItemRequested -> Barcode : " . $barcode . " | Title : " . $title . " | Call Number : " . $callnumber . " | Patronid :" . $pid );
4cdc4f67
JS
859}
860
1ac548f2
JS
861sub lookupUser {
862
5f517e0a
DW
863 my $faidScheme = $doc->findvalue('/NCIPMessage/LookupUser/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
864 $faidScheme = HTML::Entities::encode($faidScheme);
865 my $faidValue = $doc->find('/NCIPMessage/LookupUser/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
866 my $taidScheme = $doc->findvalue('/NCIPMessage/LookupUser/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
867 $taidScheme = HTML::Entities::encode($taidScheme);
868
869 my $taidValue = $doc->find('/NCIPMessage/LookupUser/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
870 my $id = $doc->findvalue('/NCIPMessage/LookupUser/VisibleUserId/VisibleUserIdentifier');
b9103e62
JG
871
872 my $uidValue;
873
874 if ($patron_id_type eq 'barcode') {
875 $uidValue = user_id_from_barcode($id);
876 } else {
877 $uidValue = $id;
878 }
5f517e0a
DW
879
880 if ( !defined($uidValue)
881 || ( ref($uidValue) && reftype($uidValue) eq 'HASH' ) )
882 {
883 do_lookup_user_error_stanza("PATRON_NOT_FOUND : $id");
884 die;
885 }
886
3bcdd8ae
JG
887 my ( $propername, $email, $good_until, $userpriv, $block_stanza ) =
888 ( "name here", "", "good until", "", "" ); # defaults
5f517e0a
DW
889
890 my $patron = flesh_user($uidValue);
891
892 #if (blessed($patron)) {
893 my $patron_ok = 1;
894 my @penalties = @{ $patron->standing_penalties };
895
896 if ( $patron->deleted eq 't' ) {
897 do_lookup_user_error_stanza("PATRON_DELETED : $uidValue");
898 die;
899 } elsif ( $patron->barred eq 't' ) {
900 do_lookup_user_error_stanza("PATRON_BARRED : $uidValue");
901 die;
902 } elsif ( $patron->active eq 'f' ) {
903 do_lookup_user_error_stanza("PATRON_INACTIVE : $uidValue");
904 die;
905 }
906
907 elsif ( $#penalties > -1 ) {
908
909# my $penalty;
910# foreach $penalty (@penalties) {
911# if (defined($penalty->standing_penalty->block_list)) {
912# my @block_list = split(/\|/, $penalty->standing_penalty->block_list);
913# foreach my $block (@block_list) {
914# foreach my $block_on (@$block_types) {
915# if ($block eq $block_on) {
916# $block_stanza .= "\n".$penalty->standing_penalty->name;
917# $patron_ok = 0;
918# }
919# last unless ($patron_ok);
920# }
921# last unless ($patron_ok);
922# }
923# }
924# }
925 $block_stanza = qq(
926 <BlockOrTrap>
927 <UniqueAgencyId>
928 <Scheme datatype="string">http://just.testing.now</Scheme>
929 <Value datatype="string">$faidValue</Value>
930 </UniqueAgencyId>
931 <BlockOrTrapType>
932 <Scheme datatype="string">http://just.testing.now</Scheme>
933 <Value datatype="string">Block Hold</Value>
934 </BlockOrTrapType>
935 </BlockOrTrap>);
936 }
937
5809d230 938 if ( defined( $patron->email ) && $conf->{behavior}->{omit_patron_email} !~ m/^y/i ) {
5f517e0a
DW
939 $email = qq(
940 <UserAddressInformation>
941 <ElectronicAddress>
942 <ElectronicAddressType>
943 <Scheme datatype="string">http://testing.now</Scheme>
944 <Value datatype="string">mailto</Value>
945 </ElectronicAddressType>
946 <ElectronicAddressData datatype="string">)
947 . HTML::Entities::encode( $patron->email )
948 . qq(</ElectronicAddressData>
949 </ElectronicAddress>
950 </UserAddressInformation>);
951 }
952
953 $propername = $patron->first_given_name . " " . $patron->family_name;
954 $good_until = $patron->expire_date || "unknown";
33a39c82 955 $userpriv = $patron->profile->name;
5f517e0a 956
3602ecf5 957 my $userpriv_map = load_map_file( $conf->{path}->{userpriv_map} );
47866eef
JG
958
959 if ($userpriv_map) {
45d455be 960 $userpriv = lookup_userpriv($userpriv, $userpriv_map);
47866eef
JG
961 }
962
5f517e0a
DW
963 #} else {
964 # do_lookup_user_error_stanza("PATRON_NOT_FOUND : $id");
965 # die;
966 #}
0b0c5299 967 my $uniqid = $patron->id;
b9103e62
JG
968 my $visid;
969 if ($patron_id_type eq 'barcode') {
970 $visid = $patron->card->barcode;
971 } else {
972 $visid = $patron->id;
973 }
5f517e0a 974 my $hd = <<LOOKUPUSERRESPONSE;
4cdc4f67
JS
975Content-type: text/xml
976
977
978<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
979<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
980 <LookupUserResponse>
981 <ResponseHeader>
982 <FromAgencyId>
983 <UniqueAgencyId>
984 <Scheme>$taidScheme</Scheme>
985 <Value>$taidValue</Value>
986 </UniqueAgencyId>
987 </FromAgencyId>
988 <ToAgencyId>
989 <UniqueAgencyId>
990 <Scheme>$faidScheme</Scheme>
991 <Value>$faidValue</Value>
992 </UniqueAgencyId>
993 </ToAgencyId>
994 </ResponseHeader>
995 <UniqueUserId>
996 <UniqueAgencyId>
997 <Scheme>$taidScheme</Scheme>
998 <Value>$taidValue</Value>
999 </UniqueAgencyId>
0b0c5299 1000 <UserIdentifierValue>$uniqid</UserIdentifierValue>
5f517e0a
DW
1001 </UniqueUserId>
1002 <UserOptionalFields>
1003 <VisibleUserId>
1004 <VisibleUserIdentifierType>
1005 <Scheme datatype="string">http://blah.com</Scheme>
1006 <Value datatype="string">Barcode</Value>
1007 </VisibleUserIdentifierType>
0b0c5299 1008 <VisibleUserIdentifier datatype="string">$visid</VisibleUserIdentifier>
5f517e0a
DW
1009 </VisibleUserId>
1010 <NameInformation>
1011 <PersonalNameInformation>
1012 <UnstructuredPersonalUserName datatype="string">$propername</UnstructuredPersonalUserName>
1013 </PersonalNameInformation>
1014 </NameInformation>
1015 <UserPrivilege>
1016 <UniqueAgencyId>
1017 <Scheme datatype="string">$faidScheme</Scheme>
1018 <Value datatype="string">$faidValue</Value>
1019 </UniqueAgencyId>
1020 <AgencyUserPrivilegeType>
1021 <Scheme datatype="string">http://testing.purposes.only</Scheme>
3bcdd8ae 1022 <Value datatype="string">$userpriv</Value>
5f517e0a
DW
1023 </AgencyUserPrivilegeType>
1024 <ValidToDate datatype="string">$good_until</ValidToDate>
1025 </UserPrivilege> $email $block_stanza
1026 </UserOptionalFields>
4cdc4f67
JS
1027 </LookupUserResponse>
1028</NCIPMessage>
1029
1030LOOKUPUSERRESPONSE
1031
5f517e0a
DW
1032 logit( $hd, ( caller(0) )[3] );
1033 staff_log( $taidValue, $faidValue,
1034 "LookupUser -> Patron Barcode : "
1035 . $id
1036 . " | Patron Id : "
1037 . $uidValue
1038 . " | User Name : "
1039 . $propername
1040 . " | User Priv : "
1041 . $userpriv );
4cdc4f67
JS
1042}
1043
4cdc4f67 1044sub fail {
5f517e0a
DW
1045 my $error_msg =
1046 shift || "THIS IS THE DEFAULT / DO NOT HANG III NCIP RESP MSG";
1047 print "Content-type: text/xml\n\n";
4cdc4f67 1048
5f517e0a 1049 print <<ITEMREQ;
4cdc4f67
JS
1050<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
1051<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
1052 <ItemRequestedResponse>
1053 <ResponseHeader>
1054 <FromAgencyId>
1055 <UniqueAgencyId>
1056 <Scheme>http://136.181.125.166:6601/IRCIRCD?target=get_scheme_values&amp;scheme=UniqueAgencyId</Scheme>
1057 <Value></Value>
1058 </UniqueAgencyId>
1059 </FromAgencyId>
1060 <ToAgencyId>
1061 <UniqueAgencyId>
1062 <Scheme>http://136.181.125.166:6601/IRCIRCD?target=get_scheme_values&amp;scheme=UniqueAgencyId</Scheme>
1063 <Value>$error_msg</Value>
1064 </UniqueAgencyId>
1065 </ToAgencyId>
1066 </ResponseHeader>
1067 </ItemRequestedResponse>
4cdc4f67
JS
1068</NCIPMessage>
1069
1070ITEMREQ
6e6bdfe9 1071
601be0bb
JG
1072 # XXX: we should log FromAgencyId and ToAgencyId values here, but they are not available to the code at this point
1073 staff_log( '', '',
5f517e0a
DW
1074 ( ( caller(0) )[3] . " -> " . $error_msg ) );
1075 die;
4cdc4f67
JS
1076}
1077
1078sub do_lookup_user_error_stanza {
1079
601be0bb 1080 # XXX: we should include FromAgencyId and ToAgencyId values, but they are not available to the code at this point
5f517e0a
DW
1081 my $error = shift;
1082 my $hd = <<LOOKUPPROB;
4cdc4f67
JS
1083Content-type: text/xml
1084
1085
1086<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
1087<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
1088 <LookupUserResponse>
1089 <ResponseHeader>
1090 <FromAgencyId>
1091 <UniqueAgencyId>
601be0bb
JG
1092 <Scheme></Scheme>
1093 <Value></Value>
5f517e0a
DW
1094 </UniqueAgencyId>
1095 </FromAgencyId>
1096 <ToAgencyId>
1097 <UniqueAgencyId>
601be0bb
JG
1098 <Scheme></Scheme>
1099 <Value></Value>
5f517e0a
DW
1100 </UniqueAgencyId>
1101 </ToAgencyId>
1102 </ResponseHeader>
1103 <Problem>
1104 <ProcessingError>
1105 <ProcessingErrorType>
1106 <Scheme>http://www.niso.org/ncip/v1_0/schemes/processingerrortype/lookupuserprocessingerror.scm</Scheme>
1107 <Value>$error</Value>
1108 </ProcessingErrorType>
1109 <ProcessingErrorElement>
1110 <ElementName>AuthenticationInput</ElementName>
1111 </ProcessingErrorElement>
1112 </ProcessingError>
1113 </Problem>
1114 </LookupUserResponse>
4cdc4f67
JS
1115</NCIPMessage>
1116
1117LOOKUPPROB
1118
5f517e0a 1119 logit( $hd, ( caller(0) )[3] );
601be0bb
JG
1120 # XXX: we should log FromAgencyId and ToAgencyId values here, but they are not available to the code at this point
1121 staff_log( '', '', ( ( caller(0) )[3] . " -> " . $error ) );
5f517e0a 1122 die;
4cdc4f67
JS
1123}
1124
1125# Login to the OpenSRF system/Evergreen.
1126#
1127# Returns a hash with the authtoken, authtime, and expiration (time in
1128# seconds since 1/1/1970).
1129sub login {
1ac548f2
JS
1130
1131 # XXX: local opensrf core conf filename should be in config.
1132 # XXX: STAFF account with ncip service related permissions should be in config.
5f517e0a 1133 my $bootstrap = '/openils/conf/opensrf_core.xml';
f5e8d07e
JG
1134 my $uname = $conf->{auth}->{username};
1135 my $password = $conf->{auth}->{password};
5f517e0a
DW
1136
1137 # Bootstrap the client
1138 OpenSRF::System->bootstrap_client( config_file => $bootstrap );
1139 my $idl = OpenSRF::Utils::SettingsClient->new->config_value("IDL");
1140 Fieldmapper->import( IDL => $idl );
1141
1142 # Initialize CStoreEditor:
1143 OpenILS::Utils::CStoreEditor->init;
1144
1145 my $seed = OpenSRF::AppSession->create('open-ils.auth')
1146 ->request( 'open-ils.auth.authenticate.init', $uname )->gather(1);
1147
1148 return undef unless $seed;
1149
1150 my $response = OpenSRF::AppSession->create('open-ils.auth')->request(
1151 'open-ils.auth.authenticate.complete',
1152 {
1153 username => $uname,
1154 password => md5_hex( $seed . md5_hex($password) ),
1155 type => 'staff'
1156 }
1157 )->gather(1);
1158
1159 return undef unless $response;
1160
1161 my %result;
1162 $result{'authtoken'} = $response->{payload}->{authtoken};
1163 $result{'authtime'} = $response->{payload}->{authtime};
1164 $result{'expiration'} = time() + $result{'authtime'}
1165 if ( defined( $result{'authtime'} ) );
1166 return %result;
4cdc4f67
JS
1167}
1168
1169# Check the time versus the session expiration time and login again if
1170# the session has expired, consequently resetting the session
1171# paramters. We want to run this before doing anything that requires
1172# us to have a current session in OpenSRF.
1173#
1174# Arguments
1175# none
1176#
1177# Returns
1178# Nothing
1179sub check_session_time {
5f517e0a
DW
1180 if ( time() > $session{'expiration'} ) {
1181 %session = login();
1182 if ( !%session ) {
1183 die("Failed to reinitialize the session after expiration.");
1184 }
1185 }
4cdc4f67
JS
1186}
1187
1188# Retrieve the logged in user.
1189#
1190sub get_session {
5f517e0a
DW
1191 my $response =
1192 OpenSRF::AppSession->create('open-ils.auth')
1193 ->request( 'open-ils.auth.session.retrieve', $session{authtoken} )
1194 ->gather(1);
1195 return $response;
4cdc4f67
JS
1196}
1197
1198# Logout/destroy the OpenSRF session
1199#
1200# Argument is
1201# none
1202#
1203# Returns
1204# Does not return anything
1205sub logout {
5f517e0a
DW
1206 if ( time() < $session{'expiration'} ) {
1207 my $response =
1208 OpenSRF::AppSession->create('open-ils.auth')
1209 ->request( 'open-ils.auth.session.delete', $session{authtoken} )
1210 ->gather(1);
1211 if ($response) {
1212
1213 # strong.silent.success
1214 exit(0);
1215 } else {
1216 fail("Logout unsuccessful. Good-bye, anyway.");
1217 }
1218 }
4cdc4f67
JS
1219}
1220
1221sub update_copy {
5f517e0a
DW
1222 check_session_time();
1223 my ( $copy, $status_id ) = @_;
1224 my $e = new_editor( authtoken => $session{authtoken} );
1225 return $e->event->{textcode} unless ( $e->checkauth );
1226 $e->xact_begin;
1227 $copy->status($status_id);
1228 return $e->event unless $e->update_asset_copy($copy);
1229 $e->commit;
1230 return 'SUCCESS';
4cdc4f67
JS
1231}
1232
1233# my paranoia re barcode on shipped items using visid for unique value
1234sub update_copy_shipped {
5f517e0a
DW
1235 check_session_time();
1236 my ( $copy, $status_id, $barcode ) = @_;
1237 my $e = new_editor( authtoken => $session{authtoken} );
1238 return $e->event->{textcode} unless ( $e->checkauth );
1239 $e->xact_begin;
1240 $copy->status($status_id);
1241 $copy->barcode($barcode);
1242 return $e->event unless $e->update_asset_copy($copy);
1243 $e->commit;
1244 return 'SUCCESS';
4cdc4f67
JS
1245}
1246
1247# Delete a copy
1248#
1249# Argument
1250# Fieldmapper asset.copy object
1251#
1252# Returns
1253# "SUCCESS" on success
1254# Event textcode if an error occurs
1255sub delete_copy {
5f517e0a
DW
1256 check_session_time();
1257 my ($copy) = @_;
1258
1259 my $e = new_editor( authtoken => $session{authtoken} );
1260 return $e->event->{textcode} unless ( $e->checkauth );
1261
1262 # Get the calnumber
1263 my $vol = $e->retrieve_asset_call_number( $copy->call_number );
1264 return $e->event->{textcode} unless ($vol);
1265
1266 # Get the biblio.record_entry
1267 my $bre = $e->retrieve_biblio_record_entry( $vol->record );
1268 return $e->event->{textcode} unless ($bre);
1269
1270 # Delete everything in a transaction and rollback if anything fails.
0b0c5299 1271 # TODO: I think there is a utility function which handles all this
5f517e0a
DW
1272 $e->xact_begin;
1273 my $r; # To hold results of editor calls
1274 $r = $e->delete_asset_copy($copy);
1275 unless ($r) {
1276 my $lval = $e->event->{textcode};
1277 $e->rollback;
1278 return $lval;
1279 }
1280 my $list =
1281 $e->search_asset_copy( { call_number => $vol->id, deleted => 'f' } );
1282 unless (@$list) {
1283 $r = $e->delete_asset_call_number($vol);
1284 unless ($r) {
1285 my $lval = $e->event->{textcode};
1286 $e->rollback;
1287 return $lval;
1288 }
0b0c5299 1289 $list = $e->search_asset_call_number( { record => $bre->id, deleted => 'f' } );
5f517e0a 1290 unless (@$list) {
0b0c5299 1291 $r = $e->delete_biblio_record_entry($bre);
5f517e0a
DW
1292 unless ($r) {
1293 my $lval = $e->event->{textcode};
1294 $e->rollback;
1295 return $lval;
1296 }
1297 }
1298 }
1299 $e->commit;
1300 return 'SUCCESS';
4cdc4f67
JS
1301}
1302
1303# Get asset.copy from asset.copy.barcode.
1304# Arguments
1305# copy barcode
1306#
1307# Returns
1308# asset.copy fieldmaper object
1309# or hash on error
1310sub copy_from_barcode {
5f517e0a
DW
1311 check_session_time();
1312 my ($barcode) = @_;
1313 my $response =
1314 OpenSRF::AppSession->create('open-ils.search')
1315 ->request( 'open-ils.search.asset.copy.find_by_barcode', $barcode )
1316 ->gather(1);
1317 return $response;
4cdc4f67
JS
1318}
1319
1320sub locid_from_barcode {
5f517e0a
DW
1321 my ($barcode) = @_;
1322 my $response =
1323 OpenSRF::AppSession->create('open-ils.search')
1324 ->request( 'open-ils.search.biblio.find_by_barcode', $barcode )
1325 ->gather(1);
1326 return $response->{ids}[0];
4cdc4f67
JS
1327}
1328
3602ecf5
JG
1329sub bre_id_from_barcode {
1330 check_session_time();
1331 my ($barcode) = @_;
1332 my $response =
1333 OpenSRF::AppSession->create('open-ils.search')
1334 ->request( 'open-ils.search.bib_id.by_barcode', $barcode )
1335 ->gather(1);
1336 return $response;
1337}
1338
1339sub holds_for_bre {
1340 check_session_time();
1341 my ($bre_id) = @_;
1342 my $response =
1343 OpenSRF::AppSession->create('open-ils.circ')
1344 ->request( 'open-ils.circ.holds.retrieve_all_from_title', $session{authtoken}, $bre_id )
1345 ->gather(1);
1346 return $response;
1347
1348}
1349
4cdc4f67
JS
1350# Convert a MARC::Record to XML for Evergreen
1351#
6e6bdfe9 1352# Copied from Dyrcona's issa framework which copied
1ac548f2 1353# it from MVLC's Safari Load program which copied it
4cdc4f67
JS
1354# from some code in the Open-ILS example import scripts.
1355#
1356# Argument
1357# A MARC::Record object
1358#
1359# Returns
1360# String with XML for the MARC::Record as Evergreen likes it
1361sub convert2marcxml {
5f517e0a
DW
1362 my $input = shift;
1363 ( my $xml = $input->as_xml_record() ) =~ s/\n//sog;
1364 $xml =~ s/^<\?xml.+\?\s*>//go;
1365 $xml =~ s/>\s+</></go;
1366 $xml =~ s/\p{Cc}//go;
0b0c5299 1367 $xml = $U->entityize($xml);
5f517e0a
DW
1368 $xml =~ s/[\x00-\x1f]//go;
1369 return $xml;
4cdc4f67
JS
1370}
1371
1372# Create a copy and marc record
1373#
1374# Arguments
1375# title
1376# call number
1377# copy barcode
1378#
1379# Returns
1380# bib id on succes
1381# event textcode on failure
1382sub create_copy {
5f517e0a
DW
1383 check_session_time();
1384 my ( $title, $callnumber, $barcode, $copy_status_id, $medium_type ) = @_;
4cdc4f67 1385
5f517e0a
DW
1386 my $e = new_editor( authtoken => $session{authtoken} );
1387 return $e->event->{textcode} unless ( $e->checkauth );
1ac548f2 1388
5f517e0a
DW
1389 my $r = $e->allowed( [ 'CREATE_COPY', 'CREATE_MARC', 'CREATE_VOLUME' ] );
1390 if ( ref($r) eq 'HASH' ) {
1391 return $r->{textcode} . ' ' . $r->{ilsperm};
1392 }
4cdc4f67 1393
5f517e0a
DW
1394 # Check if the barcode exists in asset.copy and bail if it does.
1395 my $list = $e->search_asset_copy( { deleted => 'f', barcode => $barcode } );
1396 if (@$list) {
6e6bdfe9 1397# in the future, can we update it, if it exists and only if it is an INN-Reach status item ?
5f517e0a
DW
1398 $e->finish;
1399 fail( 'BARCODE_EXISTS ! Barcode : ' . $barcode );
1400 die;
1401 }
1402
1403 # Create MARC record
1404 my $record = MARC::Record->new();
1405 $record->encoding('UTF-8');
1406 $record->leader('00881nam a2200193 4500');
1407 my $datespec = strftime( "%Y%m%d%H%M%S.0", localtime );
1408 my @fields = ();
1409 push( @fields, MARC::Field->new( '005', $datespec ) );
1410 push( @fields, MARC::Field->new( '082', '0', '4', 'a' => $callnumber ) );
1411 push( @fields, MARC::Field->new( '245', '0', '0', 'a' => $title ) );
1412 $record->append_fields(@fields);
1413
1414 # Convert the record to XML
1415 my $xml = convert2marcxml($record);
1416
1417 my $bre =
1418 OpenSRF::AppSession->create('open-ils.cat')
1419 ->request( 'open-ils.cat.biblio.record.xml.import',
1420 $session{authtoken}, $xml, 'System Local', 1 )->gather(1);
1421 return $bre->{textcode} if ( ref($bre) eq 'HASH' );
1422
1423 # Create volume record
1424 my $vol =
1425 OpenSRF::AppSession->create('open-ils.cat')
ba01d5da 1426 ->request( 'open-ils.cat.call_number.find_or_create', $session{authtoken}, $callnumber, $bre->id, $conf->{volume}->{owning_lib} )
0b0c5299 1427 ->gather(1);
5f517e0a
DW
1428 return $vol->{textcode} if ( $vol->{textcode} );
1429
1430 # Retrieve the user
1431 my $user = get_session;
1432
1433 # Create copy record
1434 my $copy = Fieldmapper::asset::copy->new();
0b0c5299
DW
1435 # XXX CUSTOMIZATION NEEDED XXX
1436 # You will need to either create a circ mod for every expected medium type,
1437 # OR you should create a single circ mod for all requests from the external
1438 # system.
1439 # Adjust these lines as needed.
1440 # $copy->circ_modifier(qq($medium_type)); # XXX CUSTOMIZATION NEEDED XXX
1441 # OR
ba01d5da 1442 $copy->circ_modifier($conf->{copy}->{circ_modifier});
5f517e0a
DW
1443 $copy->barcode($barcode);
1444 $copy->call_number( $vol->{acn_id} );
ba01d5da 1445 $copy->circ_lib($conf->{copy}->{circ_lib});
5f517e0a
DW
1446 $copy->circulate('t');
1447 $copy->holdable('t');
1448 $copy->opac_visible('t');
1449 $copy->deleted('f');
1450 $copy->fine_level(2);
1451 $copy->loan_duration(2);
ba01d5da 1452 $copy->location($conf->{copy}->{location});
5f517e0a
DW
1453 $copy->status($copy_status_id);
1454 $copy->editor('1');
1455 $copy->creator('1');
1456
5f517e0a
DW
1457 $e->xact_begin;
1458 $copy = $e->create_asset_copy($copy);
1459
5f517e0a
DW
1460 $e->commit;
1461 return $e->event->{textcode} unless ($r);
1462 return 'SUCCESS';
4cdc4f67
JS
1463}
1464
1465# Checkout a copy to a patron
1466#
1467# Arguments
1468# copy barcode
1469# patron barcode
1470#
1471# Returns
1472# textcode of the OSRF response.
1ac548f2 1473sub checkout {
5f517e0a
DW
1474 check_session_time();
1475 my ( $copy_barcode, $patron_barcode, $due_date ) = @_;
1476
1477 # Check for copy:
1478 my $copy = copy_from_barcode($copy_barcode);
1479 unless ( defined($copy) && blessed($copy) ) {
1480 return 'COPY_BARCODE_NOT_FOUND : ' . $copy_barcode;
1481 }
1482
1483 # Check for user
b9103e62
JG
1484 my $uid;
1485 if ($patron_id_type eq 'barcode') {
1486 $uid = user_id_from_barcode($patron_barcode);
1487 } else {
1488 $uid = $patron_barcode;
1489 }
5f517e0a
DW
1490 return 'PATRON_BARCODE_NOT_FOUND : ' . $patron_barcode if ( ref($uid) );
1491
1492 my $response = OpenSRF::AppSession->create('open-ils.circ')->request(
1493 'open-ils.circ.checkout.full.override',
1494 $session{authtoken},
1495 {
1496 copy_barcode => $copy_barcode,
1497 patron_id => $uid,
1498 due_date => $due_date
1499 }
1500 )->gather(1);
1501 return $response->{textcode};
4cdc4f67
JS
1502}
1503
1ac548f2 1504sub renewal {
5f517e0a
DW
1505 check_session_time();
1506 my ( $copy_barcode, $due_date ) = @_;
1507
1508 # Check for copy:
1509 my $copy = copy_from_barcode($copy_barcode);
1510 unless ( defined($copy) && blessed($copy) ) {
1511 return 'COPY_BARCODE_NOT_FOUND : ' . $copy_barcode;
1512 }
1513
1514 my $response = OpenSRF::AppSession->create('open-ils.circ')->request(
1515 'open-ils.circ.renew.override',
1516 $session{authtoken},
1517 {
1518 copy_barcode => $copy_barcode,
1519 due_date => $due_date
1520 }
1521 )->gather(1);
1522 return $response->{textcode};
4cdc4f67
JS
1523}
1524
1ac548f2 1525# Check a copy in
4cdc4f67
JS
1526#
1527# Arguments
1528# copy barcode
4cdc4f67
JS
1529#
1530# Returns
1531# "SUCCESS" on success
1532# textcode of a failed OSRF request
0b0c5299 1533# 'COPY_NOT_CHECKED_OUT' when the copy is not checked out
6e6bdfe9 1534
1ac548f2 1535sub checkin {
5f517e0a
DW
1536 check_session_time();
1537 my ($barcode) = @_;
1538
1539 my $copy = copy_from_barcode($barcode);
1540 return $copy->{textcode} unless ( blessed $copy);
1541
1542 return ("COPY_NOT_CHECKED_OUT $barcode")
1543 unless ( $copy->status == OILS_COPY_STATUS_CHECKED_OUT );
1544
1545 my $e = new_editor( authtoken => $session{authtoken} );
1546 return $e->event->{textcode} unless ( $e->checkauth );
1547
1548 my $circ = $e->search_action_circulation(
1549 [ { target_copy => $copy->id, xact_finish => undef } ] )->[0];
1550 my $r =
1551 OpenSRF::AppSession->create('open-ils.circ')
1552 ->request( 'open-ils.circ.checkin.override',
1553 $session{authtoken}, { force => 1, copy_id => $copy->id } )->gather(1);
1554 return 'SUCCESS' if ( $r->{textcode} eq 'ROUTE_ITEM' );
1555 return $r->{textcode};
4cdc4f67
JS
1556}
1557
272651d1
JG
1558# Check in an copy as part of accept_item
1559# Intent is for the copy to be captured for
1560# a hold -- the only hold that should be
1561# present on the copy
1562
1563sub checkin_accept {
1564 check_session_time();
1565 my $copy_id = shift;
1566 my $circ_lib = shift;
1567
1568 my $r = OpenSRF::AppSession->create('open-ils.circ')->request(
1569 'open-ils.circ.checkin.override',
1570 $session{authtoken}, { force => 1, copy_id => $copy_id, circ_lib => $circ_lib }
1571 )->gather(1);
1572
1573 return $r->{textcode};
1574}
1575
4cdc4f67
JS
1576# Get actor.usr.id from barcode.
1577# Arguments
1578# patron barcode
1579#
1580# Returns
1581# actor.usr.id
1582# or hash on error
1583sub user_id_from_barcode {
5f517e0a
DW
1584 check_session_time();
1585 my ($barcode) = @_;
4cdc4f67 1586
5f517e0a 1587 my $response;
4cdc4f67 1588
5f517e0a
DW
1589 my $e = new_editor( authtoken => $session{authtoken} );
1590 return $response unless ( $e->checkauth );
4cdc4f67 1591
5f517e0a
DW
1592 my $card = $e->search_actor_card( { barcode => $barcode, active => 't' } );
1593 return $e->event unless ($card);
4cdc4f67 1594
5f517e0a 1595 $response = $card->[0]->usr if (@$card);
4cdc4f67 1596
5f517e0a 1597 $e->finish;
4cdc4f67 1598
5f517e0a 1599 return $response;
4cdc4f67
JS
1600}
1601
6e6bdfe9 1602# Place a simple hold for a patron.
4cdc4f67
JS
1603#
1604# Arguments
1605# Target object appropriate for type of hold
1606# Patron for whom the hold is place
1607#
1608# Returns
1609# "SUCCESS" on success
1610# textcode of a failed OSRF request
1611# "HOLD_TYPE_NOT_SUPPORTED" if the hold type is not supported
1612# (Currently only support 'T' and 'C')
1613
1ac548f2 1614# simple hold should be removed and full holds sub should be used instead - pragmatic solution only
5082b884 1615
4cdc4f67 1616sub place_simple_hold {
5f517e0a 1617 check_session_time();
1ac548f2 1618
5f517e0a 1619 #my ($type, $target, $patron, $pickup_ou) = @_;
7e2d6432 1620 my ( $target, $patron_id ) = @_;
1ac548f2 1621
ba01d5da 1622 require $conf->{path}->{oils_header};
5f517e0a 1623 use vars qw/ $apputils $memcache $user $authtoken $authtime /;
1ac548f2 1624
ba01d5da 1625 osrf_connect( $conf->{path}->{opensrf_core} );
f5e8d07e 1626 oils_login( $conf->{auth}->{username}, $conf->{auth}->{password} );
7e2d6432
DW
1627 my $ahr = Fieldmapper::action::hold_request->new();
1628 $ahr->hold_type('C');
1629 # The targeter doesn't like our special statuses, and changing the status after the targeter finishes is difficult because it runs asynchronously. Our workaround is to create the hold frozen, unfreeze it, then run the targeter manually.
1630 $ahr->target($target);
1631 $ahr->usr($patron_id);
ba01d5da
JG
1632 $ahr->requestor($conf->{hold}->{requestor});
1633 # NOTE: When User Agency, we don't know the pickup location until ItemShipped time
1634 # TODO: When Item Agency and using holds, set this to requested copy's circ lib?
1635 $ahr->pickup_lib($conf->{hold}->{init_pickup_lib});
1636 $ahr->phone_notify(''); # TODO: set this based on usr prefs
1637 $ahr->email_notify(1); # TODO: set this based on usr prefs
7e2d6432
DW
1638 $ahr->frozen('t');
1639 my $resp = simplereq( CIRC(), 'open-ils.circ.holds.create', $authtoken, $ahr );
1640 my $e = new_editor( xact => 1, authtoken => $session{authtoken} );
1641 $ahr = $e->retrieve_action_hold_request($resp); # refresh from db
1642 $ahr->frozen('f');
1643 $e->update_action_hold_request($ahr);
1644 $e->commit;
1645 $U->storagereq( 'open-ils.storage.action.hold_request.copy_targeter', undef, $ahr->id );
5f517e0a
DW
1646
1647 #oils_event_die($resp);
1648 my $errors = "";
1649 if ( ref($resp) eq 'ARRAY' ) {
1650 ( $errors .= "error : " . $_->{textcode} ) for @$resp;
1651 return $errors;
1652 } elsif ( ref($resp) ne 'HASH' ) {
1653 return "Hold placed! hold_id = " . $resp . "\n";
1654 }
4cdc4f67
JS
1655}
1656
b5a02cf6 1657sub find_hold_on_copy {
3602ecf5
JG
1658 check_session_time();
1659
b5a02cf6 1660 my ( $copy_barcode ) = @_;
3602ecf5
JG
1661
1662 # start with barcode of item, find bib ID
1663 my $rec = bre_id_from_barcode($copy_barcode);
1664
c8f50ddf
JG
1665 return undef unless $rec;
1666
3602ecf5
JG
1667 # call for holds on that bib
1668 my $holds = holds_for_bre($rec);
1669
1670 # There should only be a single copy hold
1671 my $hold_id = @{$holds->{copy_holds}}[0];
1672
c8f50ddf
JG
1673 return undef unless $hold_id;
1674
3602ecf5
JG
1675 my $hold_details =
1676 OpenSRF::AppSession->create('open-ils.circ')
1677 ->request( 'open-ils.circ.hold.details.retrieve', $session{authtoken}, $hold_id )
1678 ->gather(1);
1679
1680 my $hold = $hold_details->{hold};
1681
c8f50ddf
JG
1682 return undef unless blessed($hold);
1683
b5a02cf6
JG
1684 return $hold;
1685}
1686
1687sub update_hold_pickup {
1688 check_session_time();
1689
1690 my ( $copy_barcode, $pickup_lib ) = @_;
1691
1692 my $hold = find_hold_on_copy($copy_barcode);
1693
7a3e1d58
JG
1694 # return if hold was not found
1695 return undef unless defined($hold) && blessed($hold);
1696
3602ecf5
JG
1697 $hold->pickup_lib($pickup_lib);
1698
b5a02cf6 1699 # update the copy hold with the new pickup lib information
3602ecf5
JG
1700 my $result =
1701 OpenSRF::AppSession->create('open-ils.circ')
1702 ->request( 'open-ils.circ.hold.update', $session{authtoken}, $hold )
1703 ->gather(1);
1704
1705 return $result;
1706}
1707
4cdc4f67
JS
1708# Flesh user information
1709# Arguments
1710# actor.usr.id
1711#
1712# Returns
1713# fieldmapped, fleshed user or
1714# event hash on error
1715sub flesh_user {
5f517e0a
DW
1716 check_session_time();
1717 my ($id) = @_;
1718 my $response =
1719 OpenSRF::AppSession->create('open-ils.actor')
1720 ->request( 'open-ils.actor.user.fleshed.retrieve',
1721 $session{'authtoken'}, $id,
1722 [ 'card', 'cards', 'standing_penalties', 'home_ou', 'profile' ] )
1723 ->gather(1);
1724 return $response;
4cdc4f67 1725}