-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#152604] Prevent abandoned reservation carts (#2292)
# Release Notes Prevent abandoned carts created when clicking an instrument name. This should also help prevent order/detail IDs from growing too fast. # Additional Context Before, if you click an instrument, a new order and order detail are created. Users are often doing this to check the schedule for the instrument. If the user closes the tab comes back to the instrument, yet another order detail is created. We were deleting the cart if the user clicks "Cancel", but the order and order detail IDs were still eaten up. Now, for normal single-reservation purchases, we go to a parallel controller where we do not create the order/detail prior to the user filling out the form. I did need to rework some of the code to avoid persisting and then rolling back the order/detail. Without these changes, the abandoned carts would not be created, but the IDs would still be used up. For the order form (purchasing more than one or with other things) or part of a bundle, as well as edit/update, we are continuing to use the old controller. Some offline discussion for context: From Aaron: > What we highly suspect is that users are clicking on the instrument links to check out the calendar, and then closing the tab. I assume that if a user clicks on an instrument name bringing them to a "Create Reservation" page, and then clicks "cancel" then this will not generate an abandoned cart? Clicking cancel from the one-off "Create Reservation" page generates the banner: "the product has been removed." I assume the Order Number has still been used up, but that this wouldn't create an "abandoned cart"? Jason: > You're right about the cancel button. I did a quick test: if you click "cancel" we do remove the order detail (so that number is used up), but we keep the order. That order gets re-used. So clicking an instrument creates, say, "100-100". Click cancel. Click the instrument again. You get "100-101". This won't count as an abandoned cart. However, if you click the browser's back button or close the tab, it leaves both of them hanging around. So "100-100", click on the instrument again, then you get "101-101". "100-100" is an abandoned cart.
- Loading branch information
Showing
15 changed files
with
184 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
class SingleReservationsController < ApplicationController | ||
|
||
customer_tab :all | ||
before_action :authenticate_user! | ||
load_resource :facility, find_by: :url_name | ||
load_resource :instrument, through: :facility, find_by: :url_name | ||
before_action :build_order | ||
before_action { @submit_action = facility_instrument_single_reservations_path } | ||
|
||
def new | ||
@reservation = NextAvailableReservationFinder.new(@instrument).next_available_for(current_user, acting_user) | ||
@reservation.order_detail = @order_detail | ||
|
||
authorize! :new, @reservation | ||
|
||
unless @instrument.can_be_used_by?(acting_user) | ||
flash[:notice] = text(".acting_as_not_on_approval_list") | ||
end | ||
set_windows | ||
render "reservations/new" | ||
end | ||
|
||
def create | ||
creator = ReservationCreator.new(@order, @order_detail, params) | ||
if creator.save(session_user) | ||
@reservation = creator.reservation | ||
authorize! :create, @reservation | ||
flash[:notice] = I18n.t "controllers.reservations.create.success" | ||
flash[:error] = I18n.t("controllers.reservations.create.admin_hold_warning") if creator.reservation.conflicting_admin_reservation? | ||
redirect_to purchase_order_path(@order, params.permit(:send_notification)) | ||
else | ||
@reservation = creator.reservation | ||
flash.now[:error] = creator.error.html_safe | ||
set_windows | ||
render "reservations/new" | ||
end | ||
end | ||
|
||
private | ||
|
||
def ability_resource | ||
@reservation | ||
end | ||
|
||
def build_order | ||
@order = Order.new( | ||
user: acting_user, | ||
facility: current_facility, | ||
created_by: session_user.id, | ||
) | ||
@order_detail = @order.order_details.build( | ||
product: @instrument, | ||
quantity: 1, | ||
created_by: session_user.id, | ||
) | ||
end | ||
|
||
def set_windows | ||
@reservation_window = ReservationWindow.new(@reservation, current_user) | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Used by the reservations controllers to find the default reservation times to display | ||
|
||
class NextAvailableReservationFinder | ||
def initialize(product) | ||
@product = product | ||
end | ||
|
||
def next_available_for(current_user, acting_user) | ||
options = current_user.can_override_restrictions?(@product) ? {} : { user: acting_user } | ||
next_available = @product.next_available_reservation( | ||
after: 1.minute.from_now, | ||
duration: default_reservation_mins.minutes, | ||
options: options | ||
) | ||
next_available ||= default_reservation | ||
next_available.round_reservation_times | ||
end | ||
|
||
private | ||
|
||
def default_reservation | ||
Reservation.new(instrument: @product, | ||
reserve_start_at: Time.current, | ||
reserve_end_at: default_reservation_mins.minutes.from_now) | ||
end | ||
|
||
def default_reservation_mins | ||
@product.min_reserve_mins.to_i > 0 ? @product.min_reserve_mins : 30 | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
class ReservationWindow | ||
def initialize(reservation, user) | ||
@reservation = reservation | ||
@user = user | ||
end | ||
|
||
def max_window | ||
return 365 if operator? | ||
@reservation.longest_reservation_window(@reservation.user.price_groups) | ||
end | ||
|
||
def max_days_ago | ||
operator? ? -365 : 0 | ||
end | ||
|
||
def min_date | ||
max_days_ago.days.from_now.strftime("%Y%m%d") | ||
end | ||
|
||
def max_date | ||
max_window.days.from_now.strftime("%Y%m%d") | ||
end | ||
|
||
private | ||
|
||
def operator? | ||
@user.operator_of?(@reservation.facility) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.