]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm
69cde27cc6f5ac982d8472352b2be3e42d8e2479
[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 @pfx = ( "apps", "open-ils.circ","app_settings", "scripts" );
29
30         my $p           = $conf->config_value(  @pfx, 'permission' );
31         my $d           = $conf->config_value(  @pfx, 'duration' );
32         my $f           = $conf->config_value(  @pfx, 'recurring_fines' );
33         my $m           = $conf->config_value(  @pfx, 'max_fines' );
34         my $pr  = $conf->config_value(  @pfx, 'permit_renew' );
35         my $ph  = $conf->config_value(  @pfx, 'permit_hold' );
36         my $lb  = $conf->config_value(  'apps', 'open-ils.circ', 'app_settings', 'script_path' );
37
38         $logger->error( "Missing circ script(s)" ) 
39                 unless( $p and $d and $f and $m and $pr and $ph );
40
41         $scripts{circ_permit}                   = $p;
42         $scripts{circ_duration}                 = $d;
43         $scripts{circ_recurring_fines}= $f;
44         $scripts{circ_max_fines}                = $m;
45         $scripts{circ_renew_permit}     = $pr;
46         $scripts{hold_permit}                   = $ph;
47
48         $lb = [ $lb ] unless ref($lb);
49         $script_libs = $lb;
50
51         $logger->debug("Loaded rules scripts for circ: " .
52                 "circ permit : $p, circ duration :$d , circ recurring fines : $f, " .
53                 "circ max fines : $m, circ renew permit : $pr, permit hold: $ph");
54 }
55
56
57 # ------------------------------------------------------------------------------
58 # Loads the necessary circ objects and pushes them into the script environment
59 # Returns ( $data, $evt ).  if $evt is defined, then an
60 # unexpedted event occurred and should be dealt with / returned to the caller
61 # ------------------------------------------------------------------------------
62 sub create_circ_ctx {
63         my %params = @_;
64
65         my $barcode                     = $params{barcode};
66         my $patron                      = $params{patron};
67         my $fetch_summary = $params{fetch_patron_circ_summary};
68         my $fetch_cstatus       = $params{fetch_copy_statuses};
69         my $fetch_clocs = $params{fetch_copy_locations};
70
71         my $evt;
72         my $ctx = {};
73         $ctx->{patron} = $patron;
74         $ctx->{type}    = $params{type};
75         $ctx->{isrenew} = $params{isrenew};
76
77         if(!defined($cache{patron_standings})) {
78                 $cache{patron_standings} = $apputils->fetch_patron_standings();
79                 $cache{group_tree} = $apputils->fetch_permission_group_tree();
80         }
81
82         $ctx->{patron_standings} = $cache{patron_standings};
83         $ctx->{group_tree} = $cache{group_tree};
84
85         $cache{copy_statuses} = $apputils->fetch_copy_statuses 
86                 if( $fetch_cstatus and !defined($cache{copy_statuses}) );
87
88         $cache{copy_locations} = $apputils->fetch_copy_locations 
89                 if( $fetch_clocs and !defined($cache{copy_locations}));
90
91         $ctx->{copy_statuses} = $cache{copy_statuses};
92         $ctx->{copy_locations} = $cache{copy_locations};
93
94         $ctx->{patron_circ_summary} = 
95                 $apputils->fetch_patron_circ_summary($patron->id) if $fetch_summary;
96
97         ( $ctx->{copy}, $evt ) = $apputils->fetch_copy_by_barcode( $barcode );
98         return ( undef, $evt ) if $evt;
99
100         ( $ctx->{title}, $evt ) = $apputils->fetch_record_by_copy( $ctx->{copy}->id );
101         return ( undef, $evt ) if $evt;
102
103         _doctor_circ_objects($ctx);
104         _build_circ_script_runner($ctx);
105         _add_script_runner_methods( $ctx );
106
107         return $ctx;
108 }
109
110
111 # ------------------------------------------------------------------------------
112 # Patches up circ objects to make them easier to use from within the script
113 # environment
114 # ------------------------------------------------------------------------------
115 sub _doctor_circ_objects {
116         my $ctx = shift;
117
118         my $patron = $ctx->{patron};
119         my $copy = $ctx->{copy};
120                         
121         for my $s (@{$ctx->{patron_standings}}) {
122                 $patron->standing($s) if ( $s->id eq $ctx->{patron}->standing );
123         }
124
125         # set the patron ptofile to the profile name
126         $patron->profile( _get_patron_profile( $patron, $ctx->{group_tree} ) );
127
128         # flesh the org unit
129         $patron->home_ou( $apputils->fetch_org_unit( $patron->home_ou ) );
130
131         # set the copy status to a status name
132         $copy->status( _get_copy_status( $copy, $ctx->{copy_statuses} ) );
133
134         # set the copy location to the location object
135         $copy->location( _get_copy_location( $copy, $ctx->{copy_locations} ) );
136
137 }
138
139 # recurse and find the patron profile name from the tree
140 # another option would be to grab the groups for the patron
141 # and cycle through those until the "profile" group has been found
142 sub _get_patron_profile { 
143         my( $patron, $group_tree ) = @_;
144         return $group_tree if ($group_tree->id eq $patron->profile);
145         for my $child (@{$group_tree->children}) {
146                 my $ret = _get_patron_profile( $patron, $child );
147                 return $ret if $ret;
148         }
149         return undef;
150 }
151
152 sub _get_copy_status {
153         my( $copy, $cstatus ) = @_;
154         for my $status (@$cstatus) {
155                 return $status if( $status->id eq $copy->status ) 
156         }
157         return undef;
158 }
159
160 sub _get_copy_location {
161         my( $copy, $locations ) = @_;
162         for my $loc (@$locations) {
163                 return $loc if $loc->id eq $copy->location;
164         }
165 }
166
167
168 # ------------------------------------------------------------------------------
169 # Constructs and shoves data into the script environment
170 # ------------------------------------------------------------------------------
171 sub _build_circ_script_runner {
172         my $ctx = shift;
173
174         $logger->debug("Loading script environment for circulation");
175
176         my $runner;
177         if( $runner = $contexts{$ctx->{type}} ) {
178                 $runner->refresh_context;
179         } else {
180                 $runner = OpenILS::Utils::ScriptRunner->new unless $runner;
181                 $contexts{type} = $runner;
182         }
183
184         for(@$script_libs) {
185                 $logger->debug("Loading circ script lib path $_");
186                 $runner->add_path( $_ );
187         }
188
189         $runner->insert( 'patron',              $ctx->{patron}, 1);
190         $runner->insert( 'title',               $ctx->{title}, 1);
191         $runner->insert( 'copy',                $ctx->{copy}, 1);
192
193         # circ script result
194         $runner->insert( 'result', {} );
195         $runner->insert( 'result.event', 'SUCCESS' );
196
197         $runner->insert('environment', {}, 1);
198         $runner->insert('environment.isRenewal', 1) if $ctx->{isrenew};
199
200         if(ref($ctx->{patron_circ_summary})) {
201                 $runner->insert( 'environment.patronItemsOut', $ctx->{patron_circ_summary}->[0], 1 );
202                 $runner->insert( 'environment.patronFines', $ctx->{patron_circ_summary}->[1], 1 );
203         }
204
205         $ctx->{runner} = $runner;
206         return $runner;
207 }
208
209
210 sub _add_script_runner_methods {
211         my $ctx = shift;
212         my $runner = $ctx->{runner};
213
214         # allows a script to fetch a hold that is currently targeting the
215         # copy in question
216         $runner->insert_method( 'copy', '__OILS_FUNC_fetch_hold', sub {
217                         my $key = shift;
218                         my $hold = $holdcode->fetch_open_hold_by_current_copy($ctx->{copy}->id);
219                         $hold = undef unless $hold;
220                         $runner->insert( $key, $hold, 1 );
221                 }
222         );
223 }
224
225 # ------------------------------------------------------------------------------
226
227 __PACKAGE__->register_method(
228         method  => "permit_circ",
229         api_name        => "open-ils.circ.permit_checkout_",
230         notes           => q/
231                 Determines if the given checkout can occur
232                 @param authtoken The login session key
233                 @param params A trailing list of named params including 
234                         barcode : The copy barcode, 
235                         patron : The patron the checkout is occurring for, 
236                         renew : true or false - whether or not this is a renewal
237                 @return The event that occurred during the permit check.  
238                         If all is well, the SUCCESS event is returned
239         /);
240
241 sub permit_circ {
242         my( $self, $client, $authtoken, %params ) = @_;
243
244         my $barcode             = $params{barcode};
245         my $patronid    = $params{patron};
246         my $isrenew             = $params{renew};
247         my ( $requestor, $patron, $ctx, $evt );
248
249
250         # check permisson of the requestor
251         ( $requestor, $patron, $evt ) = 
252                 $apputils->checkses_requestor( 
253                 $authtoken, $patronid, 'VIEW_PERMIT_CHECKOUT' );
254         return $evt if $evt;
255
256         $logger->info("Checking circulation permission for staff: " . $requestor->id .
257                 ", patron " . $patron->id . ", and barcode $barcode" );
258
259         # fetch and build the circulation environment
260         ( $ctx, $evt ) = create_circ_ctx( 
261                 barcode                                                 => $barcode, 
262                 patron                                                  => $patron, 
263                 type                                                            => 'permit',
264                 fetch_patron_circ_summary       => 1,
265                 fetch_copy_statuses                     => 1, 
266                 fetch_copy_locations                    => 1, 
267                 isrenew                                                 => ($isrenew) ? 1 : 0,
268                 );
269         return $evt if $evt;
270
271         # run the script
272         my $runner = $ctx->{runner};
273
274         $runner->load($scripts{circ_permit});
275         $runner->run or throw OpenSRF::EX::ERROR ("Circ Permit Script Died: $@");
276
277         my $evtname = $runner->retrieve('result.event');
278         $logger->activity("Permit Circ for user $patronid and barcode $barcode returned event: $evtname");
279         return OpenILS::Event->new($evtname);
280 }
281
282
283 # ------------------------------------------------------------------------------
284
285 __PACKAGE__->register_method(
286         method  => "circulate",
287         api_name        => "open-ils.circ.checkout.barcode_",
288         notes           => <<"  NOTES");
289                 Checks out an item based on barcode
290                 PARAMS( authtoken, barcode => bc, patron => pid )
291         NOTES
292
293 sub circulate {
294         my( $self, $client, $authtoken, %params ) = @_;
295         my $barcode             = $params{barcode};
296         my $patronid    = $params{patron};
297 }
298
299
300 # ------------------------------------------------------------------------------
301
302 __PACKAGE__->register_method(
303         method  => "checkin",
304         api_name        => "open-ils.circ.checkin.barcode_",
305         notes           => <<"  NOTES");
306         PARAMS( authtoken, barcode => bc )
307         Checks in based on barcode
308         Returns an event object whose payload contains the record, circ, and copy
309         If the item needs to be routed, the event is a ROUTE_COPY event
310         with an additional 'route_to' variable set on the event
311         NOTES
312
313 sub checkin {
314         my( $self, $client, $authtoken, %params ) = @_;
315         my $barcode             = $params{barcode};
316 }
317
318 # ------------------------------------------------------------------------------
319
320 __PACKAGE__->register_method(
321         method  => "renew",
322         api_name        => "open-ils.circ.renew_",
323         notes           => <<"  NOTES");
324         PARAMS( authtoken, circ => circ_id );
325         open-ils.circ.renew(login_session, circ_object);
326         Renews the provided circulation.  login_session is the requestor of the
327         renewal and if the logged in user is not the same as circ->usr, then
328         the logged in user must have RENEW_CIRC permissions.
329         NOTES
330
331 sub renew {
332         my( $self, $client, $authtoken, %params ) = @_;
333         my $circ        = $params{circ};
334 }
335
336         
337
338
339 1;