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