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