1 package OpenILS::Utils::PermitHold;
2 use strict; use warnings;
5 use OpenSRF::Utils::SettingsClient;
6 use OpenILS::Utils::ScriptRunner;
7 use OpenILS::Application::AppUtils;
8 use DateTime::Format::ISO8601;
9 use OpenILS::Application::Circ::ScriptBuilder;
10 use OpenSRF::Utils::Logger qw(:logger);
12 use OpenILS::Utils::CStoreEditor qw/:funcs/;
13 my $U = "OpenILS::Application::AppUtils";
15 my $script; # - the permit script
16 my $script_libs; # - extra script libs
17 my $legacy_script_support;
19 # mental note: open-ils.storage.biblio.record_entry.ranged_tree
22 # params within a hash are: copy, patron,
23 # requestor, request_lib, title, title_descriptor
24 sub permit_copy_hold {
28 unless(defined $legacy_script_support) {
29 my $conf = OpenSRF::Utils::SettingsClient->new;
30 $legacy_script_support = $conf->config_value(
31 apps => 'open-ils.circ' => app_settings => 'legacy_script_support');
32 $legacy_script_support = ($legacy_script_support and
33 $legacy_script_support =~ /true/i) ? 1 : 0;
36 return indb_hold_permit($params) unless $legacy_script_support;
39 patron_id => $$params{patron_id},
40 patron => $$params{patron},
41 copy => $$params{copy},
42 requestor => $$params{requestor},
43 title => $$params{title},
44 volume => $$params{volume},
45 flesh_age_protect => 1,
47 requestLib => $$params{request_lib},
48 pickupLib => $$params{pickup_lib},
49 newHold => $$params{new_hold},
53 my $runner = OpenILS::Application::Circ::ScriptBuilder->build($ctx);
55 my $ets = $ctx->{_events};
57 # --------------------------------------------------------------
58 # Strip the expired event since holds are still allowed to be
59 # captured on expired patrons.
60 # --------------------------------------------------------------
61 if( $ets and @$ets ) {
62 $ets = [ grep { $_->{textcode} ne 'PATRON_ACCOUNT_EXPIRED' } @$ets ];
66 push( @allevents, @$ets);
68 # --------------------------------------------------------------
69 # If scriptbuilder returned any events, then the script context
70 # is undefined and should not be used
71 # --------------------------------------------------------------
75 # check the various holdable flags
76 push( @allevents, OpenILS::Event->new('ITEM_NOT_HOLDABLE') )
77 unless $U->is_true($ctx->{copy}->holdable);
79 push( @allevents, OpenILS::Event->new('ITEM_NOT_HOLDABLE') )
80 unless $U->is_true($ctx->{copy}->location->holdable);
82 push( @allevents, OpenILS::Event->new('ITEM_NOT_HOLDABLE') )
83 unless $U->is_true($ctx->{copy}->status->holdable);
87 # grab the data safely
88 my $rlib = ref($$params{request_lib}) ? $$params{request_lib}->id : $$params{request_lib};
89 my $olib = ref($ctx->{volume}) ? $ctx->{volume}->owning_lib : -1;
90 my $rid = ref($ctx->{requestor}) ? $ctx->{requestor}->id : -2;
91 my $pid = ($params->{patron}) ? $params->{patron}->id : $params->{patron_id};
93 if( ($rid ne $pid) and ($olib eq $rlib) ) {
94 $logger->info("Item owning lib $olib is the same as the request lib. No age_protection will be checked");
96 $logger->info("item owning lib = $olib, request lib = $rlib, requestor=$rid, patron=$pid. checking age_protection");
97 $evt = check_age_protect($ctx->{patron}, $ctx->{copy});
98 push( @allevents, $evt ) if $evt;
101 $logger->debug("Running permit_copy_hold on copy " . $$params{copy}->id);
103 load_scripts($runner);
104 my $result = $runner->run or
105 throw OpenSRF::EX::ERROR ("Hold Copy Permit Script Died: $@");
107 # --------------------------------------------------------------
108 # Extract and uniquify the event list
109 # --------------------------------------------------------------
110 my $events = $result->{events};
111 $logger->debug("circ_permit_hold for user $pid returned events: [@$events]");
113 push( @allevents, OpenILS::Event->new($_)) for @$events;
116 my %hash = map { ($_->{ilsevent} => $_) } @allevents;
117 @allevents = values %hash;
121 return \@allevents if $$params{show_event_list};
122 return 1 unless @allevents;
131 my $conf = OpenSRF::Utils::SettingsClient->new;
132 my @pfx = ( "apps", "open-ils.circ","app_settings" );
133 my $libs = $conf->config_value(@pfx, 'script_path');
134 $script = $conf->config_value(@pfx, 'scripts', 'circ_permit_hold');
135 $script_libs = (ref($libs)) ? $libs : [$libs];
138 $runner->add_path($_) for(@$script_libs);
139 $runner->load($script);
143 sub check_age_protect {
144 my( $patron, $copy ) = @_;
146 return undef unless $copy and $copy->age_protect and $patron;
148 my $hou = (ref $patron->home_ou) ? $patron->home_ou->id : $patron->home_ou;
150 my $prox = $U->storagereq(
151 'open-ils.storage.asset.copy.proximity', $copy->id, $hou );
153 # If this copy is within the appropriate proximity,
154 # age protect does not apply
155 return undef if $prox <= $copy->age_protect->prox;
157 my $protection_list = $U->storagereq(
158 'open-ils.storage.direct.config.rules.age_hold_protect.search_where.atomic',
159 { age => { '>=' => $copy->age_protect->age },
160 prox => { '>=' => $copy->age_protect->prox },
162 { order_by => 'age' }
165 # Now, now many seconds old is this copy
166 my $create_date = DateTime::Format::ISO8601
168 ->parse_datetime( OpenSRF::Utils::cleanse_ISO8601($copy->create_date) )
171 my $age = time - $create_date;
173 for my $protection ( @$protection_list ) {
175 $logger->info("analyzing age protect ".$protection->name);
177 # age protect does not apply if within the proximity
178 last if $prox <= $protection->prox;
180 # How many seconds old does the copy have to be to escape age protection
181 my $interval = OpenSRF::Utils::interval_to_seconds($protection->age);
183 $logger->info("age_protect interval=$interval, create_date=$create_date, age=$age");
185 if( $interval > $age ) {
186 # if age of the item is less than the protection interval,
187 # the item falls within the age protect range
188 $logger->info("age_protect prevents copy from having a hold placed on it: ".$copy->id);
189 return OpenILS::Event->new('ITEM_AGE_PROTECTED', copy => $copy->id );
196 my $LEGACY_HOLD_EVENT_MAP = {
197 'config.hold_matrix_test.holdable' => 'ITEM_NOT_HOLDABLE',
198 'transit_range' => 'ITEM_NOT_HOLDABLE',
199 'no_matchpoint' => 'NO_POLICY_MATCHPOINT',
200 'config.hold_matrix_test.max_holds' => 'MAX_HOLDS',
201 'config.rule_age_hold_protect.prox' => 'ITEM_AGE_PROTECTED'
204 sub indb_hold_permit {
207 my $function = $$params{retarget} ? 'action.hold_retarget_permit_test' : 'action.hold_request_permit_test';
209 ref($$params{patron}) ? $$params{patron}->id : $$params{patron_id};
211 ref($$params{request_lib}) ? $$params{request_lib}->id : $$params{request_lib};
216 $$params{pickup_lib},
220 $$params{requestor}->id
224 my $e = new_editor(xact=>1);
225 my $results = $e->json_query($HOLD_TEST);
228 unless($$params{show_event_list}) {
229 return 1 if $U->is_true($results->[0]->{success});
235 "NO_POLICY_MATCHPOINT",
236 "payload" => {"fail_part" => "no_matchpoint"}
240 return [] if $U->is_true($results->[0]->{success});
244 my $event = new OpenILS::Event(
245 $LEGACY_HOLD_EVENT_MAP->{$_->{"fail_part"}} || $_->{"fail_part"}
247 $event->{"payload"} = {"fail_part" => $_->{"fail_part"}};