1 package OpenILS::WWW::EGCatLoader;
2 use strict; use warnings;
3 use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST);
4 use OpenSRF::Utils::Logger qw/$logger/;
5 use OpenILS::Utils::CStoreEditor qw/:funcs/;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Application::AppUtils;
8 my $U = 'OpenILS::Application::AppUtils';
12 # user : au object, fleshed
15 $self->ctx->{page} = 'myopac';
17 $self->ctx->{user} = $self->editor->retrieve_actor_user([
18 $self->ctx->{user}->id,
28 return Apache2::Const::OK;
32 sub fetch_user_holds {
40 my $e = $self->editor;
42 my $circ = OpenSRF::AppSession->create('open-ils.circ');
46 $hold_ids = $circ->request(
47 'open-ils.circ.holds.id_list.retrieve.authoritative',
52 $hold_ids = [ grep { defined $_ } @$hold_ids[$offset..($offset + $limit - 1)] ] if $limit or $offset;
56 return $hold_ids if $ids_only or @$hold_ids == 0;
59 suppress_notices => 1,
60 suppress_transits => 1,
62 suppress_patron_details => 1,
63 include_bre => $flesh ? 1 : 0
66 # ----------------------------------------------------------------
67 # Collect holds in batches of $batch_size for faster retrieval
71 my $mk_req_batch = sub {
73 my $top_idx = $batch_idx + $batch_size;
74 while($batch_idx < $top_idx) {
75 my $hold_id = $hold_ids->[$batch_idx++];
77 my $ses = OpenSRF::AppSession->create('open-ils.circ');
78 my $req = $ses->request(
79 'open-ils.circ.hold.details.retrieve',
80 $e->authtoken, $hold_id, $args);
81 push(@ses, {ses => $ses, req => $req});
91 @ses = $mk_req_batch->() if $first;
92 last if $first and not @ses;
95 while(my $blob = pop(@collected)) {
96 $blob->{marc_xml} = XML::LibXML->new->parse_string($blob->{hold}->{bre}->marc) if $flesh;
101 for my $req_data (@ses) {
102 push(@collected, {hold => $req_data->{req}->gather(1)});
103 $req_data->{ses}->kill_me;
106 @ses = $mk_req_batch->();
107 last unless @collected or @ses;
114 sub handle_hold_update {
117 my $e = $self->editor;
120 my @hold_ids = $self->cgi->param('hold_id'); # for non-_all actions
121 @hold_ids = @{$self->fetch_user_holds(undef, 1)} if $action =~ /_all/;
123 my $circ = OpenSRF::AppSession->create('open-ils.circ');
125 if($action =~ /cancel/) {
127 for my $hold_id (@hold_ids) {
128 my $resp = $circ->request(
129 'open-ils.circ.hold.cancel', $e->authtoken, $hold_id, 6 )->gather(1); # 6 == patron-cancelled-via-opac
135 for my $hold_id (@hold_ids) {
136 my $vals = {id => $hold_id};
138 if($action =~ /activate/) {
139 $vals->{frozen} = 'f';
140 $vals->{thaw_date} = undef;
142 } elsif($action =~ /suspend/) {
143 $vals->{frozen} = 't';
144 # $vals->{thaw_date} = TODO;
146 push(@$vlist, $vals);
149 $circ->request('open-ils.circ.hold.update.batch.atomic', $e->authtoken, undef, $vlist)->gather(1);
156 sub load_myopac_holds {
158 my $e = $self->editor;
159 my $ctx = $self->ctx;
162 my $limit = $self->cgi->param('limit') || 0;
163 my $offset = $self->cgi->param('offset') || 0;
164 my $action = $self->cgi->param('action') || '';
166 $self->handle_hold_update($action) if $action;
168 $ctx->{holds} = $self->fetch_user_holds(undef, 0, 1, $limit, $offset);
170 return Apache2::Const::OK;
173 sub load_place_hold {
175 my $ctx = $self->ctx;
176 my $e = $self->editor;
177 my $cgi = $self->cgi;
178 $self->ctx->{page} = 'place_hold';
180 $ctx->{hold_target} = $cgi->param('hold_target');
181 $ctx->{hold_type} = $cgi->param('hold_type');
182 $ctx->{default_pickup_lib} = $e->requestor->home_ou; # XXX staff
184 if($ctx->{hold_type} eq 'T') {
185 $ctx->{record} = $e->retrieve_biblio_record_entry($ctx->{hold_target});
189 $ctx->{marc_xml} = XML::LibXML->new->parse_string($ctx->{record}->marc);
191 if(my $pickup_lib = $cgi->param('pickup_lib')) {
194 patronid => $e->requestor->id,
195 titleid => $ctx->{hold_target}, # XXX
196 pickup_lib => $pickup_lib,
200 my $allowed = $U->simplereq(
202 'open-ils.circ.title_hold.is_possible',
206 if($allowed->{success} == 1) {
207 my $hold = Fieldmapper::action::hold_request->new;
209 $hold->pickup_lib($pickup_lib);
210 $hold->requestor($e->requestor->id);
211 $hold->usr($e->requestor->id); # XXX staff
212 $hold->target($ctx->{hold_target});
213 $hold->hold_type($ctx->{hold_type});
214 # frozen, expired, etc..
216 my $stat = $U->simplereq(
218 'open-ils.circ.holds.create',
222 if($stat and $stat > 0) {
224 # if successful, return the user to the requesting page
225 $self->apache->log->info("Redirecting back to " . $cgi->param('redirect_to'));
226 $self->apache->print($cgi->redirect(-url => $cgi->param('redirect_to')));
227 return Apache2::Const::REDIRECT;
231 $ctx->{hold_failed} = 1; # XXX process the events, etc
236 $logger->info('hold permit result ' . OpenSRF::Utils::JSON->perl2JSON($allowed));
239 return Apache2::Const::OK;
243 sub fetch_user_circs {
245 my $flesh = shift; # flesh bib data, etc.
246 my $circ_ids = shift;
250 my $e = $self->editor;
255 @circ_ids = @$circ_ids;
259 my $circ_data = $U->simplereq(
261 'open-ils.actor.user.checked_out',
266 @circ_ids = ( @{$circ_data->{overdue}}, @{$circ_data->{out}} );
268 if($limit or $offset) {
269 @circ_ids = grep { defined $_ } @circ_ids[0..($offset + $limit - 1)];
273 return [] unless @circ_ids;
275 my $cstore = OpenSRF::AppSession->create('open-ils.cstore');
280 circ => ['target_copy'],
281 acp => ['call_number'],
287 my $circs = $e->search_action_circulation(
288 [{id => \@circ_ids}, ($flesh) ? $qflesh : {}], {substream => 1});
291 for my $circ (@$circs) {
294 marc_xml => ($flesh and $circ->target_copy->call_number->id != -1) ?
295 XML::LibXML->new->parse_string($circ->target_copy->call_number->record->marc) :
296 undef # pre-cat copy, use the dummy title/author instead
301 # make sure the final list is in the correct order
303 for my $id (@circ_ids) {
306 (grep { $_->{circ}->id == $id } @circs)
310 return \@sorted_circs;
314 sub handle_circ_renew {
317 my $ctx = $self->ctx;
319 my @renew_ids = $self->cgi->param('circ');
321 my $circs = $self->fetch_user_circs(0, ($action eq 'renew') ? [@renew_ids] : undef);
323 # TODO: fire off renewal calls in batches to speed things up
325 for my $circ (@$circs) {
327 my $evt = $U->simplereq(
329 'open-ils.circ.renew',
330 $self->editor->authtoken,
332 patron_id => $self->editor->requestor->id,
333 copy_id => $circ->{circ}->target_copy,
338 # TODO return these, then insert them into the circ data
339 # blob that is shoved into the template for each circ
340 # so the template won't have to match them
341 push(@responses, {copy => $circ->{circ}->target_copy, evt => $evt});
348 sub load_myopac_circs {
350 my $e = $self->editor;
351 my $ctx = $self->ctx;
354 my $limit = $self->cgi->param('limit') || 0; # 0 == unlimited
355 my $offset = $self->cgi->param('offset') || 0;
356 my $action = $self->cgi->param('action') || '';
358 # perform the renewal first if necessary
359 my @results = $self->handle_circ_renew($action) if $action =~ /renew/;
361 $ctx->{circs} = $self->fetch_user_circs(1, undef, $limit, $offset);
363 my $success_renewals = 0;
364 my $failed_renewals = 0;
365 for my $data (@{$ctx->{circs}}) {
366 my ($resp) = grep { $_->{copy} == $data->{circ}->target_copy->id } @results;
369 my $evt = ref($resp->{evt}) eq 'ARRAY' ? $resp->{evt}->[0] : $resp->{evt};
370 $data->{renewal_response} = $evt;
371 $success_renewals++ if $evt->{textcode} eq 'SUCCESS';
372 $failed_renewals++ if $evt->{textcode} ne 'SUCCESS';
376 $ctx->{success_renewals} = $success_renewals;
377 $ctx->{failed_renewals} = $failed_renewals;
379 return Apache2::Const::OK;
382 sub load_myopac_fines {
384 my $e = $self->editor;
385 my $ctx = $self->ctx;
394 my $limit = $self->cgi->param('limit') || 0;
395 my $offset = $self->cgi->param('offset') || 0;
397 my $cstore = OpenSRF::AppSession->create('open-ils.cstore');
399 # TODO: This should really be a ML call, but the existing calls
400 # return an excessive amount of data and don't offer streaming
402 my %paging = ($limit or $offset) ? (limit => $limit, offset => $offset) : ();
404 my $req = $cstore->request(
405 'open-ils.cstore.direct.money.open_billable_transaction_summary.search',
407 usr => $e->requestor->id,
408 balance_owed => {'!=' => 0}
413 mobts => ['circulation', 'grocery'],
416 circ => ['target_copy'],
417 acp => ['call_number'],
420 order_by => { mobts => 'xact_start' },
425 while(my $resp = $req->recv) {
426 my $mobts = $resp->content;
427 my $circ = $mobts->circulation;
430 if($mobts->grocery) {
431 my @billings = sort { $a->billing_ts cmp $b->billing_ts } @{$mobts->grocery->billings};
432 $last_billing = pop(@billings);
435 # XXX TODO switch to some money-safe non-fp library for math
436 $ctx->{"fines"}->{$_} += $mobts->$_ for (
437 qw/total_paid total_owed balance_owed/
441 @{$ctx->{"fines"}->{$mobts->grocery ? "grocery" : "circulation"}},
444 last_grocery_billing => $last_billing,
445 marc_xml => ($mobts->xact_type ne 'circulation' or $circ->target_copy->call_number->id == -1) ?
447 XML::LibXML->new->parse_string($circ->target_copy->call_number->record->marc),
452 return Apache2::Const::OK;
455 sub load_myopac_update_email {
457 my $e = $self->editor;
458 my $ctx = $self->ctx;
459 my $email = $self->cgi->param('email') || '';
461 unless($email =~ /.+\@.+\..+/) { # TODO better regex?
462 $ctx->{invalid_email} = $email;
463 return Apache2::Const::OK;
466 my $stat = $U->simplereq(
468 'open-ils.actor.user.email.update',
469 $e->authtoken, $email);
471 my $url = $self->apache->unparsed_uri;
472 $url =~ s/update_email/main/;
473 $self->apache->print($self->cgi->redirect(-url => $url));
475 return Apache2::Const::REDIRECT;
478 sub load_myopac_bookbags {
480 my $e = $self->editor;
481 my $ctx = $self->ctx;
482 my $limit = $self->cgi->param('limit') || 0;
483 my $offset = $self->cgi->param('offset') || 0;
485 my $args = {order_by => {cbreb => 'name'}};
486 $args->{limit} = $limit if $limit;
487 $args->{offset} = $limit if $limit;
489 $ctx->{bookbags} = $e->search_container_biblio_record_entry_bucket([
490 {owner => $self->editor->requestor->id, btype => 'bookbag'},
494 return Apache2::Const::OK;