]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Utils/PermitHold.pm
LP#1386347: Clear hold-copy-map efficiently
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Utils / PermitHold.pm
1 package OpenILS::Utils::PermitHold;
2 use strict; use warnings;
3 use Data::Dumper;
4 use OpenSRF::Utils;
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);
11 use OpenILS::Event;
12 use OpenILS::Utils::CStoreEditor qw/:funcs/;
13 my $U   = "OpenILS::Application::AppUtils";
14
15 my $script;         # - the permit script
16 my $script_libs;    # - extra script libs
17 my $legacy_script_support;
18
19 # mental note:  open-ils.storage.biblio.record_entry.ranged_tree
20
21
22 # params within a hash are: copy, patron, 
23 # requestor, request_lib, title, title_descriptor
24 sub permit_copy_hold {
25     my $params  = shift;
26     my @allevents;
27
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;
34     }
35
36     return indb_hold_permit($params) unless $legacy_script_support;
37
38     my $ctx = {
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,
46         _direct => {
47             requestLib  => $$params{request_lib},
48             pickupLib   => $$params{pickup_lib},
49             newHold    => $$params{new_hold},
50         }
51     };
52
53     my $runner = OpenILS::Application::Circ::ScriptBuilder->build($ctx);
54
55     my $ets = $ctx->{_events};
56
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 ];
63     } else { $ets = []; }
64
65     if( @$ets ) {
66         push( @allevents, @$ets);
67
68         # --------------------------------------------------------------
69         # If scriptbuilder returned any events, then the script context
70         # is undefined and should not be used
71         # --------------------------------------------------------------
72
73     } else {
74
75         # check the various holdable flags
76         push( @allevents, OpenILS::Event->new('ITEM_NOT_HOLDABLE') )
77             unless $U->is_true($ctx->{copy}->holdable);
78     
79         push( @allevents, OpenILS::Event->new('ITEM_NOT_HOLDABLE') )
80             unless $U->is_true($ctx->{copy}->location->holdable);
81     
82         push( @allevents, OpenILS::Event->new('ITEM_NOT_HOLDABLE') )
83             unless $U->is_true($ctx->{copy}->status->holdable);
84     
85       my $evt;
86
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};
92
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");
95       } else {
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;
99       }
100     
101         $logger->debug("Running permit_copy_hold on copy " . $$params{copy}->id);
102     
103         load_scripts($runner);
104         my $result = $runner->run or 
105             throw OpenSRF::EX::ERROR ("Hold Copy Permit Script Died: $@");
106
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]");
112     
113         push( @allevents, OpenILS::Event->new($_)) for @$events;
114     }
115
116     my %hash = map { ($_->{ilsevent} => $_) } @allevents;
117     @allevents = values %hash;
118
119     $runner->cleanup;
120
121     return \@allevents if $$params{show_event_list};
122     return 1 unless @allevents;
123     return 0;
124 }
125
126
127 sub load_scripts {
128     my $runner = shift;
129
130     if(!$script) {
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];
136     }
137
138     $runner->add_path($_) for(@$script_libs);
139     $runner->load($script);
140 }
141
142
143 sub check_age_protect {
144     my( $patron, $copy ) = @_;
145
146     return undef unless $copy and $copy->age_protect and $patron;
147
148     my $hou = (ref $patron->home_ou) ? $patron->home_ou->id : $patron->home_ou;
149
150     my $prox = $U->storagereq(
151         'open-ils.storage.asset.copy.proximity', $copy->id, $hou );
152
153     # If this copy is within the appropriate proximity, 
154     # age protect does not apply
155     return undef if $prox <= $copy->age_protect->prox;
156
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 },
161         },
162         { order_by => 'age' }
163     );
164
165     # circ_lib may be fleshed
166     my $context_org = ref $copy->circ_lib ? $copy->circ_lib->id : $copy->circ_lib;
167     my $age_protect_date = $copy->create_date;
168     $age_protect_date = $copy->active_date if($U->ou_ancestor_setting_value($context_org, 'circ.holds.age_protect.active_date'));
169
170     my $age = 0;
171     my $age_protect_parsed;
172     if($age_protect_date) {
173         # Now, now many seconds old is this copy
174         $age_protect_parsed = DateTime::Format::ISO8601
175             ->new
176             ->parse_datetime( OpenSRF::Utils::cleanse_ISO8601($age_protect_date) )
177             ->epoch;
178         $age = time - $age_protect_parsed;
179     }
180
181     for my $protection ( @$protection_list ) {
182
183         $logger->info("analyzing age protect ".$protection->name);
184
185         # age protect does not apply if within the proximity
186         last if $prox <= $protection->prox;
187
188         # How many seconds old does the copy have to be to escape age protection
189         my $interval = OpenSRF::Utils::interval_to_seconds($protection->age);
190
191         $logger->info("age_protect interval=$interval, age_protect_date=$age_protect_parsed, age=$age");
192
193         if( $interval > $age ) { 
194             # if age of the item is less than the protection interval, 
195             # the item falls within the age protect range
196             $logger->info("age_protect prevents copy from having a hold placed on it: ".$copy->id);
197             return OpenILS::Event->new('ITEM_AGE_PROTECTED', copy => $copy->id );
198         }
199     }
200         
201     return undef;
202 }
203
204 my $LEGACY_HOLD_EVENT_MAP = {
205     'config.hold_matrix_test.holdable' => 'ITEM_NOT_HOLDABLE',
206     'item.holdable' => 'ITEM_NOT_HOLDABLE',
207     'location.holdable' => 'ITEM_NOT_HOLDABLE',
208     'status.holdable' => 'ITEM_NOT_HOLDABLE',
209     'transit_range' => 'ITEM_NOT_HOLDABLE',
210     'no_matchpoint' => 'NO_POLICY_MATCHPOINT',
211     'config.hold_matrix_test.max_holds' => 'MAX_HOLDS',
212     'config.rule_age_hold_protect.prox' => 'ITEM_AGE_PROTECTED'
213 };
214
215 sub indb_hold_permit {
216     my $params = shift;
217
218     my $function = $$params{retarget} ? 'action.hold_retarget_permit_test' : 'action.hold_request_permit_test';
219     my $patron_id = 
220         ref($$params{patron}) ? $$params{patron}->id : $$params{patron_id};
221     my $request_lib = 
222         ref($$params{request_lib}) ? $$params{request_lib}->id : $$params{request_lib};
223
224     my $HOLD_TEST = {
225         from => [
226             $function,
227             $$params{pickup_lib}, 
228             $request_lib,
229             $$params{copy}->id, 
230             $patron_id,
231             $$params{requestor}->id 
232         ]
233     };
234
235     my $e = new_editor(xact=>1);
236     my $results = $e->json_query($HOLD_TEST);
237     $e->rollback;
238
239     unless($$params{show_event_list}) {
240         return 1 if $U->is_true($results->[0]->{success});
241         return 0;
242     }
243
244     return [
245         new OpenILS::Event(
246             "NO_POLICY_MATCHPOINT",
247             "payload" => {"fail_part" => "no_matchpoint"}
248         )
249     ] unless @$results;
250
251     return [] if $U->is_true($results->[0]->{success});
252
253     return [
254         map {
255             my $event = new OpenILS::Event(
256                 $LEGACY_HOLD_EVENT_MAP->{$_->{"fail_part"}} || $_->{"fail_part"}
257             );
258             $event->{"payload"} = {"fail_part" => $_->{"fail_part"}};
259             $event;
260         } @$results
261     ];
262 }
263
264
265 23;