]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/SIP/Patron.pm
933ec17e83b9ff78d6c1aeeb6b70cb756e37d202
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / SIP / Patron.pm
1 #
2
3 # A Class for hiding the ILS's concept of the patron from the OpenSIP
4 # system
5 #
6
7 package OpenILS::SIP::Patron;
8
9 use strict;
10 use warnings;
11 use Exporter;
12
13 use Sys::Syslog qw(syslog);
14 use Data::Dumper;
15 use Digest::MD5 qw(md5_hex);
16
17 use OpenILS::SIP;
18 use OpenILS::Application::AppUtils;
19 use OpenILS::Application::Actor;
20 use OpenSRF::Utils qw/:datetime/;
21 use DateTime::Format::ISO8601;
22 my $U = 'OpenILS::Application::AppUtils';
23
24 our (@ISA, @EXPORT_OK);
25
26 @ISA = qw(Exporter);
27
28 @EXPORT_OK = qw(invalid_patron);
29
30 my $INET_PRIVS;
31
32 sub new {
33     my ($class, $patron_id) = @_;
34     my $type = ref($class) || $class;
35     my $self = {};
36
37         syslog("LOG_DEBUG", "OILS: new OpenILS Patron(%s): searching...", $patron_id);
38
39         my $e = OpenILS::SIP->editor();
40
41         my $c = $e->search_actor_card({barcode => $patron_id}, {idlist=>1});
42         my $user;
43
44         if( @$c ) {
45
46                 $user = $e->search_actor_user(
47                         [
48                                 { card => $$c[0] },
49                                 {
50                                         flesh => 2,
51                                         flesh_fields => {
52                                                 "au" => [
53                                                         #"cards",
54                                                         "card",
55                                                         "standing_penalties",
56                                                         "addresses",
57                                                         "billing_address",
58                                                         "mailing_address",
59                                                         #"stat_cat_entries",
60                                                         'profile',
61                                                 ],
62                         ausp => ['standing_penalty']
63                                         }
64                                 }
65                         ]
66                 );
67
68                 $user = (@$user) ? $$user[0] : undef;
69          }
70
71          if(!$user) {
72                 syslog("LOG_WARNING", "OILS: Unable to find patron %s", $patron_id);
73                 return undef;
74          }
75
76         $self->{user}           = $user;
77         $self->{id}                     = $patron_id;
78         $self->{editor} = $e;
79
80         syslog("LOG_DEBUG", "OILS: new OpenILS Patron(%s): found patron : barred=%s, card:active=%s", 
81                 $patron_id, $self->{user}->barred, $self->{user}->card->active );
82
83
84         bless $self, $type;
85         return $self;
86 }
87
88 sub id {
89     my $self = shift;
90     return $self->{id};
91 }
92
93 sub name {
94     my $self = shift;
95     my $u = $self->{user};
96     return OpenILS::SIP::clean_text($u->first_given_name . ' ' . 
97             $u->second_given_name . ' ' . $u->family_name);
98 }
99
100 sub home_library {
101     my $self = shift;
102     my $lib = $self->{editor}->retrieve_actor_org_unit($self->{user}->home_ou)->shortname;
103         syslog('LOG_DEBUG', "OILS: Patron->home_library() = $lib");
104     return $lib;
105 }
106
107 sub __addr_string {
108     my $addr = shift;
109     return "" unless $addr;
110     return OpenILS::SIP::clean_text($addr->street1 .' '. 
111         $addr->street2 .' '.
112         $addr->city .' '.
113         $addr->county .' '.
114         $addr->state .' '.
115         $addr->country .' '.
116         $addr->post_code);
117 }
118
119 sub address {
120         my $self = shift;
121         my $u = $self->{user};
122         my $addr = $u->billing_address;
123         $addr = $u->mailing_address unless $addr;
124         my $str = __addr_string($addr);
125         syslog('LOG_DEBUG', "OILS: Patron address: $str");
126         return $str;
127 }
128
129 sub email_addr {
130     my $self = shift;
131     return OpenILS::SIP::clean_text($self->{user}->email);
132 }
133
134 sub home_phone {
135     my $self = shift;
136     return $self->{user}->day_phone;
137 }
138
139 sub sip_birthdate {
140         my $self = shift;
141         my $dob = OpenILS::SIP->format_date($self->{user}->dob);
142         syslog('LOG_DEBUG', "OILS: Patron DOB = $dob");
143         return $dob;
144 }
145
146 sub ptype {
147     my $self = shift;
148     return OpenILS::SIP::clean_text($self->{user}->profile->name);
149 }
150
151 sub language {
152     my $self = shift;
153     return '000'; # Unspecified
154 }
155
156 # How much more detail do we need to check here?
157 sub charge_ok {
158     my $self = shift;
159          my $u = $self->{user};
160          return (($u->barred eq 'f') and ($u->card->active eq 't'));
161 }
162
163 # How much more detail do we need to check here?
164 sub renew_ok {
165     my $self = shift;
166          return $self->charge_ok;
167 }
168
169 sub recall_ok {
170     my $self = shift;
171     return 0;
172 }
173
174 sub hold_ok {
175     my $self = shift;
176          return $self->charge_ok;
177 }
178
179 # return true if the card provided is marked as lost
180 sub card_lost {
181     my $self = shift;
182          return $self->{user}->card->active eq 'f';
183 }
184
185 sub recall_overdue {
186     my $self = shift;
187     return 0;
188 }
189
190
191 sub check_password {
192         my ($self, $pwd) = @_;
193         syslog('LOG_DEBUG', 'OILS: Patron->check_password()');
194         return md5_hex($pwd) eq $self->{user}->passwd;
195 }
196
197
198 sub currency {
199         my $self = shift;
200         syslog('LOG_DEBUG', 'OILS: Patron->currency()');
201         return 'USD';
202 }
203
204 sub fee_amount {
205         my $self = shift;
206         syslog('LOG_DEBUG', 'OILS: Patron->fee_amount()');
207
208         my $ses = $U->start_db_session();
209         my $summary = $ses->request(
210                 'open-ils.storage.money.open_user_summary.search', $self->{user}->id )->gather(1);
211         $U->rollback_db_session($ses);
212
213         my $total = $summary->balance_owed;
214         syslog('LOG_INFO', "User ".$self->{id} .':'.$self->{user}->id." has a fee amount of \$$total");
215         return $total;
216 }
217
218 sub screen_msg {
219         my $self = shift;
220         my $u = $self->{user};
221         return 'barred' if $u->barred eq 't';
222
223         my $b = 'blocked';
224
225         return $b if $u->active eq 'f';
226         return $b if $u->card->active eq 'f';
227
228         if( $u->standing_penalties ) {
229                 return $b if 
230                         grep { $_->standing_penalty->name eq 'PATRON_EXCEEDS_OVERDUE_COUNT' } 
231                                 @{$u->standing_penalties};
232
233                 return $b if 
234                         grep { $_->standing_penalty->name eq 'PATRON_EXCEEDS_FINES' } 
235                                 @{$u->standing_penalties};
236         }
237
238         my $expire = DateTime::Format::ISO8601->new->parse_datetime(
239                 clense_ISO8601($u->expire_date));
240
241         return $b if CORE::time > $expire->epoch;
242
243         return 'OK';
244 }
245
246 sub print_line {
247     my $self = shift;
248         return '';
249 }
250
251 sub too_many_charged {
252     my $self = shift;
253         return 0;
254 }
255
256 sub too_many_overdue {
257         my $self = shift;
258         if( $self->{user}->standing_penalties ) {
259                 return grep { $_->standing_penalty->name eq 'PATRON_EXCEEDS_OVERDUE_COUNT' } 
260                         @{$self->{user}->standing_penalties};
261         }
262         return 0;
263 }
264
265 # not completely sure what this means
266 sub too_many_renewal {
267     my $self = shift;
268         return 0;
269 }
270
271 # not relevant, handled by fines/fees
272 sub too_many_claim_return {
273     my $self = shift;
274         return 0;
275 }
276
277 # not relevant, handled by fines/fees
278 sub too_many_lost {
279     my $self = shift;
280         return 0;
281 }
282
283 sub excessive_fines {
284     my $self = shift;
285         syslog('LOG_DEBUG', 'OILS: Patron->excessive_fines()');
286         if( $self->{user}->standing_penalties ) {
287                 return grep { $_->standing_penalty->name eq 'PATRON_EXCEEDS_FINES' } 
288                         @{$self->{user}->standing_penalties};
289         }
290         return 0;
291 }
292
293
294 # Until someone suggests otherwise, fees and fines are the same
295
296 sub excessive_fees {
297         my $self = shift;
298         syslog('LOG_DEBUG', 'OILS: Patron->excessive_fees()');
299         if( $self->{user}->standing_penalties ) {
300                 return grep { $_->standing_penalty->name eq 'PATRON_EXCEEDS_FINES' } 
301                         @{$self->{user}->standing_penalties};
302         }
303         return 0;
304 }
305
306 # not relevant, handled by fines/fees
307 sub too_many_billed {
308     my $self = shift;
309         return 0;
310 }
311
312
313
314 #
315 # List of outstanding holds placed
316 #
317 sub hold_items {
318     my ($self, $start, $end) = @_;
319         syslog('LOG_DEBUG', 'OILS: Patron->hold_items()');
320
321          my $holds = $self->{editor}->search_action_hold_request(
322                 { usr => $self->{user}->id, fulfillment_time => undef, cancel_time => undef }
323          );
324
325         my @holds;
326         push( @holds, OpenILS::SIP::clean_text($self->__hold_to_title($_)) ) for @$holds;
327
328         return (defined $start and defined $end) ? 
329                 [ $holds[($start-1)..($end-1)] ] : 
330                 \@holds;
331 }
332
333 sub __hold_to_title {
334         my $self = shift;
335         my $hold = shift;
336         my $e = $self->{editor};
337
338         my( $id, $mods, $title, $volume, $copy );
339
340         return __copy_to_title($e, 
341                 $e->retrieve_asset_copy($hold->target)) 
342                 if $hold->hold_type eq 'C';
343
344         return __volume_to_title($e, 
345                 $e->retrieve_asset_call_number($hold->target))
346                 if $hold->hold_type eq 'V';
347
348         return __record_to_title(
349                 $e, $hold->target) if $hold->hold_type eq 'T';
350
351         return __metarecord_to_title(
352                 $e, $hold->target) if $hold->hold_type eq 'M';
353 }
354
355 sub __copy_to_title {
356         my( $e, $copy ) = @_;
357         #syslog('LOG_DEBUG', "OILS: copy_to_title(%s)", $copy->id);
358         return $copy->dummy_title if $copy->call_number == -1;  
359
360         my $vol = (ref $copy->call_number) ?
361                 $copy->call_number :
362                 $e->retrieve_asset_call_number($copy->call_number);
363
364         return __volume_to_title($e, $vol);
365 }
366
367
368 sub __volume_to_title {
369         my( $e, $volume ) = @_;
370         #syslog('LOG_DEBUG', "OILS: volume_to_title(%s)", $volume->id);
371         return __record_to_title($e, $volume->record);
372 }
373
374
375 sub __record_to_title {
376         my( $e, $title_id ) = @_;
377         #syslog('LOG_DEBUG', "OILS: record_to_title($title_id)");
378         my $mods = $U->simplereq(
379                 'open-ils.search',
380                 'open-ils.search.biblio.record.mods_slim.retrieve', $title_id );
381         return ($mods) ? $mods->title : "";
382 }
383
384 sub __metarecord_to_title {
385         my( $e, $m_id ) = @_;
386         #syslog('LOG_DEBUG', "OILS: metarecord_to_title($m_id)");
387         my $mods = $U->simplereq(
388                 'open-ils.search',
389                 'open-ils.search.biblio.metarecord.mods_slim.retrieve', $m_id);
390         return ($U->event_code($mods)) ? "<unknown>" : $mods->title;
391 }
392
393
394 #
395 # remove the hold on item item_id from my hold queue.
396 # return true if I was holding the item, false otherwise.
397
398 sub drop_hold {
399     my ($self, $item_id) = @_;
400     return 0;
401 }
402
403 sub __patron_items_info {
404         my $self = shift;
405         return if $self->{item_info};
406         $self->{item_info} = 
407                 OpenILS::Application::Actor::_checked_out(
408                         0, $self->{editor}, $self->{user}->id);;
409 }
410
411 sub overdue_items {
412         my ($self, $start, $end) = @_;
413
414         $self->__patron_items_info();
415         my @overdues = @{$self->{item_info}->{overdue}};
416         #$overdues[$_] = __circ_to_title($self->{editor}, $overdues[$_]) for @overdues;
417
418         my @o;
419         syslog('LOG_DEBUG', "OILS: overdue_items() fleshing circs @overdues");
420         
421         
422         my @return_datatype = grep { $_->{name} eq 'msg64_summary_datatype' } OpenILS::SIP::config()->{implementation_config}->{options}->{option};
423         
424         for my $circid (@overdues) {
425                 next unless $circid;
426                 if(@return_datatype and $return_datatype[0]->{value} eq 'barcode') {
427                         push( @o, __circ_to_barcode($self->{editor}, $circid));
428                 } else {
429                         push( @o, OpenILS::SIP::clean_text(__circ_to_title($self->{editor}, $circid)));
430                 }
431         }
432         @overdues = @o;
433
434         return (defined $start and defined $end) ? 
435                 [ $overdues[($start-1)..($end-1)] ] : \@overdues;
436 }
437
438 sub __circ_to_barcode {
439         my ($e, $circ) = @_;
440         return unless $circ;
441         $circ = $e->retrieve_action_circulation($circ);
442         my $copy = $e->retrieve_asset_copy($circ->target_copy);
443         return $copy->barcode;
444 }
445
446 sub __circ_to_title {
447         my( $e, $circ ) = @_;
448         return unless $circ;
449         $circ = $e->retrieve_action_circulation($circ);
450         return __copy_to_title( $e, 
451                 $e->retrieve_asset_copy($circ->target_copy) );
452 }
453
454 sub charged_items {
455         my ($self, $start, $end) = shift;
456
457         $self->__patron_items_info();
458
459         my @charges = (
460                 @{$self->{item_info}->{out}},
461                 @{$self->{item_info}->{overdue}}
462                 );
463
464         #$charges[$_] = __circ_to_title($self->{editor}, $charges[$_]) for @charges;
465
466         my @c;
467         syslog('LOG_DEBUG', "OILS: charged_items() fleshing circs @charges");
468
469         my @return_datatype = grep { $_->{name} eq 'msg64_summary_datatype' } OpenILS::SIP::config()->{implementation_config}->{options}->{option};
470
471         for my $circid (@charges) {
472                 next unless $circid;
473                 if(@return_datatype and $return_datatype[0]->{value} eq 'barcode') {
474                         push( @c, __circ_to_barcode($self->{editor}, $circid));
475                 } else {
476                         push( @c, OpenILS::SIP::clean_text(__circ_to_title($self->{editor}, $circid)));
477                 }
478         }
479
480         @charges = @c;
481
482         return (defined $start and defined $end) ? 
483                 [ $charges[($start-1)..($end-1)] ] : 
484                 \@charges;
485 }
486
487 sub fine_items {
488         my ($self, $start, $end) = @_;
489         my @fines;
490         syslog('LOG_DEBUG', 'OILS: Patron->fine_items()');
491         return (defined $start and defined $end) ? 
492                 [ $fines[($start-1)..($end-1)] ] : \@fines;
493 }
494
495 # not currently supported
496 sub recall_items {
497     my ($self, $start, $end) = @_;
498          return [];
499 }
500
501 sub unavail_holds {
502         my ($self, $start, $end) = @_;
503         my @holds;
504         syslog('LOG_DEBUG', 'OILS: Patron->unavail_holds()');
505         return (defined $start and defined $end) ? 
506                 [ $holds[($start-1)..($end-1)] ] : \@holds;
507 }
508
509 sub block {
510         my ($self, $card_retained, $blocked_card_msg) = @_;
511
512         my $u = $self->{user};
513         my $e = $self->{editor} = OpenILS::SIP->reset_editor();
514
515         syslog('LOG_INFO', "OILS: Blocking user %s", $u->card->barcode );
516
517         return $self if $u->card->active eq 'f';
518
519         $u->card->active('f');
520         if( ! $e->update_actor_card($u->card) ) {
521                 syslog('LOG_ERR', "OILS: Block card update failed: %s", $e->event->{textcode});
522                 $e->xact_rollback;
523                 return $self;
524         }
525
526         # retrieve the un-fleshed user object for update
527         $u = $e->retrieve_actor_user($u->id);
528         my $note = OpenILS::SIP::clean_text($u->alert_message) || "";
529         $note = "CARD BLOCKED BY SELF-CHECK MACHINE\n$note"; # XXX Config option
530
531         $u->alert_message($note);
532
533         if( ! $e->update_actor_user($u) ) {
534                 syslog('LOG_ERR', "OILS: Block: patron alert update failed: %s", $e->event->{textcode});
535                 $e->xact_rollback;
536                 return $self;
537         }
538
539         # stay in synch
540         $self->{user}->alert_message( $note );
541
542         $e->commit; # commits and resets
543         $self->{editor} = OpenILS::SIP->reset_editor();
544         return $self;
545 }
546
547 # Testing purposes only
548 sub enable {
549     my $self = shift;
550          # Un-mark card as inactive, grep out the patron alert
551     $self->{screen_msg} = "All privileges restored.";
552     return $self;
553 }
554
555 #
556 # Messages
557 #
558
559 sub invalid_patron {
560     return "Please contact library staff";
561 }
562
563 sub charge_denied {
564     return "Please contact library staff";
565 }
566
567 sub inet_privileges {
568         my( $self ) = @_;
569         my $e = OpenILS::SIP->editor();
570         $INET_PRIVS = $e->retrieve_all_config_net_access_level() unless $INET_PRIVS;
571         my ($level) = grep { $_->id eq $self->{user}->net_access_level } @$INET_PRIVS;
572         my $name = OpenILS::SIP::clean_text($level->name);
573         syslog('LOG_DEBUG', "OILS: Patron inet_privs = $name");
574         return $name;
575 }
576
577
578 1;