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