]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/offline/offline.pl
Merge branch 'opac-tt-poc' of git+ssh://yeti.esilibrary.com/home/evergreen/evergreen...
[working/Evergreen.git] / Open-ILS / src / offline / offline.pl
1 #!/usr/bin/perl
2 use strict; use warnings;
3 use CGI;
4 use OpenSRF::Utils::JSON;
5 use OpenSRF::System;
6 use OpenSRF::Utils::Logger qw/$logger/;
7 use OpenILS::Application::AppUtils;
8 use OpenILS::Event;
9 use OpenSRF::EX qw/:try/;
10 use Data::Dumper;
11 use OpenILS::Utils::Fieldmapper;
12 use Digest::MD5 qw/md5_hex/;
13 use OpenSRF::Utils qw/:daemon/;
14 use OpenILS::Utils::OfflineStore;
15 use OpenSRF::Utils::SettingsClient;
16 use OpenSRF::Utils;
17 use DateTime;
18
19 use DBI;
20 $DBI::trace = 1;
21
22 my $U = "OpenILS::Application::AppUtils";
23 my $DB = "OpenILS::Utils::OfflineStore";
24 my $SES = "${DB}::Session";
25 my $SCRIPT = "OpenILS::Utils::OfflineStore::Script";
26 my $user_groups;
27
28 # --------------------------------------------------------------------
29 # Load the config
30 # --------------------------------------------------------------------
31 our %config;
32 do '##CONFIG##/offline-config.pl';
33
34
35 my $cgi                 = new CGI;
36 my $basedir             = $config{base_dir} || die "Offline config error: no base_dir defined\n";
37 my $bootstrap   = $config{bootstrap} || die "Offline config error: no bootstrap defined\n";
38 my $wsname              = $cgi->param('ws');
39 my $org                 = $cgi->param('org');
40 my $authtoken   = $cgi->param('ses') || "";
41 my $seskey              = $cgi->param('seskey');
42 my $action              = $cgi->param('action'); # - create, load, execute, status
43 my $requestor; 
44 my $wsobj;
45 my $orgobj;
46 my $evt;
47
48
49 &ol_init;
50 &ol_runtime_init;
51 &ol_do_action;
52
53
54 # --------------------------------------------------------------------
55 # Set it all up
56 # This function should behave as a child_init might behave in case 
57 # this is moved to mod_perl
58 # --------------------------------------------------------------------
59 sub ol_init {
60         $DB->DBFile($config{dsn}, $config{usr}, $config{pw});
61         ol_connect();
62 }
63
64 sub ol_connect {
65         OpenSRF::System->bootstrap_client(config_file => $bootstrap ); 
66         Fieldmapper->import(IDL => 
67                 OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
68
69 }
70
71
72 sub _ol_debug_params {
73         my $s = "";
74         my @params = $cgi->param;
75         @params = sort { $a cmp $b } @params;
76         $s .= "$_=" . $cgi->param($_) . "\n" for @params;
77         $s =~ s/\n$//o;
78         warn '-'x60 ."\n$s\n";
79 }
80
81
82 # --------------------------------------------------------------------
83 # Finds the requestor and other info specific to this request
84 # --------------------------------------------------------------------
85 sub ol_runtime_init {
86
87         # fetch the requestor object
88         ($requestor, $evt) = $U->checkses($authtoken);
89         ol_handle_result($evt) if $evt;
90
91         # try the param, the workstation, and finally the user's ws org
92         if(!$org) { 
93                 $wsobj = ol_fetch_workstation($wsname);
94                 $org = $wsobj->owning_lib if $wsobj;
95                 $org = $requestor->ws_ou unless $org;
96                 ol_handle_result(OpenILS::Event->new('OFFLINE_NO_ORG')) unless $org;
97         }
98
99     $user_groups = $U->simplereq(
100         'open-ils.actor', 'open-ils.actor.groups.retrieve');
101 }
102
103
104 # --------------------------------------------------------------------
105 # Runs the requested action
106 # --------------------------------------------------------------------
107 sub ol_do_action {
108
109         my $payload;
110
111         if( $action eq 'create' ) {
112                 
113                 $evt = $U->check_perms($requestor->id, $org, 'OFFLINE_UPLOAD');
114                 ol_handle_result($evt) if $evt;
115                 $payload = ol_create_session();
116
117         } elsif( $action eq 'load' ) {
118
119                 $evt = $U->check_perms($requestor->id, $org, 'OFFLINE_UPLOAD');
120                 ol_handle_result($evt) if $evt;
121                 $payload = ol_load();
122
123         } elsif( $action eq 'execute' ) {
124
125                 $evt = $U->check_perms($requestor->id, $org, 'OFFLINE_EXECUTE');
126                 ol_handle_result($evt) if $evt;
127                 $payload = ol_execute();
128
129         } elsif( $action eq 'status' ) {
130
131                 $evt = $U->check_perms($requestor->id, $org, 'OFFLINE_VIEW');
132                 ol_handle_result($evt) if $evt;
133                 $payload = ol_status();
134         }
135
136         ol_handle_event('SUCCESS', payload => $payload );
137 }
138
139
140 # --------------------------------------------------------------------
141 # Creates a new session
142 # --------------------------------------------------------------------
143 sub ol_create_session {
144
145         my $desc = $cgi->param('desc') || "";
146         $seskey = time . "_${$}_" . int(rand() * 1000);
147
148         $logger->debug("offline: user ".$requestor->id.
149                 " creating new session with key $seskey and description $desc");
150
151         $SES->create(
152                 {       
153                         key                             => $seskey,
154                         org                             => $org,
155                         description             => $desc,
156                         creator                 => $requestor->id,
157                         create_time             => CORE::time(), 
158                         num_complete    => 0,
159                 } 
160         );
161
162         return $seskey;
163 }
164
165
166 # --------------------------------------------------------------------
167 # Holds the meta-info for a script file
168 # --------------------------------------------------------------------
169 sub ol_create_script {
170         my $count = shift;
171
172         my $session = ol_find_session($seskey);
173         my $delta = $cgi->param('delta') || 0;
174
175         my $script = $session->add_to_scripts( 
176                 {
177                         requestor       => $requestor->id,
178                         create_time     => CORE::time,
179                         workstation     => $wsname,
180                         logfile         => "$basedir/pending/$org/$seskey/$wsname.log",
181                         time_delta      => $delta,
182                         count                   => $count,
183                 }
184         );
185 }
186
187 # --------------------------------------------------------------------
188 # Finds the current session in the db
189 # --------------------------------------------------------------------
190 sub ol_find_session {
191         my $ses = $SES->retrieve($seskey);
192         ol_handle_event('OFFLINE_INVALID_SESSION', payload => $seskey) unless $ses;
193         return $ses;
194 }
195
196 # --------------------------------------------------------------------
197 # Finds a script object in the DB based on workstation and seskey
198 # --------------------------------------------------------------------
199 sub ol_find_script {
200         my $ws = shift || $wsname;
201         my $sk = shift || $seskey;
202         my ($script) = $SCRIPT->search( session => $seskey, workstation => $ws );
203         return $script;
204 }
205
206 # --------------------------------------------------------------------
207 # Creates a new script in the database and loads the new script file
208 # --------------------------------------------------------------------
209 sub ol_load {
210
211         my $session = ol_find_session;
212         my $handle      = $cgi->upload('file');
213         my $outdir      = "$basedir/pending/$org/$seskey";
214         my $outfile = "$outdir/$wsname.log";
215
216         ol_handle_event('OFFLINE_SESSION_FILE_EXISTS') if ol_find_script();
217         ol_handle_event('OFFLINE_SESSION_ACTIVE') if $session->in_process;
218         ol_handle_event('OFFLINE_SESSION_COMPLETE') if $session->end_time;
219
220         qx/mkdir -p $outdir/;
221         my $x = 0;
222         open(FILE, ">>$outfile") or ol_handle_event('OFFLINE_FILE_ERROR');
223         while( <$handle> ) { print FILE; $x++;}
224         close(FILE);
225
226         ol_create_script($x);
227
228         return undef;
229 }
230
231
232 # --------------------------------------------------------------------
233 sub ol_handle_result {
234         my $obj = shift;
235         my $json = OpenSRF::Utils::JSON->perl2JSON($obj);
236
237         # Clear this so it's not remembered
238         $evt = undef;
239
240         if( $cgi->param('html')) {
241                 my $html = "<html><body onload='xulG.handle_event($json)'></body></html>";
242                 print "content-type: text/html\n\n";
243                 print "$html\n";
244
245         } else {
246
247                 print "content-type: text/plain\n\n";
248                 print "$json\n";
249         }
250
251         exit(0);
252 }
253
254 # --------------------------------------------------------------------
255 sub ol_handle_event {
256         my( $evt, @args ) = @_;
257         ol_handle_result(OpenILS::Event->new($evt, @args));
258 }
259
260
261 # --------------------------------------------------------------------
262 sub ol_flesh_session {
263         my $session = shift;
264         my %data;
265
266         map { $data{$_} = $session->$_ } $session->columns;
267         $data{scripts} = [];
268
269         for my $script ($session->scripts) {
270                 my %sdata;
271                 map { $sdata{$_} = $script->$_ } $script->columns;
272
273                 # the client doesn't need this info
274                 delete $sdata{session};
275                 delete $sdata{id};
276                 delete $sdata{logfile};
277
278                 push( @{$data{scripts}}, \%sdata );
279         }
280
281         return \%data;
282 }
283
284
285 # --------------------------------------------------------------------
286 # Returns various information on the sessions and scripts
287 # --------------------------------------------------------------------
288 sub ol_status {
289
290         my $type = $cgi->param('status_type') || "scripts";
291
292         # --------------------------------------------------------------------
293         # Returns info on every script upload attached to the current session
294         # --------------------------------------------------------------------
295         if( $type eq 'scripts' ) {
296                 my $session = ol_find_session();
297                 ol_handle_result(ol_flesh_session($session));
298
299
300         # --------------------------------------------------------------------
301         # Returns all scripts and sessions for the given org
302         # --------------------------------------------------------------------
303         } elsif( $type eq 'sessions' ) {
304                 my @sessions = $SES->search( org => $org );
305
306                 # can I do this in the DB without raw SQL?
307                 @sessions = sort { $a->create_time <=> $b->create_time } @sessions; 
308                 my @data;
309                 push( @data, ol_flesh_session($_) ) for @sessions;
310                 ol_handle_result(\@data);
311
312
313         # --------------------------------------------------------------------
314         # Returns total commands and completed commands counts
315         # --------------------------------------------------------------------
316         } elsif( $type eq 'summary' ) {
317                 my $session = ol_find_session();
318
319                 $logger->debug("offline: retrieving summary info ".
320                         "for session ".$session->key." with completed=".$session->num_complete);
321
322                 my $count = 0;
323                 $count += $_->count for ($session->scripts);
324                 ol_handle_result(
325                         { total => $count, num_complete => $session->num_complete });
326
327
328
329         # --------------------------------------------------------------------
330         # Returns the list of non-SUCCESS events that have occurred so far for 
331         # this set of commands
332         # --------------------------------------------------------------------
333         } elsif( $type eq 'exceptions' ) {
334
335                 my $session = ol_find_session();
336                 my $resfile = "$basedir/pending/$org/$seskey/results";
337                 if( $session->end_time ) {
338                         $resfile = "$basedir/archive/$org/$seskey/results";
339                 }
340                 my $data = ol_file_to_perl($resfile);
341         my $data2 = [];
342         for my $d (@$data) {
343             my $evt = $d->{event};
344             $evt = $evt->[0] if ref $evt eq 'ARRAY';
345             push(@$data2, $d) if $evt->{ilsevent} ne '0';
346         }
347                 #$data = [ grep { $_->{event}->{ilsevent} ne '0' } @$data ];
348                 ol_handle_result($data2);
349         }
350 }
351
352
353 sub ol_fetch_workstation {
354         my $name = shift;
355         $logger->debug("offline: Fetching workstation $name");
356         my $ws = $U->storagereq(
357                 'open-ils.storage.direct.actor.workstation.search.name', $name);
358         ol_handle_result(OpenILS::Event->new('ACTOR_WORKSTATION_NOT_FOUND')) unless $ws;
359         return $ws;
360 }
361
362
363
364
365 # --------------------------------------------------------------------
366 # Sorts the script commands then forks a child to executes them.
367 # --------------------------------------------------------------------
368 sub ol_execute {
369
370         my $session = ol_find_session();
371         ol_handle_event('OFFLINE_SESSION_ACTIVE') if $session->in_process;
372         ol_handle_event('OFFLINE_SESSION_COMPLETE') if $session->end_time;
373
374         my $commands = ol_collect_commands();
375
376         # --------------------------------------------------------------------
377         # Note that we must disconnect from opensrf before forking or the 
378         # connection will be borked...
379         # --------------------------------------------------------------------
380         OpenSRF::Transport::PeerHandle->retrieve->disconnect;
381         $DB->disconnect;
382
383
384         if( safe_fork() ) {
385
386                 # --------------------------------------------------------------------
387                 # Tell the client all is well
388                 # --------------------------------------------------------------------
389                 ol_handle_event('SUCCESS'); # - this exits
390
391         } else {
392
393
394                 # --------------------------------------------------------------------
395                 # close stdout/stderr or apache will wait on the child to finish
396                 # --------------------------------------------------------------------
397                 close(STDOUT);
398                 close(STDERR);
399
400                 $logger->debug("offline: child $$ processing data...");
401
402                 # --------------------------------------------------------------------
403                 # The child re-connects to the opensrf network and processes
404                 # the script requests 
405                 # --------------------------------------------------------------------
406                 OpenSRF::System->bootstrap_client(config_file => $bootstrap);
407         
408                 try {
409
410                         #use Class::DBI
411                         #Class::DBI->autoupdate(1);
412
413                         $DB->autoupdate(1);
414
415                         my $sesion = ol_find_session();
416                         $session->in_process(1);
417                         ol_process_commands($session, $commands);
418                         ol_archive_files($session);
419
420                 } catch Error with {
421                         my $e = shift;
422                         $logger->error("offline: child process error $e");
423                 };
424         }
425 }
426
427 sub ol_file_to_perl {
428         my $fname = shift;
429         open(F, "$fname") or ol_handle_event('OFFLINE_FILE_ERROR');
430         my @d = <F>;
431         my @p;
432         push(@p, OpenSRF::Utils::JSON->JSON2perl($_)) for @d;
433         close(F);
434         return \@p;
435 }
436
437 # collects the commands and sorts them on timestamp+delta
438 sub ol_collect_commands {
439         my $ses = ol_find_session();
440         my @commands;
441
442         # cycle through every script loaded to this session
443         for my $script ($ses->scripts) {
444                 my $coms = ol_file_to_perl($script->logfile);
445
446                 # cycle through all of the commands for this script
447                 for my $com (@$coms) {
448                         $$com{_workstation} = $script->workstation;
449                         $$com{_realtime} = $script->time_delta + $com->{timestamp};
450                         push( @commands, $com );
451                 }
452         }
453
454         # make sure thera are no blank commands
455         @commands = grep { ($_ and $_->{type}) } @commands;
456
457         # sort on realtime
458         @commands = sort { $a->{_realtime} <=> $b->{_realtime} } @commands;
459
460         # push user registrations to the front
461         my @regs                = grep { $_->{type} eq 'register' } @commands;
462         my @others      = grep { $_->{type} ne 'register' } @commands;
463
464         return [ @regs, @others ];
465 }
466
467 sub ol_date {
468         my $time = shift || CORE::time;
469         my (undef,undef, undef, $mday,$mon,$year) = localtime($time);
470         $mon++; $year   += 1900;
471         $mday   = "0$mday" unless $mday =~ /\d{2}/o;
472         $mon    = "0$mon" unless $mon   =~ /\d{2}/o;
473         return ($year, $mon, $mday);
474 }
475
476
477 # --------------------------------------------------------------------
478 # Moves all files from the pending directory to the archive directory
479 # and removes the pending directory
480 # --------------------------------------------------------------------
481 sub ol_archive_files {
482         my $session = shift;
483         my ($y, $m, $d) = ol_date();
484
485         my $dir = "$basedir/pending/$org/$seskey";
486         my $archdir = "$basedir/archive/$org/$seskey";
487         $logger->debug("offline: archiving files to $archdir");
488
489         # Tell the db the files are moving
490         $_->logfile($archdir.'/'.$_->workstation.'.log') for ($session->scripts);
491
492         qx/mkdir -p $archdir/;
493         qx/mv $_ $archdir/ for <$dir/*>;
494         qx/rmdir $dir/;
495 }
496
497
498 # --------------------------------------------------------------------
499 # Appends results to the results file.
500 # --------------------------------------------------------------------
501 my $rhandle;
502 sub ol_append_result {
503
504         my $obj = shift;
505         my $last = shift;
506
507         $obj = OpenSRF::Utils::JSON->perl2JSON($obj);
508
509         if(!$rhandle) {
510                 open($rhandle, ">>$basedir/pending/$org/$seskey/results") 
511                         or ol_handle_event('OFFLINE_FILE_ERROR');
512         }
513
514         print $rhandle "$obj\n";
515         close($rhandle) if $last;
516 }
517
518
519
520 # --------------------------------------------------------------------
521 # Runs the commands and returns the list of errors
522 # --------------------------------------------------------------------
523 sub ol_process_commands {
524
525         my $session      = shift;
526         my $commands = shift;
527         my $x        = 0;
528
529         $session->start_time(CORE::time);
530
531         for my $d ( @$commands ) {
532
533                 my $t           = $d->{type};
534                 my $last = ($x++ == scalar(@$commands) - 1) ? 1 : 0;
535                 my $res = { command => $d };
536                 my $err;
537
538                 while( 1 ) {
539
540                         $err = undef;
541                         $logger->debug("offline: top of execute loop : $t");
542
543                         try {
544                                 $res->{event} = ol_handle_checkin($d)   if $t eq 'checkin';
545                                 $res->{event} = ol_handle_inhouse($d)   if $t eq 'in_house_use';
546                                 $res->{event} = ol_handle_checkout($d) if $t eq 'checkout';
547                                 $res->{event} = ol_handle_renew($d)             if $t eq 'renew';
548                                 $res->{event} = ol_handle_register($d) if $t eq 'register';
549         
550                         } catch Error with { $err = shift; };
551
552                         if( $err ) {
553
554                                 if( ref($err) eq 'OpenSRF::EX::JabberDisconnected' ) {
555                                         $logger->error("offline: we lost jabber .. trying to reconnect");
556                                         ol_connect();
557
558                                 } else {
559                                         $res->{event} = OpenILS::Event->new('INTERNAL_SERVER_ERROR', debug => "$err");
560                                         last;
561                                 }
562
563                         } else { last; }
564
565                         sleep(1);
566                 }
567
568                 ol_append_result($res, $last);
569                 $session->num_complete( $session->num_complete + 1 );
570
571                 $logger->debug("offline: set session [".$session->key."] num_complete to ".$session->num_complete);
572         }
573
574         $session->end_time(CORE::time);
575         $session->in_process(0);
576 }
577
578
579 # --------------------------------------------------------------------
580 # Runs an in_house_use action
581 # --------------------------------------------------------------------
582 sub ol_handle_inhouse {
583
584         my $command             = shift;
585         my $realtime    = $command->{_realtime};
586         my $ws                  = $command->{_workstation};
587         my $barcode             = $command->{barcode};
588         my $count               = $command->{count} || 1;
589         my $use_time    = $command->{use_time} || "";
590
591         $logger->activity("offline: in_house_use : requestor=". $requestor->id.", realtime=$realtime, ".  
592                 "workstation=$ws, barcode=$barcode, count=$count, use_time=$use_time");
593
594         if( $count > 99 ) {
595                 return OpenILS::Event->new(
596                         'INTERNAL_SERVER_ERROR', payload => 'TOO MANY IN HOUSE USE');
597         }
598
599         my $ids = $U->simplereq(
600                 'open-ils.circ', 
601                 'open-ils.circ.in_house_use.create', $authtoken, 
602                 { barcode => $barcode, count => $count, location => $org, use_time => $use_time } );
603         
604         return OpenILS::Event->new('SUCCESS', payload => $ids) if( ref($ids) eq 'ARRAY' );
605         return $ids;
606 }
607
608
609
610 # --------------------------------------------------------------------
611 # Pulls the relevant circ args from the command, fetches data where 
612 # necessary
613 # --------------------------------------------------------------------
614 my %user_id_cache;
615 sub ol_circ_args_from_command {
616         my $command = shift;
617
618         my $type                        = $command->{type};
619         my $realtime    = $command->{_realtime};
620         my $ws                  = $command->{_workstation};
621         my $barcode             = $command->{barcode} || "";
622         my $cotime              = $command->{checkout_time} || "";
623         my $pbc                 = $command->{patron_barcode};
624         my $due_date    = $command->{due_date} || "";
625         my $noncat              = ($command->{noncat}) ? "yes" : "no"; # for logging
626
627         $logger->activity("offline: $type : requestor=". $requestor->id.
628                 ", realtime=$realtime, workstation=$ws, checkout_time=$cotime, ".
629                 "patron=$pbc, due_date=$due_date, noncat=$noncat");
630
631
632         my $args = { 
633                 permit_override => 1, 
634                 barcode         => $barcode,            
635                 checkout_time   => $cotime, 
636                 patron_barcode  => $pbc,
637                 due_date        => $due_date 
638     };
639
640     if(ol_get_org_setting('circ.offline.username_allowed')) {
641
642         my $format = ol_get_org_setting('opac.barcode_regex');
643         if($format) {
644
645             $format =~ s/^\/|\/$//g; # remove any couching //'s
646             if($pbc !~ qr/$format/) {
647
648                 # the patron barcode does not match the configured barcode format
649                 # assume they passed a username instead
650
651                 my $user_id = $user_id_cache{$pbc} ||
652                     $U->simplereq(
653                         'open-ils.actor', 
654                         'open-ils.actor.username.exists', 
655                         $authtoken, $pbc);
656
657                 
658                 if($user_id) {
659                     # a valid username was provided, update the args and cache
660                     $user_id_cache{$pbc} = $user_id;
661                     $args->{patron_id} = $user_id;
662                     delete $args->{patron_barcode};
663                 }
664             }
665         }
666     }
667
668
669         if( $command->{noncat} ) {
670                 $args->{noncat} = 1;
671                 $args->{noncat_type} = $command->{noncat_type};
672                 $args->{noncat_count} = $command->{noncat_count};
673         }
674
675         return $args;
676 }
677
678 sub ol_get_org_setting {
679     my $name = shift;
680     return $U->simplereq(
681         'open-ils.actor',
682         'open-ils.actor.ou_setting.ancestor_default',
683         $org, $name, $authtoken);
684 }
685
686
687
688 # --------------------------------------------------------------------
689 # Performs a checkout action
690 # --------------------------------------------------------------------
691 sub ol_handle_checkout {
692         my $command     = shift;
693         my $args = ol_circ_args_from_command($command);
694
695         if( $args->{noncat} and $args->{noncat_count} > 99 ) {
696                 return OpenILS::Event->new(
697                         'INTERNAL_SERVER_ERROR', payload => 'TOO MANY NON CATS');
698         }
699
700     if( $args->{barcode} ) {
701         my( $c, $e ) = $U->fetch_copy_by_barcode($args->{barcode});
702         return $e if $e;
703     }
704
705     my $evt = $U->simplereq(
706                 'open-ils.circ', 'open-ils.circ.checkout', $authtoken, $args );
707
708     # if the item is already checked out to this user and we are past 
709     # the configured auto-renewal interval, try to renew the circ.
710     if( ref $evt ne 'ARRAY' and
711         $evt->{textcode} == 'OPEN_CIRCULATION_EXISTS' and 
712         $evt->{payload}->{auto_renew}) {
713
714             return ol_handle_renew($command);
715     }
716
717     return $evt;
718 }
719
720
721 # --------------------------------------------------------------------
722 # Performs the renewal action
723 # --------------------------------------------------------------------
724 sub ol_handle_renew {
725         my $command = shift;
726         my $args = ol_circ_args_from_command($command);
727         my $t = time;
728         return $U->simplereq(
729                 'open-ils.circ', 'open-ils.circ.renew.override', $authtoken, $args );
730 }
731
732
733 # --------------------------------------------------------------------
734 # Runs a checkin action
735 # --------------------------------------------------------------------
736 sub ol_handle_checkin {
737
738         my $command             = shift;
739         my $realtime    = $command->{_realtime};
740         my $ws                  = $command->{_workstation};
741         my $barcode             = $command->{barcode};
742         my $backdate    = $command->{backdate} || "";
743
744         $logger->activity("offline: checkin : requestor=". $requestor->id.
745                 ", realtime=$realtime, ".  "workstation=$ws, barcode=$barcode, backdate=$backdate");
746
747         return $U->simplereq(
748                 'open-ils.circ', 
749                 'open-ils.circ.checkin', $authtoken,
750                 { barcode => $barcode, backdate => $backdate } );
751 }
752
753
754
755 # --------------------------------------------------------------------
756 # Registers a new patron
757 # --------------------------------------------------------------------
758 sub ol_handle_register {
759         my $command = shift;
760
761         my $barcode = $command->{user}->{card}->{barcode};
762         delete $command->{user}->{card}; 
763
764         $logger->info("offline: creating new user with barcode $barcode");
765
766         # now, create the user
767         my $actor       = Fieldmapper::actor::user->new;
768         my $card                = Fieldmapper::actor::card->new;
769
770
771         # username defaults to the barcode
772         $actor->usrname( ($actor->usrname) ? $actor->usrname : $barcode );
773
774         # Set up all of the virtual IDs, isnew, etc.
775         $actor->isnew(1);
776         $actor->id(-1);
777         $actor->card(-1);
778         $actor->cards([$card]);
779
780         $card->isnew(1);
781         $card->id(-1);
782         $card->usr(-1);
783         $card->barcode($barcode);
784
785         my $billing_address;
786         my $mailing_address;
787
788         my @sresp;
789         for my $resp (@{$command->{user}->{survey_responses}}) {
790                 my $sr = Fieldmapper::action::survey_response->new;
791                 $sr->$_( $resp->{$_} ) for keys %$resp;
792                 $sr->isnew(1);
793                 $sr->usr(-1);
794                 push(@sresp, $sr);
795                 $logger->debug("offline: created new survey response for survey ".$sr->survey);
796         }
797         delete $command->{user}->{survey_responses};
798         $actor->survey_responses(\@sresp) if @sresp;
799
800         # extract the billing address
801         if( my $addr = $command->{user}->{billing_address} ) {
802                 $billing_address = Fieldmapper::actor::user_address->new;
803                 $billing_address->$_($addr->{$_}) for keys %$addr;
804                 $billing_address->isnew(1);
805                 $billing_address->id(-1);
806                 $billing_address->usr(-1);
807                 delete $command->{user}->{billing_address};
808                 $logger->debug("offline: read billing address ".$billing_address->street1);
809         }
810
811         # extract the mailing address
812         if( my $addr = $command->{user}->{mailing_address} ) {
813                 $mailing_address = Fieldmapper::actor::user_address->new;
814                 $mailing_address->$_($addr->{$_}) for keys %$addr;
815                 $mailing_address->isnew(1);
816                 $mailing_address->id(-2);
817                 $mailing_address->usr(-1);
818                 delete $command->{user}->{mailing_address};
819                 $logger->debug("offline: read mailing address ".$mailing_address->street1);
820         }
821
822         # make sure we have values for both
823         $billing_address ||= $mailing_address;
824         $mailing_address ||= $billing_address;
825
826         $actor->billing_address($billing_address->id);
827         $actor->mailing_address($mailing_address->id);
828         $actor->addresses([$mailing_address]);
829
830         push( @{$actor->addresses}, $billing_address ) 
831                 unless $billing_address->id eq $mailing_address->id;
832         
833         # pull all of the rest of the data from the command blob
834         $actor->$_( $command->{user}->{$_} ) for keys %{$command->{user}};
835
836     # calculate the expire date for the patron based on the profile group
837     my ($grp) = grep {$_->id == $actor->profile} @$user_groups;
838     if($grp) {
839         my $seconds = OpenSRF::Utils->interval_to_seconds($grp->perm_interval);
840         my $expire_date = DateTime->from_epoch(epoch => DateTime->now->epoch + $seconds)->epoch;
841                 $logger->debug("offline: setting expire date to $expire_date");
842         $actor->expire_date($U->epoch2ISO8601($expire_date));
843     }
844
845         $logger->debug("offline: creating user object...");
846         $actor = $U->simplereq(
847                 'open-ils.actor', 
848                 'open-ils.actor.patron.update', $authtoken, $actor);
849
850         return $actor if(ref($actor) eq 'HASH'); # an event occurred
851
852         return OpenILS::Event->new('SUCCESS', payload => $actor);
853 }
854
855
856
857
858
859
860