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
15 my $standings; # - cached patron standings
16 my $group_tree; # - cached permission group tree
17 my $script_libs; # - any additional script libraries
18 my $copy_statuses; # - copy status objects
19 my $copy_locations; # - shelving locations
26 my %contexts; # - Script runner contexts
28 # ------------------------------------------------------------------------------
29 # Load the circ script from the config
30 # ------------------------------------------------------------------------------
34 my $conf = OpenSRF::Utils::SettingsClient->new;
35 my @pfx = ( "apps", "open-ils.circ","app_settings", "scripts" );
37 my $p = $conf->config_value( @pfx, 'permission' );
38 my $d = $conf->config_value( @pfx, 'duration' );
39 my $f = $conf->config_value( @pfx, 'recurring_fines' );
40 my $m = $conf->config_value( @pfx, 'max_fines' );
41 my $pr = $conf->config_value( @pfx, 'permit_renew' );
42 my $ph = $conf->config_value( @pfx, 'permit_hold' );
43 my $lb = $conf->config_value( 'apps', 'open-ils.circ', 'app_settings', 'script_path' );
45 $logger->error( "Missing circ script(s)" )
46 unless( $p and $d and $f and $m and $pr and $ph );
48 $scripts{circ_permit} = $p;
49 $scripts{circ_duration} = $d;
50 $scripts{circ_recurring_fines}= $f;
51 $scripts{circ_max_fines} = $m;
52 $scripts{circ_renew_permit} = $pr;
53 $scripts{hold_permit} = $ph;
55 $lb = [ $lb ] unless ref($lb);
58 $logger->debug("Loaded rules scripts for circ: " .
59 "circ permit : $p, circ duration :$d , circ recurring fines : $f, " .
60 "circ max fines : $m, circ renew permit : $pr, permit hold: $ph");
64 # ------------------------------------------------------------------------------
65 # Loads the necessary circ objects and pushes them into the script environment
66 # Returns ( $data, $evt ). if $evt is defined, then an
67 # unexpedted event occurred and should be dealt with / returned to the caller
68 # ------------------------------------------------------------------------------
72 my $barcode = $params{barcode};
73 my $patron = $params{patron};
74 my $fetch_summary = $params{fetch_patron_circ_summary};
75 my $fetch_cstatus = $params{fetch_copy_statuses};
76 my $fetch_clocs = $params{fetch_copy_locations};
78 my ( $copy, $title, $evt );
81 $cur_standings = $apputils->fetch_patron_standings();
82 $group_tree = $apputils->fetch_permission_group_tree();
85 my $cstatus = $apputils->fetch_copy_statuses if( $fetch_cstatus and !$copy_statuses );
86 my $clocs = $apputils->fetch_copy_locations if( $fetch_clocs and !$copy_locations);
87 my $summary = $apputils->fetch_patron_circ_summary($patron->id) if $fetch_summary;
89 ( $copy, $evt ) = $apputils->fetch_copy_by_barcode( $barcode );
90 return ( undef, $evt ) if $evt;
92 ( $title, $evt ) = $apputils->fetch_record_by_copy( $copy->id );
93 return ( undef, $evt ) if $evt;
95 _doctor_circ_objects( $patron, $title, $copy, $summary, $cstatus, $clocs );
97 my $runner = _build_circ_script_runner( $patron, $title, $copy, $summary, $params{type} );
99 $cur_patron = $patron;
108 circ_summary => $summary,
113 # ------------------------------------------------------------------------------
114 # Patches up circ objects to make them easier to use from within the script
116 # ------------------------------------------------------------------------------
117 sub _doctor_circ_objects {
118 my( $patron, $title, $copy, $summary, $cstatus, $clocs ) = @_;
120 # set the patron standing to the standing name
121 for my $s (@$cur_standings) {
122 $patron->standing( $s->value ) if( $s->id eq $patron->standing);
125 # set the patron ptofile to the profile name
126 $patron->profile( _patron_get_profile( $patron, $group_tree ) );
128 # set the copy status to a status name
129 $copy->status( _get_copy_status_name( $copy, $cstatus ) );
131 # set the copy location to the location object
132 $copy->location( _get_copy_location( $copy, $clocs ) );
136 # recurse and find the patron profile name from the tree
137 # another option would be to grab the groups for the patron
138 # and cycle through those until the "profile" group has been found
139 sub _patron_get_profile {
140 my( $patron, $group_tree ) = @_;
141 return $group_tree->name if ($group_tree->id eq $patron->profile);
142 for my $child (@{$group_tree->children}) {
143 my $ret = _patron_get_profile( $patron, $child );
149 sub _get_copy_status_name {
150 my( $copy, $cstatus ) = @_;
151 for my $status (@$cstatus) {
152 return $status->name if( $status->id eq $copy->status )
157 sub _get_copy_location {
158 my( $copy, $locations ) = @_;
159 for my $loc (@$locations) {
160 return $loc if $loc->id eq $copy->location;
165 # ------------------------------------------------------------------------------
166 # Constructs and shoves data into the script environment
167 # ------------------------------------------------------------------------------
168 sub _build_circ_script_runner {
169 my( $patron, $title, $copy, $summary, $type ) = @_;
171 $logger->debug("Loading script environment for circulation");
174 if( $runner = $contexts{$type} ) {
175 $runner->refresh_context;
177 $runner = OpenILS::Utils::ScriptRunner->new unless $runner;
178 $contexts{$type} = $runner;
182 $logger->debug("Loading circ script lib path $_");
183 $runner->add_path( $_ );
186 $runner->insert( 'patron', $patron );
187 $runner->insert( 'title', $title );
188 $runner->insert( 'copy', $copy );
191 $runner->insert( 'result', {} );
192 $runner->insert( 'result.event', 'SUCCESS' );
195 $runner->insert( 'patron_info', {} );
196 $runner->insert( 'patron_info.items_out', $summary->[0] );
197 $runner->insert( 'patron_info.fines', $summary->[1] );
200 _add_script_runner_methods( $runner );
204 sub _add_script_runner_methods {
207 $runner->insert_method( 'copy', '__OILS_FUNC_fetch_hold', sub {
209 my $hold = $holdcode->fetch_open_hold_by_current_copy($cur_copy->id);
210 $hold = undef unless $hold;
211 $runner->insert( $key, $hold );
215 # $runner->insert_method( 'patron', '__OILS_FUNC_get_standing', sub {
218 # for my $s (@$cur_standings) {
219 # $standing = $s->value if ( $s->id eq $cur_patron->standing );
221 # $runner->insert( $key, $standing );
232 $runner->insert('result.event', $evt->{textcode} );
235 sub _add_script_methods {
238 $runner->insert( 'fetch_patron', sub {
239 my ( $key, $id ) = @_;
240 my ( $user, $evt ) = $apputils->fetch_user($id);
241 _insert_event( $runner, $evt ) if $evt;
242 $runner->insert( $key, $user );
246 $runner->insert( 'fetch_copy_by_barcode', sub {
247 my( $key, $barcode ) = @_;
248 my( $copy, $evt ) = $apputils->fetch_copy_by_barcode( $barcode );
249 _insert_event( $runner, $evt ) if $evt;
250 $runner->insert( $key, $copy );
254 $runner->insert( 'fetch_copy_statuses', sub {
256 $runner->insert( $key, $apputils->fetch_copy_statuses );
259 $runner->insert( 'fetch_copy_locations', sub {
261 $runner->insert( $key, $apputils->fetch_copy_locations ); });
263 $runner->insert( 'fetch_patron_circ_summary', sub {
264 my( $key, $patron_id ) = @_;
265 $runner->insert( $key, $apputils->fetch_patron_circ_summary($patron_id)); });
267 $runner->insert( 'fetch_group_tree', sub {
269 $runner->insert( $key, $apputils->fetch_permission_group_tree ); });
271 $runner->insert( 'fetch_patron_standings', sub {
273 $runner->insert( $key, $apputils->fetch_patron_standings ); } );
277 sub _build_script_runner {
280 my $runner = OpenILS::Utils::ScriptRunner->new(
281 type => 'js', libs => $script_libs );
283 # return status event
284 $runner->insert( 'result', {} );
285 $runner->insert( 'result.event', 'SUCCESS' );
287 $runner->insert('env.patron_id', $params{patron_id} ) if defined $params{patron_id};
288 $runner->insert('env.copy_barcode', $params{copy_barcode} ) if defined $params{copy_barcode};
290 $runner->insert( 'arr', [ 1, 5, 10 ] );
299 # ------------------------------------------------------------------------------
301 __PACKAGE__->register_method(
302 method => "permit_circ",
303 api_name => "open-ils.circ.permit_checkout_",
304 notes => <<" NOTES");
305 Determines if the given checkout can occur
306 PARAMS( authtoken, barcode => bc, patron => pid, renew => t/f )
311 my( $self, $client, $authtoken, %params ) = @_;
313 my $barcode = $params{barcode};
314 my $patronid = $params{patron};
315 my $isrenew = $params{renew};
316 my ( $requestor, $patron, $env, $evt );
319 # check permisson of the requestor
320 ( $requestor, $patron, $evt ) =
321 $apputils->checkses_requestor(
322 $authtoken, $patronid, 'VIEW_PERMIT_CHECKOUT' );
325 $logger->info("Checking circulation permission for staff: " . $requestor->id .
326 ", patron " . $patron->id . ", and barcode $barcode" );
328 # fetch and build the circulation environment
329 ( $env, $evt ) = create_circ_env(
333 fetch_patron_circ_summary => 1,
334 fetch_copy_statuses => 1,
335 fetch_copy_locations => 1,
340 my $runner = $env->{runner};
342 # my $runner = _build_script_runner( patron_id => $patronid, copy_barcode => $barcode );
343 # _add_script_methods( $runner );
345 $runner->load($scripts{circ_permit});
346 $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Script Died: $@");
348 my $evtname = $runner->retrieve('result.event');
349 $logger->activity("Permit Circ for user $patronid and barcode $barcode returned event: $evtname");
350 return OpenILS::Event->new($evtname);
354 # ------------------------------------------------------------------------------
356 __PACKAGE__->register_method(
357 method => "circulate",
358 api_name => "open-ils.circ.checkout.barcode_",
359 notes => <<" NOTES");
360 Checks out an item based on barcode
361 PARAMS( authtoken, barcode => bc, patron => pid )
365 my( $self, $client, $authtoken, %params ) = @_;
366 my $barcode = $params{barcode};
367 my $patronid = $params{patron};
371 # ------------------------------------------------------------------------------
373 __PACKAGE__->register_method(
375 api_name => "open-ils.circ.checkin.barcode_",
376 notes => <<" NOTES");
377 PARAMS( authtoken, barcode => bc )
378 Checks in based on barcode
379 Returns an event object whose payload contains the record, circ, and copy
380 If the item needs to be routed, the event is a ROUTE_COPY event
381 with an additional 'route_to' variable set on the event
385 my( $self, $client, $authtoken, %params ) = @_;
386 my $barcode = $params{barcode};
389 # ------------------------------------------------------------------------------
391 __PACKAGE__->register_method(
393 api_name => "open-ils.circ.renew_",
394 notes => <<" NOTES");
395 PARAMS( authtoken, circ => circ_id );
396 open-ils.circ.renew(login_session, circ_object);
397 Renews the provided circulation. login_session is the requestor of the
398 renewal and if the logged in user is not the same as circ->usr, then
399 the logged in user must have RENEW_CIRC permissions.
403 my( $self, $client, $authtoken, %params ) = @_;
404 my $circ = $params{circ};