]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Circ/ScriptBuilder.pm
9537e0f48eeed3d8744f8e6a70e9f69bef3bed28
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Circ / ScriptBuilder.pm
1 package OpenILS::Application::Circ::ScriptBuilder;
2 use strict; use warnings;
3 use OpenILS::Utils::ScriptRunner;
4 use OpenILS::Utils::CStoreEditor qw/:funcs/;
5 use OpenILS::Application::AppUtils;
6 use OpenILS::Application::Actor;
7 use OpenSRF::Utils::Logger qw/$logger/;
8 use OpenILS::Application::Circ::Holds;
9 use DateTime::Format::ISO8601;
10 use OpenSRF::Utils qw/:datetime/;
11 use Scalar::Util qw/weaken/;
12 my $U = "OpenILS::Application::AppUtils";
13 use Data::Dumper;
14
15 my $holdcode = "OpenILS::Application::Circ::Holds";
16
17 my $evt = "environment";
18 my %GROUP_SET;
19 my $GROUP_TREE;
20 my $ORG_TREE;
21 my @ORG_LIST;
22
23
24 # -----------------------------------------------------------------------
25 # Possible Args:
26 #  copy
27 #  copy_id
28 #  copy_barcode
29 #
30 #  patron
31 #  patron_id
32 #  patron_barcode
33 #
34 #  fetch_patron_circ_info - load info on items out, overdues, and fines.
35 #
36 #  _direct - this is a hash of key/value pairs to shove directly into the 
37 #  script runner.  Use this to cover data not covered by this module
38 # -----------------------------------------------------------------------
39 sub build {
40         my( $class, $args ) = @_;
41
42         my $evt;
43         my @evts;
44
45         my $editor = $$args{editor} || new_editor();
46
47         $args->{_direct} = {} unless $args->{_direct};
48         
49         $evt = fetch_bib_data($editor, $args);
50         push(@evts, $evt) if $evt;
51         $evt = fetch_user_data($editor, $args);
52         push(@evts, $evt) if $evt;
53
54         if(@evts) {
55                 my @e;
56                 push( @e, $_->{textcode} ) for @evts;
57                 $logger->info("script_builder: some events occurred: @e");
58                 $logger->debug("script_builder: some events occurred: " . Dumper(\@evts));
59                 $args->{_events} = \@evts;
60         }
61
62         return build_runner($editor, $args);
63 }
64
65
66 sub build_runner {
67         my $editor      = shift;
68         my $ctx         = shift;
69
70         my $runner = OpenILS::Utils::ScriptRunner->new;
71
72         my $gt = $GROUP_TREE;
73         $runner->insert( "$evt.groupTree",      $gt, 1);
74
75
76         $runner->insert( "$evt.patron",         $ctx->{patron}, 1);
77         $runner->insert( "$evt.copy",                   $ctx->{copy}, 1);
78         $runner->insert( "$evt.volume",         $ctx->{volume}, 1);
79         $runner->insert( "$evt.title",          $ctx->{title}, 1);
80
81         if( ref $ctx->{requestor} ) {
82                 $runner->insert( "$evt.requestor",      $ctx->{requestor}, 1);
83                 if($ctx->{requestor}->ws_ou) {
84                         $runner->insert( "$evt.location",       
85                                 $editor->retrieve_actor_org_unit($ctx->{requestor}->ws_ou), 1);
86                 }
87         }
88
89         $runner->insert( "$evt.patronItemsOut", $ctx->{patronItemsOut}, 1 );
90         $runner->insert( "$evt.patronOverdueCount", $ctx->{patronOverdue}, 1 );
91         $runner->insert( "$evt.patronFines", $ctx->{patronFines}, 1 );
92
93         $runner->insert("$evt.$_", $ctx->{_direct}->{$_}, 1) for keys %{$ctx->{_direct}};
94
95         insert_org_methods( $editor, $runner );
96         insert_copy_methods( $editor, $ctx, $runner );
97
98         return $runner;
99 }
100
101 sub fetch_bib_data {
102         my $e = shift;
103         my $ctx = shift;
104
105         if(!$ctx->{copy}) {
106
107                 my $flesh = { flesh => 1, flesh_fields => { acp => [ 'location', 'status', 'circ_lib' ] } };
108
109                 if($ctx->{copy_id}) {
110                         $ctx->{copy} = $e->retrieve_asset_copy(
111                                 [$ctx->{copy_id}, $flesh ]) or return $e->event;
112
113                 } elsif( $ctx->{copy_barcode} ) {
114
115                         $ctx->{copy} = $e->search_asset_copy(
116                                 [{barcode => $ctx->{copy_barcode}, deleted => 'f'}, $flesh ])->[0]
117                                 or return $e->event;
118                 }
119         }
120
121         return undef unless my $copy = $ctx->{copy};
122
123         $copy->location($e->retrieve_asset_copy_location($copy->location))
124                 unless( ref $copy->location );
125
126         $copy->status($e->retrieve_config_copy_status($copy->status))
127                 unless( ref $copy->status );
128
129         $copy->circ_lib( 
130                 $e->retrieve_actor_org_unit($copy->circ_lib)) 
131                 unless ref $copy->circ_lib;
132
133         $ctx->{volume} = $e->retrieve_asset_call_number(
134                 $ctx->{copy}->call_number) or return $e->event;
135
136         $ctx->{title} = $e->retrieve_biblio_record_entry(
137                 $ctx->{volume}->record) or return $e->event;
138
139         $copy->age_protect(
140                 $e->retrieve_config_rules_age_hold_protect($copy->age_protect))
141                 if $ctx->{flesh_age_protect} and $copy->age_protect;
142
143         return undef;
144 }
145
146
147
148 sub fetch_user_data {
149         my( $e, $ctx ) = @_;
150         
151         if(!$ctx->{patron}) {
152
153                 if( $ctx->{patron_id} ) {
154                         $ctx->{patron} = $e->retrieve_actor_user($ctx->{patron_id});
155
156                 } elsif( $ctx->{patron_barcode} ) {
157
158                         my $card = $e->search_actor_card( 
159                                 { barcode => $ctx->{patron_barcode} } )->[0] or return $e->event;
160
161                         $ctx->{patron} = $e->search_actor_user( 
162                                 { card => $card->id })->[0] or return $e->event;
163
164                 } elsif( $ctx->{fetch_patron_by_circ_copy} ) {
165
166                         if( my $copy = $ctx->{copy} ) {
167                                 my $circs = $e->search_action_circulation(
168                                         { target_copy => $copy->id, stop_fines_time => undef });
169
170                                 if( my $circ = $circs->[0] ) {
171                                         $ctx->{patron} = $e->retrieve_actor_user($circ->usr)
172                                                 or return $e->event;
173                                 }
174                         }
175                 }
176         }
177
178         return undef unless my $patron = $ctx->{patron};
179
180         unless( $ctx->{ignore_user_status} ) {
181                 return OpenILS::Event->new('PATRON_INACTIVE')
182                         unless $U->is_true($patron->active);
183         
184                 $patron->card($e->retrieve_actor_card($patron->card));
185         
186                 return OpenILS::Event->new('PATRON_CARD_INACTIVE')
187                         unless $U->is_true($patron->card->active);
188         
189                 my $expire = DateTime::Format::ISO8601->new->parse_datetime(
190                         clense_ISO8601($patron->expire_date));
191         
192                 return OpenILS::Event->new('PATRON_ACCOUNT_EXPIRED')
193                         if( CORE::time > $expire->epoch ) ;
194         }
195
196         $patron->home_ou( 
197                 $e->retrieve_actor_org_unit($patron->home_ou) ) 
198                 unless ref $patron->home_ou;
199
200         $patron->home_ou->ou_type(
201                 $patron->home_ou->ou_type->id) 
202                 if ref $patron->home_ou->ou_type;
203
204         if(!%GROUP_SET) {
205                 $GROUP_TREE = $e->search_permission_grp_tree(
206                         [
207                                 { parent => undef }, 
208                                 { 
209                                         flesh => 100,
210                                         flesh_fields => { pgt => ['children'] }
211                                 } 
212                         ]
213                 )->[0];
214
215                 flatten_groups($GROUP_TREE);
216         }
217
218         $patron->profile( $GROUP_SET{$patron->profile} )
219                 unless ref $patron->profile;
220
221
222         $ctx->{requestor} = $ctx->{requestor} || $e->requestor;
223
224         # this could alter the requestor object within the editor..
225         #if( my $req = $ctx->{requestor} ) {
226         #       $req->home_ou( $e->retrieve_actor_org_unit($requestor->home_ou) );      
227         #       $req->ws_ou( $e->retrieve_actor_org_unit($requestor->ws_ou) );  
228         #}
229
230         if( $ctx->{fetch_patron_circ_info} ) {
231
232                 my $circ_counts = 
233                         OpenILS::Application::Actor::_checked_out(1, $e, $patron->id);
234
235                 $ctx->{patronOverdue} = $circ_counts->{overdue} || 0;
236                 $ctx->{patronItemsOut} = $ctx->{patronOverdue} + $circ_counts->{out};
237
238                 # Grab the fines
239                 my $fxacts = $e->search_money_open_billable_transaction_summary(
240                         { usr => $patron->id, balance_owed => { "!=" => 0 } });
241
242                 my $fines = 0;
243                 $fines += $_->balance_owed for @$fxacts;
244                 $ctx->{patronFines} = $fines;
245
246                 $logger->debug("script_builder: patron fines determined to be $fines");
247                 $logger->debug("script_builder: patron overdue count is " . $ctx->{patronOverdue});
248         }
249
250         return undef;
251 }
252
253
254 sub flatten_groups {
255         my $tree = shift;
256         return undef unless $tree;
257         $GROUP_SET{$tree->id} = $tree;
258         if( $tree->children ) {
259                 flatten_groups($_) for @{$tree->children};
260         }
261 }
262
263 sub flatten_org_tree {
264         my $tree = shift;
265         return undef unless $tree;
266         push( @ORG_LIST, $tree );
267         if( $tree->children ) {
268                 flatten_org_tree($_) for @{$tree->children};
269         }
270 }
271
272
273
274 sub insert_org_methods {
275         my ( $editor, $runner ) = @_;
276
277         if(!$ORG_TREE) {
278                 $ORG_TREE = $editor->search_actor_org_unit(
279                         [
280                                 {"parent_ou" => undef },
281                                 {
282                                         flesh                           => 2,
283                                         flesh_fields    => { aou =>  ['children'] },
284                                         order_by                        => { aou => 'name'}
285                                 }
286                         ]
287                 )->[0];
288                 flatten_org_tree($ORG_TREE);
289         }
290
291         my $r = $runner;
292         weaken($r);
293
294         $r->insert(__OILS_FUNC_isOrgDescendent  => 
295                 sub {
296                         my( $write_key, $sname, $id ) = @_;
297                         my ($parent)    = grep { $_->shortname eq $sname } @ORG_LIST;
298                         my ($child)             = grep { $_->id == $id } @ORG_LIST;
299                         my $val = is_org_descendent( $parent, $child );
300                         $logger->debug("script_builder: is_org_desc returned val $val, writing to $write_key");
301                         $r->insert($write_key, $val, 1) if $val; # Needs testing, was dying before
302                         return $val;
303                 }
304         );
305
306 }
307
308
309 sub is_org_descendent {
310         my( $parent, $child ) = @_;
311         return 0 unless $parent and $child;
312         $logger->debug("script_builder: is_org_desc checking parent=".$parent->id.", child=".$child->id);
313         do {
314                 return 0 unless defined $child->parent_ou;
315                 return 1 if $parent->id == $child->id;
316         } while( ($child) = grep { $_->id == $child->parent_ou } @ORG_LIST );
317         return 0;
318 }
319
320 sub insert_copy_methods {
321         my( $e, $ctx,  $runner ) = @_;
322         if( my $copy = $ctx->{copy} ) {
323                 $runner->insert_method( 'environment.copy', '__OILS_FUNC_fetch_best_hold', sub {
324                                 my $key = shift;
325                                 $logger->debug("script_builder: searching for permitted hold for copy ".$copy->barcode);
326                                 my ($hold) = $holdcode->find_nearest_permitted_hold(
327                                         OpenSRF::AppSession->create('open-ils.storage'), $copy, $e->requestor );
328                                 $runner->insert( $key, $hold, 1 );
329                         }
330                 );
331         }
332 }
333
334
335
336
337
338 1;