2 use strict; use warnings;
4 use OpenSRF::Transport::PeerHandle;
10 require 'offline-lib.pl';
12 $SIG{CHLD} = 'IGNORE'; # - we don't care what happens to our child process
17 # --------------------------------------------------------------------
18 # Loads the offline script files for a given org, sorts and runs the
19 # scripts, and returns the exception list
20 # --------------------------------------------------------------------
23 # --------------------------------------------------------------------
24 # Make sure the caller has the right permissions
25 # --------------------------------------------------------------------
26 my $evt = $U->check_perms(&offline_requestor->id, &offline_org, 'OFFLINE_EXECUTE');
27 handle_event($evt) if $evt;
30 # --------------------------------------------------------------------
31 # First make sure the data is there and in a good state
32 # --------------------------------------------------------------------
33 my $data = &sort_data( &collect_data );
36 # --------------------------------------------------------------------
37 # Note that we must disconnect from opensrf before forking or the
38 # connection will be borked...
39 # --------------------------------------------------------------------
40 my $con = OpenSRF::Transport::PeerHandle->retrieve;
41 $con->disconnect if $con;
46 # --------------------------------------------------------------------
47 # Tell the client all is well
48 # --------------------------------------------------------------------
49 handle_event(OpenILS::Event->new('SUCCESS')); # - this exits
53 # --------------------------------------------------------------------
54 # close stdout/stderr or apache will wait on the child to finish
55 # --------------------------------------------------------------------
59 $logger->debug("offline: child $$ processing data...");
61 # --------------------------------------------------------------------
62 # The child re-connects to the opensrf network and processes
64 # --------------------------------------------------------------------
65 my %config = &offline_config;
66 OpenSRF::System->bootstrap_client(config_file => $config{bootstrap});
68 &process_data( $data );
75 # --------------------------------------------------------------------
76 # Collects all of the script logs into an in-memory structure that
78 # Returns a blob like -> { $ws1 => [ commands... ], $ws2 => [ commands... ] }
79 # --------------------------------------------------------------------
82 my $dir = &offline_pending_dir;
83 my $lock = &offline_lock_file;
85 handle_event(OpenILS::Event->new('OFFLINE_PARAM_ERROR')) unless &offline_org;
86 handle_event(OpenILS::Event->new('OFFLINE_SESSION_ACTIVE')) if ( -e $lock );
88 qx/touch $lock/ and handle_event(OpenILS::Event->new(
89 'OFFLINE_FILE_ERROR', payload => "cannot touch lock file: $lock"));
94 # Load the data from the list of files
95 while( ($file = <$dir/*.log>) ) {
96 $logger->debug("offline: Loading script file $file");
97 open(F, $file) or handle_event(
98 OpenILS::Event->new('OFFLINE_FILE_ERROR'));
99 my $ws = log_to_wsname($file);
101 push(@{$data{$ws}}, , <F>);
108 # --------------------------------------------------------------------
110 # --------------------------------------------------------------------
115 $logger->debug("offline: Sorting data");
116 my $meta = &offline_read_meta;
118 # cycle through the workstations
119 for my $ws (keys %$data) {
121 # find the meta line for this ws.
122 my ($m) = grep { $_->{workstation} eq $ws } @$meta;
124 $logger->debug("offline: Sorting workstations $ws with a time delta of ".$m->{delta});
126 my @scripts = @{$$data{$ws}};
128 # cycle through the scripts for the current workstation
129 for my $s (@scripts) {
130 my $command = JSON->JSON2perl($s);
131 $command->{_workstation} = $ws;
132 $command->{_realtime} = $command->{timestamp} + $m->{delta};
133 $logger->debug("offline: setting realtime to ".
134 $command->{_realtime} . " from timestamp " . $command->{timestamp});
135 push( @parsed, $command );
140 @parsed = sort { $a->{_realtime} <=> $b->{_realtime} } @parsed;
145 # --------------------------------------------------------------------
146 # Runs the commands and returns the list of errors
147 # --------------------------------------------------------------------
156 append_result( {command => $d, event => handle_checkin($d)}) if $t eq 'checkin';
157 append_result( {command => $d, event => handle_inhouse($d)}) if $t eq 'in_house_use';
158 append_result( {command => $d, event => handle_checkout($d)}) if $t eq 'checkout';
159 append_result( {command => $d, event => handle_renew($d)}) if $t eq 'renew';
160 append_result( {command => $d, event => handle_register($d)}) if $t eq 'register';
167 # --------------------------------------------------------------------
168 # Runs an in_house_use action
169 # --------------------------------------------------------------------
173 my $realtime = $command->{_realtime};
174 my $ws = $command->{_workstation};
175 my $barcode = $command->{barcode};
176 my $count = $command->{count} || 1;
177 my $use_time = $command->{use_time} || "";
179 $logger->activity("offline: in_house_use : requestor=". &offline_requestor->id.", realtime=$realtime, ".
180 "workstation=$ws, barcode=$barcode, count=$count, use_time=$use_time");
182 my $ids = $U->simplereq(
184 'open-ils.circ.in_house_use.create', &offline_authtoken,
185 { barcode => $barcode, count => $count, location => &offline_org, use_time => $use_time } );
187 return OpenILS::Event->new('SUCCESS', payload => $ids) if( ref($ids) eq 'ARRAY' );
193 # --------------------------------------------------------------------
194 # Pulls the relevant circ args from the command, fetches data where
196 # --------------------------------------------------------------------
197 sub circ_args_from_command {
200 my $type = $command->{type};
201 my $realtime = $command->{_realtime};
202 my $ws = $command->{_workstation};
203 my $barcode = $command->{barcode} || "";
204 my $cotime = $command->{checkout_time} || "";
205 my $pbc = $command->{patron_barcode};
206 my $due_date = $command->{due_date} || "";
207 my $noncat = ($command->{noncat}) ? "yes" : "no"; # for logging
209 $logger->activity("offline: $type : requestor=". &offline_requestor->id.
210 ", realtime=$realtime, workstation=$ws, checkout_time=$cotime, ".
211 "patron=$pbc, due_date=$due_date, noncat=$noncat");
214 permit_override => 1,
216 checkout_time => $cotime,
217 patron_barcode => $pbc,
218 due_date => $due_date };
220 if( $command->{noncat} ) {
222 $args->{noncat_type} = $command->{noncat_type};
223 $args->{noncat_count} = $command->{noncat_count};
231 # --------------------------------------------------------------------
232 # Performs a checkout action
233 # --------------------------------------------------------------------
234 sub handle_checkout {
236 my $args = circ_args_from_command($command);
237 return $U->simplereq(
238 'open-ils.circ', 'open-ils.circ.checkout', &offline_authtoken, $args );
242 # --------------------------------------------------------------------
243 # Performs the renewal action
244 # --------------------------------------------------------------------
247 my $args = circ_args_from_command($command);
249 return $U->simplereq(
250 'open-ils.circ', 'open-ils.circ.renew', &offline_authtoken, $args );
254 # --------------------------------------------------------------------
255 # Runs a checkin action
256 # --------------------------------------------------------------------
260 my $realtime = $command->{_realtime};
261 my $ws = $command->{_workstation};
262 my $barcode = $command->{barcode};
263 my $backdate = $command->{backdate} || "";
265 $logger->activity("offline: checkin : requestor=". &offline_requestor()->id.
266 ", realtime=$realtime, ". "workstation=$ws, barcode=$barcode, backdate=$backdate");
268 return $U->simplereq(
270 'open-ils.circ.checkin', &offline_authtoken,
271 { barcode => $barcode, backdate => $backdate } );
276 # --------------------------------------------------------------------
277 # Registers a new patron
278 # --------------------------------------------------------------------
279 sub handle_register {
282 my $barcode = $command->{user}->{card}->{barcode};
283 delete $command->{user}->{card};
285 $logger->info("offline: creating new user with barcode $barcode");
287 # now, create the user
288 my $actor = Fieldmapper::actor::user->new;
289 my $card = Fieldmapper::actor::card->new;
292 # username defaults to the barcode
293 $actor->usrname( ($actor->usrname) ? $actor->usrname : $barcode );
295 # Set up all of the virtual IDs, isnew, etc.
299 $actor->cards([$card]);
304 $card->barcode($barcode);
309 # extract the billing address
310 if( my $addr = $command->{user}->{billing_address} ) {
311 $billing_address = Fieldmapper::actor::user_address->new;
312 $billing_address->$_($addr->{$_}) for keys %$addr;
313 $billing_address->isnew(1);
314 $billing_address->id(-1);
315 $billing_address->usr(-1);
316 delete $command->{user}->{billing_address};
317 $logger->debug("offline: read billing address ".$billing_address->street1);
320 # extract the mailing address
321 if( my $addr = $command->{user}->{mailing_address} ) {
322 $mailing_address = Fieldmapper::actor::user_address->new;
323 $mailing_address->$_($addr->{$_}) for keys %$addr;
324 $mailing_address->isnew(1);
325 $mailing_address->id(-2);
326 $mailing_address->usr(-1);
327 delete $command->{user}->{mailing_address};
328 $logger->debug("offline: read mailing address ".$mailing_address->street1);
331 # make sure we have values for both
332 $billing_address ||= $mailing_address;
333 $mailing_address ||= $billing_address;
335 $actor->billing_address($billing_address->id);
336 $actor->mailing_address($mailing_address->id);
337 $actor->addresses([$mailing_address]);
339 push( @{$actor->addresses}, $billing_address )
340 unless $billing_address->id eq $mailing_address->id;
342 # pull all of the rest of the data from the command blob
343 $actor->$_( $command->{user}->{$_} ) for keys %{$command->{user}};
345 $logger->debug("offline: creating user object...");
346 $actor = $U->simplereq(
348 'open-ils.actor.patron.update', &offline_authtoken, $actor);
350 return $actor if(ref($actor) eq 'HASH'); # an event occurred
352 return OpenILS::Event->new('SUCCESS', payload => $actor);
358 # --------------------------------------------------------------------
359 # Removes the log file and Moves the script files from the pending
360 # directory to the archive dir
361 # --------------------------------------------------------------------
363 my $archivedir = &create_archive_dir;
364 my $pendingdir = &offline_pending_dir;
366 my @files = <$pendingdir/*.log>;
367 push(@files, &offline_meta_file);
368 push(@files, &offline_result_file);
370 $logger->debug("offline: Archiving files [@files] to $archivedir...");
372 my $lock = &offline_lock_file;
373 qx/rm $lock/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
375 return unless (<$pendingdir/*>);
378 qx/mv $f $archivedir/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
381 qx/rmdir $pendingdir/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
383 (my $parentdir = $pendingdir) =~ s#^(/.*)/\w+/?$#$1#og; # - grab the parent dir
384 qx/rmdir $parentdir/ unless <$parentdir/*>; # - remove the parent if empty