LP#1879983: My Account curbside TPAC app
authorMike Rylander <mrylander@gmail.com>
Fri, 29 May 2020 16:17:07 +0000 (12:17 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Tue, 15 Sep 2020 20:20:45 +0000 (16:20 -0400)
This adds code for managing curbside appointments in the
public catalog My Account page.

In addition to Mike Rylander, significant contributions to this
patch were made and Galen Charlton.

Sponsored-by: PaILS
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Michele Morgan <mmorgan@noblenet.org>
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm

index 3c78ea2..201d1f4 100644 (file)
@@ -5,9 +5,11 @@ use OpenSRF::Utils::Logger qw/$logger/;
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
 use OpenILS::Utils::Fieldmapper;
 use OpenILS::Application::AppUtils;
+use OpenSRF::EX qw/:try/;
 use OpenILS::Event;
 use OpenSRF::Utils::JSON;
 use OpenSRF::Utils::Cache;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use Digest::MD5 qw(md5_hex);
 use Data::Dumper;
 $Data::Dumper::Indent = 0;
@@ -1169,11 +1171,30 @@ sub fetch_user_holds {
 
     # put the holds back into the original server sort order
     my @sorted;
+    my %pickup_libs;
     for my $id (@$hold_ids) {
         push @sorted, grep { $_->{hold}->{hold}->id == $id } @holds;
+
+        my $h = $sorted[-1]->{hold}->{hold};
+        # if available, report the pickup lib in the list
+        $pickup_libs{$h->pickup_lib} = 1 if (
+            $h && $h->pickup_lib == $h->current_shelf_lib &&
+            $h->shelf_time && !$h->cancel_time && !$h->fulfillment_time
+        );
     }
 
-    return { holds => \@sorted, ids => $hold_ids, all_ids => $all_ids };
+    my $curbsides = [];
+    try { # if the service is not running, just let this fail silently
+        $curbsides = $U->simplereq(
+            'open-ils.curbside',
+            'open-ils.curbside.fetch_mine.atomic',
+            $e->authtoken
+        );
+    } catch Error with {};
+
+    my @pickup_libs = sort { $U->find_org($U->get_org_tree,$a)->name cmp $U->find_org($U->get_org_tree,$b)->name } keys %pickup_libs; 
+
+    return { holds => \@sorted, ids => $hold_ids, all_ids => $all_ids, pickup_libs => \@pickup_libs, curbsides => $curbsides };
 }
 
 sub handle_hold_update {
@@ -1181,6 +1202,7 @@ sub handle_hold_update {
     my $action = shift;
     my $hold_ids = shift;
     my $e = $self->editor;
+    my $ctx = $self->ctx;
     my $url;
 
     my @hold_ids = ($hold_ids) ? @$hold_ids : $self->cgi->param('hold_id'); # for non-_all actions
@@ -1252,6 +1274,93 @@ sub handle_hold_update {
                 $url .= ";$param=" . uri_escape_utf8($_) foreach @vals;
             }
         }
+    } elsif ($action eq 'curbside') { # we'll only work on one curbside slot per refresh
+        $circ->kill_me;
+
+        $circ = OpenSRF::AppSession->create('open-ils.curbside');
+
+        # see what we're doing with curbside here...
+        my $cs_action = $self->cgi->param("cs_action");
+        my $slot_id = $self->cgi->param("cs_slot_id");
+
+        # we have an id, let's grab it if we can
+        my $slot = $e->retrieve_action_curbside($slot_id);
+        $slot = undef if ($slot && $slot->patron != $e->requestor->id); # nice try!
+
+        my $org = $self->cgi->param("cs_org");
+        my $date = $self->cgi->param("cs_date");
+        my $time = $self->cgi->param("cs_time");
+        my $notes = $self->cgi->param("cs_notes");
+
+        if ($slot) {
+            $org ||= $slot->org;
+            $notes ||= $slot->notes;
+            if ($slot->slot) {
+                my $dt = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($slot->slot));
+                $date ||= $dt->strftime('%F');
+                $time ||= $dt->strftime('%T');
+            }
+        }
+
+        $ctx->{cs_org} = $org;
+        $ctx->{cs_date} = $date;
+        $ctx->{cs_time} = $time;
+        $ctx->{cs_notes} = $notes;
+        $ctx->{cs_slot_id} = $slot->id if ($slot);
+        $ctx->{cs_slot} = $slot;
+
+        if ($cs_action eq 'reset') {
+            $ctx->{cs_org} = $org = undef;
+            $ctx->{cs_date} = $date = undef;
+            $ctx->{cs_time} = $time = undef;
+            $ctx->{cs_notes} = $notes = undef;
+            $ctx->{cs_slot_id} = $slot_id = undef;
+            $ctx->{cs_slot} = $slot = undef;
+        } elsif ($cs_action eq 'save' && $org && $date && $time) {
+            my $mode = $slot ? 'update' : 'create';
+            $slot = $circ->request(
+                "open-ils.curbside.${mode}_appointment",
+                $e->authtoken, $e->requestor->id, $date, $time, $org, $notes
+            )->gather(1);
+
+            if (defined $U->event_code($slot)) {
+                $self->apache->log->warn(
+                    "error attempting to $mode a curbside appointment for patron ".
+                    $e->requestor->id . ", got event " .  $slot->{textcode}
+                );
+                $ctx->{curbside_action_event} = $slot;
+                $ctx->{cs_slot} = undef;
+            } else {
+                $ctx->{cs_slot} = $slot;
+            }
+            $url = $self->ctx->{proto} . '://' . $self->ctx->{hostname} . $self->ctx->{opac_root} . '/myopac/holds_curbside';
+        } elsif ($cs_action eq 'cancel' && $slot) {
+            my $curbsides = $U->simplereq(
+                'open-ils.curbside',
+                'open-ils.curbside.delete_appointment',
+                $e->authtoken, $slot->id
+            );
+            $url = $self->ctx->{proto} . '://' . $self->ctx->{hostname} . $self->ctx->{opac_root} . '/myopac/holds_curbside';
+        } elsif ($cs_action eq 'arrive' && $slot) {
+            my $curbsides = $U->simplereq(
+                'open-ils.curbside',
+                'open-ils.curbside.mark_arrived',
+                $e->authtoken, $slot->id
+            );
+        } elsif ($cs_action eq 'deliver' && $slot) {
+            my $curbsides = $U->simplereq(
+                'open-ils.curbside',
+                'open-ils.curbside.mark_delivered',
+                $e->authtoken, $slot->id
+            );
+        }
+
+        if ($date and $org and !$ctx->{cs_times}{$date}) {
+            $ctx->{cs_times}{$date} = $circ->request(
+                'open-ils.curbside.times_for_date.atomic',
+                $e->authtoken, $date, $org
+            )->gather(1);
+        }
     }
 
     $circ->kill_me;
@@ -1282,6 +1391,25 @@ sub load_myopac_holds {
 
     if($holds_object->{holds}) {
         $ctx->{holds} = $holds_object->{holds};
+        $ctx->{curbside_appointments} = {};
+
+        $logger->info('curbside: found '.scalar(@{$holds_object->{curbsides}}).' appointments');
+
+        for my $cs (@{$holds_object->{curbsides}}) {
+            if ($cs->slot) {
+                my $dt = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($cs->slot))->strftime('%F');
+                $ctx->{cs_times}{$dt} = $U->simplereq(
+                    'open-ils.curbside', 'open-ils.curbside.times_for_date.atomic',
+                    $e->authtoken, $dt, $cs->org
+                );
+            }
+            $ctx->{curbside_appointments}{$cs->org} = $cs;
+        }
+
+        $ctx->{curbside_pickup_libs} = [];
+        for my $pul (@{$holds_object->{pickup_libs}}) {
+            push(@{$ctx->{curbside_pickup_libs}}, $pul) if $ctx->{get_org_setting}->($pul, 'circ.curbside');
+        }
     }
     $ctx->{holds_ids} = $holds_object->{all_ids};
     $ctx->{holds_limit} = $limit;
@@ -2100,6 +2228,15 @@ sub load_myopac_hold_history {
         where => {id => $e->requestor->id}
     });
 
+    # This is used to detect whether we want to show the curbside tab
+    my $extant_holds_object = $self->fetch_user_holds();
+    if($extant_holds_object->{holds}) {
+        $ctx->{curbside_pickup_libs} = [];
+        for my $pul (@{$extant_holds_object->{pickup_libs}}) {
+            push(@{$ctx->{curbside_pickup_libs}}, $pul) if $ctx->{get_org_setting}->($pul, 'circ.curbside');
+        }
+    }
+
     my $holds_object = $self->fetch_user_holds([map { $_->{id} } @$hold_ids], 0, 1, 0, $limit, $offset);
     if($holds_object->{holds}) {
         $ctx->{holds} = $holds_object->{holds};