1 package OpenILS::Application::Actor::ClosedDates;
2 use base 'OpenILS::Application';
3 use strict; use warnings;
4 use OpenSRF::EX qw(:try);
5 use OpenILS::Utils::DateTime qw(:datetime);
7 use DateTime::Format::ISO8601;
8 use OpenILS::Utils::CStoreEditor q/:funcs/;
9 use OpenILS::Application::AppUtils;
10 my $U = "OpenILS::Application::AppUtils";
12 sub initialize { return 1; }
14 sub process_emergency {
15 my( $self, $conn, $auth, $date ) = @_;
17 my $e = new_editor(authtoken=>$auth);
18 return $e->event unless $e->checkauth;
20 return $e->die_event unless $e->allowed(
21 'EMERGENCY_CLOSING', $date->org_unit);
23 my $id = ref($date->emergency_closing) ? $date->emergency_closing->id : $date->emergency_closing;
27 my $rows = $e->json_query({
28 from => ['action.emergency_closing_stage_1', $id]
31 return unless ($rows && @$rows);
33 $conn->respond({stage => 'start', stats => $$rows[0]});
35 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
38 my $circs = $e->search_action_emergency_closing_circulation(
39 { emergency_closing => $id }
41 my $circ_total = scalar(@$circs);
44 $mod = int($circ_total / 10) if ($circ_total >= 100);
45 $mod = int($circ_total / 100) if ($circ_total >= 1000);
48 for my $circ (@$circs) {
50 my $rows = $e->json_query({ from => ['action.emergency_closing_stage_2_circ', $circ->id] });
53 $ses->request('open-ils.trigger.event.autocreate', 'checkout.due.emergency_closing', $circ, $e->requestor->ws_ou)
54 if (ref($rows) && @$rows && $U->is_true($$rows[0]{'action.emergency_closing_stage_2_circ'}));
55 $conn->respond({stage => 'circulations', circulations => [$count,$circ_total]})
56 if ($mod == 1 or !($circ_total % $mod));
60 my $holds = $e->search_action_emergency_closing_hold(
61 { emergency_closing => $id }
63 my $hold_total = scalar(@$holds);
66 $mod = int($hold_total / 10) if ($hold_total >= 100);
67 $mod = int($hold_total / 100) if ($hold_total >= 1000);
70 for my $hold (@$holds) {
72 my $rows = $e->json_query({ from => ['action.emergency_closing_stage_2_hold', $hold->id] });
75 $ses->request('open-ils.trigger.event.autocreate', 'hold.shelf_expire.emergency_closing', $hold, $e->requestor->ws_ou)
76 if (ref($rows) && @$rows && $U->is_true($$rows[0]{'action.emergency_closing_stage_2_hold'}));
77 $conn->respond({stage => 'holds', holds => [$count,$hold_total]})
78 if ($mod == 1 or !($hold_total % $mod));
81 # Stage 2 - reservations
82 my $ress = $e->search_action_emergency_closing_reservation(
83 { emergency_closing => $id }
85 my $res_total = scalar(@$ress);
88 $mod = int($res_total / 10) if ($res_total >= 100);
89 $mod = int($res_total / 100) if ($res_total >= 1000);
92 for my $res (@$ress) {
94 my $rows = $e->json_query({ from => ['action.emergency_closing_stage_2_reservation', $res->id] });
97 $ses->request('open-ils.trigger.event.autocreate', 'booking.due.emergency_closing', $res, $e->requestor->ws_ou)
98 if (ref($rows) && @$rows && $U->is_true($$rows[0]{'action.emergency_closing_stage_2_reservation'}));
99 $conn->respond({stage => 'ress', ress => [$count,$res_total]})
100 if ($mod == 1 or !($res_total % $mod));
104 my $eclosing = $e->retrieve_action_emergency_closing($id);
105 $eclosing->process_end_time('now');
107 $e->update_action_emergency_closing($eclosing);
110 return {stage => 'complete', complete => 1};
112 __PACKAGE__->register_method(
113 method => 'process_emergency',
114 api_name => 'open-ils.actor.org_unit.closed.process_emergency',
116 max_bundle_count => 1,
117 signature => q/Processes an emergency closing/
120 __PACKAGE__->register_method(
121 method => 'fetch_dates',
122 api_name => 'open-ils.actor.org_unit.closed.retrieve.all',
124 Retrieves a list of closed date object IDs
129 my( $self, $conn, $auth, $args ) = @_;
131 my $e = new_editor(authtoken=>$auth);
132 return $e->event unless $e->checkauth;
134 my $org = $$args{orgid} || $e->requestor->ws_ou;
135 my @date = localtime;
136 my $start = $$args{start_date} || #default to today
137 ($date[5] + 1900) .'-'. ($date[4] + 1) .'-'. $date[3];
138 my $end = $$args{end_date} || '3000-01-01'; # Y3K, here I come..
140 my $dates = $e->search_actor_org_unit_closed_date(
143 { close_start => { ">=" => $start }, close_end => { "<=" => $end } },
144 { emergency_closing => { "!=" => undef }, "+aec" => { process_end_time => { "=" => undef } } }
148 flesh_fields => { aoucd => ['emergency_closing'], aec => ['status'] },
149 join => { "aec" => { type => "left" } },
150 limit => $$args{limit},
151 offset => $$args{offset}
152 }], { idlist => $$args{idlist} } ) or return $e->event;
154 if(!$$args{idlist} and @$dates) {
155 $dates = [ sort { $a->close_start cmp $b->close_start } @$dates ];
161 __PACKAGE__->register_method(
162 method => 'fetch_date',
163 api_name => 'open-ils.actor.org_unit.closed.retrieve',
165 Retrieves a single date object
170 my( $self, $conn, $auth, $id ) = @_;
171 my $e = new_editor(authtoken=>$auth);
172 return $e->event unless $e->checkauth;
173 my $date = $e->retrieve_actor_org_unit_closed_date($id) or return $e->event;
174 $date->emergency_closing(
175 $e->retrieve_action_emergency_closing($date->emergency_closing)
176 ) if $date->emergency_closing;
181 __PACKAGE__->register_method(
182 method => 'delete_date',
183 api_name => 'open-ils.actor.org_unit.closed.delete',
185 Removes a single date object
190 my( $self, $conn, $auth, $id ) = @_;
191 my $e = new_editor(authtoken=>$auth, xact => 1);
192 return $e->die_event unless $e->checkauth;
193 my $date = $e->retrieve_actor_org_unit_closed_date($id) or return $e->die_event;
194 if ($date->emergency_closing) {
195 return $e->die_event unless $e->allowed(
196 'EMERGENCY_CLOSING', $date->org_unit);
198 return $e->die_event unless $e->allowed(
199 'actor.org_unit.closed_date.delete', $date->org_unit);
200 $e->delete_actor_org_unit_closed_date($date) or return $e->die_event;
208 __PACKAGE__->register_method(
209 method => 'create_date',
210 api_name => 'open-ils.actor.org_unit.closed.create',
212 Creates a new org closed data
217 my( $self, $conn, $auth, $date, $emergency ) = @_;
219 my $e = new_editor(authtoken=>$auth, xact =>1);
220 return $e->die_event unless $e->checkauth;
222 return $e->die_event unless $e->allowed(
223 'actor.org_unit.closed_date.create', $date->org_unit);
227 unless $e->allowed('EMERGENCY_CLOSING', $date->org_unit);
228 $e->create_action_emergency_closing($emergency)
229 or return $e->die_event;
230 $date->emergency_closing($emergency->id);
233 $e->create_actor_org_unit_closed_date($date) or return $e->die_event;
235 my $newobj = $e->retrieve_actor_org_unit_closed_date($date->id)
236 or return $e->die_event;
238 $newobj->emergency_closing(
239 $e->retrieve_action_emergency_closing($newobj->emergency_closing)
247 __PACKAGE__->register_method(
248 method => 'edit_date',
249 api_name => 'open-ils.actor.org_unit.closed.update',
251 Updates a closed date object
256 my( $self, $conn, $auth, $date ) = @_;
257 my $e = new_editor(authtoken=>$auth, xact =>1);
258 return $e->die_event unless $e->checkauth;
260 # First make sure they have the right to update the selected date object
261 my $odate = $e->retrieve_actor_org_unit_closed_date($date->id)
262 or return $e->die_event;
264 if ($odate->emergency_closing) {
265 return $e->die_event unless $e->allowed(
266 'EMERGENCY_CLOSING', $odate->org_unit);
269 return $e->die_event unless $e->allowed(
270 'actor.org_unit.closed_date.update', $odate->org_unit);
272 $e->update_actor_org_unit_closed_date($date) or return $e->die_event;
274 my $newobj = $e->retrieve_actor_org_unit_closed_date($date->id)
275 or return $e->die_event;
277 $newobj->emergency_closing(
278 $e->retrieve_action_emergency_closing($newobj->emergency_closing)
279 ) if $odate->emergency_closing;
287 __PACKAGE__->register_method(
288 method => 'is_probably_emergency_closing',
289 api_name => 'open-ils.actor.org_unit.closed_date.emergency_test',
291 Returns a truthy value if the closing start date is either in
292 the past or is nearer in the future than the longest configured
293 circulation duration.
294 @param auth An auth token
295 @param date A closed date object
298 sub is_probably_emergency_closing {
299 my( $self, $conn, $auth, $date ) = @_;
300 my $e = new_editor(authtoken=>$auth);
301 return $e->event unless $e->checkauth;
304 my $start_seconds = DateTime::Format::ISO8601->parse_datetime(
305 clean_ISO8601($date->close_start)
309 return 1 if ($start_seconds < time); # It is!
311 # No? Let's see if it's coming up sooner than
312 # the currently-furthest normal due date...
313 my $rules = $e->search_config_rules_circ_duration({
316 transform => 'interval_pl_timestamptz',
318 value => $date->close_start
321 }); # That is basically: WHERE 'now'::timestamptz + extended > $date->close_start
322 # which translates to "the closed start happens earlier than the theoretically
323 # latest due date we could currently have, so it might need emergency
326 return scalar(@$rules); # No rows means "not emergency".
330 __PACKAGE__->register_method(
331 method => 'closed_dates_overlap',
332 api_name => 'open-ils.actor.org_unit.closed_date.overlap',
334 Returns an object with 'start' and 'end' fields
335 start is the first day the org is open going backwards from
336 'date'. end is the next day the org is open going
338 @param auth An auth token
339 @param orgid The org unit in question
340 @param date The date to search
343 sub closed_dates_overlap {
344 my( $self, $conn, $auth, $orgid, $date ) = @_;
345 my $e = new_editor(authtoken=>$auth);
346 return $e->event unless $e->checkauth;
348 'open-ils.storage.actor.org_unit.closed_date.overlap', $orgid, $date );