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 my $apputils = "OpenILS::Application::AppUtils";
11 my $holdcode = "OpenILS::Application::Circ::Holds";
13 my %scripts; # - circulation script filenames
14 my $standings; # - cached patron standings
15 my $group_tree; # - cached permission group tree
16 my $script_libs; # - any additional script libraries
17 my $copy_statuses; # - copy status objects
18 my $copy_locations; # - shelving locations
20 # ------------------------------------------------------------------------------
21 # Load the circ script from the config
22 # ------------------------------------------------------------------------------
26 my $conf = OpenSRF::Utils::SettingsClient->new;
27 my @pfx = ( "apps", "open-ils.circ","app_settings", "scripts" );
29 my $p = $conf->config_value( @pfx, 'permission' );
30 my $d = $conf->config_value( @pfx, 'duration' );
31 my $f = $conf->config_value( @pfx, 'recurring_fines' );
32 my $m = $conf->config_value( @pfx, 'max_fines' );
33 my $pr = $conf->config_value( @pfx, 'permit_renew' );
34 my $ph = $conf->config_value( @pfx, 'permit_hold' );
35 my $lb = $conf->config_value( @pfx, 'script_lib' );
37 $logger->error( "Missing circ script(s)" )
38 unless( $p and $d and $f and $m and $pr and $ph );
40 $scripts{circ_permit} = $p;
41 $scripts{circ_duration} = $d;
42 $scripts{circ_recurring_fines}= $f;
43 $scripts{circ_max_fines} = $m;
44 $scripts{circ_renew_permit} = $pr;
45 $scripts{hold_permit} = $ph;
47 $lb = [ $lb ] unless ref($lb);
50 $logger->debug("Loaded rules scripts for circ: " .
51 "circ permit : $p, circ duration :$d , circ recurring fines : $f, " .
52 "circ max fines : $m, circ renew permit : $pr, permit hold: $ph");
56 # ------------------------------------------------------------------------------
57 # Loads the necessary circ objects and pushes them into the script environment
58 # Returns ( $data, $evt ). if $evt is defined, then an
59 # unexpedted event occurred and should be dealt with / returned to the caller
60 # ------------------------------------------------------------------------------
64 my $barcode = $params{barcode};
65 my $patron = $params{patron};
66 my $fetch_summary = $params{fetch_patron_circ_summary};
67 my $fetch_cstatus = $params{fetch_copy_statuses};
68 my $fetch_clocs = $params{fetch_copy_locations};
70 my ( $copy, $title, $evt );
73 $standings = $apputils->fetch_patron_standings();
74 $group_tree = $apputils->fetch_permission_group_tree();
77 # XXX must decide if caching is "right"
78 my $cstatus = $apputils->fetch_copy_statuses if( $fetch_cstatus and !$copy_statuses );
79 my $clocs = $apputils->fetch_copy_locations if( $fetch_clocs and !$copy_locations);
80 my $summary = $apputils->fetch_patron_circ_summary($patron->id) if $fetch_summary;
82 ( $copy, $evt ) = $apputils->fetch_copy_by_barcode( $barcode );
83 return ( undef, $evt ) if $evt;
85 ( $title, $evt ) = $apputils->fetch_record_by_copy( $copy->id );
86 return ( undef, $evt ) if $evt;
88 _doctor_circ_objects( $patron, $title, $copy, $summary, $cstatus, $clocs );
90 my $runner = _build_circ_script_runner( $patron, $title, $copy, $summary );
97 circ_summary => $summary,
102 # ------------------------------------------------------------------------------
103 # Patches up circ objects to make them easier to use from within the script
105 # ------------------------------------------------------------------------------
106 sub _doctor_circ_objects {
107 my( $patron, $title, $copy, $summary, $cstatus, $clocs ) = @_;
109 # set the patron standing to the standing name
110 for my $s (@$standings) {
111 $patron->standing( $s->value ) if( $s->id eq $patron->standing);
114 # set the patron ptofile to the profile name
115 $patron->profile( _patron_get_profile( $patron, $group_tree ) );
117 # set the copy status to a status name
118 $copy->status( _get_copy_status_name( $copy, $cstatus ) );
120 # set the copy location to the location object
121 $copy->location( _get_copy_location( $copy, $clocs ) );
125 # recurse and find the patron profile name from the tree
126 # another option would be to grab the groups for the patron
127 # and cycle through those until the "profile" group has been found
128 sub _patron_get_profile {
129 my( $patron, $group_tree ) = @_;
130 return $group_tree->name if ($group_tree->id eq $patron->profile);
131 for my $child (@{$group_tree->children}) {
132 my $ret = _patron_get_profile( $patron, $child );
138 sub _get_copy_status_name {
139 my( $copy, $cstatus ) = @_;
140 for my $status (@$cstatus) {
141 return $status->name if( $status->id eq $copy->status )
146 sub _get_copy_location {
147 my( $copy, $locations ) = @_;
148 for my $loc (@$locations) {
149 return $loc if $loc->id eq $copy->location;
154 # ------------------------------------------------------------------------------
155 # Constructs and shoves data into the script environment
156 # ------------------------------------------------------------------------------
157 sub _build_circ_script_runner {
158 my( $patron, $title, $copy, $summary ) = @_;
160 $logger->debug("Loading script environment for circulation");
162 my $runner = OpenILS::Utils::ScriptRunner->new(
163 type => 'js', libs => $script_libs );
165 $runner->insert( 'patron', $patron );
166 $runner->insert( 'title', $title );
167 $runner->insert( 'copy', $copy );
170 $runner->insert( 'result', {} );
171 $runner->insert( 'result.event', 'SUCCESS' );
174 $runner->insert( 'patron_info', {} );
175 $runner->insert( 'patron_info.items_out', $summary->[0] );
176 $runner->insert( 'patron_info.fines', $summary->[1] );
179 _add_script_runner_methods( $runner );
183 sub _add_script_runner_methods {
186 $runner->context->function_set(
187 'fetch_hold_by_copy', sub {
189 my $hold = $holdcode->fetch_open_hold_by_current_copy($copyid);
190 $runner->insert( 'hold', $hold ) if $hold;
196 # ------------------------------------------------------------------------------
198 __PACKAGE__->register_method(
199 method => "permit_circ",
200 api_name => "open-ils.circ.permit_checkout_",
201 notes => <<" NOTES");
202 Determines if the given checkout can occur
203 PARAMS( authtoken, barcode => bc, patron => pid, renew => t/f )
208 my( $self, $client, $authtoken, %params ) = @_;
209 my $barcode = $params{barcode};
210 my $patronid = $params{patron};
211 my $isrenew = $params{renew};
212 my ( $requestor, $patron, $env, $evt );
214 # check permisson of the requestor
215 ( $requestor, $patron, $evt ) =
216 $apputils->checkses_requestor(
217 $authtoken, $patronid, 'VIEW_PERMIT_CHECKOUT' );
220 # fetch and build the circulation environment
221 ( $env, $evt ) = create_circ_env(
224 fetch_patron_circ_summary => 1,
225 fetch_copy_statuses => 1,
226 fetch_copy_locations => 1,
231 my $runner = $env->{runner};
232 $runner->load($scripts{circ_permit});
233 $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Script Died");
235 my $evtname = $runner->retrieve('result.event');
236 $logger->activity("Permit Circ for user $patronid and barcode $barcode returned event: $evtname");
237 return OpenILS::Event->new($evtname);
241 # ------------------------------------------------------------------------------
243 __PACKAGE__->register_method(
244 method => "circulate",
245 api_name => "open-ils.circ.checkout.barcode_",
246 notes => <<" NOTES");
247 Checks out an item based on barcode
248 PARAMS( authtoken, barcode => bc, patron => pid )
252 my( $self, $client, $authtoken, %params ) = @_;
253 my $barcode = $params{barcode};
254 my $patronid = $params{patron};
258 # ------------------------------------------------------------------------------
260 __PACKAGE__->register_method(
262 api_name => "open-ils.circ.checkin.barcode_",
263 notes => <<" NOTES");
264 PARAMS( authtoken, barcode => bc )
265 Checks in based on barcode
266 Returns an event object whose payload contains the record, circ, and copy
267 If the item needs to be routed, the event is a ROUTE_COPY event
268 with an additional 'route_to' variable set on the event
272 my( $self, $client, $authtoken, %params ) = @_;
273 my $barcode = $params{barcode};
276 # ------------------------------------------------------------------------------
278 __PACKAGE__->register_method(
280 api_name => "open-ils.circ.renew_",
281 notes => <<" NOTES");
282 PARAMS( authtoken, circ => circ_id );
283 open-ils.circ.renew(login_session, circ_object);
284 Renews the provided circulation. login_session is the requestor of the
285 renewal and if the logged in user is not the same as circ->usr, then
286 the logged in user must have RENEW_CIRC permissions.
290 my( $self, $client, $authtoken, %params ) = @_;
291 my $circ = $params{circ};