Add option to use patron id instead of barcode
[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');
0b0c5299 562 my $patron_barcode = "zyyyy"; # XXX: CUSTOMIZATION NEEDED XXX institution/eg_as_item_agency user lookup here
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}