LP1797934: Add a patron's booking reservations to their OPAC account
authorJane Sandberg <sandbej@linnbenton.edu>
Mon, 15 Oct 2018 17:00:00 +0000 (12:00 -0500)
committerDan Wells <dbw2@calvin.edu>
Wed, 20 Feb 2019 22:58:13 +0000 (17:58 -0500)
This commit creates a new tab in the patron OPAC account area where a
patron can view their current bookings.

Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
Signed-off-by: Remington Steed <rjs7@calvin.edu>
Signed-off-by: Dan Wells <dbw2@calvin.edu>
Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm
Open-ILS/src/templates/opac/css/style.css.tt2
Open-ILS/src/templates/opac/myopac/reservations.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac/parts/myopac/base.tt2
docs/RELEASE_NOTES_NEXT/OPAC/add_booking_reservations_to_opac.adoc [new file with mode: 0644]
docs/opac/my_account.adoc

index 09680b8..a715f88 100644 (file)
@@ -455,6 +455,8 @@ sub resource_list_by_attrs {
         return [map { $_->{id} } @$rows];
     }
 }
+
+
 __PACKAGE__->register_method(
     method   => "resource_list_by_attrs",
     api_name => "open-ils.booking.resources.filtered_id_list",
@@ -600,6 +602,59 @@ sub reservation_list_by_filters {
     $e->disconnect;
     return $bresv_list ? $bresv_list : [];
 }
+
+__PACKAGE__->register_method(
+    method   => "upcoming_reservation_list_by_user",
+    api_name => "open-ils.booking.reservations.upcoming_reservation_list_by_user",
+    argc     => 2,
+    signature=> {
+        params => [
+            {type => 'string', desc => 'Authentication token'},
+            {type => 'User ID', type => 'number', desc => 'User ID'},
+        ],
+        return => { desc => "Information about all reservations for a user that haven't yet ended." },
+    },
+    notes    => "You can send undef/NULL as the User ID to get reservations for the logged in user."
+);
+
+sub upcoming_reservation_list_by_user {
+    my ($self, $conn, $auth, $user_id) = @_;
+    my $e = new_editor(authtoken => $auth);
+    return $e->event unless $e->checkauth;
+
+    $user_id = $e->requestor->id unless defined $user_id;
+    
+    unless($e->requestor->id == $user_id) {
+        my $user = $e->retrieve_actor_user($user_id) or return $e->event;
+        return $e->event unless $e->allowed('VIEW_TRANSACTION');
+    }
+
+    my $select = { 'bresv' => [qw/start_time end_time cancel_time capture_time pickup_time pickup_lib/],
+        'brsrc' => [ 'barcode' ],
+        'brt' => [{'column' => 'name', 'alias' => 'resource_type_name'}],
+        'aou' => ['shortname', {'column' => 'name', 'alias' => 'pickup_name'}] };
+
+    my $from = { 'bresv' => {'brsrc' => {'field' => 'id', 'fkey' => 'current_resource'},
+        'brt' => {'field' => 'id', 'fkey' => 'target_resource_type'},
+        'aou' => {'field' => 'id', 'fkey' => 'pickup_lib'}} };
+
+    my $query = {
+        'select'   => $select,
+        'from'     => $from,
+        'where'    => { 'usr' => $user_id, 'return_time' => undef, 'end_time' => {'>' => gmtime_ISO8601() }},
+        'order_by' => [{ class => bresv => field => start_time => direction => 'asc' }]
+    };
+
+    my $cstore = OpenSRF::AppSession->connect('open-ils.cstore');
+    my $rows = $cstore->request(
+        'open-ils.cstore.json_query.atomic', $query
+    )->gather(1);
+    $cstore->disconnect;
+    $e->disconnect;
+    return [] if not @$rows;
+    return $rows;
+}
+
 __PACKAGE__->register_method(
     method   => "reservation_list_by_filters",
     api_name => "open-ils.booking.reservations.filtered_id_list",
index 2bf6704..19a3b25 100644 (file)
@@ -229,6 +229,7 @@ sub load {
     return $self->load_myopac_prefs_settings if $path =~ m|opac/myopac/prefs_settings|;
     return $self->load_myopac_prefs_my_lists if $path =~ m|opac/myopac/prefs_my_lists|;
     return $self->load_myopac_prefs if $path =~ m|opac/myopac/prefs|;
+    return $self->load_myopac_reservations if $path =~ m|opac/myopac/reservations|;
     return $self->load_sms_cn if $path =~ m|opac/sms_cn|;
 
     return Apache2::Const::OK;
index 19e8fbc..3ec7900 100644 (file)
@@ -2886,6 +2886,20 @@ sub load_myopac_circ_history_export {
 
 }
 
+sub load_myopac_reservations {
+    my $self = shift;
+    my $e = $self->editor;
+    my $ctx = $self->ctx;
+
+    my $upcoming = $U->simplereq("open-ils.booking", "open-ils.booking.reservations.upcoming_reservation_list_by_user",
+        $e->authtoken, undef
+    );
+
+    $ctx->{reservations} = $upcoming;
+    return Apache2::Const::OK;
+
+}
+
 sub load_password_reset {
     my $self = shift;
     my $cgi = $self->cgi;
index 31bd817..6c1dc70 100644 (file)
@@ -1381,13 +1381,13 @@ div.result_table_utils_cont {
 }
 
 .hold_note_title { font-weight: bold; }
-#acct_checked_main_header td, #acct_holds_main_header td, #acct_fines_main_header td, #acct_fines_confirm_header td, #acct_fees_main_header td,#acct_checked_hist_header td, #acct_holds_hist_header td, #acct_list_header td, #acct_list_header_anon td, #temp_list_holds td, #acct_messages_main_header td, #ebook_circs_main_table td, #ebook_holds_main_table td {
+#acct_checked_main_header td, #acct_holds_main_header td, #acct_fines_main_header td, #acct_fines_confirm_header td, #acct_fees_main_header td,#acct_checked_hist_header td, #acct_holds_hist_header td, #acct_list_header td, #acct_list_header_anon td, #temp_list_holds td, #acct_messages_main_header td, #ebook_circs_main_table td, #ebook_holds_main_table td, #acct_reservations_main_header td {
     background-color: [% css_colors.background %]; 
     padding: 10px;
         border: 1px solid #d3d3d3; 
 } 
 
-#acct_checked_main_header th, #acct_holds_main_header th, #acct_fines_main_header th, #acct_fines_confirm_header th, #acct_fees_main_header th, #acct_checked_hist_header th, #acct_holds_hist_header th, #acct_list_header th, #acct_list_header_anon th, #temp_list_holds th, #acct_messages_main_header th, #ebook_holds_main_table th {
+#acct_checked_main_header th, #acct_holds_main_header th, #acct_fines_main_header th, #acct_fines_confirm_header th, #acct_fees_main_header th, #acct_checked_hist_header th, #acct_holds_hist_header th, #acct_list_header th, #acct_list_header_anon th, #temp_list_holds th, #acct_messages_main_header th, #ebook_holds_main_table th, #acct_reservations_main_header th {
     [% IF rtl == 't' -%]
     text-align: right;
     [% ELSE -%]
@@ -3123,7 +3123,7 @@ a.preflib_change {
         border-bottom: none;
     }
         /* Force table to not be like tables anymore */
-        table#acct_checked_main_header thead tr th, table#acct_holds_main_header thead tr th, table#acct_checked_hist_header thead tr th, table#acct_holds_hist_header thead tr th, table#ebook_circs_main_table thead tr th, table#ebook_holds_main_table thead tr th {
+        table#acct_checked_main_header thead tr th, table#acct_holds_main_header thead tr th, table#acct_checked_hist_header thead tr th, table#acct_holds_hist_header thead tr th, table#ebook_circs_main_table thead tr th, table#ebook_holds_main_table thead tr th, table#acct_reservations_main_header tr th{
                 display: block;
         }
         table#acct_checked_main_header tbody tr td, table#acct_holds_main_header tbody tr td, table#acct_checked_hist_header tbody tr td, table#acct_holds_hist_header tbody tr td, table#ebook_circs_main_table tbody tr td, table#ebook_holds_main_table tbody tr td {
@@ -3141,11 +3141,11 @@ a.preflib_change {
                 [% END -%]
         }
 
-        table#acct_checked_main_header, table#acct_holds_main_header, table#acct_checked_hist_header, table#acct_holds_hist_header, table#ebook_circs_main_table, table#ebook_holds_main_table {
+        table#acct_checked_main_header, table#acct_holds_main_header, table#acct_checked_hist_header, table#acct_holds_hist_header, table#ebook_circs_main_table, table#ebook_holds_main_table, table#acct_reservations_main_header {
                 width: 90%;
         }
 
-        table#acct_checked_main_header tr, table#acct_holds_main_header tr, table#acct_checked_hist_header tr { border: 1px solid #ddd; }
+        table#acct_checked_main_header tr, table#acct_holds_main_header tr, table#acct_checked_hist_header tr, table#acct_reservations_main_header { border: 1px solid #ddd; }
 
         /* Holds history gets large white border to mimic header cell on other
            account screens that provide visual cue for next title. We should do
@@ -3156,7 +3156,7 @@ a.preflib_change {
         table#acct_holds_hist_header tr, table#ebook_circs_main_table tr, table#ebook_holds_main_table tr { border-top: 25px solid #fff; }
 
 
-        table#acct_checked_main_header td, table#acct_holds_main_header td, table#acct_checked_hist_header td, table#acct_holds_hist_header td, table#ebook_circs_main_table td, table#ebook_holds_main_table td {
+        table#acct_checked_main_header td, table#acct_holds_main_header td, table#acct_checked_hist_header td, table#acct_holds_hist_header td, table#ebook_circs_main_table td, table#ebook_holds_main_table td, table#acct_reservations_main_header td {
                 /* Behave  like a "row" */
                 border: none;
                 border-bottom: 1px solid #eee;
@@ -3168,7 +3168,7 @@ a.preflib_change {
                 [% END -%]
         }
 
-         table#acct_checked_main_header td:before, table#acct_holds_main_header td:before, table#acct_checked_hist_header td:before, table#acct_holds_hist_header td:before, table#ebook_circs_main_table td:before, table#ebook_holds_main_table td:before {
+         table#acct_checked_main_header td:before, table#acct_holds_main_header td:before, table#acct_checked_hist_header td:before, table#acct_holds_hist_header td:before, table#ebook_circs_main_table td:before, table#ebook_holds_main_table td:before, table#acct_reservations_main_header td {
                 /* Now like a table header */
                 position: absolute;
                 /* Top/left values mimic padding */
diff --git a/Open-ILS/src/templates/opac/myopac/reservations.tt2 b/Open-ILS/src/templates/opac/myopac/reservations.tt2
new file mode 100644 (file)
index 0000000..d134246
--- /dev/null
@@ -0,0 +1,39 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "reservations";
+%]
+<h3 class="sr-only">[% l('Reservations') %]</h3>
+[% IF ctx.reservations.size %]
+    <table id="acct_reservations_main_header" class="table_no_border_space table_no_cell_pad item_list_padding">
+        <tr>
+            <th scope="col">[% l('Resource type') %]</th>
+            <th scope="col">[% l('Reservation start time') %]</th>
+            <th scope="col">[% l('Reservation end time') %]</th>
+            <th scope="col">[% l('Pickup location') %]</th>
+            <th scope="col">[% l('Status') %]</th>
+        </tr>
+    [% FOREACH r IN ctx.reservations %]
+        <tr>
+            <td>[% r.resource_type_name %]</td>
+            <td>[% date.format(ctx.parse_datetime(r.start_time, r.pickup_lib), DATE_FORMAT _ ' %I:%M %p') %]</td>
+            <td>[% date.format(ctx.parse_datetime(r.end_time, r.pickup_lib), DATE_FORMAT _ ' %I:%M %p') %]</td>
+            <td><a href="../library/[% r.shortname %]">[% r.pickup_name %]</a></td>
+            <td>
+            [% IF r.cancel_time %]
+                [% l('Canceled') %]
+            [% ELSIF r.pickup_time %]
+                [% l('Checked Out') %]
+            [% ELSIF r.capture_time %]
+                [% l('Ready for Pickup') %]
+            [% ELSE %]
+                [% l('Reserved') %]
+            [% END %]
+            </td>
+        </tr>
+    [% END %]
+    </table>
+[% ELSE %]
+    [% l('You have no current reservations') %]
+[% END %]
+[% END %]
index 15cf56e..2153a1d 100644 (file)
@@ -6,7 +6,8 @@
         {url => "circs", name => l("Items Checked Out")},
         {url => "holds", name => l("Holds")},
         {url => "prefs", name => l("Account Preferences")},
-        {url => "lists", name => l("My Lists")}
+        {url => "lists", name => l("My Lists")},
+        {url => "reservations", name => l("Reservations")}
     ];
     skin_root = "../"
 %]
diff --git a/docs/RELEASE_NOTES_NEXT/OPAC/add_booking_reservations_to_opac.adoc b/docs/RELEASE_NOTES_NEXT/OPAC/add_booking_reservations_to_opac.adoc
new file mode 100644 (file)
index 0000000..fb724c3
--- /dev/null
@@ -0,0 +1,13 @@
+View upcoming booking reservations in the OPAC
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A new tab in the My Account section of the OPAC shows
+patrons information about reservations on their account.
+Here, patrons can check on upcoming reservations, as 
+well as reservations they currently have checked out.
+
+Note: this interface pulls its timezone from the Library
+Settings Editor.  Make sure that you have a timezone
+listed for your library in the Library Settings Editor
+before using this feature.
+
index 240e6ab..3366ecc 100644 (file)
@@ -270,3 +270,24 @@ image::media/message_center12.PNG[Message Center 12]
 
 NOTE: Patron deleted messages will still appear in the patron's account in the 
 staff client under Other -> Message Center.
+
+Reservations
+^^^^^^^^^^^^
+
+When patrons place a reservation for a particular item at a particular time,
+they can check on its status using the *Reservations* tab.
+
+After they initially place a reservation, its status will display as _Reserved_.
+After staff capture the reservation, the status will change to _Ready for Pickup_.
+After the patron picks up the reservation, the status will change to _Checked Out_.
+Finally, after the patron returns the item, the reservation will be removed from
+the list.
+
+[NOTE]
+====================
+This interface pulls its timezone from the Library
+Settings Editor.  Make sure that you have a timezone
+listed for your library in the Library Settings Editor
+before using this feature.
+====================
+