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