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