1 package OpenILS::Application::Circ::Circulate;
2 use base 'OpenSRF::Application';
3 use strict; use warnings;
4 use OpenSRF::EX qw(:try);
6 use OpenSRF::Utils::Logger qw(:logger);
7 use OpenILS::Utils::ScriptRunner;
8 use OpenILS::Application::AppUtils;
9 use OpenILS::Application::Circ::Holds;
10 $Data::Dumper::Indent = 0;
11 my $apputils = "OpenILS::Application::AppUtils";
12 my $holdcode = "OpenILS::Application::Circ::Holds";
14 my %scripts; # - circulation script filenames
16 my $script_libs; # - any additional script libraries
19 my %contexts; # - Script runner contexts
21 # ------------------------------------------------------------------------------
22 # Load the circ script from the config
23 # ------------------------------------------------------------------------------
27 my $conf = OpenSRF::Utils::SettingsClient->new;
28 my @pfx2 = ( "apps", "open-ils.circ","app_settings" );
29 my @pfx = ( @pfx2, "scripts" );
31 my $p = $conf->config_value( @pfx, 'circ_permit_patron' );
32 my $c = $conf->config_value( @pfx, 'circ_permit_copy' );
33 my $d = $conf->config_value( @pfx, 'circ_duration' );
34 my $f = $conf->config_value( @pfx, 'circ_recurring_fines' );
35 my $m = $conf->config_value( @pfx, 'circ_max_fines' );
36 my $pr = $conf->config_value( @pfx, 'renew_permit' );
37 my $ph = $conf->config_value( @pfx, 'hold_permit' );
38 my $lb = $conf->config_value( @pfx2, 'script_path' );
40 $logger->error( "Missing circ script(s)" )
41 unless( $p and $c and $d and $f and $m and $pr and $ph );
43 $scripts{circ_permit_patron} = $p;
44 $scripts{circ_permit_copy} = $c;
45 $scripts{circ_duration} = $d;
46 $scripts{circ_recurring_fines}= $f;
47 $scripts{circ_max_fines} = $m;
48 $scripts{circ_renew_permit} = $pr;
49 $scripts{hold_permit} = $ph;
51 $lb = [ $lb ] unless ref($lb);
54 $logger->debug("Loaded rules scripts for circ: " .
55 "circ permit patron: $p, circ permit copy: $c, ".
56 "circ duration :$d , circ recurring fines : $f, " .
57 "circ max fines : $m, circ renew permit : $pr, permit hold: $ph");
61 # ------------------------------------------------------------------------------
62 # Loads the necessary circ objects and pushes them into the script environment
63 # Returns ( $data, $evt ). if $evt is defined, then an
64 # unexpedted event occurred and should be dealt with / returned to the caller
65 # ------------------------------------------------------------------------------
69 my $barcode = $params{barcode};
74 $ctx->{type} = $params{type};
75 $ctx->{isrenew} = $params{isrenew};
76 $ctx->{noncat} = $params{noncat};
78 $evt = _ctx_add_patron_objects($ctx, %params);
80 $evt = _ctx_add_copy_objects($ctx, %params) unless $ctx->{noncat};
83 _doctor_circ_objects($ctx);
84 _build_circ_script_runner($ctx);
85 _add_script_runner_methods( $ctx );
90 sub _ctx_add_patron_objects {
91 my( $ctx, %params) = @_;
93 $ctx->{patron} = $params{patron};
95 if(!defined($cache{patron_standings})) {
96 $cache{patron_standings} = $apputils->fetch_patron_standings();
97 $cache{group_tree} = $apputils->fetch_permission_group_tree();
100 $ctx->{patron_standings} = $cache{patron_standings};
101 $ctx->{group_tree} = $cache{group_tree};
103 $ctx->{patron_circ_summary} =
104 $apputils->fetch_patron_circ_summary($ctx->{patron}->id)
105 if $params{fetch_patron_circsummary};
111 sub _ctx_add_copy_objects {
112 my($ctx, %params) = @_;
115 $cache{copy_statuses} = $apputils->fetch_copy_statuses
116 if( $params{fetch_copy_statuses} and !defined($cache{copy_statuses}) );
118 $cache{copy_locations} = $apputils->fetch_copy_locations
119 if( $params{fetch_copy_locations} and !defined($cache{copy_locations}));
121 $ctx->{copy_statuses} = $cache{copy_statuses};
122 $ctx->{copy_locations} = $cache{copy_locations};
124 ( $ctx->{copy}, $evt ) = $apputils->fetch_copy_by_barcode( $params{barcode} );
127 ( $ctx->{title}, $evt ) = $apputils->fetch_record_by_copy( $ctx->{copy}->id );
135 # ------------------------------------------------------------------------------
136 # Patches up circ objects to make them easier to use from within the script
138 # ------------------------------------------------------------------------------
139 sub _doctor_circ_objects {
142 my $patron = $ctx->{patron};
143 my $copy = $ctx->{copy};
145 for my $s (@{$ctx->{patron_standings}}) {
146 $patron->standing($s) if ( $s->id eq $ctx->{patron}->standing );
149 # set the patron ptofile to the profile name
150 $patron->profile( _get_patron_profile( $patron, $ctx->{group_tree} ) );
153 $patron->home_ou( $apputils->fetch_org_unit( $patron->home_ou ) );
155 # set the copy status to a status name
156 $copy->status( _get_copy_status( $copy, $ctx->{copy_statuses} ) ) if $copy;
158 # set the copy location to the location object
159 $copy->location( _get_copy_location( $copy, $ctx->{copy_locations} ) ) if $copy;
163 # recurse and find the patron profile name from the tree
164 # another option would be to grab the groups for the patron
165 # and cycle through those until the "profile" group has been found
166 sub _get_patron_profile {
167 my( $patron, $group_tree ) = @_;
168 return $group_tree if ($group_tree->id eq $patron->profile);
169 return undef unless ($group_tree->children);
171 for my $child (@{$group_tree->children}) {
172 my $ret = _get_patron_profile( $patron, $child );
178 sub _get_copy_status {
179 my( $copy, $cstatus ) = @_;
180 for my $status (@$cstatus) {
181 return $status if( $status->id eq $copy->status )
186 sub _get_copy_location {
187 my( $copy, $locations ) = @_;
188 for my $loc (@$locations) {
189 return $loc if $loc->id eq $copy->location;
194 # ------------------------------------------------------------------------------
195 # Constructs and shoves data into the script environment
196 # ------------------------------------------------------------------------------
197 sub _build_circ_script_runner {
200 $logger->debug("Loading script environment for circulation");
203 if( $runner = $contexts{$ctx->{type}} ) {
204 $runner->refresh_context;
206 $runner = OpenILS::Utils::ScriptRunner->new unless $runner;
207 $contexts{type} = $runner;
211 $logger->debug("Loading circ script lib path $_");
212 $runner->add_path( $_ );
215 $runner->insert( 'environment.patron', $ctx->{patron}, 1);
216 $runner->insert( 'environment.title', $ctx->{title}, 1);
217 $runner->insert( 'environment.copy', $ctx->{copy}, 1);
220 $runner->insert( 'result', {} );
221 $runner->insert( 'result.event', 'SUCCESS' );
223 $runner->insert('environment.isRenewal', 1) if $ctx->{isrenew};
225 if(ref($ctx->{patron_circ_summary})) {
226 $runner->insert( 'environment.patronItemsOut', $ctx->{patron_circ_summary}->[0], 1 );
227 $runner->insert( 'environment.patronFines', $ctx->{patron_circ_summary}->[1], 1 );
230 $ctx->{runner} = $runner;
235 sub _add_script_runner_methods {
237 my $runner = $ctx->{runner};
239 # allows a script to fetch a hold that is currently targeting the
241 $runner->insert_method( 'environment.copy', '__OILS_FUNC_fetch_hold', sub {
243 my $hold = $holdcode->fetch_open_hold_by_current_copy($ctx->{copy}->id);
244 $hold = undef unless $hold;
245 $runner->insert( $key, $hold, 1 );
250 # ------------------------------------------------------------------------------
252 __PACKAGE__->register_method(
253 method => "permit_circ",
254 api_name => "open-ils.circ.permit_checkout_",
256 Determines if the given checkout can occur
257 @param authtoken The login session key
258 @param params A trailing list of named params including
259 barcode : The copy barcode,
260 patron : The patron the checkout is occurring for,
261 renew : true or false - whether or not this is a renewal
262 @return The event that occurred during the permit check.
263 If all is well, the SUCCESS event is returned
267 my( $self, $client, $authtoken, %params ) = @_;
269 my $barcode = $params{barcode};
270 my $patronid = $params{patron};
272 my ( $requestor, $patron, $ctx, $evt );
274 # check permisson of the requestor
275 ( $requestor, $patron, $evt ) =
276 $apputils->checkses_requestor(
277 $authtoken, $patronid, 'VIEW_PERMIT_CHECKOUT' );
280 $logger->info("Checking circulation permission for staff: " .
281 $requestor->id . ", patron " . $patron->id .
282 ", and barcode " . (($barcode) ? $barcode : "") );
284 # fetch and build the circulation environment
285 ( $ctx, $evt ) = create_circ_ctx(
289 fetch_patron_circ_summary => 1,
290 fetch_copy_statuses => 1,
291 fetch_copy_locations => 1,
292 isrenew => ($params{renew}) ? 1 : 0,
293 noncat => $params{noncat},
297 $ctx->{noncat_type} = $params{noncat_type};
298 return _run_permit_scripts($ctx);
302 # Runs the patron and copy permit scripts
303 # if this is a non-cat circulation, the copy permit script
305 sub _run_permit_scripts {
308 my $runner = $ctx->{runner};
309 my $patronid = $ctx->{patron}->id;
310 my $barcode = ($ctx->{copy}) ? $ctx->{copy}->barcode : undef;
312 $runner->load($scripts{circ_permit_patron});
313 $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Patron Script Died: $@");
314 my $evtname = $runner->retrieve('result.event');
315 $logger->activity("circ_permit_patron for user $patronid returned event: $evtname");
317 return OpenILS::Event->new($evtname)
318 if ( $ctx->{noncat} or $evtname ne 'SUCCESS' );
320 $runner->load($scripts{circ_permit_copy});
321 $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Copy Script Died: $@");
322 $evtname = $runner->retrieve('result.event');
323 $logger->activity("circ_permit_patron for user $patronid ".
324 "and copy $barcode returned event: $evtname");
326 return OpenILS::Event->new($evtname);
331 # ------------------------------------------------------------------------------
333 __PACKAGE__->register_method(
334 method => "circulate",
335 api_name => "open-ils.circ.checkout.barcode_",
336 notes => <<" NOTES");
337 Checks out an item based on barcode
338 PARAMS( authtoken, barcode => bc, patron => pid )
342 my( $self, $client, $authtoken, %params ) = @_;
343 my $barcode = $params{barcode};
344 my $patronid = $params{patron};
348 # ------------------------------------------------------------------------------
350 __PACKAGE__->register_method(
352 api_name => "open-ils.circ.checkin.barcode_",
353 notes => <<" NOTES");
354 PARAMS( authtoken, barcode => bc )
355 Checks in based on barcode
356 Returns an event object whose payload contains the record, circ, and copy
357 If the item needs to be routed, the event is a ROUTE_COPY event
358 with an additional 'route_to' variable set on the event
362 my( $self, $client, $authtoken, %params ) = @_;
363 my $barcode = $params{barcode};
366 # ------------------------------------------------------------------------------
368 __PACKAGE__->register_method(
370 api_name => "open-ils.circ.renew_",
371 notes => <<" NOTES");
372 PARAMS( authtoken, circ => circ_id );
373 open-ils.circ.renew(login_session, circ_object);
374 Renews the provided circulation. login_session is the requestor of the
375 renewal and if the logged in user is not the same as circ->usr, then
376 the logged in user must have RENEW_CIRC permissions.
380 my( $self, $client, $authtoken, %params ) = @_;
381 my $circ = $params{circ};