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