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 Scalar::Util qw/weaken/;
10 my $U = "OpenILS::Application::AppUtils";
13 my $holdcode = "OpenILS::Application::Circ::Holds";
15 my $evt = "environment";
24 # -----------------------------------------------------------------------
34 # fetch_patron_circ_info - load info on items out, overdues, and fines.
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 # -----------------------------------------------------------------------
40 my( $class, $args ) = @_;
45 my $editor = $$args{editor} || new_editor();
47 $args->{_direct} = {} unless $args->{_direct};
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;
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;
62 return build_runner($editor, $args);
70 my $runner = OpenILS::Utils::ScriptRunner->new;
73 #weaken($gt); # just to be safe
74 $runner->insert( "$evt.groupTree", $gt, 1);
77 $runner->insert( "$evt.patron", $ctx->{patron}, 1);
78 $runner->insert( "$evt.copy", $ctx->{copy}, 1);
79 $runner->insert( "$evt.volume", $ctx->{volume}, 1);
80 $runner->insert( "$evt.title", $ctx->{title}, 1);
81 $runner->insert( "$evt.requestor", $ctx->{requestor}, 1);
83 $runner->insert( "$evt.patronItemsOut", $ctx->{patronItemsOut}, 1 );
84 $runner->insert( "$evt.patronOverdueCount", $ctx->{patronOverdue}, 1 );
85 $runner->insert( "$evt.patronFines", $ctx->{patronFines}, 1 );
87 $runner->insert("$evt.$_", $ctx->{_direct}->{$_}, 1) for keys %{$ctx->{_direct}};
89 insert_org_methods( $editor, $runner );
90 insert_copy_methods( $editor, $ctx, $runner );
101 if($ctx->{copy_id}) {
102 $ctx->{copy} = $e->retrieve_asset_copy($ctx->{copy_id})
105 } elsif( $ctx->{copy_barcode} ) {
107 my $cps = $e->search_asset_copy({barcode => $ctx->{copy_barcode}});
108 return $e->event unless @$cps;
109 $ctx->{copy} = $$cps[0];
113 return undef unless my $copy = $ctx->{copy};
115 # --------------------------------------------------------------------
116 # Fetch/Cache the copy status and location objects
117 # --------------------------------------------------------------------
118 if(!@COPY_STATUSES) {
119 my $s = $e->retrieve_all_config_copy_status();
120 @COPY_STATUSES = @$s;
121 $s = $e->retrieve_all_asset_copy_location();
122 @COPY_LOCATIONS = @$s;
125 # Flesh the status and location
127 grep { $_->id == $copy->status } @COPY_STATUSES )
128 unless ref $copy->status;
131 grep { $_->id == $copy->location } @COPY_LOCATIONS )
132 unless ref $copy->location;
135 $e->retrieve_actor_org_unit($copy->circ_lib))
136 unless ref $copy->circ_lib;
138 $ctx->{volume} = $e->retrieve_asset_call_number(
139 $ctx->{copy}->call_number) or return $e->event;
141 $ctx->{title} = $e->retrieve_biblio_record_entry(
142 $ctx->{volume}->record) or return $e->event;
145 $e->retrieve_config_rules_age_hold_protect($copy->age_protect))
146 if $ctx->{flesh_age_protect} and $copy->age_protect;
153 sub fetch_user_data {
156 if(!$ctx->{patron}) {
158 if( $ctx->{patron_id} ) {
159 $ctx->{patron} = $e->retrieve_actor_user($ctx->{patron_id});
161 } elsif( $ctx->{patron_barcode} ) {
163 my $card = $e->search_actor_card(
164 { barcode => $ctx->{patron_barcode} } )->[0] or return $e->event;
166 $ctx->{patron} = $e->search_actor_user(
167 { card => $card->id })->[0] or return $e->event;
169 } elsif( $ctx->{fetch_patron_by_circ_copy} ) {
171 if( my $copy = $ctx->{copy} ) {
172 my $circs = $e->search_action_circulation(
173 { target_copy => $copy->id, stop_fines_time => undef });
175 if( my $circ = $circs->[0] ) {
176 $ctx->{patron} = $e->retrieve_actor_user($circ->usr)
183 return undef unless my $patron = $ctx->{patron};
185 return OpenILS::Event->new('PATRON_INACTIVE')
186 unless $U->is_true($patron->active);
188 $patron->card($e->retrieve_actor_card($patron->card));
190 return OpenILS::Event->new('PATRON_CARD_INACTIVE')
191 unless $U->is_true($patron->card->active);
194 $e->retrieve_actor_org_unit($patron->home_ou) )
195 unless ref $patron->home_ou;
197 $patron->home_ou->ou_type(
198 $patron->home_ou->ou_type->id)
199 if ref $patron->home_ou->ou_type;
202 $GROUP_TREE = $e->search_permission_grp_tree(
207 flesh_fields => { pgt => ['children'] }
212 flatten_groups($GROUP_TREE);
215 $patron->profile( $GROUP_SET{$patron->profile} )
216 unless ref $patron->profile;
219 $ctx->{requestor} = $ctx->{requestor} || $e->requestor;
221 # this could alter the requestor object within the editor..
222 #if( my $req = $ctx->{requestor} ) {
223 # $req->home_ou( $e->retrieve_actor_org_unit($requestor->home_ou) );
224 # $req->ws_ou( $e->retrieve_actor_org_unit($requestor->ws_ou) );
227 if( $ctx->{fetch_patron_circ_info} ) {
230 OpenILS::Application::Actor::_checked_out(1, $e, $patron->id);
232 $ctx->{patronOverdue} = $circ_counts->{overdue} || 0;
233 $ctx->{patronItemsOut} = $ctx->{patronOverdue} + $circ_counts->{out};
236 my $fxacts = $e->search_money_open_billable_transaction_summary(
237 { usr => $patron->id, balance_owed => { "!=" => 0 } });
240 $fines += $_->balance_owed for @$fxacts;
241 $ctx->{patronFines} = $fines;
243 $logger->debug("script_builder: patron fines determined to be $fines");
244 $logger->debug("script_builder: patron overdue count is " . $ctx->{patronOverdue});
253 return undef unless $tree;
254 $GROUP_SET{$tree->id} = $tree;
255 if( $tree->children ) {
256 flatten_groups($_) for @{$tree->children};
260 sub flatten_org_tree {
262 return undef unless $tree;
263 push( @ORG_LIST, $tree );
264 if( $tree->children ) {
265 flatten_org_tree($_) for @{$tree->children};
271 sub insert_org_methods {
272 my ( $editor, $runner ) = @_;
275 $ORG_TREE = $editor->search_actor_org_unit(
277 {"parent_ou" => undef },
280 flesh_fields => { aou => ['children'] },
281 order_by => { aou => 'name'}
285 flatten_org_tree($ORG_TREE);
291 $r->insert(__OILS_FUNC_isOrgDescendent =>
293 my( $write_key, $sname, $id ) = @_;
294 my ($parent) = grep { $_->shortname eq $sname } @ORG_LIST;
295 my ($child) = grep { $_->id == $id } @ORG_LIST;
296 my $val = is_org_descendent( $parent, $child );
297 $logger->debug("script_builder: is_org_desc returned val $val, writing to $write_key");
298 $r->insert($write_key, $val, 1) if $val; # Needs testing, was dying before
306 sub is_org_descendent {
307 my( $parent, $child ) = @_;
308 return 0 unless $parent and $child;
309 $logger->debug("script_builder: is_org_desc checking parent=".$parent->id.", child=".$child->id);
311 return 0 unless defined $child->parent_ou;
312 return 1 if $parent->id == $child->id;
313 } while( ($child) = grep { $_->id == $child->parent_ou } @ORG_LIST );
317 sub insert_copy_methods {
318 my( $e, $ctx, $runner ) = @_;
319 if( my $copy = $ctx->{copy} ) {
320 $runner->insert_method( 'environment.copy', '__OILS_FUNC_fetch_best_hold', sub {
322 $logger->debug("script_builder: searching for permitted hold for copy ".$copy->barcode);
323 my ($hold) = $holdcode->find_nearest_permitted_hold(
324 OpenSRF::AppSession->create('open-ils.storage'), $copy, $e->requestor );
325 $runner->insert( $key, $hold, 1 );