]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm
now have non-cataloged items checkouts working with test script
[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 $evt;
70         my $ctx = {};
71
72         $ctx->{type}                    = $params{type};
73         $ctx->{isrenew}         = $params{isrenew};
74         $ctx->{noncat}                  = $params{noncat};
75         $ctx->{noncat_type}     = $params{noncat_type};
76
77         $evt = _ctx_add_patron_objects($ctx, %params);
78         return $evt if $evt;
79
80         if( ($params{copy} or $params{copyid} or $params{barcode}) and !$params{noncat} ) {
81                 $evt = _ctx_add_copy_objects($ctx, %params);
82                 return $evt if $evt;
83         }
84
85         _doctor_patron_object($ctx) if $ctx->{patron};
86         _doctor_copy_object($ctx) if $ctx->{copy};
87         _doctor_circ_objects($ctx);
88         _build_circ_script_runner($ctx);
89         _add_script_runner_methods( $ctx );
90
91         return $ctx;
92 }
93
94 sub _ctx_add_patron_objects {
95         my( $ctx, %params) = @_;
96
97         $ctx->{patron}  = $params{patron};
98
99         if(!defined($cache{patron_standings})) {
100                 $cache{patron_standings} = $apputils->fetch_patron_standings();
101                 $cache{group_tree} = $apputils->fetch_permission_group_tree();
102         }
103
104         $ctx->{patron_standings} = $cache{patron_standings};
105         $ctx->{group_tree} = $cache{group_tree};
106
107         $ctx->{patron_circ_summary} = 
108                 $apputils->fetch_patron_circ_summary($ctx->{patron}->id) 
109                 if $params{fetch_patron_circsummary};
110
111         return undef;
112 }
113
114
115 sub _ctx_add_copy_objects {
116         my($ctx, %params)  = @_;
117         my $evt;
118
119         $cache{copy_statuses} = $apputils->fetch_copy_statuses 
120                 if( $params{fetch_copy_statuses} and !defined($cache{copy_statuses}) );
121
122         $cache{copy_locations} = $apputils->fetch_copy_locations 
123                 if( $params{fetch_copy_locations} and !defined($cache{copy_locations}));
124
125         $ctx->{copy_statuses} = $cache{copy_statuses};
126         $ctx->{copy_locations} = $cache{copy_locations};
127
128         my $copy = $params{copy} if $params{copy};
129
130         if(!$copy) {
131
132                 ( $copy, $evt ) = 
133                         $apputils->fetch_copy($params{copyid}) if $params{copyid};
134                 return $evt if $evt;
135
136                 if(!$copy) {
137                         ( $copy, $evt ) = 
138                                 $apputils->fetch_copy_by_barcode( $params{barcode} ) if $params{barcode};
139                         return $evt if $evt;
140                 }
141         }
142
143         $ctx->{copy} = $copy;
144
145         ( $ctx->{title}, $evt ) = $apputils->fetch_record_by_copy( $ctx->{copy}->id );
146         return $evt if $evt;
147
148         return undef;
149 }
150
151
152 # ------------------------------------------------------------------------------
153 # Fleshes parts of the patron object
154 # ------------------------------------------------------------------------------
155 sub _doctor_copy_object {
156
157         my $ctx = shift;
158         my $copy = $ctx->{copy};
159
160         # set the copy status to a status name
161         $copy->status( _get_copy_status( 
162                 $copy, $ctx->{copy_statuses} ) ) if $copy;
163
164         # set the copy location to the location object
165         $copy->location( _get_copy_location( 
166                 $copy, $ctx->{copy_locations} ) ) if $copy;
167
168 }
169
170
171 # ------------------------------------------------------------------------------
172 # Fleshes parts of the copy object
173 # ------------------------------------------------------------------------------
174 sub _doctor_patron_object {
175         my $ctx = shift;
176         my $patron = $ctx->{patron};
177
178         # push the standing object into the patron
179         if(ref($ctx->{patron_standings})) {
180                 for my $s (@{$ctx->{patron_standings}}) {
181                         $patron->standing($s) if ( $s->id eq $ctx->{patron}->standing );
182                 }
183         }
184
185         # set the patron ptofile to the profile name
186         $patron->profile( _get_patron_profile( 
187                 $patron, $ctx->{group_tree} ) ) if $ctx->{group_tree};
188
189         # flesh the org unit
190         $patron->home_ou( 
191                 $apputils->fetch_org_unit( $patron->home_ou ) ) if $patron;
192
193 }
194
195 # recurse and find the patron profile name from the tree
196 # another option would be to grab the groups for the patron
197 # and cycle through those until the "profile" group has been found
198 sub _get_patron_profile { 
199         my( $patron, $group_tree ) = @_;
200         return $group_tree if ($group_tree->id eq $patron->profile);
201         return undef unless ($group_tree->children);
202
203         for my $child (@{$group_tree->children}) {
204                 my $ret = _get_patron_profile( $patron, $child );
205                 return $ret if $ret;
206         }
207         return undef;
208 }
209
210 sub _get_copy_status {
211         my( $copy, $cstatus ) = @_;
212         for my $status (@$cstatus) {
213                 return $status if( $status->id eq $copy->status ) 
214         }
215         return undef;
216 }
217
218 sub _get_copy_location {
219         my( $copy, $locations ) = @_;
220         for my $loc (@$locations) {
221                 return $loc if $loc->id eq $copy->location;
222         }
223 }
224
225
226 # ------------------------------------------------------------------------------
227 # Constructs and shoves data into the script environment
228 # ------------------------------------------------------------------------------
229 sub _build_circ_script_runner {
230         my $ctx = shift;
231
232         $logger->debug("Loading script environment for circulation");
233
234         my $runner;
235         if( $runner = $contexts{$ctx->{type}} ) {
236                 $runner->refresh_context;
237         } else {
238                 $runner = OpenILS::Utils::ScriptRunner->new unless $runner;
239                 $contexts{type} = $runner;
240         }
241
242         for(@$script_libs) {
243                 $logger->debug("Loading circ script lib path $_");
244                 $runner->add_path( $_ );
245         }
246
247         $runner->insert( 'environment.patron',          $ctx->{patron}, 1);
248         $runner->insert( 'environment.title',           $ctx->{title}, 1);
249         $runner->insert( 'environment.copy',            $ctx->{copy}, 1);
250
251         # circ script result
252         $runner->insert( 'result', {} );
253         $runner->insert( 'result.event', 'SUCCESS' );
254
255         $runner->insert('environment.isRenewal', 1) if $ctx->{isrenew};
256         $runner->insert('environment.isNonCat', 1) if $ctx->{noncat};
257         $runner->insert('environment.nonCatType', $ctx->{noncat_type}) if $ctx->{noncat};
258
259         if(ref($ctx->{patron_circ_summary})) {
260                 $runner->insert( 'environment.patronItemsOut', $ctx->{patron_circ_summary}->[0], 1 );
261                 $runner->insert( 'environment.patronFines', $ctx->{patron_circ_summary}->[1], 1 );
262         }
263
264         $ctx->{runner} = $runner;
265         return $runner;
266 }
267
268
269 sub _add_script_runner_methods {
270         my $ctx = shift;
271         my $runner = $ctx->{runner};
272
273         if( $ctx->{copy} ) {
274                 
275                 # allows a script to fetch a hold that is currently targeting the
276                 # copy in question
277                 $runner->insert_method( 'environment.copy', '__OILS_FUNC_fetch_hold', sub {
278                                 my $key = shift;
279                                 my $hold = $holdcode->fetch_related_holds($ctx->{copy}->id);
280                                 $hold = undef unless $hold;
281                                 $runner->insert( $key, $hold, 1 );
282                         }
283                 );
284         }
285 }
286
287 # ------------------------------------------------------------------------------
288
289 __PACKAGE__->register_method(
290         method  => "permit_circ",
291         api_name        => "open-ils.circ.permit_checkout_",
292         notes           => q/
293                 Determines if the given checkout can occur
294                 @param authtoken The login session key
295                 @param params A trailing list of named params including 
296                         barcode : The copy barcode, 
297                         patron : The patron the checkout is occurring for, 
298                         renew : true or false - whether or not this is a renewal
299                 @return The event that occurred during the permit check.  
300                         If all is well, the SUCCESS event is returned
301         /);
302
303 sub permit_circ {
304         my( $self, $client, $authtoken, %params ) = @_;
305
306         my ( $requestor, $patron, $ctx, $evt );
307
308         # check permisson of the requestor
309         ( $requestor, $patron, $evt ) = 
310                 $apputils->checkses_requestor( 
311                 $authtoken, $params{patron}, 'VIEW_PERMIT_CHECKOUT' );
312         return $evt if $evt;
313
314         # fetch and build the circulation environment
315         ( $ctx, $evt ) = create_circ_ctx( 
316                 barcode                                                 => $params{barcode}, 
317                 copyid                                                  => $params{copyid},
318                 copy                                                            => $params{copy},
319                 patron                                                  => $patron, 
320                 type                                                            => 'permit',
321                 fetch_patron_circ_summary       => 1,
322                 fetch_copy_statuses                     => 1, 
323                 fetch_copy_locations                    => 1, 
324                 isrenew                                                 => ($params{renew}) ? 1 : 0,
325                 noncat                                                  => ($params{noncat}) ? 1 : 0,
326                 noncat_type                                             => $params{noncat_type},
327                 );
328         return $evt if $evt;
329
330         return _run_permit_scripts($ctx);
331 }
332
333
334 # Runs the patron and copy permit scripts
335 # if this is a non-cat circulation, the copy permit script 
336 # is not run
337 sub _run_permit_scripts {
338
339         my $ctx                 = shift;
340         my $runner              = $ctx->{runner};
341         my $patronid    = $ctx->{patron}->id;
342         my $barcode             = ($ctx->{copy}) ? $ctx->{copy}->barcode : undef;
343
344         $runner->load($scripts{circ_permit_patron});
345         $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Patron Script Died: $@");
346         my $evtname = $runner->retrieve('result.event');
347         $logger->activity("circ_permit_patron for user $patronid returned event: $evtname");
348
349         return OpenILS::Event->new($evtname) 
350                 if ( $ctx->{noncat} or $evtname ne 'SUCCESS' );
351
352         $runner->load($scripts{circ_permit_copy});
353         $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Copy Script Died: $@");
354         $evtname = $runner->retrieve('result.event');
355         $logger->activity("circ_permit_patron for user $patronid ".
356                 "and copy $barcode returned event: $evtname");
357
358         return OpenILS::Event->new($evtname);
359
360 }
361
362
363 # ------------------------------------------------------------------------------
364
365 __PACKAGE__->register_method(
366         method  => "checkout",
367         api_name        => "open-ils.circ.checkout",
368         notes => q/
369                 Checks out an item
370                 @param authtoken The login session key
371                 @param params A named list of params including:
372                         copy                    The copy object
373                         barcode         If no copy is provided, the copy is retrieved via barcode
374                         copyid          If no copy or barcode is provide, the copy id will be use
375                         patron          The patron's id
376                         noncat          True if this is a circulation for a non-cataloted item
377                         noncat_type     The non-cataloged type id
378                         noncat_circ_lib The location for the noncat circ.  
379                                 Default is the home org of the staff member
380                 @return The SUCCESS event on success, any other event depending on the error
381         /);
382
383 sub checkout {
384         my( $self, $client, $authtoken, %params ) = @_;
385
386         my ( $requestor, $patron, $ctx, $evt );
387
388         # check permisson of the requestor
389         ( $requestor, $patron, $evt ) = 
390                 $apputils->checkses_requestor( 
391                         $authtoken, $params{patron}, 'COPY_CHECKOUT' );
392         return $evt if $evt;
393
394         return _checkout_noncat( $requestor, $patron, %params ) if $params{noncat};
395
396         # fetch and build the circulation environment
397         ( $ctx, $evt ) = create_circ_ctx( 
398                 barcode                                                 => $params{barcode}, 
399                 copyid                                                  => $params{copyid},
400                 copy                                                            => $params{copy},
401                 patron                                                  => $patron, 
402                 type                                                            => 'checkout',
403                 fetch_patron_circ_summary       => 1,
404                 fetch_copy_statuses                     => 1, 
405                 fetch_copy_locations                    => 1, 
406                 isrenew                                                 => ($params{renew}) ? 1 : 0,
407                 noncat                                                  => ($params{noncat}) ? 1 : 0,
408                 );
409         return $evt if $evt;
410
411         return _run_checkout_scripts( $ctx );
412
413 }
414
415
416 sub _run_checkout_scripts {
417         my $ctx = shift;
418
419         my $runner = $ctx->{runner};
420
421 #       $runner->load($scripts{circ_duration});
422 #       $runner->run or throw OpenSRF::EX::ERROR ("Circ Duration Script Died: $@");
423
424         return OpenILS::Event->new('SUCCESS', 
425                 payload => { copy => $ctx->{copy} } );
426 }
427
428
429
430 sub _checkout_noncat {
431         my ( $requestor, $patron, %params ) = @_;
432         my $circlib = $params{noncat_circ_lib} || $requestor->home_ou;
433         my( $circ, $evt ) = 
434                 OpenILS::Application::Circ::NonCat::create_non_cat_circ(
435                         $requestor->id, $patron->id, $circlib, $params{noncat_type} );
436         return $evt if $evt;
437         return OpenILS::Event->new('SUCCESS');
438 }
439
440
441 # ------------------------------------------------------------------------------
442
443 __PACKAGE__->register_method(
444         method  => "checkin",
445         api_name        => "open-ils.circ.checkin.barcode_",
446         notes           => <<"  NOTES");
447         PARAMS( authtoken, barcode => bc )
448         Checks in based on barcode
449         Returns an event object whose payload contains the record, circ, and copy
450         If the item needs to be routed, the event is a ROUTE_COPY event
451         with an additional 'route_to' variable set on the event
452         NOTES
453
454 sub checkin {
455         my( $self, $client, $authtoken, %params ) = @_;
456         my $barcode             = $params{barcode};
457 }
458
459 # ------------------------------------------------------------------------------
460
461 __PACKAGE__->register_method(
462         method  => "renew",
463         api_name        => "open-ils.circ.renew_",
464         notes           => <<"  NOTES");
465         PARAMS( authtoken, circ => circ_id );
466         open-ils.circ.renew(login_session, circ_object);
467         Renews the provided circulation.  login_session is the requestor of the
468         renewal and if the logged in user is not the same as circ->usr, then
469         the logged in user must have RENEW_CIRC permissions.
470         NOTES
471
472 sub renew {
473         my( $self, $client, $authtoken, %params ) = @_;
474         my $circ        = $params{circ};
475 }
476
477         
478
479
480 1;