Update pickup lib at ItemShipped time
[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
JG
688
689 my $pickup_lib;
690
691 if ($address) {
692 my $pickup_lib_map = load_map_file( $conf->{path}->{pickup_lib_map} );
693
694 if ($pickup_lib_map) {
695 $pickup_lib = lookup_pickup_lib($address, $pickup_lib_map);
696 }
697 }
698
699 if ($pickup_lib) {
700 update_hold_pickup($barcode, $pickup_lib);
701 }
702
5f517e0a 703 fail( $copy->{textcode} . " $barcode" ) unless ( blessed $copy);
ba01d5da 704 my $r = update_copy_shipped( $copy, $conf->{status}->{transit}, $visid ); # put copy into INN-Reach Transit status & modify barcode = Visid != tempIIIiNumber
5f517e0a
DW
705
706 my $hd = <<ITEMSHIPPED;
4cdc4f67
JS
707Content-type: text/xml
708
709
710<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
711<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
712 <ItemShippedResponse>
713 <ResponseHeader>
714 <FromAgencyId>
715 <UniqueAgencyId>
716 <Scheme>$faidScheme</Scheme>
717 <Value>$faidValue</Value>
718 </UniqueAgencyId>
719 </FromAgencyId>
720 <ToAgencyId>
721 <UniqueAgencyId>
722 <Scheme>$taidScheme</Scheme>
723 <Value>$taidValue</Value>
724 </UniqueAgencyId>
725 </ToAgencyId>
726 </ResponseHeader>
727 <UniqueItemId>
728 <ItemIdentifierValue datatype="string">$visid</ItemIdentifierValue>
729 </UniqueItemId>
730 </ItemShippedResponse>
4cdc4f67
JS
731</NCIPMessage>
732
733ITEMSHIPPED
734
5f517e0a
DW
735 logit( $hd, ( caller(0) )[3] );
736 staff_log( $taidValue, $faidValue,
0b0c5299 737 "ItemShipped -> Visible Id : " . $visid . " | Barcode : " . $barcode . " | Title : " . $title . " | Call Number : " . $callnumber );
4cdc4f67
JS
738}
739
740sub item_request {
5f517e0a
DW
741 my $faidSchemeX = $doc->findvalue('/NCIPMessage/ItemRequested/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
742 my $faidScheme = HTML::Entities::encode($faidSchemeX);
743 my $faidValue = $doc->find('/NCIPMessage/ItemRequested/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
744
745 my $taidSchemeX = $doc->findvalue('/NCIPMessage/ItemRequested/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
746 my $taidScheme = HTML::Entities::encode($taidSchemeX);
747 my $taidValue = $doc->find('/NCIPMessage/ItemRequested/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
748 my $UniqueItemIdAgencyIdValue = $doc->findvalue('/NCIPMessage/ItemRequested/UniqueItemId/UniqueAgencyId/Value');
749
0b0c5299
DW
750 # TODO: should we use the VisibleID for item agency variation of this method call
751
752 my $pid = $doc->findvalue('/NCIPMessage/ItemRequested/UniqueUserId/UserIdentifierValue');
5f517e0a
DW
753 my $barcode = $doc->findvalue('/NCIPMessage/ItemRequested/UniqueItemId/ItemIdentifierValue');
754 my $author = $doc->findvalue('/NCIPMessage/ItemRequested/ItemOptionalFields/BibliographicDescription/Author');
755 my $title = $doc->findvalue('/NCIPMessage/ItemRequested/ItemOptionalFields/BibliographicDescription/Title');
756 my $callnumber = $doc->findvalue('/NCIPMessage/ItemRequested/ItemOptionalFields/ItemDescription/CallNumber');
757 my $medium_type = $doc->find('/NCIPMessage/ItemRequested/ItemOptionalFields/BibliographicDescription/MediumType/Value');
758
759 my $r = "default error checking response";
760
a30f6f9e 761 if ( $barcode =~ /^i/ ) { # XXX EG is User Agency # create copy only if barcode is an iNUMBER
ba01d5da 762 my $copy_status_id = $conf->{status}->{loan_requested}; # INN-Reach Loan Requested - local configured status
5f517e0a 763 $barcode .= $faidValue;
a30f6f9e
DW
764 # 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
765 $r = create_copy( $title, $callnumber, $barcode, 0, $medium_type );
766 my $copy = copy_from_barcode($barcode);
767 my $r2 = place_simple_hold( $copy->id, $pid );
768 my $r3 = update_copy( $copy, $copy_status_id );
769 } else { # XXX EG is Item Agency
15d084ee
JG
770 unless ( $conf->{behavior}->{no_item_agency_holds} =~ m/^y/i ) {
771 # place hold for user UniqueUserId/UniqueAgencyId/Value = institution account
772 my $copy = copy_from_barcode($barcode);
773 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
774 $r = place_simple_hold( $copy->id, $pid2 );
775 my $r2 = update_copy( $copy, $conf->{status}->{hold} ); # put into INN-Reach Hold status
776 }
5f517e0a
DW
777 }
778
20bfa6cb
JG
779 # Avoid generating invalid XML responses by encoding title/author
780 # TODO: Move away from heredocs for generating XML
781 $title = HTML::Entities::encode($title);
782 $author = HTML::Entities::encode($author);
783
5f517e0a 784 my $hd = <<ITEMREQ;
4cdc4f67
JS
785Content-type: text/xml
786
787
788<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
789<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
790 <ItemRequestedResponse>
791 <ResponseHeader>
792 <FromAgencyId>
793 <UniqueAgencyId>
794 <Scheme>$faidScheme</Scheme>
795 <Value>$faidValue</Value>
796 </UniqueAgencyId>
797 </FromAgencyId>
798 <ToAgencyId>
799 <UniqueAgencyId>
800 <Scheme>$taidScheme</Scheme>
801 <Value>$taidValue</Value>
802 </UniqueAgencyId>
803 </ToAgencyId>
804 </ResponseHeader>
805 <UniqueUserId>
806 <UniqueAgencyId>
807 <Scheme datatype="string">$taidScheme</Scheme>
808 <Value datatype="string">$taidValue</Value>
809 </UniqueAgencyId>
0b0c5299 810 <UserIdentifierValue datatype="string">$pid</UserIdentifierValue>
5f517e0a
DW
811 </UniqueUserId>
812 <UniqueItemId>
813 <ItemIdentifierValue datatype="string">$barcode</ItemIdentifierValue>
814 </UniqueItemId>
815 <ItemOptionalFields>
816 <BibliographicDescription>
817 <Author datatype="string">$author</Author>
818 <Title datatype="string">$title</Title>
819 </BibliographicDescription>
820 <ItemDescription>
821 <CallNumber datatype="string">$callnumber</CallNumber>
822 </ItemDescription>
823 </ItemOptionalFields>
824 </ItemRequestedResponse>
4cdc4f67
JS
825</NCIPMessage>
826
827ITEMREQ
828
5f517e0a
DW
829 logit( $hd, ( caller(0) )[3] );
830 staff_log( $taidValue, $faidValue,
0b0c5299 831 "ItemRequested -> Barcode : " . $barcode . " | Title : " . $title . " | Call Number : " . $callnumber . " | Patronid :" . $pid );
4cdc4f67
JS
832}
833
1ac548f2
JS
834sub lookupUser {
835
5f517e0a
DW
836 my $faidScheme = $doc->findvalue('/NCIPMessage/LookupUser/InitiationHeader/FromAgencyId/UniqueAgencyId/Scheme');
837 $faidScheme = HTML::Entities::encode($faidScheme);
838 my $faidValue = $doc->find('/NCIPMessage/LookupUser/InitiationHeader/FromAgencyId/UniqueAgencyId/Value');
839 my $taidScheme = $doc->findvalue('/NCIPMessage/LookupUser/InitiationHeader/ToAgencyId/UniqueAgencyId/Scheme');
840 $taidScheme = HTML::Entities::encode($taidScheme);
841
842 my $taidValue = $doc->find('/NCIPMessage/LookupUser/InitiationHeader/ToAgencyId/UniqueAgencyId/Value');
843 my $id = $doc->findvalue('/NCIPMessage/LookupUser/VisibleUserId/VisibleUserIdentifier');
b9103e62
JG
844
845 my $uidValue;
846
847 if ($patron_id_type eq 'barcode') {
848 $uidValue = user_id_from_barcode($id);
849 } else {
850 $uidValue = $id;
851 }
5f517e0a
DW
852
853 if ( !defined($uidValue)
854 || ( ref($uidValue) && reftype($uidValue) eq 'HASH' ) )
855 {
856 do_lookup_user_error_stanza("PATRON_NOT_FOUND : $id");
857 die;
858 }
859
3bcdd8ae
JG
860 my ( $propername, $email, $good_until, $userpriv, $block_stanza ) =
861 ( "name here", "", "good until", "", "" ); # defaults
5f517e0a
DW
862
863 my $patron = flesh_user($uidValue);
864
865 #if (blessed($patron)) {
866 my $patron_ok = 1;
867 my @penalties = @{ $patron->standing_penalties };
868
869 if ( $patron->deleted eq 't' ) {
870 do_lookup_user_error_stanza("PATRON_DELETED : $uidValue");
871 die;
872 } elsif ( $patron->barred eq 't' ) {
873 do_lookup_user_error_stanza("PATRON_BARRED : $uidValue");
874 die;
875 } elsif ( $patron->active eq 'f' ) {
876 do_lookup_user_error_stanza("PATRON_INACTIVE : $uidValue");
877 die;
878 }
879
880 elsif ( $#penalties > -1 ) {
881
882# my $penalty;
883# foreach $penalty (@penalties) {
884# if (defined($penalty->standing_penalty->block_list)) {
885# my @block_list = split(/\|/, $penalty->standing_penalty->block_list);
886# foreach my $block (@block_list) {
887# foreach my $block_on (@$block_types) {
888# if ($block eq $block_on) {
889# $block_stanza .= "\n".$penalty->standing_penalty->name;
890# $patron_ok = 0;
891# }
892# last unless ($patron_ok);
893# }
894# last unless ($patron_ok);
895# }
896# }
897# }
898 $block_stanza = qq(
899 <BlockOrTrap>
900 <UniqueAgencyId>
901 <Scheme datatype="string">http://just.testing.now</Scheme>
902 <Value datatype="string">$faidValue</Value>
903 </UniqueAgencyId>
904 <BlockOrTrapType>
905 <Scheme datatype="string">http://just.testing.now</Scheme>
906 <Value datatype="string">Block Hold</Value>
907 </BlockOrTrapType>
908 </BlockOrTrap>);
909 }
910
5809d230 911 if ( defined( $patron->email ) && $conf->{behavior}->{omit_patron_email} !~ m/^y/i ) {
5f517e0a
DW
912 $email = qq(
913 <UserAddressInformation>
914 <ElectronicAddress>
915 <ElectronicAddressType>
916 <Scheme datatype="string">http://testing.now</Scheme>
917 <Value datatype="string">mailto</Value>
918 </ElectronicAddressType>
919 <ElectronicAddressData datatype="string">)
920 . HTML::Entities::encode( $patron->email )
921 . qq(</ElectronicAddressData>
922 </ElectronicAddress>
923 </UserAddressInformation>);
924 }
925
926 $propername = $patron->first_given_name . " " . $patron->family_name;
927 $good_until = $patron->expire_date || "unknown";
33a39c82 928 $userpriv = $patron->profile->name;
5f517e0a 929
3602ecf5 930 my $userpriv_map = load_map_file( $conf->{path}->{userpriv_map} );
47866eef
JG
931
932 if ($userpriv_map) {
45d455be 933 $userpriv = lookup_userpriv($userpriv, $userpriv_map);
47866eef
JG
934 }
935
5f517e0a
DW
936 #} else {
937 # do_lookup_user_error_stanza("PATRON_NOT_FOUND : $id");
938 # die;
939 #}
0b0c5299 940 my $uniqid = $patron->id;
b9103e62
JG
941 my $visid;
942 if ($patron_id_type eq 'barcode') {
943 $visid = $patron->card->barcode;
944 } else {
945 $visid = $patron->id;
946 }
5f517e0a 947 my $hd = <<LOOKUPUSERRESPONSE;
4cdc4f67
JS
948Content-type: text/xml
949
950
951<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
952<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
953 <LookupUserResponse>
954 <ResponseHeader>
955 <FromAgencyId>
956 <UniqueAgencyId>
957 <Scheme>$taidScheme</Scheme>
958 <Value>$taidValue</Value>
959 </UniqueAgencyId>
960 </FromAgencyId>
961 <ToAgencyId>
962 <UniqueAgencyId>
963 <Scheme>$faidScheme</Scheme>
964 <Value>$faidValue</Value>
965 </UniqueAgencyId>
966 </ToAgencyId>
967 </ResponseHeader>
968 <UniqueUserId>
969 <UniqueAgencyId>
970 <Scheme>$taidScheme</Scheme>
971 <Value>$taidValue</Value>
972 </UniqueAgencyId>
0b0c5299 973 <UserIdentifierValue>$uniqid</UserIdentifierValue>
5f517e0a
DW
974 </UniqueUserId>
975 <UserOptionalFields>
976 <VisibleUserId>
977 <VisibleUserIdentifierType>
978 <Scheme datatype="string">http://blah.com</Scheme>
979 <Value datatype="string">Barcode</Value>
980 </VisibleUserIdentifierType>
0b0c5299 981 <VisibleUserIdentifier datatype="string">$visid</VisibleUserIdentifier>
5f517e0a
DW
982 </VisibleUserId>
983 <NameInformation>
984 <PersonalNameInformation>
985 <UnstructuredPersonalUserName datatype="string">$propername</UnstructuredPersonalUserName>
986 </PersonalNameInformation>
987 </NameInformation>
988 <UserPrivilege>
989 <UniqueAgencyId>
990 <Scheme datatype="string">$faidScheme</Scheme>
991 <Value datatype="string">$faidValue</Value>
992 </UniqueAgencyId>
993 <AgencyUserPrivilegeType>
994 <Scheme datatype="string">http://testing.purposes.only</Scheme>
3bcdd8ae 995 <Value datatype="string">$userpriv</Value>
5f517e0a
DW
996 </AgencyUserPrivilegeType>
997 <ValidToDate datatype="string">$good_until</ValidToDate>
998 </UserPrivilege> $email $block_stanza
999 </UserOptionalFields>
4cdc4f67
JS
1000 </LookupUserResponse>
1001</NCIPMessage>
1002
1003LOOKUPUSERRESPONSE
1004
5f517e0a
DW
1005 logit( $hd, ( caller(0) )[3] );
1006 staff_log( $taidValue, $faidValue,
1007 "LookupUser -> Patron Barcode : "
1008 . $id
1009 . " | Patron Id : "
1010 . $uidValue
1011 . " | User Name : "
1012 . $propername
1013 . " | User Priv : "
1014 . $userpriv );
4cdc4f67
JS
1015}
1016
4cdc4f67 1017sub fail {
5f517e0a
DW
1018 my $error_msg =
1019 shift || "THIS IS THE DEFAULT / DO NOT HANG III NCIP RESP MSG";
1020 print "Content-type: text/xml\n\n";
4cdc4f67 1021
5f517e0a 1022 print <<ITEMREQ;
4cdc4f67
JS
1023<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
1024<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
1025 <ItemRequestedResponse>
1026 <ResponseHeader>
1027 <FromAgencyId>
1028 <UniqueAgencyId>
1029 <Scheme>http://136.181.125.166:6601/IRCIRCD?target=get_scheme_values&amp;scheme=UniqueAgencyId</Scheme>
1030 <Value></Value>
1031 </UniqueAgencyId>
1032 </FromAgencyId>
1033 <ToAgencyId>
1034 <UniqueAgencyId>
1035 <Scheme>http://136.181.125.166:6601/IRCIRCD?target=get_scheme_values&amp;scheme=UniqueAgencyId</Scheme>
1036 <Value>$error_msg</Value>
1037 </UniqueAgencyId>
1038 </ToAgencyId>
1039 </ResponseHeader>
1040 </ItemRequestedResponse>
4cdc4f67
JS
1041</NCIPMessage>
1042
1043ITEMREQ
6e6bdfe9 1044
601be0bb
JG
1045 # XXX: we should log FromAgencyId and ToAgencyId values here, but they are not available to the code at this point
1046 staff_log( '', '',
5f517e0a
DW
1047 ( ( caller(0) )[3] . " -> " . $error_msg ) );
1048 die;
4cdc4f67
JS
1049}
1050
1051sub do_lookup_user_error_stanza {
1052
601be0bb 1053 # XXX: we should include FromAgencyId and ToAgencyId values, but they are not available to the code at this point
5f517e0a
DW
1054 my $error = shift;
1055 my $hd = <<LOOKUPPROB;
4cdc4f67
JS
1056Content-type: text/xml
1057
1058
1059<!DOCTYPE NCIPMessage PUBLIC "-//NISO//NCIP DTD Version 1.0//EN" "http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
1060<NCIPMessage version="http://www.niso.org/ncip/v1_0/imp1/dtd/ncip_v1_0.dtd">
5f517e0a
DW
1061 <LookupUserResponse>
1062 <ResponseHeader>
1063 <FromAgencyId>
1064 <UniqueAgencyId>
601be0bb
JG
1065 <Scheme></Scheme>
1066 <Value></Value>
5f517e0a
DW
1067 </UniqueAgencyId>
1068 </FromAgencyId>
1069 <ToAgencyId>
1070 <UniqueAgencyId>
601be0bb
JG
1071 <Scheme></Scheme>
1072 <Value></Value>
5f517e0a
DW
1073 </UniqueAgencyId>
1074 </ToAgencyId>
1075 </ResponseHeader>
1076 <Problem>
1077 <ProcessingError>
1078 <ProcessingErrorType>
1079 <Scheme>http://www.niso.org/ncip/v1_0/schemes/processingerrortype/lookupuserprocessingerror.scm</Scheme>
1080 <Value>$error</Value>
1081 </ProcessingErrorType>
1082 <ProcessingErrorElement>
1083 <ElementName>AuthenticationInput</ElementName>
1084 </ProcessingErrorElement>
1085 </ProcessingError>
1086 </Problem>
1087 </LookupUserResponse>
4cdc4f67
JS
1088</NCIPMessage>
1089
1090LOOKUPPROB
1091
5f517e0a 1092 logit( $hd, ( caller(0) )[3] );
601be0bb
JG
1093 # XXX: we should log FromAgencyId and ToAgencyId values here, but they are not available to the code at this point
1094 staff_log( '', '', ( ( caller(0) )[3] . " -> " . $error ) );
5f517e0a 1095 die;
4cdc4f67
JS
1096}
1097
1098# Login to the OpenSRF system/Evergreen.
1099#
1100# Returns a hash with the authtoken, authtime, and expiration (time in
1101# seconds since 1/1/1970).
1102sub login {
1ac548f2
JS
1103
1104 # XXX: local opensrf core conf filename should be in config.
1105 # XXX: STAFF account with ncip service related permissions should be in config.
5f517e0a 1106 my $bootstrap = '/openils/conf/opensrf_core.xml';
f5e8d07e
JG
1107 my $uname = $conf->{auth}->{username};
1108 my $password = $conf->{auth}->{password};
5f517e0a
DW
1109
1110 # Bootstrap the client
1111 OpenSRF::System->bootstrap_client( config_file => $bootstrap );
1112 my $idl = OpenSRF::Utils::SettingsClient->new->config_value("IDL");
1113 Fieldmapper->import( IDL => $idl );
1114
1115 # Initialize CStoreEditor:
1116 OpenILS::Utils::CStoreEditor->init;
1117
1118 my $seed = OpenSRF::AppSession->create('open-ils.auth')
1119 ->request( 'open-ils.auth.authenticate.init', $uname )->gather(1);
1120
1121 return undef unless $seed;
1122
1123 my $response = OpenSRF::AppSession->create('open-ils.auth')->request(
1124 'open-ils.auth.authenticate.complete',
1125 {
1126 username => $uname,
1127 password => md5_hex( $seed . md5_hex($password) ),
1128 type => 'staff'
1129 }
1130 )->gather(1);
1131
1132 return undef unless $response;
1133
1134 my %result;
1135 $result{'authtoken'} = $response->{payload}->{authtoken};
1136 $result{'authtime'} = $response->{payload}->{authtime};
1137 $result{'expiration'} = time() + $result{'authtime'}
1138 if ( defined( $result{'authtime'} ) );
1139 return %result;
4cdc4f67
JS
1140}
1141
1142# Check the time versus the session expiration time and login again if
1143# the session has expired, consequently resetting the session
1144# paramters. We want to run this before doing anything that requires
1145# us to have a current session in OpenSRF.
1146#
1147# Arguments
1148# none
1149#
1150# Returns
1151# Nothing
1152sub check_session_time {
5f517e0a
DW
1153 if ( time() > $session{'expiration'} ) {
1154 %session = login();
1155 if ( !%session ) {
1156 die("Failed to reinitialize the session after expiration.");
1157 }
1158 }
4cdc4f67
JS
1159}
1160
1161# Retrieve the logged in user.
1162#
1163sub get_session {
5f517e0a
DW
1164 my $response =
1165 OpenSRF::AppSession->create('open-ils.auth')
1166 ->request( 'open-ils.auth.session.retrieve', $session{authtoken} )
1167 ->gather(1);
1168 return $response;
4cdc4f67
JS
1169}
1170
1171# Logout/destroy the OpenSRF session
1172#
1173# Argument is
1174# none
1175#
1176# Returns
1177# Does not return anything
1178sub logout {
5f517e0a
DW
1179 if ( time() < $session{'expiration'} ) {
1180 my $response =
1181 OpenSRF::AppSession->create('open-ils.auth')
1182 ->request( 'open-ils.auth.session.delete', $session{authtoken} )
1183 ->gather(1);
1184 if ($response) {
1185
1186 # strong.silent.success
1187 exit(0);
1188 } else {
1189 fail("Logout unsuccessful. Good-bye, anyway.");
1190 }
1191 }
4cdc4f67
JS
1192}
1193
1194sub update_copy {
5f517e0a
DW
1195 check_session_time();
1196 my ( $copy, $status_id ) = @_;
1197 my $e = new_editor( authtoken => $session{authtoken} );
1198 return $e->event->{textcode} unless ( $e->checkauth );
1199 $e->xact_begin;
1200 $copy->status($status_id);
1201 return $e->event unless $e->update_asset_copy($copy);
1202 $e->commit;
1203 return 'SUCCESS';
4cdc4f67
JS
1204}
1205
1206# my paranoia re barcode on shipped items using visid for unique value
1207sub update_copy_shipped {
5f517e0a
DW
1208 check_session_time();
1209 my ( $copy, $status_id, $barcode ) = @_;
1210 my $e = new_editor( authtoken => $session{authtoken} );
1211 return $e->event->{textcode} unless ( $e->checkauth );
1212 $e->xact_begin;
1213 $copy->status($status_id);
1214 $copy->barcode($barcode);
1215 return $e->event unless $e->update_asset_copy($copy);
1216 $e->commit;
1217 return 'SUCCESS';
4cdc4f67
JS
1218}
1219
1220# Delete a copy
1221#
1222# Argument
1223# Fieldmapper asset.copy object
1224#
1225# Returns
1226# "SUCCESS" on success
1227# Event textcode if an error occurs
1228sub delete_copy {
5f517e0a
DW
1229 check_session_time();
1230 my ($copy) = @_;
1231
1232 my $e = new_editor( authtoken => $session{authtoken} );
1233 return $e->event->{textcode} unless ( $e->checkauth );
1234
1235 # Get the calnumber
1236 my $vol = $e->retrieve_asset_call_number( $copy->call_number );
1237 return $e->event->{textcode} unless ($vol);
1238
1239 # Get the biblio.record_entry
1240 my $bre = $e->retrieve_biblio_record_entry( $vol->record );
1241 return $e->event->{textcode} unless ($bre);
1242
1243 # Delete everything in a transaction and rollback if anything fails.
0b0c5299 1244 # TODO: I think there is a utility function which handles all this
5f517e0a
DW
1245 $e->xact_begin;
1246 my $r; # To hold results of editor calls
1247 $r = $e->delete_asset_copy($copy);
1248 unless ($r) {
1249 my $lval = $e->event->{textcode};
1250 $e->rollback;
1251 return $lval;
1252 }
1253 my $list =
1254 $e->search_asset_copy( { call_number => $vol->id, deleted => 'f' } );
1255 unless (@$list) {
1256 $r = $e->delete_asset_call_number($vol);
1257 unless ($r) {
1258 my $lval = $e->event->{textcode};
1259 $e->rollback;
1260 return $lval;
1261 }
0b0c5299 1262 $list = $e->search_asset_call_number( { record => $bre->id, deleted => 'f' } );
5f517e0a 1263 unless (@$list) {
0b0c5299 1264 $r = $e->delete_biblio_record_entry($bre);
5f517e0a
DW
1265 unless ($r) {
1266 my $lval = $e->event->{textcode};
1267 $e->rollback;
1268 return $lval;
1269 }
1270 }
1271 }
1272 $e->commit;
1273 return 'SUCCESS';
4cdc4f67
JS
1274}
1275
1276# Get asset.copy from asset.copy.barcode.
1277# Arguments
1278# copy barcode
1279#
1280# Returns
1281# asset.copy fieldmaper object
1282# or hash on error
1283sub copy_from_barcode {
5f517e0a
DW
1284 check_session_time();
1285 my ($barcode) = @_;
1286 my $response =
1287 OpenSRF::AppSession->create('open-ils.search')
1288 ->request( 'open-ils.search.asset.copy.find_by_barcode', $barcode )
1289 ->gather(1);
1290 return $response;
4cdc4f67
JS
1291}
1292
1293sub locid_from_barcode {
5f517e0a
DW
1294 my ($barcode) = @_;
1295 my $response =
1296 OpenSRF::AppSession->create('open-ils.search')
1297 ->request( 'open-ils.search.biblio.find_by_barcode', $barcode )
1298 ->gather(1);
1299 return $response->{ids}[0];
4cdc4f67
JS
1300}
1301
3602ecf5
JG
1302sub bre_id_from_barcode {
1303 check_session_time();
1304 my ($barcode) = @_;
1305 my $response =
1306 OpenSRF::AppSession->create('open-ils.search')
1307 ->request( 'open-ils.search.bib_id.by_barcode', $barcode )
1308 ->gather(1);
1309 return $response;
1310}
1311
1312sub holds_for_bre {
1313 check_session_time();
1314 my ($bre_id) = @_;
1315 my $response =
1316 OpenSRF::AppSession->create('open-ils.circ')
1317 ->request( 'open-ils.circ.holds.retrieve_all_from_title', $session{authtoken}, $bre_id )
1318 ->gather(1);
1319 return $response;
1320
1321}
1322
4cdc4f67
JS
1323# Convert a MARC::Record to XML for Evergreen
1324#
6e6bdfe9 1325# Copied from Dyrcona's issa framework which copied
1ac548f2 1326# it from MVLC's Safari Load program which copied it
4cdc4f67
JS
1327# from some code in the Open-ILS example import scripts.
1328#
1329# Argument
1330# A MARC::Record object
1331#
1332# Returns
1333# String with XML for the MARC::Record as Evergreen likes it
1334sub convert2marcxml {
5f517e0a
DW
1335 my $input = shift;
1336 ( my $xml = $input->as_xml_record() ) =~ s/\n//sog;
1337 $xml =~ s/^<\?xml.+\?\s*>//go;
1338 $xml =~ s/>\s+</></go;
1339 $xml =~ s/\p{Cc}//go;
0b0c5299 1340 $xml = $U->entityize($xml);
5f517e0a
DW
1341 $xml =~ s/[\x00-\x1f]//go;
1342 return $xml;
4cdc4f67
JS
1343}
1344
1345# Create a copy and marc record
1346#
1347# Arguments
1348# title
1349# call number
1350# copy barcode
1351#
1352# Returns
1353# bib id on succes
1354# event textcode on failure
1355sub create_copy {
5f517e0a
DW
1356 check_session_time();
1357 my ( $title, $callnumber, $barcode, $copy_status_id, $medium_type ) = @_;
4cdc4f67 1358
5f517e0a
DW
1359 my $e = new_editor( authtoken => $session{authtoken} );
1360 return $e->event->{textcode} unless ( $e->checkauth );
1ac548f2 1361
5f517e0a
DW
1362 my $r = $e->allowed( [ 'CREATE_COPY', 'CREATE_MARC', 'CREATE_VOLUME' ] );
1363 if ( ref($r) eq 'HASH' ) {
1364 return $r->{textcode} . ' ' . $r->{ilsperm};
1365 }
4cdc4f67 1366
5f517e0a
DW
1367 # Check if the barcode exists in asset.copy and bail if it does.
1368 my $list = $e->search_asset_copy( { deleted => 'f', barcode => $barcode } );
1369 if (@$list) {
6e6bdfe9 1370# in the future, can we update it, if it exists and only if it is an INN-Reach status item ?
5f517e0a
DW
1371 $e->finish;
1372 fail( 'BARCODE_EXISTS ! Barcode : ' . $barcode );
1373 die;
1374 }
1375
1376 # Create MARC record
1377 my $record = MARC::Record->new();
1378 $record->encoding('UTF-8');
1379 $record->leader('00881nam a2200193 4500');
1380 my $datespec = strftime( "%Y%m%d%H%M%S.0", localtime );
1381 my @fields = ();
1382 push( @fields, MARC::Field->new( '005', $datespec ) );
1383 push( @fields, MARC::Field->new( '082', '0', '4', 'a' => $callnumber ) );
1384 push( @fields, MARC::Field->new( '245', '0', '0', 'a' => $title ) );
1385 $record->append_fields(@fields);
1386
1387 # Convert the record to XML
1388 my $xml = convert2marcxml($record);
1389
1390 my $bre =
1391 OpenSRF::AppSession->create('open-ils.cat')
1392 ->request( 'open-ils.cat.biblio.record.xml.import',
1393 $session{authtoken}, $xml, 'System Local', 1 )->gather(1);
1394 return $bre->{textcode} if ( ref($bre) eq 'HASH' );
1395
1396 # Create volume record
1397 my $vol =
1398 OpenSRF::AppSession->create('open-ils.cat')
ba01d5da 1399 ->request( 'open-ils.cat.call_number.find_or_create', $session{authtoken}, $callnumber, $bre->id, $conf->{volume}->{owning_lib} )
0b0c5299 1400 ->gather(1);
5f517e0a
DW
1401 return $vol->{textcode} if ( $vol->{textcode} );
1402
1403 # Retrieve the user
1404 my $user = get_session;
1405
1406 # Create copy record
1407 my $copy = Fieldmapper::asset::copy->new();
0b0c5299
DW
1408 # XXX CUSTOMIZATION NEEDED XXX
1409 # You will need to either create a circ mod for every expected medium type,
1410 # OR you should create a single circ mod for all requests from the external
1411 # system.
1412 # Adjust these lines as needed.
1413 # $copy->circ_modifier(qq($medium_type)); # XXX CUSTOMIZATION NEEDED XXX
1414 # OR
ba01d5da 1415 $copy->circ_modifier($conf->{copy}->{circ_modifier});
5f517e0a
DW
1416 $copy->barcode($barcode);
1417 $copy->call_number( $vol->{acn_id} );
ba01d5da 1418 $copy->circ_lib($conf->{copy}->{circ_lib});
5f517e0a
DW
1419 $copy->circulate('t');
1420 $copy->holdable('t');
1421 $copy->opac_visible('t');
1422 $copy->deleted('f');
1423 $copy->fine_level(2);
1424 $copy->loan_duration(2);
ba01d5da 1425 $copy->location($conf->{copy}->{location});
5f517e0a
DW
1426 $copy->status($copy_status_id);
1427 $copy->editor('1');
1428 $copy->creator('1');
1429
5f517e0a
DW
1430 $e->xact_begin;
1431 $copy = $e->create_asset_copy($copy);
1432
5f517e0a
DW
1433 $e->commit;
1434 return $e->event->{textcode} unless ($r);
1435 return 'SUCCESS';
4cdc4f67
JS
1436}
1437
1438# Checkout a copy to a patron
1439#
1440# Arguments
1441# copy barcode
1442# patron barcode
1443#
1444# Returns
1445# textcode of the OSRF response.
1ac548f2 1446sub checkout {
5f517e0a
DW
1447 check_session_time();
1448 my ( $copy_barcode, $patron_barcode, $due_date ) = @_;
1449
1450 # Check for copy:
1451 my $copy = copy_from_barcode($copy_barcode);
1452 unless ( defined($copy) && blessed($copy) ) {
1453 return 'COPY_BARCODE_NOT_FOUND : ' . $copy_barcode;
1454 }
1455
1456 # Check for user
b9103e62
JG
1457 my $uid;
1458 if ($patron_id_type eq 'barcode') {
1459 $uid = user_id_from_barcode($patron_barcode);
1460 } else {
1461 $uid = $patron_barcode;
1462 }
5f517e0a
DW
1463 return 'PATRON_BARCODE_NOT_FOUND : ' . $patron_barcode if ( ref($uid) );
1464
1465 my $response = OpenSRF::AppSession->create('open-ils.circ')->request(
1466 'open-ils.circ.checkout.full.override',
1467 $session{authtoken},
1468 {
1469 copy_barcode => $copy_barcode,
1470 patron_id => $uid,
1471 due_date => $due_date
1472 }
1473 )->gather(1);
1474 return $response->{textcode};
4cdc4f67
JS
1475}
1476
1ac548f2 1477sub renewal {
5f517e0a
DW
1478 check_session_time();
1479 my ( $copy_barcode, $due_date ) = @_;
1480
1481 # Check for copy:
1482 my $copy = copy_from_barcode($copy_barcode);
1483 unless ( defined($copy) && blessed($copy) ) {
1484 return 'COPY_BARCODE_NOT_FOUND : ' . $copy_barcode;
1485 }
1486
1487 my $response = OpenSRF::AppSession->create('open-ils.circ')->request(
1488 'open-ils.circ.renew.override',
1489 $session{authtoken},
1490 {
1491 copy_barcode => $copy_barcode,
1492 due_date => $due_date
1493 }
1494 )->gather(1);
1495 return $response->{textcode};
4cdc4f67
JS
1496}
1497
1ac548f2 1498# Check a copy in
4cdc4f67
JS
1499#
1500# Arguments
1501# copy barcode
4cdc4f67
JS
1502#
1503# Returns
1504# "SUCCESS" on success
1505# textcode of a failed OSRF request
0b0c5299 1506# 'COPY_NOT_CHECKED_OUT' when the copy is not checked out
6e6bdfe9 1507
1ac548f2 1508sub checkin {
5f517e0a
DW
1509 check_session_time();
1510 my ($barcode) = @_;
1511
1512 my $copy = copy_from_barcode($barcode);
1513 return $copy->{textcode} unless ( blessed $copy);
1514
1515 return ("COPY_NOT_CHECKED_OUT $barcode")
1516 unless ( $copy->status == OILS_COPY_STATUS_CHECKED_OUT );
1517
1518 my $e = new_editor( authtoken => $session{authtoken} );
1519 return $e->event->{textcode} unless ( $e->checkauth );
1520
1521 my $circ = $e->search_action_circulation(
1522 [ { target_copy => $copy->id, xact_finish => undef } ] )->[0];
1523 my $r =
1524 OpenSRF::AppSession->create('open-ils.circ')
1525 ->request( 'open-ils.circ.checkin.override',
1526 $session{authtoken}, { force => 1, copy_id => $copy->id } )->gather(1);
1527 return 'SUCCESS' if ( $r->{textcode} eq 'ROUTE_ITEM' );
1528 return $r->{textcode};
4cdc4f67
JS
1529}
1530
1531# Get actor.usr.id from barcode.
1532# Arguments
1533# patron barcode
1534#
1535# Returns
1536# actor.usr.id
1537# or hash on error
1538sub user_id_from_barcode {
5f517e0a
DW
1539 check_session_time();
1540 my ($barcode) = @_;
4cdc4f67 1541
5f517e0a 1542 my $response;
4cdc4f67 1543
5f517e0a
DW
1544 my $e = new_editor( authtoken => $session{authtoken} );
1545 return $response unless ( $e->checkauth );
4cdc4f67 1546
5f517e0a
DW
1547 my $card = $e->search_actor_card( { barcode => $barcode, active => 't' } );
1548 return $e->event unless ($card);
4cdc4f67 1549
5f517e0a 1550 $response = $card->[0]->usr if (@$card);
4cdc4f67 1551
5f517e0a 1552 $e->finish;
4cdc4f67 1553
5f517e0a 1554 return $response;
4cdc4f67
JS
1555}
1556
6e6bdfe9 1557# Place a simple hold for a patron.
4cdc4f67
JS
1558#
1559# Arguments
1560# Target object appropriate for type of hold
1561# Patron for whom the hold is place
1562#
1563# Returns
1564# "SUCCESS" on success
1565# textcode of a failed OSRF request
1566# "HOLD_TYPE_NOT_SUPPORTED" if the hold type is not supported
1567# (Currently only support 'T' and 'C')
1568
1ac548f2 1569# simple hold should be removed and full holds sub should be used instead - pragmatic solution only
5082b884 1570
4cdc4f67 1571sub place_simple_hold {
5f517e0a 1572 check_session_time();
1ac548f2 1573
5f517e0a 1574 #my ($type, $target, $patron, $pickup_ou) = @_;
7e2d6432 1575 my ( $target, $patron_id ) = @_;
1ac548f2 1576
ba01d5da 1577 require $conf->{path}->{oils_header};
5f517e0a 1578 use vars qw/ $apputils $memcache $user $authtoken $authtime /;
1ac548f2 1579
ba01d5da 1580 osrf_connect( $conf->{path}->{opensrf_core} );
f5e8d07e 1581 oils_login( $conf->{auth}->{username}, $conf->{auth}->{password} );
7e2d6432
DW
1582 my $ahr = Fieldmapper::action::hold_request->new();
1583 $ahr->hold_type('C');
1584 # 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.
1585 $ahr->target($target);
1586 $ahr->usr($patron_id);
ba01d5da
JG
1587 $ahr->requestor($conf->{hold}->{requestor});
1588 # NOTE: When User Agency, we don't know the pickup location until ItemShipped time
1589 # TODO: When Item Agency and using holds, set this to requested copy's circ lib?
1590 $ahr->pickup_lib($conf->{hold}->{init_pickup_lib});
1591 $ahr->phone_notify(''); # TODO: set this based on usr prefs
1592 $ahr->email_notify(1); # TODO: set this based on usr prefs
7e2d6432
DW
1593 $ahr->frozen('t');
1594 my $resp = simplereq( CIRC(), 'open-ils.circ.holds.create', $authtoken, $ahr );
1595 my $e = new_editor( xact => 1, authtoken => $session{authtoken} );
1596 $ahr = $e->retrieve_action_hold_request($resp); # refresh from db
1597 $ahr->frozen('f');
1598 $e->update_action_hold_request($ahr);
1599 $e->commit;
1600 $U->storagereq( 'open-ils.storage.action.hold_request.copy_targeter', undef, $ahr->id );
5f517e0a
DW
1601
1602 #oils_event_die($resp);
1603 my $errors = "";
1604 if ( ref($resp) eq 'ARRAY' ) {
1605 ( $errors .= "error : " . $_->{textcode} ) for @$resp;
1606 return $errors;
1607 } elsif ( ref($resp) ne 'HASH' ) {
1608 return "Hold placed! hold_id = " . $resp . "\n";
1609 }
4cdc4f67
JS
1610}
1611
3602ecf5
JG
1612sub update_hold_pickup {
1613 check_session_time();
1614
1615 my ( $copy_barcode, $pickup_lib ) = @_;
1616
1617 # start with barcode of item, find bib ID
1618 my $rec = bre_id_from_barcode($copy_barcode);
1619
1620 # call for holds on that bib
1621 my $holds = holds_for_bre($rec);
1622
1623 # There should only be a single copy hold
1624 my $hold_id = @{$holds->{copy_holds}}[0];
1625
1626 # update the copy hold with the new pickup lib information
1627 my $hold_details =
1628 OpenSRF::AppSession->create('open-ils.circ')
1629 ->request( 'open-ils.circ.hold.details.retrieve', $session{authtoken}, $hold_id )
1630 ->gather(1);
1631
1632 my $hold = $hold_details->{hold};
1633
1634 $hold->pickup_lib($pickup_lib);
1635
1636 my $result =
1637 OpenSRF::AppSession->create('open-ils.circ')
1638 ->request( 'open-ils.circ.hold.update', $session{authtoken}, $hold )
1639 ->gather(1);
1640
1641 return $result;
1642}
1643
4cdc4f67
JS
1644# Flesh user information
1645# Arguments
1646# actor.usr.id
1647#
1648# Returns
1649# fieldmapped, fleshed user or
1650# event hash on error
1651sub flesh_user {
5f517e0a
DW
1652 check_session_time();
1653 my ($id) = @_;
1654 my $response =
1655 OpenSRF::AppSession->create('open-ils.actor')
1656 ->request( 'open-ils.actor.user.fleshed.retrieve',
1657 $session{'authtoken'}, $id,
1658 [ 'card', 'cards', 'standing_penalties', 'home_ou', 'profile' ] )
1659 ->gather(1);
1660 return $response;
4cdc4f67 1661}