bd11e1978e1ed008d7a5529fd803d62d3a911ad4
[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, $state) = @_;
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}, $state );
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, $state ) = @_;
228     syslog('LOG_DEBUG', "OILS: Logging in with username $username");
229
230
231     if ($state and ref $state and $$state{authtoken}) {
232         $self->{authtoken} = $$state{authtoken};
233         return $self->{authtoken} if ($self->fetch_session); # fetch the session
234     }
235
236     my $nonce = rand($$);
237     my $seed = $U->simplereq( 
238         'open-ils.auth',
239         'open-ils.auth.authenticate.init', $username, $nonce );
240
241     my $response = $U->simplereq(
242         'open-ils.auth', 
243         'open-ils.auth.authenticate.complete', 
244         {    
245             username => $username, 
246             password => md5_hex($seed . md5_hex($password)), 
247             type     => 'opac',
248             nonce    => $nonce
249         }
250     );
251
252     if( my $code = $U->event_code($response) ) {
253         my $txt = $response->{textcode};
254         syslog('LOG_WARNING', "OILS: Login failed for $username.  $txt:$code");
255         return undef;
256     }
257
258     my $key = $response->{payload}->{authtoken};
259     syslog('LOG_INFO', "OILS: Login succeeded for $username : authkey = $key");
260
261     $self->{authtoken} = $key;
262
263     $self->fetch_session; # to cache the login
264
265     return $key;
266 }
267
268 sub state {
269     my $self = shift;
270     return { authtoken => $self->{authtoken} };
271 }
272
273 #
274 # find_patron($barcode);
275 # find_patron(barcode => $barcode);   # same as above
276 # find_patron(usr => $id);
277
278 sub find_patron {
279     my $self = shift;
280     my $key  =  (@_ > 1) ? shift : 'barcode';  # if we have multiple args, the first is the key index (default barcode)
281     my $patron_id = shift;
282
283     return OpenILS::SIP::Patron->new($key => $patron_id, authtoken => $self->{authtoken}, @_);
284 }
285
286
287 sub find_item {
288     my $self = shift;
289     return OpenILS::SIP::Item->new(@_);
290 }
291
292
293 sub institution {
294     my $self = shift;
295     return $self->{institution}->{id};  # consider making this return the whole institution
296 }
297
298 sub institution_id {
299     my $self = shift;
300     return $self->{institution}->{id};  # then use this for just the ID
301 }
302
303 sub supports {
304     my ($self, $op) = @_;
305     my ($i) = grep { $_->{name} eq $op }  
306         @{$config->{implementation_config}->{supports}->{item}};
307     return to_bool($i->{value});
308 }
309
310 sub check_inst_id {
311     my ($self, $id, $whence) = @_;
312     if ($id ne $self->{institution}->{id}) {
313         syslog("LOG_WARNING", "OILS: %s: received institution '%s', expected '%s'", $whence, $id, $self->{institution}->{id});
314         # Just an FYI check, we don't expect the user to change location from that in SIPconfig.xml
315     }
316 }
317
318
319 sub to_bool {
320     my $bool = shift;
321     # If it's defined, and matches a true sort of string, or is
322     # a non-zero number, then it's true.
323     defined($bool) or return;                   # false
324     ($bool =~ /true|y|yes/i) and return 1;      # true
325     return ($bool =~ /^\d+$/ and $bool != 0);   # true for non-zero numbers, false otherwise
326 }
327
328 sub checkout_ok {
329     return to_bool($config->{policy}->{checkout});
330 }
331
332 sub checkin_ok {
333     return to_bool($config->{policy}->{checkin});
334 }
335
336 sub renew_ok {
337     return to_bool($config->{policy}->{renewal});
338 }
339
340 sub status_update_ok {
341     return to_bool($config->{policy}->{status_update});
342 }
343
344 sub offline_ok {
345     return to_bool($config->{policy}->{offline});
346 }
347
348
349
350 ##
351 ## Checkout(patron_id, item_id, sc_renew, fee_ack):
352 ##    patron_id & item_id are the identifiers send by the terminal
353 ##    sc_renew is the renewal policy configured on the terminal
354 ## returns a status opject that can be queried for the various bits
355 ## of information that the protocol (SIP or NCIP) needs to generate
356 ## the response.
357 ##    fee_ack is the fee_acknowledged field (BO) sent from the sc
358 ## when doing chargeable loans.
359 ##
360
361 sub checkout {
362     my ($self, $patron_id, $item_id, $sc_renew, $fee_ack) = @_;
363     # In order to allow renewals the selfcheck AND the config have to say they are allowed
364     $sc_renew = (chr($sc_renew) eq 'Y' && $self->renew_ok());
365
366     $self->verify_session;
367
368     syslog('LOG_DEBUG', "OILS: OpenILS::Checkout attempt: patron=$patron_id, item=$item_id");
369
370     my $xact   = OpenILS::SIP::Transaction::Checkout->new( authtoken => $self->{authtoken} );
371     my $patron = $self->find_patron($patron_id);
372     my $item   = $self->find_item($item_id);
373
374     $xact->patron($patron);
375     $xact->item($item);
376
377     if (!$patron) {
378         $xact->screen_msg("Invalid Patron Barcode '$patron_id'");
379         return $xact;
380     }
381
382     if (!$patron->charge_ok) {
383         $xact->screen_msg("Patron Blocked");
384         return $xact;
385     }
386
387     if( !$item ) {
388         $xact->screen_msg("Invalid Item Barcode: '$item_id'");
389         return $xact;
390     }
391
392     syslog('LOG_DEBUG', "OILS: OpenILS::Checkout data loaded OK, checking out...");
393
394     if ($item->{patron} && ($item->{patron} eq $patron_id)) {
395         $xact->renew_ok(1); # So that accept/reject responses have the correct value later
396         if($sc_renew) {
397             syslog('LOG_INFO', "OILS: OpenILS::Checkout data loaded OK, doing renew...");
398         } else {
399             syslog('LOG_INFO', "OILS: OpenILS::Checkout appears to be renew, but renewal disallowed...");
400             $xact->screen_msg("Renewals not permitted");
401             $xact->ok(0);
402             return $xact; # Don't attempt later
403         }
404     } elsif ($item->{patron} && ($item->{patron} ne $patron_id)) {
405         # I can't deal with this right now
406         # XXX check in then check out?
407         $xact->screen_msg("Item checked out to another patron");
408         $xact->ok(0);
409         return $xact; # Don't wipe out the screen message later
410     } else {
411         $sc_renew = 0;
412     } 
413
414     # Check for fee and $fee_ack. If there is a fee, and $fee_ack
415     # is 'Y', we proceed, otherwise we reject the checkout.
416     if ($item->fee > 0.0) {
417         $xact->fee_amount($item->fee);
418         $xact->sip_fee_type($item->sip_fee_type);
419         $xact->sip_currency($item->fee_currency);
420         if ($fee_ack && $fee_ack eq 'Y') {
421             $xact->fee_ack(1);
422         } else {
423             $xact->screen_msg('Fee required');
424             $xact->ok(0);
425             return $xact;
426         }
427     }
428
429     $xact->do_checkout($sc_renew);
430     $xact->desensitize(!$item->magnetic);
431
432     if( $xact->ok ) {
433         #editor()->commit;
434         syslog("LOG_DEBUG", "OILS: OpenILS::Checkout: " .
435             "patron %s checkout %s succeeded", $patron_id, $item_id);
436     } else {
437         #editor()->xact_rollback;
438         syslog("LOG_DEBUG", "OILS: OpenILS::Checkout: " .
439             "patron %s checkout %s FAILED, rolling back xact...", $patron_id, $item_id);
440     }
441
442     return $xact;
443 }
444
445
446 sub checkin {
447     my ($self, $item_id, $inst_id, $trans_date, $return_date,
448         $current_loc, $item_props, $cancel) = @_;
449
450     my $start_time = time();
451
452     $self->verify_session;
453
454     syslog('LOG_DEBUG', "OILS: OpenILS::Checkin of item=$item_id (to $inst_id)");
455     
456     my $xact = OpenILS::SIP::Transaction::Checkin->new(authtoken => $self->{authtoken});
457     my $item = OpenILS::SIP::Item->new($item_id);
458
459     unless ( $xact->item($item) ) {
460         $xact->ok(0);
461         # $circ->alert(1); $circ->alert_type(99);
462         $xact->screen_msg("Invalid Item Barcode: '$item_id'");
463         syslog('LOG_INFO', "OILS: Checkin failed.  " . $xact->screen_msg() );
464         return $xact;
465     }
466
467     $xact->do_checkin( $self, $inst_id, $trans_date, $return_date, $current_loc, $item_props );
468     
469     if ($xact->ok) {
470         $xact->patron($self->find_patron(usr => $xact->{circ_user_id}, slim_user => 1)) if $xact->{circ_user_id};
471         delete $item->{patron};
472         delete $item->{due_date};
473         syslog('LOG_INFO', "OILS: Checkin succeeded");
474     } else {
475         syslog('LOG_WARNING', "OILS: Checkin failed");
476     }
477
478     syslog('LOG_INFO', "OILS: SIP Checkin request took %0.3f seconds", (time() - $start_time));
479     return $xact;
480 }
481
482 ## If the ILS caches patron information, this lets it free it up.
483 ## Also, this could be used for centrally logging session duration.
484 ## We don't do anything with it.
485 sub end_patron_session {
486     my ($self, $patron_id) = @_;
487     return (1, 'Thank you!', '');
488 }
489
490
491 sub pay_fee {
492     my ($self, $patron_id, $patron_pwd, $fee_amt, $fee_type,
493     $pay_type, $fee_id, $trans_id, $currency) = @_;
494
495     $self->verify_session;
496
497     my $xact = OpenILS::SIP::Transaction::FeePayment->new(authtoken => $self->{authtoken});
498     my $patron = $self->find_patron($patron_id);
499
500     if (!$patron) {
501         $xact->screen_msg("Invalid Patron Barcode '$patron_id'");
502         $xact->ok(0);
503         return $xact;
504     }
505
506     $xact->patron($patron);
507     $xact->sip_currency($currency);
508     $xact->fee_amount($fee_amt);
509     $xact->sip_fee_type($fee_type);
510     $xact->transaction_id($trans_id);
511     $xact->fee_id($fee_id);
512     $xact->sip_payment_type($pay_type);
513     # We don't presently use this, but we might in the future.
514     $xact->patron_password($patron_pwd);
515
516     $xact->do_fee_payment();
517
518     return $xact;
519 }
520
521 #sub add_hold {
522 #    my ($self, $patron_id, $patron_pwd, $item_id, $title_id,
523 #    $expiry_date, $pickup_location, $hold_type, $fee_ack) = @_;
524 #    my ($patron, $item);
525 #    my $hold;
526 #    my $trans;
527 #
528 #
529 #    $trans = new ILS::Transaction::Hold;
530 #
531 #    # BEGIN TRANSACTION
532 #    $patron = new ILS::Patron $patron_id;
533 #    if (!$patron
534 #    || (defined($patron_pwd) && !$patron->check_password($patron_pwd))) {
535 #    $trans->screen_msg("Invalid Patron.");
536 #
537 #    return $trans;
538 #    }
539 #
540 #    $item = new ILS::Item ($item_id || $title_id);
541 #    if (!$item) {
542 #    $trans->screen_msg("No such item.");
543 #
544 #    # END TRANSACTION (conditionally)
545 #    return $trans;
546 #    } elsif ($item->fee && ($fee_ack ne 'Y')) {
547 #    $trans->screen_msg = "Fee required to place hold.";
548 #
549 #    # END TRANSACTION (conditionally)
550 #    return $trans;
551 #    }
552 #
553 #    $hold = {
554 #    item_id         => $item->id,
555 #    patron_id       => $patron->id,
556 #    expiration_date => $expiry_date,
557 #    pickup_location => $pickup_location,
558 #    hold_type       => $hold_type,
559 #    };
560 #
561 #    $trans->ok(1);
562 #    $trans->patron($patron);
563 #    $trans->item($item);
564 #    $trans->pickup_location($pickup_location);
565 #
566 #    push(@{$item->hold_queue}, $hold);
567 #    push(@{$patron->{hold_items}}, $hold);
568 #
569 #
570 #    # END TRANSACTION
571 #    return $trans;
572 #}
573 #
574
575 # Note: item_id in this context is the hold id
576 sub cancel_hold {
577     my ($self, $patron_id, $patron_pwd, $item_id, $title_id) = @_;
578
579     my $trans = OpenILS::SIP::Transaction::Hold->new(authtoken => $self->{authtoken});
580     my $patron = $self->find_patron($patron_id);
581
582     if (!$patron) {
583         $trans->screen_msg("Invalid patron barcode.");
584         $trans->ok(0);
585         return $trans;
586     }
587
588     if (defined($patron_pwd) && !$patron->check_password($patron_pwd)) {
589         $trans->screen_msg('Invalid patron password.');
590         $trans->ok(0);
591         return $trans;
592     }
593
594     $trans->patron($patron);
595     my $hold = $patron->find_hold_from_copy($item_id);
596
597     if (!$hold) {
598         syslog('LOG_WARNING', "OILS: No hold found from copy $item_id");
599         $trans->screen_msg("No such hold.");
600         $trans->ok(0);
601         return $trans;
602     }
603
604     if ($hold->usr ne $patron->{user}->id) {
605         $trans->screen_msg("No such hold on patron record.");
606         $trans->ok(0);
607         return $trans;
608     }
609
610     $trans->hold($hold);
611     $trans->do_hold_cancel($self);
612
613     if ($trans->cancel_ok) {
614         $trans->screen_msg("Hold Cancelled.");
615     } else {
616         $trans->screen_msg("Hold was not cancelled.");
617     }
618
619     # if the hold had no current_copy, use the representative
620     # item as the item for the hold.  Without this, the SIP 
621     # server gets angry.
622     $trans->item($self->find_item($item_id)) unless $trans->item;
623
624     return $trans;
625 }
626
627 #
628 ## The patron and item id's can't be altered, but the
629 ## date, location, and type can.
630 #sub alter_hold {
631 #    my ($self, $patron_id, $patron_pwd, $item_id, $title_id,
632 #    $expiry_date, $pickup_location, $hold_type, $fee_ack) = @_;
633 #    my ($patron, $item);
634 #    my $hold;
635 #    my $trans;
636 #
637 #    $trans = new ILS::Transaction::Hold;
638 #
639 #    # BEGIN TRANSACTION
640 #    $patron = new ILS::Patron $patron_id;
641 #    if (!$patron) {
642 #    $trans->screen_msg("Invalid patron barcode.");
643 #
644 #    return $trans;
645 #    }
646 #
647 #    foreach my $i (0 .. scalar @{$patron->{hold_items}}) {
648 #    $hold = $patron->{hold_items}[$i];
649 #
650 #    if ($hold->{item_id} eq $item_id) {
651 #        # Found it.  So fix it.
652 #        $hold->{expiration_date} = $expiry_date if $expiry_date;
653 #        $hold->{pickup_location} = $pickup_location if $pickup_location;
654 #        $hold->{hold_type} = $hold_type if $hold_type;
655 #
656 #        $trans->ok(1);
657 #        $trans->screen_msg("Hold updated.");
658 #        $trans->patron($patron);
659 #        $trans->item(new ILS::Item $hold->{item_id});
660 #        last;
661 #    }
662 #    }
663 #
664 #    # The same hold structure is linked into both the patron's
665 #    # list of hold items and into the queue of outstanding holds
666 #    # for the item, so we don't need to search the hold queue for
667 #    # the item, since it's already been updated by the patron code.
668 #
669 #    if (!$trans->ok) {
670 #    $trans->screen_msg("No such outstanding hold.");
671 #    }
672 #
673 #    return $trans;
674 #}
675
676
677 sub renew {
678     my ($self, $patron_id, $patron_pwd, $item_id, $title_id,
679         $no_block, $nb_due_date, $third_party, $item_props, $fee_ack) = @_;
680
681     $self->verify_session;
682
683     my $trans = OpenILS::SIP::Transaction::Renew->new( authtoken => $self->{authtoken} );
684     $trans->patron($self->find_patron($patron_id));
685     $trans->item($self->find_item($item_id));
686
687     if(!$trans->patron) {
688         $trans->screen_msg("Invalid patron barcode.");
689         $trans->ok(0);
690         return $trans;
691     }
692
693     if(!$trans->patron->renew_ok) {
694         $trans->screen_msg("Renewals not allowed.");
695         $trans->ok(0);
696         return $trans;
697     }
698
699     if(!$trans->item) {
700         if( $title_id ) {
701             $trans->screen_msg("Title ID renewal not supported.  Use item barcode.");
702         } else {
703             $trans->screen_msg("Invalid item barcode.");
704         }
705         $trans->ok(0);
706         return $trans;
707     }
708
709     if(!$trans->item->{patron} or 
710             $trans->item->{patron} ne $patron_id) {
711         $trans->screen_msg("Item not checked out to " . $trans->patron->name);
712         $trans->ok(0);
713         return $trans;
714     }
715
716     # Perform the renewal
717     $trans->do_renew();
718
719     $trans->desensitize(0);    # It's already checked out
720     $trans->item->{due_date} = $nb_due_date if $no_block eq 'Y';
721     $trans->item->{sip_item_properties} = $item_props if $item_props;
722
723     return $trans;
724 }
725
726
727 sub renew_all {
728     my ($self, $patron_id, $patron_pwd, $fee_ack) = @_;
729
730     $self->verify_session;
731
732     my $trans = OpenILS::SIP::Transaction::RenewAll->new(authtoken => $self->{authtoken});
733     $trans->patron($self->find_patron($patron_id));
734
735     if(!$trans->patron) {
736         $trans->screen_msg("Invalid patron barcode.");
737         $trans->ok(0);
738         return $trans;
739     }
740
741     if(!$trans->patron->renew_ok) {
742         $trans->screen_msg("Renewals not allowed.");
743         $trans->ok(0);
744         return $trans;
745     }
746
747     $trans->do_renew_all($self);
748     return $trans;
749 }
750
751
752 #
753 #sub renew_all {
754 #    my ($self, $patron_id, $patron_pwd, $fee_ack) = @_;
755 #    my ($patron, $item_id);
756 #    my $trans;
757 #
758 #    $trans = new ILS::Transaction::RenewAll;
759 #
760 #    $trans->patron($patron = new ILS::Patron $patron_id);
761 #    if (defined $patron) {
762 #    syslog("LOG_DEBUG", "ILS::renew_all: patron '%s': renew_ok: %s",
763 #           $patron->name, $patron->renew_ok);
764 #    } else {
765 #    syslog("LOG_DEBUG", "ILS::renew_all: Invalid patron id: '%s'",
766 #           $patron_id);
767 #    }
768 #
769 #    if (!defined($patron)) {
770 #    $trans->screen_msg("Invalid patron barcode.");
771 #    return $trans;
772 #    } elsif (!$patron->renew_ok) {
773 #    $trans->screen_msg("Renewals not allowed.");
774 #    return $trans;
775 #    } elsif (defined($patron_pwd) && !$patron->check_password($patron_pwd)) {
776 #    $trans->screen_msg("Invalid patron password.");
777 #    return $trans;
778 #    }
779 #
780 #    foreach $item_id (@{$patron->{items}}) {
781 #    my $item = new ILS::Item $item_id;
782 #
783 #    if (!defined($item)) {
784 #        syslog("LOG_WARNING",
785 #           "renew_all: Invalid item id associated with patron '%s'",
786 #           $patron->id);
787 #        next;
788 #    }
789 #
790 #    if (@{$item->hold_queue}) {
791 #        # Can't renew if there are outstanding holds
792 #        push @{$trans->unrenewed}, $item_id;
793 #    } else {
794 #        $item->{due_date} = time + (14*24*60*60); # two weeks hence
795 #        push @{$trans->renewed}, $item_id;
796 #    }
797 #    }
798 #
799 #    $trans->ok(1);
800 #
801 #    return $trans;
802 #}
803
804 1;