2 use strict; use warnings;
4 use OpenSRF::Transport::PeerHandle;
6 use OpenSRF::EX qw/:try/;
11 require 'offline-lib.pl';
13 $SIG{CHLD} = 'IGNORE'; # - we don't care what happens to our child process
18 # --------------------------------------------------------------------
19 # Loads the offline script files for a given org, sorts and runs the
20 # scripts, and returns the exception list
21 # --------------------------------------------------------------------
24 # --------------------------------------------------------------------
25 # Make sure the caller has the right permissions
26 # --------------------------------------------------------------------
27 my $evt = $U->check_perms(&offline_requestor->id, &offline_org, 'OFFLINE_EXECUTE');
28 handle_event($evt) if $evt;
31 # --------------------------------------------------------------------
32 # First make sure the data is there and in a good state
33 # --------------------------------------------------------------------
34 my $data = &sort_data( &collect_data );
37 # --------------------------------------------------------------------
38 # Note that we must disconnect from opensrf before forking or the
39 # connection will be borked...
40 # --------------------------------------------------------------------
41 my $con = OpenSRF::Transport::PeerHandle->retrieve;
42 $con->disconnect if $con;
47 # --------------------------------------------------------------------
48 # Tell the client all is well
49 # --------------------------------------------------------------------
50 handle_event(OpenILS::Event->new('SUCCESS')); # - this exits
54 # --------------------------------------------------------------------
55 # close stdout/stderr or apache will wait on the child to finish
56 # --------------------------------------------------------------------
60 $logger->debug("offline: child $$ processing data...");
62 # --------------------------------------------------------------------
63 # The child re-connects to the opensrf network and processes
65 # --------------------------------------------------------------------
66 my %config = &offline_config;
67 OpenSRF::System->bootstrap_client(config_file => $config{bootstrap});
70 &process_data( $data );
74 $logger->error("offline: child process error $e");
81 # --------------------------------------------------------------------
82 # Collects all of the script logs into an in-memory structure that
84 # Returns a blob like -> { $ws1 => [ commands... ], $ws2 => [ commands... ] }
85 # --------------------------------------------------------------------
88 my $dir = &offline_pending_dir;
89 my $lock = &offline_lock_file;
91 handle_event(OpenILS::Event->new('OFFLINE_SESSION_NOT_FOUND')) unless -e $dir;
92 handle_event(OpenILS::Event->new('OFFLINE_PARAM_ERROR')) unless &offline_org;
93 handle_event(OpenILS::Event->new('OFFLINE_SESSION_ACTIVE')) if ( -e $lock );
95 # - create the lock file
101 # Load the data from the list of files
102 while( ($file = <$dir/*.log>) ) {
103 $logger->debug("offline: Loading script file $file");
104 open(F, $file) or handle_event(
105 OpenILS::Event->new('OFFLINE_FILE_ERROR'));
106 my $ws = log_to_wsname($file);
108 push(@{$data{$ws}}, , <F>);
115 # --------------------------------------------------------------------
117 # --------------------------------------------------------------------
122 $logger->debug("offline: Sorting data");
123 my $meta = &offline_read_meta;
125 # cycle through the workstations
126 for my $ws (keys %$data) {
128 # find the meta line for this ws.
129 my ($m) = grep { $_->{workstation} eq $ws } @$meta;
131 $logger->debug("offline: Sorting workstations $ws with a time delta of ".$m->{delta});
133 my @scripts = @{$$data{$ws}};
135 # cycle through the scripts for the current workstation
136 for my $s (@scripts) {
137 my $command = JSON->JSON2perl($s);
138 $command->{_workstation} = $ws;
139 $command->{_realtime} = $command->{timestamp} + $m->{delta};
140 $logger->debug("offline: setting realtime to ".
141 $command->{_realtime} . " from timestamp " . $command->{timestamp});
142 push( @parsed, $command );
147 @parsed = sort { $a->{_realtime} <=> $b->{_realtime} } @parsed;
152 # --------------------------------------------------------------------
153 # Runs the commands and returns the list of errors
154 # --------------------------------------------------------------------
163 append_result( {command => $d, event => handle_checkin($d)}) if $t eq 'checkin';
164 append_result( {command => $d, event => handle_inhouse($d)}) if $t eq 'in_house_use';
165 append_result( {command => $d, event => handle_checkout($d)}) if $t eq 'checkout';
166 append_result( {command => $d, event => handle_renew($d)}) if $t eq 'renew';
167 append_result( {command => $d, event => handle_register($d)}) if $t eq 'register';
174 # --------------------------------------------------------------------
175 # Runs an in_house_use action
176 # --------------------------------------------------------------------
180 my $realtime = $command->{_realtime};
181 my $ws = $command->{_workstation};
182 my $barcode = $command->{barcode};
183 my $count = $command->{count} || 1;
184 my $use_time = $command->{use_time} || "";
186 $logger->activity("offline: in_house_use : requestor=". &offline_requestor->id.", realtime=$realtime, ".
187 "workstation=$ws, barcode=$barcode, count=$count, use_time=$use_time");
189 my $ids = $U->simplereq(
191 'open-ils.circ.in_house_use.create', &offline_authtoken,
192 { barcode => $barcode, count => $count, location => &offline_org, use_time => $use_time } );
194 return OpenILS::Event->new('SUCCESS', payload => $ids) if( ref($ids) eq 'ARRAY' );
200 # --------------------------------------------------------------------
201 # Pulls the relevant circ args from the command, fetches data where
203 # --------------------------------------------------------------------
204 sub circ_args_from_command {
207 my $type = $command->{type};
208 my $realtime = $command->{_realtime};
209 my $ws = $command->{_workstation};
210 my $barcode = $command->{barcode} || "";
211 my $cotime = $command->{checkout_time} || "";
212 my $pbc = $command->{patron_barcode};
213 my $due_date = $command->{due_date} || "";
214 my $noncat = ($command->{noncat}) ? "yes" : "no"; # for logging
216 $logger->activity("offline: $type : requestor=". &offline_requestor->id.
217 ", realtime=$realtime, workstation=$ws, checkout_time=$cotime, ".
218 "patron=$pbc, due_date=$due_date, noncat=$noncat");
221 permit_override => 1,
223 checkout_time => $cotime,
224 patron_barcode => $pbc,
225 due_date => $due_date };
227 if( $command->{noncat} ) {
229 $args->{noncat_type} = $command->{noncat_type};
230 $args->{noncat_count} = $command->{noncat_count};
238 # --------------------------------------------------------------------
239 # Performs a checkout action
240 # --------------------------------------------------------------------
241 sub handle_checkout {
243 my $args = circ_args_from_command($command);
244 return $U->simplereq(
245 'open-ils.circ', 'open-ils.circ.checkout', &offline_authtoken, $args );
249 # --------------------------------------------------------------------
250 # Performs the renewal action
251 # --------------------------------------------------------------------
254 my $args = circ_args_from_command($command);
256 return $U->simplereq(
257 'open-ils.circ', 'open-ils.circ.renew', &offline_authtoken, $args );
261 # --------------------------------------------------------------------
262 # Runs a checkin action
263 # --------------------------------------------------------------------
267 my $realtime = $command->{_realtime};
268 my $ws = $command->{_workstation};
269 my $barcode = $command->{barcode};
270 my $backdate = $command->{backdate} || "";
272 $logger->activity("offline: checkin : requestor=". &offline_requestor()->id.
273 ", realtime=$realtime, ". "workstation=$ws, barcode=$barcode, backdate=$backdate");
275 return $U->simplereq(
277 'open-ils.circ.checkin', &offline_authtoken,
278 { barcode => $barcode, backdate => $backdate } );
283 # --------------------------------------------------------------------
284 # Registers a new patron
285 # --------------------------------------------------------------------
286 sub handle_register {
289 my $barcode = $command->{user}->{card}->{barcode};
290 delete $command->{user}->{card};
292 $logger->info("offline: creating new user with barcode $barcode");
294 # now, create the user
295 my $actor = Fieldmapper::actor::user->new;
296 my $card = Fieldmapper::actor::card->new;
299 # username defaults to the barcode
300 $actor->usrname( ($actor->usrname) ? $actor->usrname : $barcode );
302 # Set up all of the virtual IDs, isnew, etc.
306 $actor->cards([$card]);
311 $card->barcode($barcode);
317 for my $resp (@{$command->{user}->{survey_responses}}) {
318 my $sr = Fieldmapper::action::survey_response->new;
319 $sr->$_( $resp->{$_} ) for keys %$resp;
323 $logger->debug("offline: created new survey response for survey ".$sr->survey);
325 delete $command->{user}->{survey_responses};
326 $actor->survey_responses(\@sresp) if @sresp;
328 # extract the billing address
329 if( my $addr = $command->{user}->{billing_address} ) {
330 $billing_address = Fieldmapper::actor::user_address->new;
331 $billing_address->$_($addr->{$_}) for keys %$addr;
332 $billing_address->isnew(1);
333 $billing_address->id(-1);
334 $billing_address->usr(-1);
335 delete $command->{user}->{billing_address};
336 $logger->debug("offline: read billing address ".$billing_address->street1);
339 # extract the mailing address
340 if( my $addr = $command->{user}->{mailing_address} ) {
341 $mailing_address = Fieldmapper::actor::user_address->new;
342 $mailing_address->$_($addr->{$_}) for keys %$addr;
343 $mailing_address->isnew(1);
344 $mailing_address->id(-2);
345 $mailing_address->usr(-1);
346 delete $command->{user}->{mailing_address};
347 $logger->debug("offline: read mailing address ".$mailing_address->street1);
350 # make sure we have values for both
351 $billing_address ||= $mailing_address;
352 $mailing_address ||= $billing_address;
354 $actor->billing_address($billing_address->id);
355 $actor->mailing_address($mailing_address->id);
356 $actor->addresses([$mailing_address]);
358 push( @{$actor->addresses}, $billing_address )
359 unless $billing_address->id eq $mailing_address->id;
361 # pull all of the rest of the data from the command blob
362 $actor->$_( $command->{user}->{$_} ) for keys %{$command->{user}};
364 $logger->debug("offline: creating user object...");
365 $actor = $U->simplereq(
367 'open-ils.actor.patron.update', &offline_authtoken, $actor);
369 return $actor if(ref($actor) eq 'HASH'); # an event occurred
371 return OpenILS::Event->new('SUCCESS', payload => $actor);
377 # --------------------------------------------------------------------
378 # Removes the log file and Moves the script files from the pending
379 # directory to the archive dir
380 # --------------------------------------------------------------------
382 my $archivedir = &offline_archive_dir(1);
383 my $pendingdir = &offline_pending_dir;
385 my @files = <$pendingdir/*.log>;
386 push(@files, &offline_meta_file);
387 push(@files, &offline_result_file);
389 $logger->debug("offline: Archiving files [@files] to $archivedir...");
391 my $lock = &offline_lock_file;
392 qx/rm $lock/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
394 return unless (<$pendingdir/*>);
397 qx/mv $f $archivedir/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
400 qx/rmdir $pendingdir/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
402 (my $parentdir = $pendingdir) =~ s#^(/.*)/\w+/?$#$1#og; # - grab the parent dir
403 qx/rmdir $parentdir/ unless <$parentdir/*>; # - remove the parent if empty