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