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