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