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