broke the permit portion of circ into patron and copy permit scripts
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Circ / Circulate.pm
1 package OpenILS::Application::Circ::Circulate;
2 use base 'OpenSRF::Application';
3 use strict; use warnings;
4 use OpenSRF::EX qw(:try);
5 use Data::Dumper;
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";
13
14 my %scripts;                    # - circulation script filenames
15
16 my $script_libs;                # - any additional script libraries
17 my %cache;
18
19 my %contexts;                   # - Script runner contexts
20
21 # ------------------------------------------------------------------------------
22 # Load the circ script from the config
23 # ------------------------------------------------------------------------------
24 sub initialize {
25
26         my $self = shift;
27         my $conf = OpenSRF::Utils::SettingsClient->new;
28         my @pfx2 = ( "apps", "open-ils.circ","app_settings" );
29         my @pfx = ( @pfx2, "scripts" );
30
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' );
39
40         $logger->error( "Missing circ script(s)" ) 
41                 unless( $p and $c and $d and $f and $m and $pr and $ph );
42
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;
50
51         $lb = [ $lb ] unless ref($lb);
52         $script_libs = $lb;
53
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");
58 }
59
60
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 # ------------------------------------------------------------------------------
66 sub create_circ_ctx {
67         my %params = @_;
68
69         my $barcode                     = $params{barcode};
70
71         my $evt;
72         my $ctx = {};
73
74         $ctx->{type}            = $params{type};
75         $ctx->{isrenew} = $params{isrenew};
76         $ctx->{noncat}          = $params{noncat};
77
78         $evt = _ctx_add_patron_objects($ctx, %params);
79         return $evt if $evt;
80         $evt = _ctx_add_copy_objects($ctx, %params) unless $ctx->{noncat};
81         return $evt if $evt;
82
83         _doctor_circ_objects($ctx);
84         _build_circ_script_runner($ctx);
85         _add_script_runner_methods( $ctx );
86
87         return $ctx;
88 }
89
90 sub _ctx_add_patron_objects {
91         my( $ctx, %params) = @_;
92
93         $ctx->{patron}  = $params{patron};
94
95         if(!defined($cache{patron_standings})) {
96                 $cache{patron_standings} = $apputils->fetch_patron_standings();
97                 $cache{group_tree} = $apputils->fetch_permission_group_tree();
98         }
99
100         $ctx->{patron_standings} = $cache{patron_standings};
101         $ctx->{group_tree} = $cache{group_tree};
102
103         $ctx->{patron_circ_summary} = 
104                 $apputils->fetch_patron_circ_summary($ctx->{patron}->id) 
105                 if $params{fetch_patron_circsummary};
106
107         return undef;
108 }
109
110
111 sub _ctx_add_copy_objects {
112         my($ctx, %params)  = @_;
113         my $evt;
114
115         $cache{copy_statuses} = $apputils->fetch_copy_statuses 
116                 if( $params{fetch_copy_statuses} and !defined($cache{copy_statuses}) );
117
118         $cache{copy_locations} = $apputils->fetch_copy_locations 
119                 if( $params{fetch_copy_locations} and !defined($cache{copy_locations}));
120
121         $ctx->{copy_statuses} = $cache{copy_statuses};
122         $ctx->{copy_locations} = $cache{copy_locations};
123
124         ( $ctx->{copy}, $evt ) = $apputils->fetch_copy_by_barcode( $params{barcode} );
125         return $evt if $evt;
126
127         ( $ctx->{title}, $evt ) = $apputils->fetch_record_by_copy( $ctx->{copy}->id );
128         return $evt if $evt;
129
130         return undef;
131 }
132
133
134
135 # ------------------------------------------------------------------------------
136 # Patches up circ objects to make them easier to use from within the script
137 # environment
138 # ------------------------------------------------------------------------------
139 sub _doctor_circ_objects {
140         my $ctx = shift;
141
142         my $patron = $ctx->{patron};
143         my $copy = $ctx->{copy};
144                         
145         for my $s (@{$ctx->{patron_standings}}) {
146                 $patron->standing($s) if ( $s->id eq $ctx->{patron}->standing );
147         }
148
149         # set the patron ptofile to the profile name
150         $patron->profile( _get_patron_profile( $patron, $ctx->{group_tree} ) );
151
152         # flesh the org unit
153         $patron->home_ou( $apputils->fetch_org_unit( $patron->home_ou ) );
154
155         # set the copy status to a status name
156         $copy->status( _get_copy_status( $copy, $ctx->{copy_statuses} ) );
157
158         # set the copy location to the location object
159         $copy->location( _get_copy_location( $copy, $ctx->{copy_locations} ) );
160
161 }
162
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);
170
171         for my $child (@{$group_tree->children}) {
172                 my $ret = _get_patron_profile( $patron, $child );
173                 return $ret if $ret;
174         }
175         return undef;
176 }
177
178 sub _get_copy_status {
179         my( $copy, $cstatus ) = @_;
180         for my $status (@$cstatus) {
181                 return $status if( $status->id eq $copy->status ) 
182         }
183         return undef;
184 }
185
186 sub _get_copy_location {
187         my( $copy, $locations ) = @_;
188         for my $loc (@$locations) {
189                 return $loc if $loc->id eq $copy->location;
190         }
191 }
192
193
194 # ------------------------------------------------------------------------------
195 # Constructs and shoves data into the script environment
196 # ------------------------------------------------------------------------------
197 sub _build_circ_script_runner {
198         my $ctx = shift;
199
200         $logger->debug("Loading script environment for circulation");
201
202         my $runner;
203         if( $runner = $contexts{$ctx->{type}} ) {
204                 $runner->refresh_context;
205         } else {
206                 $runner = OpenILS::Utils::ScriptRunner->new unless $runner;
207                 $contexts{type} = $runner;
208         }
209
210         for(@$script_libs) {
211                 $logger->debug("Loading circ script lib path $_");
212                 $runner->add_path( $_ );
213         }
214
215         $runner->insert( 'environment.patron',          $ctx->{patron}, 1);
216         $runner->insert( 'environment.title',           $ctx->{title}, 1);
217         $runner->insert( 'environment.copy',            $ctx->{copy}, 1);
218
219         # circ script result
220         $runner->insert( 'result', {} );
221         $runner->insert( 'result.event', 'SUCCESS' );
222
223         $runner->insert('environment.isRenewal', 1) if $ctx->{isrenew};
224
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 );
228         }
229
230         $ctx->{runner} = $runner;
231         return $runner;
232 }
233
234
235 sub _add_script_runner_methods {
236         my $ctx = shift;
237         my $runner = $ctx->{runner};
238
239         # allows a script to fetch a hold that is currently targeting the
240         # copy in question
241         $runner->insert_method( 'environment.copy', '__OILS_FUNC_fetch_hold', sub {
242                         my $key = shift;
243                         my $hold = $holdcode->fetch_open_hold_by_current_copy($ctx->{copy}->id);
244                         $hold = undef unless $hold;
245                         $runner->insert( $key, $hold, 1 );
246                 }
247         );
248 }
249
250 # ------------------------------------------------------------------------------
251
252 __PACKAGE__->register_method(
253         method  => "permit_circ",
254         api_name        => "open-ils.circ.permit_checkout_",
255         notes           => q/
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
264         /);
265
266 sub permit_circ {
267         my( $self, $client, $authtoken, %params ) = @_;
268
269         my $barcode             = $params{barcode};
270         my $patronid    = $params{patron};
271         my $isrenew             = $params{renew};
272         my $noncat              = $params{noncat};
273
274         my ( $requestor, $patron, $ctx, $evt );
275
276         # check permisson of the requestor
277         ( $requestor, $patron, $evt ) = 
278                 $apputils->checkses_requestor( 
279                 $authtoken, $patronid, 'VIEW_PERMIT_CHECKOUT' );
280         return $evt if $evt;
281
282         $logger->info("Checking circulation permission for staff: " . 
283                 $requestor->id .  ", patron " . $patron->id . 
284                 ", and barcode " . (($barcode) ? $barcode : "") );
285
286         # fetch and build the circulation environment
287         ( $ctx, $evt ) = create_circ_ctx( 
288                 barcode                                                 => $barcode, 
289                 patron                                                  => $patron, 
290                 type                                                            => 'permit',
291                 fetch_patron_circ_summary       => 1,
292                 fetch_copy_statuses                     => 1, 
293                 fetch_copy_locations                    => 1, 
294                 isrenew                                                 => ($isrenew) ? 1 : 0,
295                 noncat                                                  => $noncat,
296                 );
297         return $evt if $evt;
298
299         return _run_permit_scripts($ctx);
300 }
301
302
303 # Runs the patron and copy permit scripts
304 # if this is a non-cat circulation, the copy permit script 
305 # is not run
306 sub _run_permit_scripts {
307
308         my $ctx                 = shift;
309         my $runner              = $ctx->{runner};
310         my $patronid    = $ctx->{patron}->id;
311         my $barcode             = $ctx->{copy}->barcode;
312
313         $runner->load($scripts{circ_permit_patron});
314         $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Patron Script Died: $@");
315         my $evtname = $runner->retrieve('result.event');
316         $logger->activity("circ_permit_patron for user $patronid returned event: $evtname");
317
318         return OpenILS::Event->new($evtname) 
319                 if ( $ctx->{noncat} or $evtname ne 'SUCCESS' );
320
321         $runner->load($scripts{circ_permit_copy});
322         $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Copy Script Died: $@");
323         $evtname = $runner->retrieve('result.event');
324         $logger->activity("circ_permit_patron for user $patronid ".
325                 "and copy $barcode returned event: $evtname");
326
327         return OpenILS::Event->new($evtname);
328
329 }
330
331
332 # ------------------------------------------------------------------------------
333
334 __PACKAGE__->register_method(
335         method  => "circulate",
336         api_name        => "open-ils.circ.checkout.barcode_",
337         notes           => <<"  NOTES");
338                 Checks out an item based on barcode
339                 PARAMS( authtoken, barcode => bc, patron => pid )
340         NOTES
341
342 sub circulate {
343         my( $self, $client, $authtoken, %params ) = @_;
344         my $barcode             = $params{barcode};
345         my $patronid    = $params{patron};
346 }
347
348
349 # ------------------------------------------------------------------------------
350
351 __PACKAGE__->register_method(
352         method  => "checkin",
353         api_name        => "open-ils.circ.checkin.barcode_",
354         notes           => <<"  NOTES");
355         PARAMS( authtoken, barcode => bc )
356         Checks in based on barcode
357         Returns an event object whose payload contains the record, circ, and copy
358         If the item needs to be routed, the event is a ROUTE_COPY event
359         with an additional 'route_to' variable set on the event
360         NOTES
361
362 sub checkin {
363         my( $self, $client, $authtoken, %params ) = @_;
364         my $barcode             = $params{barcode};
365 }
366
367 # ------------------------------------------------------------------------------
368
369 __PACKAGE__->register_method(
370         method  => "renew",
371         api_name        => "open-ils.circ.renew_",
372         notes           => <<"  NOTES");
373         PARAMS( authtoken, circ => circ_id );
374         open-ils.circ.renew(login_session, circ_object);
375         Renews the provided circulation.  login_session is the requestor of the
376         renewal and if the logged in user is not the same as circ->usr, then
377         the logged in user must have RENEW_CIRC permissions.
378         NOTES
379
380 sub renew {
381         my( $self, $client, $authtoken, %params ) = @_;
382         my $circ        = $params{circ};
383 }
384
385         
386
387
388 1;