]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
Fix failing SIP unit test
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / SIP.pm
1 #
2 # ILS.pm: Test ILS interface module
3 #
4
5 package OpenILS::SIP;
6 use warnings; use strict;
7
8 use Sys::Syslog qw(syslog);
9 use Time::HiRes q/time/;
10
11 use OpenILS::SIP::Item;
12 use OpenILS::SIP::Patron;
13 use OpenILS::SIP::Transaction;
14 use OpenILS::SIP::Transaction::Checkout;
15 use OpenILS::SIP::Transaction::Checkin;
16 use OpenILS::SIP::Transaction::Renew;
17 use OpenILS::SIP::Transaction::FeePayment;
18
19 use OpenSRF::System;
20 use OpenSRF::AppSession;
21 use OpenILS::Utils::Fieldmapper;
22 use OpenSRF::Utils::SettingsClient;
23 use OpenILS::Application::AppUtils;
24 use OpenSRF::Utils qw/:datetime/;
25 use DateTime::Format::ISO8601;
26 use Encode;
27 use Unicode::Normalize;
28
29 my $U = 'OpenILS::Application::AppUtils';
30
31 my $editor;
32 my $config;
33 my $login_account;
34 my $target_encoding;    # FIXME: this is configured at the institution level. 
35
36 use Digest::MD5 qw(md5_hex);
37
38 # Copied from Sip::Constants
39 use constant {
40     SIP_DATETIME => "%Y%m%d    %H%M%S",
41 };
42
43 sub new {
44     my ($class, $institution, $login) = @_;
45     my $type = ref($class) || $class;
46     my $self = {};
47
48     $self->{login} = $login_account = $login;
49
50     $config = $institution;
51     syslog("LOG_DEBUG", "OILS: new ILS '%s'", $institution->{id});
52     $self->{institution} = $institution;
53
54     my $bsconfig     = $institution->{implementation_config}->{bootstrap};
55     $target_encoding = $institution->{implementation_config}->{encoding} || 'ascii';
56
57     syslog('LOG_DEBUG', "OILS: loading bootstrap config: $bsconfig");
58
59     # ingress will persist throughout
60     OpenSRF::AppSession->ingress('sip2');
61     
62     local $/ = "\n";    # why?
63     OpenSRF::System->bootstrap_client(config_file => $bsconfig);
64     syslog('LOG_DEBUG', "OILS: bootstrap loaded..");
65
66     $self->{osrf_config} = OpenSRF::Utils::SettingsClient->new;
67
68     Fieldmapper->import($self->{osrf_config}->config_value('IDL'));
69
70     bless( $self, $type );
71
72     return undef unless 
73         $self->login( $login->{id}, $login->{password} );
74
75     return $self;
76 }
77
78 sub fetch_session {
79     my $self = shift;
80
81     my $ses = $U->simplereq( 
82         'open-ils.auth',
83         'open-ils.auth.session.retrieve',  $self->{authtoken});
84
85     return undef if $U->event_code($ses); # auth timed out
86     return $self->{login_session} = $ses;
87 }
88
89 sub verify_session {
90     my $self = shift;
91
92     return 1 if $self->fetch_session;
93
94     syslog('LOG_INFO', "OILS: Logging back after session timeout as user ".$self->{login}->{id});
95     return $self->login( $self->{login}->{id}, $self->{login}->{password} );
96 }
97
98 sub editor {
99     return $editor = make_editor();
100 }
101
102 sub config {
103     return $config;
104 }
105 sub login_account {
106     return $login_account;
107 }
108
109 sub get_option_value {
110     my($self, $option) = @_;
111     my $ops = $config->{implementation_config}->{options}->{option};
112     $ops = [$ops] unless ref $ops eq 'ARRAY';
113     my @vals = grep { $_->{name} eq $option } @$ops;
114     return @vals ? $vals[0]->{value} : undef;
115 }
116
117
118 # Creates the global editor object
119 my $cstore_init = 1; # call init on first use
120 sub make_editor {
121     OpenILS::Utils::CStoreEditor::init() if $cstore_init;
122     $cstore_init = 0;
123     return OpenILS::Utils::CStoreEditor->new;
124 }
125
126 =head2 clean_text(scalar)
127
128 Evergreen uses the UTF8 encoding for everything from the database up. Perl
129 doesn't know this, however, so we have to convince it to treat our UTF8 strings
130 as UTF8 strings. This may enable OpenNCIP to correctly calculate the checksums
131 for UTF8 text for SIP clients that support such modern options.
132
133 The target encoding is set in the <encoding> element of the SIPServer.pm
134 configuration file.
135
136 =cut
137
138 sub clean_text {
139     my $text = shift || '';
140
141     # Convert our incoming UTF8 data into Perl's internal string format
142
143     # Also convert to Normalization Form D, as the ASCII, iso-8859-1,
144     # and latin-1 encodings (at least) require this to substitute
145     # characters rather than simply returning a string truncated
146     # after the first non-ASCII character
147     $text = NFD(decode_utf8($text));
148
149     if ($target_encoding eq 'ascii') {
150
151         # Try to maintain a reasonable version of the content by
152         # stripping diacritics from the text, given that the SIP client
153         # wants just plain ASCII. This is the base requirement according
154         # to the SIP2 specification.
155
156         # Stripping the combining characters converts ""béè♁ts"
157         # into "bee?ts" instead of "b???ts" - better, eh?
158         $text =~ s/\pM+//og;
159     }
160
161     # Characters that cannot be represented in the target encoding will
162     # generally be replaced with a question mark (?) character.
163     $text = encode($target_encoding, $text);
164
165     return $text;
166 }
167
168 my %org_sn_cache;
169 sub shortname_from_id {
170     my $id = shift or return;
171     return $id->shortname if ref $id;
172     return $org_sn_cache{$id} if $org_sn_cache{$id};
173     return $org_sn_cache{$id} = editor()->retrieve_actor_org_unit($id)->shortname;
174 }
175 sub patron_barcode_from_id {
176     my $id = shift or return;
177     return editor()->search_actor_card({ usr => $id, active => 't' })->[0]->barcode;
178 }
179
180 sub format_date {
181     my $class = shift;
182     my $date = shift;
183     my $type = shift || 'dob';
184
185     return "" unless $date;
186
187     my $dt = DateTime::Format::ISO8601->new->
188         parse_datetime(OpenSRF::Utils::cleanse_ISO8601($date));
189     my @time = localtime($dt->epoch);
190
191     my $year   = $time[5]+1900;
192     my $mon    = $time[4]+1;
193     my $day    = $time[3];
194     my $hour   = $time[2];
195     my $minute = $time[1];
196     my $second = $time[0];
197   
198     $date = sprintf("%04d%02d%02d", $year, $mon, $day);
199
200     # Due dates need hyphen separators and time of day as well
201     if ($type eq 'due') {
202
203         my $use_sdf = $class->get_option_value('use_sip_date_format') | '';
204
205         if ($use_sdf =~ /true/i) {
206             $date = $dt->strftime(SIP_DATETIME);
207
208         } else {
209             $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", 
210                 $year, $mon, $day, $hour, $minute, $second);
211         }
212     }
213
214     syslog('LOG_DEBUG', "OILS: formatted date [type=$type]: $date");
215     return $date;
216 }
217
218
219
220 sub login {
221     my( $self, $username, $password ) = @_;
222     syslog('LOG_DEBUG', "OILS: Logging in with username $username");
223
224     my $seed = $U->simplereq( 
225         'open-ils.auth',
226         'open-ils.auth.authenticate.init', $username );
227
228     my $response = $U->simplereq(
229         'open-ils.auth', 
230         'open-ils.auth.authenticate.complete', 
231         {    
232             username => $username, 
233             password => md5_hex($seed . md5_hex($password)), 
234             type     => 'opac',
235         }
236     );
237
238     if( my $code = $U->event_code($response) ) {
239         my $txt = $response->{textcode};
240         syslog('LOG_WARNING', "OILS: Login failed for $username.  $txt:$code");
241         return undef;
242     }
243
244     my $key = $response->{payload}->{authtoken};
245     syslog('LOG_INFO', "OILS: Login succeeded for $username : authkey = $key");
246
247     $self->fetch_session; # to cache the login
248
249     return $self->{authtoken} = $key;
250 }
251
252 #
253 # find_patron($barcode);
254 # find_patron(barcode => $barcode);   # same as above
255 # find_patron(usr => $id);
256
257 sub find_patron {
258     my $self = shift;
259     my $key  =  (@_ > 1) ? shift : 'barcode';  # if we have multiple args, the first is the key index (default barcode)
260     my $patron_id = shift;
261
262     return OpenILS::SIP::Patron->new($key => $patron_id, authtoken => $self->{authtoken}, @_);
263 }
264
265
266 sub find_item {
267     my $self = shift;
268     return OpenILS::SIP::Item->new(@_);
269 }
270
271
272 sub institution {
273     my $self = shift;
274     return $self->{institution}->{id};  # consider making this return the whole institution
275 }
276
277 sub institution_id {
278     my $self = shift;
279     return $self->{institution}->{id};  # then use this for just the ID
280 }
281
282 sub supports {
283     my ($self, $op) = @_;
284     my ($i) = grep { $_->{name} eq $op }  
285         @{$config->{implementation_config}->{supports}->{item}};
286     return to_bool($i->{value});
287 }
288
289 sub check_inst_id {
290     my ($self, $id, $whence) = @_;
291     if ($id ne $self->{institution}->{id}) {
292         syslog("LOG_WARNING", "OILS: %s: received institution '%s', expected '%s'", $whence, $id, $self->{institution}->{id});
293         # Just an FYI check, we don't expect the user to change location from that in SIPconfig.xml
294     }
295 }
296
297
298 sub to_bool {
299     my $bool = shift;
300     # If it's defined, and matches a true sort of string, or is
301     # a non-zero number, then it's true.
302     defined($bool) or return;                   # false
303     ($bool =~ /true|y|yes/i) and return 1;      # true
304     return ($bool =~ /^\d+$/ and $bool != 0);   # true for non-zero numbers, false otherwise
305 }
306
307 sub checkout_ok {
308     return to_bool($config->{policy}->{checkout});
309 }
310
311 sub checkin_ok {
312     return to_bool($config->{policy}->{checkin});
313 }
314
315 sub renew_ok {
316     return to_bool($config->{policy}->{renewal});
317 }
318
319 sub status_update_ok {
320     return to_bool($config->{policy}->{status_update});
321 }
322
323 sub offline_ok {
324     return to_bool($config->{policy}->{offline});
325 }
326
327
328
329 ##
330 ## Checkout(patron_id, item_id, sc_renew, fee_ack):
331 ##    patron_id & item_id are the identifiers send by the terminal
332 ##    sc_renew is the renewal policy configured on the terminal
333 ## returns a status opject that can be queried for the various bits
334 ## of information that the protocol (SIP or NCIP) needs to generate
335 ## the response.
336 ##    fee_ack is the fee_acknowledged field (BO) sent from the sc
337 ## when doing chargeable loans.
338 ##
339
340 sub checkout {
341     my ($self, $patron_id, $item_id, $sc_renew, $fee_ack) = @_;
342     # In order to allow renewals the selfcheck AND the config have to say they are allowed
343     $sc_renew = (chr($sc_renew) eq 'Y' && $self->renew_ok());
344
345     $self->verify_session;
346
347     syslog('LOG_DEBUG', "OILS: OpenILS::Checkout attempt: patron=$patron_id, item=$item_id");
348
349     my $xact   = OpenILS::SIP::Transaction::Checkout->new( authtoken => $self->{authtoken} );
350     my $patron = $self->find_patron($patron_id);
351     my $item   = $self->find_item($item_id);
352
353     $xact->patron($patron);
354     $xact->item($item);
355
356     if (!$patron) {
357         $xact->screen_msg("Invalid Patron Barcode '$patron_id'");
358         return $xact;
359     }
360
361     if (!$patron->charge_ok) {
362         $xact->screen_msg("Patron Blocked");
363         return $xact;
364     }
365
366     if( !$item ) {
367         $xact->screen_msg("Invalid Item Barcode: '$item_id'");
368         return $xact;
369     }
370
371     syslog('LOG_DEBUG', "OILS: OpenILS::Checkout data loaded OK, checking out...");
372
373     if ($item->{patron} && ($item->{patron} eq $patron_id)) {
374         $xact->renew_ok(1); # So that accept/reject responses have the correct value later
375         if($sc_renew) {
376             syslog('LOG_INFO', "OILS: OpenILS::Checkout data loaded OK, doing renew...");
377         } else {
378             syslog('LOG_INFO', "OILS: OpenILS::Checkout appears to be renew, but renewal disallowed...");
379             $xact->screen_msg("Renewals not permitted");
380             $xact->ok(0);
381             return $xact; # Don't attempt later
382         }
383     } elsif ($item->{patron} && ($item->{patron} ne $patron_id)) {
384         # I can't deal with this right now
385         # XXX check in then check out?
386         $xact->screen_msg("Item checked out to another patron");
387         $xact->ok(0);
388         return $xact; # Don't wipe out the screen message later
389     } else {
390         $sc_renew = 0;
391     } 
392
393     # Check for fee and $fee_ack. If there is a fee, and $fee_ack
394     # is 'Y', we proceed, otherwise we reject the checkout.
395     if ($item->fee > 0.0) {
396         $xact->fee_amount($item->fee);
397         $xact->sip_fee_type($item->sip_fee_type);
398         $xact->sip_currency($item->fee_currency);
399         if ($fee_ack && $fee_ack eq 'Y') {
400             $xact->fee_ack(1);
401         } else {
402             $xact->screen_msg('Fee required');
403             $xact->ok(0);
404             return $xact;
405         }
406     }
407
408     $xact->do_checkout($sc_renew);
409     $xact->desensitize(!$item->magnetic);
410
411     if( $xact->ok ) {
412         #editor()->commit;
413         syslog("LOG_DEBUG", "OILS: OpenILS::Checkout: " .
414             "patron %s checkout %s succeeded", $patron_id, $item_id);
415     } else {
416         #editor()->xact_rollback;
417         syslog("LOG_DEBUG", "OILS: OpenILS::Checkout: " .
418             "patron %s checkout %s FAILED, rolling back xact...", $patron_id, $item_id);
419     }
420
421     return $xact;
422 }
423
424
425 sub checkin {
426     my ($self, $item_id, $inst_id, $trans_date, $return_date,
427         $current_loc, $item_props, $cancel) = @_;
428
429     my $start_time = time();
430
431     $self->verify_session;
432
433     syslog('LOG_DEBUG', "OILS: OpenILS::Checkin of item=$item_id (to $inst_id)");
434     
435     my $xact = OpenILS::SIP::Transaction::Checkin->new(authtoken => $self->{authtoken});
436     my $item = OpenILS::SIP::Item->new($item_id);
437
438     unless ( $xact->item($item) ) {
439         $xact->ok(0);
440         # $circ->alert(1); $circ->alert_type(99);
441         $xact->screen_msg("Invalid Item Barcode: '$item_id'");
442         syslog('LOG_INFO', "OILS: Checkin failed.  " . $xact->screen_msg() );
443         return $xact;
444     }
445
446     $xact->do_checkin( $self, $inst_id, $trans_date, $return_date, $current_loc, $item_props );
447     
448     if ($xact->ok) {
449         $xact->patron($self->find_patron(usr => $xact->{circ_user_id}, slim_user => 1)) if $xact->{circ_user_id};
450         delete $item->{patron};
451         delete $item->{due_date};
452         syslog('LOG_INFO', "OILS: Checkin succeeded");
453     } else {
454         syslog('LOG_WARNING', "OILS: Checkin failed");
455     }
456
457     syslog('LOG_INFO', "OILS: SIP Checkin request took %0.3f seconds", (time() - $start_time));
458     return $xact;
459 }
460
461 ## If the ILS caches patron information, this lets it free it up.
462 ## Also, this could be used for centrally logging session duration.
463 ## We don't do anything with it.
464 sub end_patron_session {
465     my ($self, $patron_id) = @_;
466     return (1, 'Thank you!', '');
467 }
468
469
470 sub pay_fee {
471     my ($self, $patron_id, $patron_pwd, $fee_amt, $fee_type,
472     $pay_type, $fee_id, $trans_id, $currency) = @_;
473
474     $self->verify_session;
475
476     my $xact = OpenILS::SIP::Transaction::FeePayment->new(authtoken => $self->{authtoken});
477     my $patron = $self->find_patron($patron_id);
478
479     if (!$patron) {
480         $xact->screen_msg("Invalid Patron Barcode '$patron_id'");
481         $xact->ok(0);
482         return $xact;
483     }
484
485     $xact->patron($patron);
486     $xact->sip_currency($currency);
487     $xact->fee_amount($fee_amt);
488     $xact->sip_fee_type($fee_type);
489     $xact->transaction_id($trans_id);
490     $xact->fee_id($fee_id);
491     $xact->sip_payment_type($pay_type);
492     # We don't presently use this, but we might in the future.
493     $xact->patron_password($patron_pwd);
494
495     $xact->do_fee_payment();
496
497     return $xact;
498 }
499
500 #sub add_hold {
501 #    my ($self, $patron_id, $patron_pwd, $item_id, $title_id,
502 #    $expiry_date, $pickup_location, $hold_type, $fee_ack) = @_;
503 #    my ($patron, $item);
504 #    my $hold;
505 #    my $trans;
506 #
507 #
508 #    $trans = new ILS::Transaction::Hold;
509 #
510 #    # BEGIN TRANSACTION
511 #    $patron = new ILS::Patron $patron_id;
512 #    if (!$patron
513 #    || (defined($patron_pwd) && !$patron->check_password($patron_pwd))) {
514 #    $trans->screen_msg("Invalid Patron.");
515 #
516 #    return $trans;
517 #    }
518 #
519 #    $item = new ILS::Item ($item_id || $title_id);
520 #    if (!$item) {
521 #    $trans->screen_msg("No such item.");
522 #
523 #    # END TRANSACTION (conditionally)
524 #    return $trans;
525 #    } elsif ($item->fee && ($fee_ack ne 'Y')) {
526 #    $trans->screen_msg = "Fee required to place hold.";
527 #
528 #    # END TRANSACTION (conditionally)
529 #    return $trans;
530 #    }
531 #
532 #    $hold = {
533 #    item_id         => $item->id,
534 #    patron_id       => $patron->id,
535 #    expiration_date => $expiry_date,
536 #    pickup_location => $pickup_location,
537 #    hold_type       => $hold_type,
538 #    };
539 #
540 #    $trans->ok(1);
541 #    $trans->patron($patron);
542 #    $trans->item($item);
543 #    $trans->pickup_location($pickup_location);
544 #
545 #    push(@{$item->hold_queue}, $hold);
546 #    push(@{$patron->{hold_items}}, $hold);
547 #
548 #
549 #    # END TRANSACTION
550 #    return $trans;
551 #}
552 #
553 #sub cancel_hold {
554 #    my ($self, $patron_id, $patron_pwd, $item_id, $title_id) = @_;
555 #    my ($patron, $item, $hold);
556 #    my $trans;
557 #
558 #    $trans = new ILS::Transaction::Hold;
559 #
560 #    # BEGIN TRANSACTION
561 #    $patron = new ILS::Patron $patron_id;
562 #    if (!$patron) {
563 #    $trans->screen_msg("Invalid patron barcode.");
564 #
565 #    return $trans;
566 #    } elsif (defined($patron_pwd) && !$patron->check_password($patron_pwd)) {
567 #    $trans->screen_msg('Invalid patron password.');
568 #
569 #    return $trans;
570 #    }
571 #
572 #    $item = new ILS::Item ($item_id || $title_id);
573 #    if (!$item) {
574 #    $trans->screen_msg("No such item.");
575 #
576 #    # END TRANSACTION (conditionally)
577 #    return $trans;
578 #    }
579 #
580 #    # Remove the hold from the patron's record first
581 #    $trans->ok($patron->drop_hold($item_id));
582 #
583 #    if (!$trans->ok) {
584 #    # We didn't find it on the patron record
585 #    $trans->screen_msg("No such hold on patron record.");
586 #
587 #    # END TRANSACTION (conditionally)
588 #    return $trans;
589 #    }
590 #
591 #    # Now, remove it from the item record.  If it was on the patron
592 #    # record but not on the item record, we'll treat that as success.
593 #    foreach my $i (0 .. scalar @{$item->hold_queue}) {
594 #    $hold = $item->hold_queue->[$i];
595 #
596 #    if ($hold->{patron_id} eq $patron->id) {
597 #        # found it: delete it.
598 #        splice @{$item->hold_queue}, $i, 1;
599 #        last;
600 #    }
601 #    }
602 #
603 #    $trans->screen_msg("Hold Cancelled.");
604 #    $trans->patron($patron);
605 #    $trans->item($item);
606 #
607 #    return $trans;
608 #}
609 #
610 #
611 ## The patron and item id's can't be altered, but the
612 ## date, location, and type can.
613 #sub alter_hold {
614 #    my ($self, $patron_id, $patron_pwd, $item_id, $title_id,
615 #    $expiry_date, $pickup_location, $hold_type, $fee_ack) = @_;
616 #    my ($patron, $item);
617 #    my $hold;
618 #    my $trans;
619 #
620 #    $trans = new ILS::Transaction::Hold;
621 #
622 #    # BEGIN TRANSACTION
623 #    $patron = new ILS::Patron $patron_id;
624 #    if (!$patron) {
625 #    $trans->screen_msg("Invalid patron barcode.");
626 #
627 #    return $trans;
628 #    }
629 #
630 #    foreach my $i (0 .. scalar @{$patron->{hold_items}}) {
631 #    $hold = $patron->{hold_items}[$i];
632 #
633 #    if ($hold->{item_id} eq $item_id) {
634 #        # Found it.  So fix it.
635 #        $hold->{expiration_date} = $expiry_date if $expiry_date;
636 #        $hold->{pickup_location} = $pickup_location if $pickup_location;
637 #        $hold->{hold_type} = $hold_type if $hold_type;
638 #
639 #        $trans->ok(1);
640 #        $trans->screen_msg("Hold updated.");
641 #        $trans->patron($patron);
642 #        $trans->item(new ILS::Item $hold->{item_id});
643 #        last;
644 #    }
645 #    }
646 #
647 #    # The same hold structure is linked into both the patron's
648 #    # list of hold items and into the queue of outstanding holds
649 #    # for the item, so we don't need to search the hold queue for
650 #    # the item, since it's already been updated by the patron code.
651 #
652 #    if (!$trans->ok) {
653 #    $trans->screen_msg("No such outstanding hold.");
654 #    }
655 #
656 #    return $trans;
657 #}
658
659
660 sub renew {
661     my ($self, $patron_id, $patron_pwd, $item_id, $title_id,
662         $no_block, $nb_due_date, $third_party, $item_props, $fee_ack) = @_;
663
664     $self->verify_session;
665
666     my $trans = OpenILS::SIP::Transaction::Renew->new( authtoken => $self->{authtoken} );
667     $trans->patron($self->find_patron($patron_id));
668     $trans->item($self->find_item($item_id));
669
670     if(!$trans->patron) {
671         $trans->screen_msg("Invalid patron barcode.");
672         $trans->ok(0);
673         return $trans;
674     }
675
676     if(!$trans->patron->renew_ok) {
677         $trans->screen_msg("Renewals not allowed.");
678         $trans->ok(0);
679         return $trans;
680     }
681
682     if(!$trans->item) {
683         if( $title_id ) {
684             $trans->screen_msg("Title ID renewal not supported.  Use item barcode.");
685         } else {
686             $trans->screen_msg("Invalid item barcode.");
687         }
688         $trans->ok(0);
689         return $trans;
690     }
691
692     if(!$trans->item->{patron} or 
693             $trans->item->{patron} ne $patron_id) {
694         $trans->screen_msg("Item not checked out to " . $trans->patron->name);
695         $trans->ok(0);
696         return $trans;
697     }
698
699     # Perform the renewal
700     $trans->do_renew();
701
702     $trans->desensitize(0);    # It's already checked out
703     $trans->item->{due_date} = $nb_due_date if $no_block eq 'Y';
704     $trans->item->{sip_item_properties} = $item_props if $item_props;
705
706     return $trans;
707 }
708
709
710
711
712
713 #
714 #sub renew_all {
715 #    my ($self, $patron_id, $patron_pwd, $fee_ack) = @_;
716 #    my ($patron, $item_id);
717 #    my $trans;
718 #
719 #    $trans = new ILS::Transaction::RenewAll;
720 #
721 #    $trans->patron($patron = new ILS::Patron $patron_id);
722 #    if (defined $patron) {
723 #    syslog("LOG_DEBUG", "ILS::renew_all: patron '%s': renew_ok: %s",
724 #           $patron->name, $patron->renew_ok);
725 #    } else {
726 #    syslog("LOG_DEBUG", "ILS::renew_all: Invalid patron id: '%s'",
727 #           $patron_id);
728 #    }
729 #
730 #    if (!defined($patron)) {
731 #    $trans->screen_msg("Invalid patron barcode.");
732 #    return $trans;
733 #    } elsif (!$patron->renew_ok) {
734 #    $trans->screen_msg("Renewals not allowed.");
735 #    return $trans;
736 #    } elsif (defined($patron_pwd) && !$patron->check_password($patron_pwd)) {
737 #    $trans->screen_msg("Invalid patron password.");
738 #    return $trans;
739 #    }
740 #
741 #    foreach $item_id (@{$patron->{items}}) {
742 #    my $item = new ILS::Item $item_id;
743 #
744 #    if (!defined($item)) {
745 #        syslog("LOG_WARNING",
746 #           "renew_all: Invalid item id associated with patron '%s'",
747 #           $patron->id);
748 #        next;
749 #    }
750 #
751 #    if (@{$item->hold_queue}) {
752 #        # Can't renew if there are outstanding holds
753 #        push @{$trans->unrenewed}, $item_id;
754 #    } else {
755 #        $item->{due_date} = time + (14*24*60*60); # two weeks hence
756 #        push @{$trans->renewed}, $item_id;
757 #    }
758 #    }
759 #
760 #    $trans->ok(1);
761 #
762 #    return $trans;
763 #}
764
765 1;