diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ee158f255..36964eaba 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -143,6 +143,13 @@ def skip_authentication?
.include?(params[:action]) && !guests_disabled?)
end
+ def set_user
+ @user = User.find(params[:user_id])
+ return unless @user.role == 'banned'
+ flash[:error] = 'This user is banned and cannot check out equipment.'
+ params[:banned] = true
+ end
+
#-------- end before_filter methods --------#
def update_cart # rubocop:disable MethodLength, AbcSize
diff --git a/app/controllers/manage_controller.rb b/app/controllers/manage_controller.rb
new file mode 100644
index 000000000..05f3e5283
--- /dev/null
+++ b/app/controllers/manage_controller.rb
@@ -0,0 +1,165 @@
+# frozen_string_literal: true
+# rubocop:disable ClassLength
+class ManageController < ApplicationController
+ load_and_authorize_resource
+ before_action :set_user, only: [:show, :checkout]
+ before_action :set_reservation, only: [:send_receipt]
+
+ private
+
+ def set_reservation
+ @reservation = Reservation.find(params[:receipt_id])
+ end
+
+ def check_for_banned_user
+ if @user.role == 'banned'
+ flash[:error] = 'Banned users cannot check out equipment.'
+ redirect_to(root_path) && return
+ end
+ true
+ end
+
+ def check_terms_of_service
+ unless @user.terms_of_service_accepted ||
+ params[:terms_of_service_accepted].present?
+ flash[:error] = 'You must confirm that the user accepts the Terms of '\
+ 'Service.'
+ redirect_to(:back) && return
+ end
+ true
+ end
+
+ def handle_overdue_reservations
+ if @user.overdue_reservations?
+ if can? :override, :checkout_errors
+ # Admins can ignore this
+ flash[:notice] = 'Admin Override: Equipment has been checked out '\
+ 'successfully, even though the reserver has overdue equipment.'
+ else
+ # Everyone else is redirected
+ flash[:error] = 'Could not check out the equipment, because the '\
+ 'reserver has reservations that are overdue.'
+ redirect_to(:back) && return
+ end
+ end
+ true
+ end
+
+ def approve_checkout
+ # check for banned user
+ return unless check_for_banned_user
+
+ # check terms of service
+ return unless check_terms_of_service
+
+ # Overdue validation
+ return unless handle_overdue_reservations
+ true
+ end
+
+ def check_nonemptiness_of(checked_out_reservations)
+ if checked_out_reservations.empty?
+ flash[:error] = 'No reservation selected.'
+ redirect_to(:back) && return
+ end
+ true
+ end
+
+ def check_validity_of(checked_in_reservations)
+ unless checked_in_reservations
+ flash[:error] = 'One of the items you tried to check in has already '\
+ 'been checked in.'
+ redirect_to(:back) && return
+ end
+ true
+ end
+
+ def check_uniqueness_of(checked_out_reservations)
+ unless Reservation.unique_equipment_items?(checked_out_reservations)
+ flash[:error] = 'The same equipment item cannot be simultaneously '\
+ 'checked out in multiple reservations.'
+ redirect_to(:back) && return
+ end
+ true
+ end
+
+ def prep_receipt_page(check_in_set:, check_out_set:, user: nil)
+ @check_in_set = check_in_set
+ @check_out_set = check_out_set
+ @user = user if user
+ render('receipt', layout: 'application_with_search_sidebar') && return
+ end
+
+ public
+
+ def show # initializer
+ @check_out_set = @user.due_for_checkout.includes(:equipment_model)
+ @check_in_set = @user.due_for_checkin.includes(:equipment_model)
+
+ render :show, layout: 'application'
+ end
+
+ def checkout
+ return unless approve_checkout
+
+ checked_out_reservations =
+ CheckoutHelper.preprocess_checkout(params[:reservations],
+ @user, current_user)
+
+ return unless check_nonemptiness_of(checked_out_reservations)
+ return unless check_uniqueness_of(checked_out_reservations)
+
+ ## Save reservations
+ Reservation.transaction do
+ begin
+ checked_out_reservations.each do |r|
+ CheckoutHelper.checkout_reservation(r, params[:reservations])
+ end
+ rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => e
+ flash[:error] = "Checking out your reservation failed: #{e.message}"
+ redirect_to manage_reservations_for_user_path(@user)
+ raise ActiveRecord::Rollback
+ end
+ end
+
+ CheckoutHelper.update_tos(@user)
+ CheckoutHelper.send_checkout_receipts(checked_out_reservations)
+ prep_receipt_page(check_in_set: [], check_out_set: checked_out_reservations)
+ end
+
+ def checkin
+ # see comments for checkout, this method proceeds in a similar way
+ checked_in_reservations =
+ CheckoutHelper.preproccess_checkins(params[:reservations], current_user)
+
+ return unless check_validity_of(checked_in_reservations)
+
+ return unless check_nonemptiness_of(checked_in_reservations)
+ ## Save reservations
+ Reservation.transaction do
+ begin
+ checked_in_reservations.each do |r|
+ CheckoutHelper.checkin_reservation(r, params[:reservations])
+ end
+ rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => e
+ flash[:error] = "Checking in your reservation failed: #{e.message}"
+ redirect_to :back
+ raise ActiveRecord::Rollback
+ end
+ end
+
+ prep_receipt_page(check_in_set: checked_in_reservations, check_out_set: [],
+ user: checked_in_reservations.first.reserver)
+ end
+
+ def send_receipt
+ if UserMailer.reservation_status_update(@reservation, 'checked out')
+ .deliver_now
+ flash[:notice] = 'Successfully delivered receipt email.'
+ else
+ flash[:error] = 'Unable to deliver receipt email. Please contact '\
+ 'administrator for more support.'
+ end
+ redirect_to @reservation
+ end
+end
diff --git a/app/controllers/reservations_controller.rb b/app/controllers/reservations_controller.rb
index 1e3337131..391e8471f 100644
--- a/app/controllers/reservations_controller.rb
+++ b/app/controllers/reservations_controller.rb
@@ -3,23 +3,12 @@
class ReservationsController < ApplicationController
load_and_authorize_resource
- before_action :set_reservation,
- only: [:show, :edit, :update, :destroy, :checkout_email,
- :checkin_email, :renew, :review, :approve_request,
- :deny_request]
- before_action :set_user, only: [:manage, :current, :checkout]
+ before_action :set_user, only: [:current, :checkout]
private
- def set_user
- @user = User.find(params[:user_id])
- return unless @user.role == 'banned'
- flash[:error] = 'This user is banned and cannot check out equipment.'
- params[:banned] = true
- end
-
- def set_reservation
- @reservation = Reservation.find(params[:id])
+ def reservation
+ @reservation ||= Reservation.find(params[:id])
end
def set_index_dates
@@ -181,12 +170,13 @@ def create # rubocop:disable all
def edit
@option_array =
- @reservation.equipment_model.equipment_items
- .collect { |e| [e.name, e.id] }
+ reservation.equipment_model.equipment_items
+ .collect { |e| [e.name, e.id] }
end
# for editing reservations; not for checkout or check-in
def update # rubocop:disable all
+ @reservation = Reservation.find(params[:id])
message = 'Successfully edited reservation.'
res = reservation_params
# add new equipment item id to hash if it's being changed and save old
@@ -195,19 +185,19 @@ def update # rubocop:disable all
res[:equipment_item_id] = params[:equipment_item]
new_item = EquipmentItem.find(params[:equipment_item])
old_item =
- EquipmentItem.find_by id: @reservation.equipment_item_id
+ EquipmentItem.find_by id: reservation.equipment_item_id
# check to see if new item is available
unless new_item.available?
r = new_item.current_reservation
r.update(current_user,
- { equipment_item_id: @reservation.equipment_item_id },
+ { equipment_item_id: reservation.equipment_item_id },
'')
end
end
# save changes to database
- @reservation.update(current_user, res, params[:new_notes])
- if @reservation.save
+ reservation.update(current_user, res, params[:new_notes])
+ if reservation.save
# code for switching equipment items
unless params[:equipment_item].blank?
# if the item was previously assigned to a different reservation
@@ -219,163 +209,23 @@ def update # rubocop:disable all
end
# update the item history / histories
- old_item.make_switch_notes(@reservation, r, current_user) if old_item
+ old_item&.make_switch_notes(reservation, r, current_user)
- new_item.make_switch_notes(r, @reservation, current_user)
+ new_item.make_switch_notes(r, reservation, current_user)
end
# flash success and exit
flash[:notice] = message
- redirect_to @reservation
+ redirect_to reservation
else
flash[:error] = "Unable to update reservation:\n"\
- "#{@reservation.errors.full_messages.to_sentence}"
- redirect_to edit_reservation_path(@reservation)
- end
- end
-
- def checkout # rubocop:disable all
- # convert all the reservations that are being checked out into an array
- # of Reservation objects. only select the ones who are selected, eg
- # they have an equipment item id set.
-
- ## Basic-logic checks, only need to be done once
-
- # check for banned user
- if @user.role == 'banned'
- flash[:error] = 'Banned users cannot check out equipment.'
- redirect_to(root_path) && return
- end
-
- # check terms of service
- unless @user.terms_of_service_accepted ||
- params[:terms_of_service_accepted].present?
- flash[:error] = 'You must confirm that the user accepts the Terms of '\
- 'Service.'
- redirect_to(:back) && return
- end
-
- # Overdue validation
- if @user.overdue_reservations?
- if can? :override, :checkout_errors
- # Admins can ignore this
- flash[:notice] = 'Admin Override: Equipment has been checked out '\
- 'successfully, even though the reserver has overdue equipment.'
- else
- # Everyone else is redirected
- flash[:error] = 'Could not check out the equipment, because the '\
- 'reserver has reservations that are overdue.'
- redirect_to(:back) && return
- end
- end
-
- checked_out_reservations = []
- params[:reservations].each do |r_id, r_attrs|
- next if r_attrs[:equipment_item_id].blank?
- r = Reservation.includes(:reserver).find(r_id)
- # check that we don't somehow checkout a reservation that doesn't belong
- # to the @user we're checking out for (params hacking?)
- next if r.reserver != @user
- checked_out_reservations <<
- r.checkout(r_attrs[:equipment_item_id], current_user,
- r_attrs[:checkout_procedures], r_attrs[:notes])
- end
-
- if checked_out_reservations.empty?
- flash[:error] = 'No reservation selected.'
- redirect_to(:back) && return
- end
-
- unless Reservation.unique_equipment_items?(checked_out_reservations)
- flash[:error] = 'The same equipment item cannot be simultaneously '\
- 'checked out in multiple reservations.'
- redirect_to(:back) && return
- end
-
- ## Save reservations
- Reservation.transaction do
- begin
- checked_out_reservations.each do |r|
- r.save!
- # update equipment item notes
- new_notes = params[:reservations][r.id.to_s][:notes]
- r.equipment_item.make_reservation_notes('checked out', r,
- r.checkout_handler,
- new_notes, r.checked_out)
- end
- rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => e
- flash[:error] = "Checking out your reservation failed: #{e.message}"
- redirect_to manage_reservations_for_user_path(@user)
- raise ActiveRecord::Rollback
- end
- end
-
- # update user with terms of service acceptance now that checkout worked
- unless @user.terms_of_service_accepted
- @user.update_attributes(terms_of_service_accepted: true)
+ "#{reservation.errors.full_messages.to_sentence}"
+ redirect_to edit_reservation_path(reservation)
end
-
- # Send checkout receipts
- checked_out_reservations.each do |res|
- UserMailer.reservation_status_update(res, 'checked out').deliver_now
- end
-
- # prep for receipt page and exit
- @check_in_set = []
- @check_out_set = checked_out_reservations
- render('receipt', layout: 'application_with_search_sidebar') && return
- end
-
- def checkin # rubocop:disable all
- # see comments for checkout, this method proceeds in a similar way
-
- checked_in_reservations = []
- params[:reservations].each do |r_id, r_attrs|
- next if r_attrs[:checkin?].blank?
- r = Reservation.find(r_id)
- if r.checked_in
- flash[:error] = 'One of the items you tried to check in has already '\
- 'been checked in.'
- redirect_to(:back) && return # rubocop:disable NonLocalExitFromIterator
- end
-
- checked_in_reservations << r.checkin(current_user,
- r_attrs[:checkin_procedures],
- r_attrs[:notes])
- end
-
- if checked_in_reservations.empty?
- flash[:error] = 'No reservation selected!'
- redirect_to(:back) && return
- end
-
- ## Save reservations
- Reservation.transaction do
- begin
- checked_in_reservations.each do |r|
- r.save!
- # update equipment item notes
- new_notes = params[:reservations][r.id.to_s][:notes]
- r.equipment_item.make_reservation_notes('checked in', r,
- r.checkin_handler, new_notes,
- r.checked_in)
- end
- rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => e
- flash[:error] = "Checking in your reservation failed: #{e.message}"
- redirect_to :back
- raise ActiveRecord::Rollback
- end
- end
-
- # prep for receipt page and exit
- @user = checked_in_reservations.first.reserver
- @check_in_set = checked_in_reservations
- @check_out_set = []
- render('receipt', layout: 'application_with_search_sidebar') && return
end
def destroy
- @reservation.destroy
+ reservation.destroy
flash[:notice] = 'Successfully destroyed reservation.'
redirect_to reservations_url
end
@@ -384,13 +234,6 @@ def upcoming
@reservations_set = [Reservation.upcoming].delete_if(&:empty?)
end
- def manage # initializer
- @check_out_set = @user.due_for_checkout.includes(:equipment_model)
- @check_in_set = @user.due_for_checkin.includes(:equipment_model)
-
- render :manage, layout: 'application'
- end
-
def current
if params[:banned] && current_user.view_mode != 'superuser'
redirect_to(root_path) && return
@@ -408,67 +251,56 @@ def current
render 'current_reservations'
end
- def send_receipt
- if UserMailer.reservation_status_update(@reservation, 'checked out')
- .deliver_now
- flash[:notice] = 'Successfully delivered receipt email.'
- else
- flash[:error] = 'Unable to deliver receipt email. Please contact '\
- 'administrator for more support.'
- end
- redirect_to @reservation
- end
-
def renew
- message = @reservation.renew(current_user)
+ message = reservation.renew(current_user)
if message
flash[:error] = message
- redirect_to(@reservation) && return
+ redirect_to(reservation) && return
else
flash[:notice] = 'Your reservation has been renewed until '\
- "#{@reservation.due_date.to_s(:long)}."
- redirect_to @reservation
+ "#{reservation.due_date.to_s(:long)}."
+ redirect_to reservation
end
end
def review
@all_current_requests_by_user =
- @reservation.reserver.reservations.requested.reject do |res|
- res.id == @reservation.id
+ reservation.reserver.reservations.requested.reject do |res|
+ res.id == reservation.id
end
- @errors = @reservation.validate
+ @errors = reservation.validate
end
def approve_request
- @reservation.status = 'reserved'
- @reservation.notes = @reservation.notes.to_s # in case of nil
- @reservation.notes += "\n\n### Approved on #{Time.zone.now.to_s(:long)} "\
+ reservation.status = 'reserved'
+ reservation.notes = @reservation.notes.to_s # in case of nil
+ reservation.notes += "\n\n### Approved on #{Time.zone.now.to_s(:long)} "\
"by #{current_user.md_link}"
- if @reservation.save
+ if reservation.save
flash[:notice] = 'Request successfully approved'
- UserMailer.reservation_status_update(@reservation,
+ UserMailer.reservation_status_update(reservation,
'request approved').deliver_now
redirect_to reservations_path(requested: true)
else
flash[:error] = 'Oops! Something went wrong. Unable to approve '\
'reservation.'
- redirect_to @reservation
+ redirect_to reservation
end
end
def deny_request
- @reservation.status = 'denied'
- @reservation.notes = @reservation.notes.to_s # in case of nil
- @reservation.notes += "\n\n### Denied on #{Time.zone.now.to_s(:long)} by "\
+ reservation.status = 'denied'
+ reservation.notes = reservation.notes.to_s # in case of nil
+ reservation.notes += "\n\n### Denied on #{Time.zone.now.to_s(:long)} by "\
"#{current_user.md_link}"
- if @reservation.save
+ if reservation.save
flash[:notice] = 'Request successfully denied'
- UserMailer.reservation_status_update(@reservation).deliver_now
+ UserMailer.reservation_status_update(reservation).deliver_now
redirect_to reservations_path(requested: true)
else
flash[:error] = 'Oops! Something went wrong. Unable to deny '\
'reservation. We\'re not sure what that\'s all about.'
- redirect_to @reservation
+ redirect_to reservation
end
end
@@ -480,25 +312,24 @@ def archive # rubocop:disable all
flash[:error] = 'Reason for archiving cannot be empty.'
redirect_to(:back) && return
end
- set_reservation
- if @reservation.checked_in
+ if reservation.checked_in
flash[:error] = 'Cannot archive checked-in reservation.'
redirect_to(:back) && return
end
begin
- @reservation.archive(current_user, params[:archive_note])
- .save(validate: false)
+ reservation.archive(current_user, params[:archive_note])
+ .save(validate: false)
# archive equipment item if checked out
- if @reservation.equipment_item
- @reservation.equipment_item
- .make_reservation_notes('archived',
- @reservation, current_user,
- params[:archive_note],
- @reservation.checked_in)
+ if reservation.equipment_item
+ reservation.equipment_item
+ .make_reservation_notes('archived',
+ reservation, current_user,
+ params[:archive_note],
+ reservation.checked_in)
if AppConfig.check(:autodeactivate_on_archive)
- @reservation.equipment_item.deactivate(user: current_user,
- reason: params[:archive_note])
+ reservation.equipment_item.deactivate(user: current_user,
+ reason: params[:archive_note])
flash_end = ' The equipment item has been automatically deactivated.'
end
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 131d3f878..9d0bc77be 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -35,6 +35,7 @@ def admin
end
def checkout
+ can :manage, Manage
can :manage, Reservation
cannot :archive, Reservation
cannot :renew, Reservation unless AppConfig.check(:enable_renewals)
diff --git a/app/models/manage.rb b/app/models/manage.rb
new file mode 100644
index 000000000..0288a73f4
--- /dev/null
+++ b/app/models/manage.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+class Manage < ActiveRecord::Base
+end
diff --git a/app/models/reservation.rb b/app/models/reservation.rb
index 89c49676e..c9a97eb93 100644
--- a/app/models/reservation.rb
+++ b/app/models/reservation.rb
@@ -361,7 +361,6 @@ def checkout(eq_item, checkout_handler, procedures, new_notes)
# notes from the checkout.
#
# Returns the unsaved, checked out reservation
-
self.checkout_handler = checkout_handler
self.checked_out = Time.zone.now
self.equipment_item_id = eq_item
diff --git a/app/views/reservations/_check_in_form.html.erb b/app/views/manage/_check_in_form.html.erb
similarity index 100%
rename from app/views/reservations/_check_in_form.html.erb
rename to app/views/manage/_check_in_form.html.erb
diff --git a/app/views/reservations/_check_out_form.html.erb b/app/views/manage/_check_out_form.html.erb
similarity index 100%
rename from app/views/reservations/_check_out_form.html.erb
rename to app/views/manage/_check_out_form.html.erb
diff --git a/app/views/reservations/receipt.html.erb b/app/views/manage/receipt.html.erb
similarity index 87%
rename from app/views/reservations/receipt.html.erb
rename to app/views/manage/receipt.html.erb
index 68db47cd8..e98cf9148 100644
--- a/app/views/reservations/receipt.html.erb
+++ b/app/views/manage/receipt.html.erb
@@ -1,5 +1,5 @@
- <%= render partial: 'top_buttons', locals: { reserver: @user } %>
+ <%= render partial: 'reservations/top_buttons', locals: { reserver: @user } %>
@@ -18,7 +18,7 @@
<%= link_to "Reservation ##{reservation.id}", reservation %>
- <%= render partial: 'reservation_details', locals: { :@reservation => reservation } %>
+ <%= render partial: 'reservations/reservation_details', locals: { :@reservation => reservation } %>
@@ -40,7 +40,7 @@
<%= link_to "Reservation ##{reservation.id}", reservation %>
- <%= render partial: 'reservation_details', locals: { :@reservation => reservation } %>
+ <%= render partial: 'reservations/reservation_details', locals: { :@reservation => reservation } %>
diff --git a/app/views/reservations/manage.html.erb b/app/views/manage/show.html.erb
similarity index 96%
rename from app/views/reservations/manage.html.erb
rename to app/views/manage/show.html.erb
index 24fefb959..01486b5be 100644
--- a/app/views/reservations/manage.html.erb
+++ b/app/views/manage/show.html.erb
@@ -2,7 +2,7 @@
- <%= render partial: 'top_buttons', locals: {reserver: @user} %>
+ <%= render partial: '/reservations/top_buttons', locals: {reserver: @user} %>
<% unless @check_in_set.empty? %>
diff --git a/config/routes.rb b/config/routes.rb
index 002c4fb60..c73750cdc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -72,15 +72,12 @@
resources :reservations do
member do
- get :send_receipt
put :renew
put :archive
end
end
# reservations views
- get '/reservations/manage/:user_id', to: 'reservations#manage',
- as: :manage_reservations_for_user
get '/reservations/current/:user_id', to: 'reservations#current',
as: :current_reservations_for_user
@@ -90,12 +87,6 @@
as: :approve_request
put '/reservations/deny/:id', to: 'reservations#deny_request',
as: :deny_request
-
- # reservation checkout / check-in actions
- put '/reservations/checkout/:user_id', to: 'reservations#checkout',
- as: :checkout
- put '/reservations/check-in/:user_id', to: 'reservations#checkin',
- as: :checkin
get '/blackouts/flash_message', to: 'blackouts#flash_message',
as: :flash_message
get '/blackouts/new_recurring', to: 'blackouts#new_recurring',
@@ -106,6 +97,16 @@
put '/reservation/view_all_dates', to: 'reservations#view_all_dates',
as: :view_all_dates
+ get 'manage/:user_id', to: 'manage#show',
+ as: :manage_reservations_for_user
+
+ put 'manage/checkout/:user_id', to: 'manage#checkout',
+ as: :checkout
+ put 'manage/check-in/:user_id', to: 'manage#checkin',
+ as: :checkin
+
+ put 'manage/send_receipt/:receipt_id', to: 'manage#send_receipt',
+ as: :send_receipt
resources :blackouts do
collection do
post :create_recurring
diff --git a/lib/checkout_helper.rb b/lib/checkout_helper.rb
new file mode 100644
index 000000000..92dc63e6c
--- /dev/null
+++ b/lib/checkout_helper.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+class CheckoutHelper
+ def self.checkout_reservation(r, reservations)
+ check_reservation(r, reservations, 'checked out',
+ r.checkout_handler, r.checked_out)
+ end
+
+ def self.checkin_reservation(r, reservations)
+ check_reservation(r, reservations, 'checked in',
+ r.checkin_handler, r.checked_in)
+ end
+
+ def self.check_reservation(r, reservations, message, handler, time)
+ r.save!
+ new_notes = reservations[r.id.to_s][:notes]
+ r.equipment_item.make_reservation_notes(message, r,
+ handler,
+ new_notes, time)
+ end
+
+ def self.reservation_for(r_id, r_attrs, user)
+ return if r_attrs[:equipment_item_id].blank?
+ r = Reservation.includes(:reserver).find(r_id)
+ # check that we don't somehow checkout a reservation that doesn't belong
+ # to the @user we're checking out for (params hacking?)
+ return if r.reserver != user
+ r
+ end
+
+ def self.preprocess_checkout(reservations, user, checkout_handler)
+ checked_out_reservations = []
+ reservations.each do |r_id, r_attrs|
+ r = CheckoutHelper.reservation_for(r_id, r_attrs, user)
+ next if r.nil?
+ checked_out_reservations << r.checkout(r_attrs[:equipment_item_id],
+ checkout_handler,
+ r_attrs[:checkout_procedures],
+ r_attrs[:notes])
+ end
+ checked_out_reservations
+ end
+
+ def self.send_checkout_receipts(checked_out_reservations)
+ checked_out_reservations.each do |res|
+ UserMailer.reservation_status_update(res, 'checked out').deliver_now
+ end
+ end
+
+ def self.update_tos(user)
+ # update user with terms of service acceptance now that checkout worked
+ return if user.terms_of_service_accepted
+ user.update_attributes(terms_of_service_accepted: true)
+ end
+
+ def self.preproccess_checkins(reservations, user)
+ checked_in_reservations = []
+ reservations.each do |r_id, r_attrs|
+ next if r_attrs[:checkin?].blank?
+ r = Reservation.find(r_id)
+ return nil if r.checked_in
+ checked_in_reservations << r.checkin(user,
+ r_attrs[:checkin_procedures],
+ r_attrs[:notes])
+ end
+ checked_in_reservations
+ end
+end
diff --git a/spec/controllers/manage_controller_spec.rb b/spec/controllers/manage_controller_spec.rb
new file mode 100644
index 000000000..cbdfc5190
--- /dev/null
+++ b/spec/controllers/manage_controller_spec.rb
@@ -0,0 +1,526 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe ManageController, type: :controller do
+ AC_DEFAULTS = { disable_user_emails: false,
+ override_on_create: false,
+ override_at_checkout: false,
+ res_exp_time: false,
+ admin_email: 'admin@email.com' }.freeze
+
+ before(:each) { mock_app_config(AC_DEFAULTS) }
+
+ shared_examples 'inaccessible by banned user' do
+ before { mock_user_sign_in(FactoryGirl.build_stubbed(:banned)) }
+ it_behaves_like 'redirected request'
+ end
+ describe '#show (GET /manage/:user_id)' do
+ # Access: admins and checkout persons
+ # Functionality:
+ # - assigns @user, @check_out_set and @check_in_set
+ # - renders :show
+
+ shared_examples 'can access #show' do
+ let!(:user) { UserMock.new(traits: [:findable]) }
+ before(:each) do
+ allow(user).to receive(:due_for_checkout)
+ .and_return(instance_spy('ActiveRecord::Relation'))
+ allow(user).to receive(:due_for_checkin)
+ .and_return(instance_spy('ActiveRecord::Relation'))
+ get :show, user_id: user.id
+ end
+ it { expect(response).to be_success }
+ it { is_expected.to render_template(:show) }
+ it 'assigns @user correctly' do
+ expect(assigns(:user)).to eq(user)
+ end
+ it 'assigns @check_out_set correctly' do
+ expect(assigns(:check_out_set)).to eq(user.due_for_checkout)
+ end
+ it 'assigns @check_in_set correctly' do
+ expect(assigns(:check_in_set)).to eq(user.due_for_checkin)
+ end
+ end
+
+ context 'when accessed by admin' do
+ before(:each) { mock_user_sign_in(UserMock.new(:admin)) }
+ include_examples 'can access #show'
+ end
+
+ context 'when accessed by checkout person' do
+ before(:each) { mock_user_sign_in(UserMock.new(:checkout_person)) }
+ include_examples 'can access #show'
+ end
+ context 'when accessed by patron' do
+ before(:each) do
+ user = UserMock.new
+ mock_user_sign_in(user)
+ get :show, user_id: user.id
+ end
+ include_examples 'redirected request'
+ end
+ end
+ describe '#checkout (PUT /manage/checkout/:user_id)' do
+ # Access: Admins, checkout persons.
+ # Functionality: very complicated (almost 100 lines)
+ # - pass TOS (if not, redirect)
+ # - params[:reservations] contains hash of
+ # {reservation_id => {equipment_item_id: int, notes: str,
+ # checkout_precedures: {checkout_procedure_id => int}}}
+ # - stops checkout if user has overdue reservations
+ # - stops checkout if no reservations are selected
+ # - overrides errors if you can and if there are some, otherwise
+ # redirects away
+ # - also prevents checkout if reserver is banned
+
+ # Effects if successful:
+ # - sets empty @check_in_set, populates @check_out_set with the
+ # reservations
+ # - processes all reservations in params[:reservations] -- adds
+ # checkout_handler, checked_out (time), equipment_item; updates
+ # notes
+ # - renders :receipt template
+ # - sets reservation status to 'checked_out'
+
+ # Note: Many of these can be cross-applied to #checkin as well
+
+ before(:all) do
+ @user = FactoryGirl.create(:user)
+ @checkout_person = FactoryGirl.create(:checkout_person)
+ @admin = FactoryGirl.create(:admin)
+ @banned = FactoryGirl.create(:banned)
+ end
+
+ after(:all) { User.destroy_all }
+
+ before(:each) do
+ sign_in @user
+ @reservation = FactoryGirl.create(:valid_reservation, reserver: @user)
+ end
+
+ shared_examples 'has successful checkout' do
+ before(:each) do
+ @item =
+ FactoryGirl.create(:equipment_item,
+ equipment_model: @reservation.equipment_model)
+ reservations_params =
+ { @reservation.id.to_s => { notes: '',
+ equipment_item_id: @item.id } }
+ ActionMailer::Base.deliveries = []
+ put :checkout, user_id: @user.id, reservations: reservations_params
+ end
+
+ it { expect(response).to be_success }
+ it { is_expected.to render_template(:receipt) }
+
+ it 'assigns empty @check_in_set' do
+ expect(assigns(:check_in_set)).to be_empty
+ end
+
+ it 'populates @check_out_set' do
+ expect(assigns(:check_out_set)).to eq [@reservation]
+ end
+
+ it 'updates the reservation' do
+ expect(@reservation.checkout_handler).to be_nil
+ expect(@reservation.checked_out).to be_nil
+ expect(@reservation.equipment_item).to be_nil
+ expect(@reservation.reserved?).to be_truthy
+ @reservation.reload
+ expect(@reservation.checkout_handler).to be_a(User)
+ expect(@reservation.checked_out).to_not be_nil
+ expect(@reservation.equipment_item).to eq @item
+ expect(@reservation.checked_out).to be_truthy
+ end
+
+ it 'updates the equipment item history' do
+ expect { @item.reload }.to change(@item, :notes)
+ end
+
+ it 'updates the reservation notes' do
+ expect { @reservation.reload }.to change(@reservation, :notes)
+ end
+
+ it 'sends checkout receipts' do
+ expect(ActionMailer::Base.deliveries.count).to eq(1)
+ end
+ end
+
+ context 'when accessed by admin' do
+ before(:each) do
+ sign_in @admin
+ end
+
+ include_examples 'has successful checkout'
+ end
+
+ context 'when accessed by checkout person' do
+ before(:each) do
+ sign_in @checkout_person
+ end
+
+ include_examples 'has successful checkout'
+ end
+
+ context 'when accessed by patron' do
+ before(:each) do
+ sign_in @user
+ put :checkout, user_id: @user.id
+ end
+
+ include_examples 'redirected request'
+ end
+
+ it_behaves_like 'inaccessible by banned user' do
+ before { put :checkout, user_id: @banned.id }
+ end
+
+ context 'when tos not accepted and not checked off' do
+ before do
+ request.env['HTTP_REFERER'] = 'where_i_came_from'
+ sign_in @admin
+ @user.update_attributes(terms_of_service_accepted: false)
+ put :checkout, user_id: @user.id, reservations: {}
+ end
+ it { expect(response).to redirect_to 'where_i_came_from' }
+ end
+
+ context 'when tos accepted' do
+ before do
+ sign_in @admin
+ @user.update_attributes(terms_of_service_accepted: false)
+ @item =
+ FactoryGirl.create(:equipment_item,
+ equipment_model: @reservation.equipment_model)
+ reservations_params =
+ { @reservation.id.to_s => { notes: '',
+ equipment_item_id: @item.id } }
+ put :checkout, user_id: @user.id, reservations: reservations_params,
+ terms_of_service_accepted: true
+ end
+
+ it { expect(response).to be_success }
+ end
+
+ context 'with duplicate equipment item selection' do
+ before do
+ request.env['HTTP_REFERER'] = 'where_i_came_from'
+ sign_in @admin
+ @item =
+ FactoryGirl.create :equipment_item,
+ equipment_model: @reservation.equipment_model
+ FactoryGirl.create :equipment_item,
+ equipment_model: @reservation.equipment_model
+ @res2 =
+ FactoryGirl.create :valid_reservation,
+ reserver: @user,
+ equipment_model: @reservation.equipment_model
+ res_params = { notes: '', equipment_item_id: @item.id }
+ reservations_params = { @reservation.id.to_s => res_params,
+ @res2.id.to_s => res_params }
+ put :checkout, user_id: @user.id, reservations: reservations_params
+ end
+
+ it { expect(response).to redirect_to 'where_i_came_from' }
+
+ it 'does not update the equipment item history' do
+ expect { @item.reload }.not_to change(@item, :notes)
+ end
+ end
+
+ context 'when not all procedures are filled out' do
+ before do
+ sign_in @admin
+ @item =
+ FactoryGirl.create(:equipment_item,
+ equipment_model: @reservation.equipment_model)
+ @procedure =
+ FactoryGirl.create(:checkout_procedure,
+ equipment_model: @reservation.equipment_model)
+ reservations_params =
+ { @reservation.id.to_s => { notes: '',
+ equipment_item_id: @item.id,
+ checkout_procedures: {} } }
+ put :checkout, user_id: @user.id, reservations: reservations_params
+ end
+
+ it { expect(response).to be_success }
+
+ it { is_expected.to render_template(:receipt) }
+
+ it 'assigns empty @check_in_set' do
+ expect(assigns(:check_in_set)).to be_empty
+ end
+
+ it 'populates @check_out_set' do
+ expect(assigns(:check_out_set)).to eq [@reservation]
+ end
+
+ it 'updates the reservation' do
+ expect(@reservation.checkout_handler).to be_nil
+ expect(@reservation.checked_out).to be_nil
+ expect(@reservation.equipment_item).to be_nil
+ @reservation.reload
+ expect(@reservation.checkout_handler).to be_a(User)
+ expect(@reservation.checked_out).to_not be_nil
+ expect(@reservation.equipment_item).to eq @item
+ expect(@reservation.notes).to include(@procedure.step)
+ end
+ end
+
+ context 'no reservations selected' do
+ before do
+ reservations_params = {}
+ request.env['HTTP_REFERER'] = 'where_i_came_from'
+ sign_in @checkout_person
+ put :checkout, user_id: @user.id, reservations: reservations_params
+ end
+ it { is_expected.to set_flash }
+ it { expect(response).to redirect_to 'where_i_came_from' }
+ end
+
+ context 'reserver has overdue reservations' do
+ context 'can override reservations?' do
+ before do
+ sign_in @admin
+ @item =
+ FactoryGirl.create(:equipment_item,
+ equipment_model: @reservation.equipment_model)
+ reservations_params =
+ { @reservation.id.to_s => { notes: '',
+ equipment_item_id: @item.id } }
+ overdue =
+ FactoryGirl.build(:overdue_reservation, reserver_id: @user.id)
+ overdue.save(validate: false)
+ put :checkout, user_id: @user.id, reservations: reservations_params
+ end
+ it { expect(response).to be_success }
+ it { is_expected.to render_template(:receipt) }
+ end
+
+ context 'cannot override' do
+ before do
+ request.env['HTTP_REFERER'] = 'where_i_came_from'
+ sign_in @checkout_person
+ @item =
+ FactoryGirl.create(:equipment_item,
+ equipment_model: @reservation.equipment_model)
+ reservations_params =
+ { @reservation.id.to_s => { notes: '',
+ equipment_item_id: @item.id } }
+ overdue =
+ FactoryGirl.build(:overdue_reservation, reserver_id: @user.id)
+ overdue.save(validate: false)
+ put :checkout, user_id: @user.id, reservations: reservations_params
+ end
+ it { is_expected.to set_flash }
+ it { expect(response).to redirect_to 'where_i_came_from' }
+ end
+ end
+
+ context 'with banned reserver' do
+ before(:each) do
+ @reservation.update_attribute(:reserver_id, @banned.id)
+ request.env['HTTP_REFERER'] = 'where_i_came_from'
+ sign_in @checkout_person
+ @obj =
+ FactoryGirl.create(:equipment_item,
+ equipment_model: @reservation.equipment_model)
+ reservations_params =
+ { @reservation.id.to_s => { notes: '',
+ equipment_item_id: @obj.id } }
+ put :checkout, user_id: @banned.id, reservations: reservations_params
+ end
+
+ it { is_expected.to set_flash }
+ it { expect(response).to redirect_to root_path }
+ end
+ end
+
+ describe '#checkin (PUT /manage/check-in/:user_id)' do
+ # Access: Admins, checkout persons.
+ # Functionality: very complicated (almost 80 lines)
+ # - params[:reservations] contains a hash of
+ # {reservation_id => {checkin?: int, notes: str,
+ # (nil?) checkin_procedures: {checkin_procedure_id => int}}}
+ # - processes all reservations in params[:reservations] -- adds
+ # checkin_handler, checked_in (time); updates notes
+ # - stops checkin if no reservations are selected
+ # - overrides errors if you can and if there are some, otherwise
+ # redirects away
+ # - renders :receipt template
+
+ before(:all) do
+ @user = FactoryGirl.create(:user)
+ @checkout_person = FactoryGirl.create(:checkout_person)
+ @admin = FactoryGirl.create(:admin)
+ @banned = FactoryGirl.create(:banned)
+ end
+
+ after(:all) { User.destroy_all }
+
+ before(:each) do
+ sign_in @user
+ @reservation = FactoryGirl.create(:valid_reservation, reserver: @user)
+ end
+
+ shared_examples 'has successful checkin' do
+ before(:each) do
+ @reservation =
+ FactoryGirl.create(:checked_out_reservation, reserver: @user)
+ @item = @reservation.equipment_item
+ reservations_params =
+ { @reservation.id.to_s => { notes: '', checkin?: '1' } }
+ put :checkin, user_id: @user.id, reservations: reservations_params
+ end
+
+ it { expect(response).to be_success }
+ it { is_expected.to render_template(:receipt) }
+
+ it 'assigns empty @check_out_set' do
+ expect(assigns(:check_out_set)).to be_empty
+ end
+
+ it 'populates @check_in_set' do
+ expect(assigns(:check_in_set)).to eq [@reservation]
+ end
+
+ it 'updates the reservation' do
+ expect(@reservation.checkin_handler).to be_nil
+ expect(@reservation.checked_in).to be_nil
+ @reservation.reload
+ expect(@reservation.checkin_handler).to be_a(User)
+ expect(@reservation.checked_in).to_not be_nil
+ end
+
+ it 'updates the equipment item history' do
+ expect { @item.reload }.to change(@item, :notes)
+ end
+
+ it 'updates the reservation notes' do
+ expect { @reservation.reload }.to change(@reservation, :notes)
+ end
+ end
+
+ context 'when accessed by admin' do
+ before(:each) do
+ sign_in @admin
+ end
+
+ include_examples 'has successful checkin'
+ end
+
+ context 'when accessed by checkout person' do
+ before(:each) do
+ sign_in @checkout_person
+ end
+
+ include_examples 'has successful checkin'
+ end
+
+ context 'when accessed by patron' do
+ before(:each) do
+ sign_in @user
+ put :checkin, user_id: @user.id
+ end
+
+ include_examples 'redirected request'
+ end
+
+ it_behaves_like 'inaccessible by banned user' do
+ before { put :checkin, user_id: @banned.id }
+ end
+
+ context 'items have already been checked in' do
+ before do
+ sign_in @admin
+ request.env['HTTP_REFERER'] = 'where_i_came_from'
+ @reservation =
+ FactoryGirl.build(:checked_in_reservation, reserver: @user)
+ @reservation.save(validate: false)
+ reservations_params =
+ { @reservation.id.to_s => { notes: '', checkin?: '1' } }
+ put :checkin, user_id: @user.id, reservations: reservations_params
+ end
+
+ it { is_expected.to set_flash }
+ it { expect(response).to redirect_to 'where_i_came_from' }
+ end
+
+ context 'no reservations to check in' do
+ before do
+ request.env['HTTP_REFERER'] = 'where_i_came_from'
+ sign_in @admin
+ put :checkin, user_id: @user.id, reservations: {}
+ end
+ it { is_expected.to set_flash }
+ it { expect(response).to redirect_to 'where_i_came_from' }
+ end
+
+ context 'when not all procedures are filled out' do
+ before do
+ sign_in @admin
+ @reservation =
+ FactoryGirl.create(:checked_out_reservation, reserver: @user)
+ @procedure =
+ FactoryGirl.create(:checkin_procedure,
+ equipment_model: @reservation.equipment_model)
+ reservations_params =
+ { @reservation.id.to_s => { notes: '', checkin?: '1',
+ checkin_procedures: {} } }
+ put :checkin, user_id: @user.id, reservations: reservations_params
+ end
+
+ it { expect(response).to be_success }
+ it { is_expected.to render_template(:receipt) }
+
+ it 'assigns empty @check_out_set' do
+ expect(assigns(:check_out_set)).to be_empty
+ end
+
+ it 'populates @check_in_set' do
+ expect(assigns(:check_in_set)).to eq [@reservation]
+ end
+
+ it 'updates the reservation' do
+ expect(@reservation.checkin_handler).to be_nil
+ expect(@reservation.checked_in).to be_nil
+ @reservation.reload
+ expect(@reservation.checkin_handler).to be_a(User)
+ expect(@reservation.checked_in).to_not be_nil
+ expect(@reservation.notes).to include(@procedure.step)
+ end
+ end
+ end
+
+ describe '#send_receipt (PUT /manage/send_receipt/:receipt_id)' do
+ before { mock_user_sign_in(UserMock.new(:checkout_person)) }
+ let!(:res) do
+ FactoryGirl.build_stubbed(:valid_reservation).tap do |r|
+ allow(Reservation).to receive(:find).with(r.id.to_s).and_return(r)
+ end
+ end
+
+ context 'successfully emails' do
+ before do
+ allow(UserMailer).to \
+ receive_message_chain(:reservation_status_update, :deliver_now)
+ .and_return(true)
+ put :send_receipt, receipt_id: res.id
+ end
+ it { is_expected.to redirect_to(res) }
+ it { is_expected.to set_flash[:notice] }
+ end
+
+ context 'fails to send email' do
+ before do
+ allow(UserMailer).to \
+ receive_message_chain(:reservation_status_update, :deliver_now)
+ .and_return(false)
+ put :send_receipt, receipt_id: res.id
+ end
+ it { is_expected.to redirect_to(res) }
+ it { is_expected.to set_flash[:error] }
+ end
+ end
+end
diff --git a/spec/controllers/reservations_controller_spec.rb b/spec/controllers/reservations_controller_spec.rb
index 320038827..777ebd0aa 100644
--- a/spec/controllers/reservations_controller_spec.rb
+++ b/spec/controllers/reservations_controller_spec.rb
@@ -748,54 +748,6 @@
end
end
- describe '#manage (GET /reservations/manage/:user_id)' do
- # Access: admins and checkout persons
- # Functionality:
- # - assigns @user, @check_out_set and @check_in_set
- # - renders :manage
-
- shared_examples 'can access #manage' do
- let!(:user) { UserMock.new(traits: [:findable]) }
- before(:each) do
- allow(user).to receive(:due_for_checkout)
- .and_return(instance_spy('ActiveRecord::Relation'))
- allow(user).to receive(:due_for_checkin)
- .and_return(instance_spy('ActiveRecord::Relation'))
- get :manage, user_id: user.id
- end
- it { expect(response).to be_success }
- it { is_expected.to render_template(:manage) }
- it 'assigns @user correctly' do
- expect(assigns(:user)).to eq(user)
- end
- it 'assigns @check_out_set correctly' do
- expect(assigns(:check_out_set)).to eq(user.due_for_checkout)
- end
- it 'assigns @check_in_set correctly' do
- expect(assigns(:check_in_set)).to eq(user.due_for_checkin)
- end
- end
-
- context 'when accessed by admin' do
- before(:each) { mock_user_sign_in(UserMock.new(:admin)) }
- include_examples 'can access #manage'
- end
-
- context 'when accessed by checkout person' do
- before(:each) { mock_user_sign_in(UserMock.new(:checkout_person)) }
- include_examples 'can access #manage'
- end
-
- context 'when accessed by patron' do
- before(:each) do
- user = UserMock.new
- mock_user_sign_in(user)
- get :manage, user_id: user.id
- end
- include_examples 'redirected request'
- end
- end
-
describe '#current (GET /reservations/current/:user_id)' do
# not particularily messy but the method is written in a way that
# makes mocking + stubbing difficult
@@ -888,439 +840,6 @@
end
end
- describe '#checkout (PUT /reservations/checkout/:user_id)' do
- # Access: Admins, checkout persons.
- # Functionality: very complicated (almost 100 lines)
- # - pass TOS (if not, redirect)
- # - params[:reservations] contains hash of
- # {reservation_id => {equipment_item_id: int, notes: str,
- # checkout_precedures: {checkout_procedure_id => int}}}
- # - stops checkout if user has overdue reservations
- # - stops checkout if no reservations are selected
- # - overrides errors if you can and if there are some, otherwise
- # redirects away
- # - also prevents checkout if reserver is banned
-
- # Effects if successful:
- # - sets empty @check_in_set, populates @check_out_set with the
- # reservations
- # - processes all reservations in params[:reservations] -- adds
- # checkout_handler, checked_out (time), equipment_item; updates
- # notes
- # - renders :receipt template
- # - sets reservation status to 'checked_out'
-
- # Note: Many of these can be cross-applied to #checkin as well
-
- before(:all) do
- @user = FactoryGirl.create(:user)
- @checkout_person = FactoryGirl.create(:checkout_person)
- @admin = FactoryGirl.create(:admin)
- @banned = FactoryGirl.create(:banned)
- end
-
- after(:all) { User.destroy_all }
-
- before(:each) do
- sign_in @user
- @reservation = FactoryGirl.create(:valid_reservation, reserver: @user)
- end
-
- shared_examples 'has successful checkout' do
- before(:each) do
- @item =
- FactoryGirl.create(:equipment_item,
- equipment_model: @reservation.equipment_model)
- reservations_params =
- { @reservation.id.to_s => { notes: '',
- equipment_item_id: @item.id } }
- ActionMailer::Base.deliveries = []
- put :checkout, user_id: @user.id, reservations: reservations_params
- end
-
- it { expect(response).to be_success }
- it { is_expected.to render_template(:receipt) }
-
- it 'assigns empty @check_in_set' do
- expect(assigns(:check_in_set)).to be_empty
- end
-
- it 'populates @check_out_set' do
- expect(assigns(:check_out_set)).to eq [@reservation]
- end
-
- it 'updates the reservation' do
- expect(@reservation.checkout_handler).to be_nil
- expect(@reservation.checked_out).to be_nil
- expect(@reservation.equipment_item).to be_nil
- expect(@reservation.reserved?).to be_truthy
- @reservation.reload
- expect(@reservation.checkout_handler).to be_a(User)
- expect(@reservation.checked_out).to_not be_nil
- expect(@reservation.equipment_item).to eq @item
- expect(@reservation.checked_out).to be_truthy
- end
-
- it 'updates the equipment item history' do
- expect { @item.reload }.to change(@item, :notes)
- end
-
- it 'updates the reservation notes' do
- expect { @reservation.reload }.to change(@reservation, :notes)
- end
-
- it 'sends checkout receipts' do
- expect(ActionMailer::Base.deliveries.count).to eq(1)
- end
- end
-
- context 'when accessed by admin' do
- before(:each) do
- sign_in @admin
- end
-
- include_examples 'has successful checkout'
- end
-
- context 'when accessed by checkout person' do
- before(:each) do
- sign_in @checkout_person
- end
-
- include_examples 'has successful checkout'
- end
-
- context 'when accessed by patron' do
- before(:each) do
- sign_in @user
- put :checkout, user_id: @user.id
- end
-
- include_examples 'redirected request'
- end
-
- it_behaves_like 'inaccessible by banned user' do
- before { put :checkout, user_id: @banned.id }
- end
-
- context 'when tos not accepted and not checked off' do
- before do
- request.env['HTTP_REFERER'] = 'where_i_came_from'
- sign_in @admin
- @user.update_attributes(terms_of_service_accepted: false)
- put :checkout, user_id: @user.id, reservations: {}
- end
- it { expect(response).to redirect_to 'where_i_came_from' }
- end
-
- context 'when tos accepted' do
- before do
- sign_in @admin
- @user.update_attributes(terms_of_service_accepted: false)
- @item =
- FactoryGirl.create(:equipment_item,
- equipment_model: @reservation.equipment_model)
- reservations_params =
- { @reservation.id.to_s => { notes: '',
- equipment_item_id: @item.id } }
- put :checkout, user_id: @user.id, reservations: reservations_params,
- terms_of_service_accepted: true
- end
-
- it { expect(response).to be_success }
- end
-
- context 'with duplicate equipment item selection' do
- before do
- request.env['HTTP_REFERER'] = 'where_i_came_from'
- sign_in @admin
- @item =
- FactoryGirl.create :equipment_item,
- equipment_model: @reservation.equipment_model
- FactoryGirl.create :equipment_item,
- equipment_model: @reservation.equipment_model
- @res2 =
- FactoryGirl.create :valid_reservation,
- reserver: @user,
- equipment_model: @reservation.equipment_model
- res_params = { notes: '', equipment_item_id: @item.id }
- reservations_params = { @reservation.id.to_s => res_params,
- @res2.id.to_s => res_params }
- put :checkout, user_id: @user.id, reservations: reservations_params
- end
-
- it { expect(response).to redirect_to 'where_i_came_from' }
-
- it 'does not update the equipment item history' do
- expect { @item.reload }.not_to change(@item, :notes)
- end
- end
-
- context 'when not all procedures are filled out' do
- before do
- sign_in @admin
- @item =
- FactoryGirl.create(:equipment_item,
- equipment_model: @reservation.equipment_model)
- @procedure =
- FactoryGirl.create(:checkout_procedure,
- equipment_model: @reservation.equipment_model)
- reservations_params =
- { @reservation.id.to_s => { notes: '',
- equipment_item_id: @item.id,
- checkout_procedures: {} } }
- put :checkout, user_id: @user.id, reservations: reservations_params
- end
-
- it { expect(response).to be_success }
-
- it { is_expected.to render_template(:receipt) }
-
- it 'assigns empty @check_in_set' do
- expect(assigns(:check_in_set)).to be_empty
- end
-
- it 'populates @check_out_set' do
- expect(assigns(:check_out_set)).to eq [@reservation]
- end
-
- it 'updates the reservation' do
- expect(@reservation.checkout_handler).to be_nil
- expect(@reservation.checked_out).to be_nil
- expect(@reservation.equipment_item).to be_nil
- @reservation.reload
- expect(@reservation.checkout_handler).to be_a(User)
- expect(@reservation.checked_out).to_not be_nil
- expect(@reservation.equipment_item).to eq @item
- expect(@reservation.notes).to include(@procedure.step)
- end
- end
-
- context 'no reservations selected' do
- before do
- reservations_params = {}
- request.env['HTTP_REFERER'] = 'where_i_came_from'
- sign_in @checkout_person
- put :checkout, user_id: @user.id, reservations: reservations_params
- end
- it { is_expected.to set_flash }
- it { expect(response).to redirect_to 'where_i_came_from' }
- end
-
- context 'reserver has overdue reservations' do
- context 'can override reservations?' do
- before do
- sign_in @admin
- @item =
- FactoryGirl.create(:equipment_item,
- equipment_model: @reservation.equipment_model)
- reservations_params =
- { @reservation.id.to_s => { notes: '',
- equipment_item_id: @item.id } }
- overdue =
- FactoryGirl.build(:overdue_reservation, reserver_id: @user.id)
- overdue.save(validate: false)
- put :checkout, user_id: @user.id, reservations: reservations_params
- end
- it { expect(response).to be_success }
- it { is_expected.to render_template(:receipt) }
- end
-
- context 'cannot override' do
- before do
- request.env['HTTP_REFERER'] = 'where_i_came_from'
- sign_in @checkout_person
- @item =
- FactoryGirl.create(:equipment_item,
- equipment_model: @reservation.equipment_model)
- reservations_params =
- { @reservation.id.to_s => { notes: '',
- equipment_item_id: @item.id } }
- overdue =
- FactoryGirl.build(:overdue_reservation, reserver_id: @user.id)
- overdue.save(validate: false)
- put :checkout, user_id: @user.id, reservations: reservations_params
- end
- it { is_expected.to set_flash }
- it { expect(response).to redirect_to 'where_i_came_from' }
- end
- end
-
- context 'with banned reserver' do
- before(:each) do
- @reservation.update_attribute(:reserver_id, @banned.id)
- request.env['HTTP_REFERER'] = 'where_i_came_from'
- sign_in @checkout_person
- @obj =
- FactoryGirl.create(:equipment_item,
- equipment_model: @reservation.equipment_model)
- reservations_params =
- { @reservation.id.to_s => { notes: '',
- equipment_item_id: @obj.id } }
- put :checkout, user_id: @banned.id, reservations: reservations_params
- end
-
- it { is_expected.to set_flash }
- it { expect(response).to redirect_to root_path }
- end
- end
-
- describe '#checkin (PUT /reservations/check-in/:user_id)' do
- # Access: Admins, checkout persons.
- # Functionality: very complicated (almost 80 lines)
- # - params[:reservations] contains a hash of
- # {reservation_id => {checkin?: int, notes: str,
- # (nil?) checkin_procedures: {checkin_procedure_id => int}}}
- # - processes all reservations in params[:reservations] -- adds
- # checkin_handler, checked_in (time); updates notes
- # - stops checkin if no reservations are selected
- # - overrides errors if you can and if there are some, otherwise
- # redirects away
- # - renders :receipt template
-
- before(:all) do
- @user = FactoryGirl.create(:user)
- @checkout_person = FactoryGirl.create(:checkout_person)
- @admin = FactoryGirl.create(:admin)
- @banned = FactoryGirl.create(:banned)
- end
-
- after(:all) { User.destroy_all }
-
- before(:each) do
- sign_in @user
- @reservation = FactoryGirl.create(:valid_reservation, reserver: @user)
- end
-
- shared_examples 'has successful checkin' do
- before(:each) do
- @reservation =
- FactoryGirl.create(:checked_out_reservation, reserver: @user)
- @item = @reservation.equipment_item
- reservations_params =
- { @reservation.id.to_s => { notes: '', checkin?: '1' } }
- put :checkin, user_id: @user.id, reservations: reservations_params
- end
-
- it { expect(response).to be_success }
- it { is_expected.to render_template(:receipt) }
-
- it 'assigns empty @check_out_set' do
- expect(assigns(:check_out_set)).to be_empty
- end
-
- it 'populates @check_in_set' do
- expect(assigns(:check_in_set)).to eq [@reservation]
- end
-
- it 'updates the reservation' do
- expect(@reservation.checkin_handler).to be_nil
- expect(@reservation.checked_in).to be_nil
- @reservation.reload
- expect(@reservation.checkin_handler).to be_a(User)
- expect(@reservation.checked_in).to_not be_nil
- end
-
- it 'updates the equipment item history' do
- expect { @item.reload }.to change(@item, :notes)
- end
-
- it 'updates the reservation notes' do
- expect { @reservation.reload }.to change(@reservation, :notes)
- end
- end
-
- context 'when accessed by admin' do
- before(:each) do
- sign_in @admin
- end
-
- include_examples 'has successful checkin'
- end
-
- context 'when accessed by checkout person' do
- before(:each) do
- sign_in @checkout_person
- end
-
- include_examples 'has successful checkin'
- end
-
- context 'when accessed by patron' do
- before(:each) do
- sign_in @user
- put :checkin, user_id: @user.id
- end
-
- include_examples 'redirected request'
- end
-
- it_behaves_like 'inaccessible by banned user' do
- before { put :checkin, user_id: @banned.id }
- end
-
- context 'items have already been checked in' do
- before do
- sign_in @admin
- request.env['HTTP_REFERER'] = 'where_i_came_from'
- @reservation =
- FactoryGirl.build(:checked_in_reservation, reserver: @user)
- @reservation.save(validate: false)
- reservations_params =
- { @reservation.id.to_s => { notes: '', checkin?: '1' } }
- put :checkin, user_id: @user.id, reservations: reservations_params
- end
-
- it { is_expected.to set_flash }
- it { expect(response).to redirect_to 'where_i_came_from' }
- end
-
- context 'no reservations to check in' do
- before do
- request.env['HTTP_REFERER'] = 'where_i_came_from'
- sign_in @admin
- put :checkin, user_id: @user.id, reservations: {}
- end
- it { is_expected.to set_flash }
- it { expect(response).to redirect_to 'where_i_came_from' }
- end
-
- context 'when not all procedures are filled out' do
- before do
- sign_in @admin
- @reservation =
- FactoryGirl.create(:checked_out_reservation, reserver: @user)
- @procedure =
- FactoryGirl.create(:checkin_procedure,
- equipment_model: @reservation.equipment_model)
- reservations_params =
- { @reservation.id.to_s => { notes: '', checkin?: '1',
- checkin_procedures: {} } }
- put :checkin, user_id: @user.id, reservations: reservations_params
- end
-
- it { expect(response).to be_success }
- it { is_expected.to render_template(:receipt) }
-
- it 'assigns empty @check_out_set' do
- expect(assigns(:check_out_set)).to be_empty
- end
-
- it 'populates @check_in_set' do
- expect(assigns(:check_in_set)).to eq [@reservation]
- end
-
- it 'updates the reservation' do
- expect(@reservation.checkin_handler).to be_nil
- expect(@reservation.checked_in).to be_nil
- @reservation.reload
- expect(@reservation.checkin_handler).to be_a(User)
- expect(@reservation.checked_in).to_not be_nil
- expect(@reservation.notes).to include(@procedure.step)
- end
- end
- end
-
describe '#renew (PUT /reservations/:id/renew)' do
# SMELL: this mostly tests permissions
shared_examples 'can renew reservation' do
@@ -1524,37 +1043,6 @@
end
end
- describe '#send_receipt (GET /reservations/:id/send_receipt)' do
- before { mock_user_sign_in(UserMock.new(:checkout_person)) }
- let!(:res) do
- FactoryGirl.build_stubbed(:valid_reservation).tap do |r|
- allow(Reservation).to receive(:find).with(r.id.to_s).and_return(r)
- end
- end
-
- context 'successfully emails' do
- before do
- allow(UserMailer).to \
- receive_message_chain(:reservation_status_update, :deliver_now)
- .and_return(true)
- get :send_receipt, id: res.id
- end
- it { is_expected.to redirect_to(res) }
- it { is_expected.to set_flash[:notice] }
- end
-
- context 'fails to send email' do
- before do
- allow(UserMailer).to \
- receive_message_chain(:reservation_status_update, :deliver_now)
- .and_return(false)
- get :send_receipt, id: res.id
- end
- it { is_expected.to redirect_to(res) }
- it { is_expected.to set_flash[:error] }
- end
- end
-
describe '#review GET' do
let!(:res) { ReservationMock.new(traits: [:findable]) }
context 'as admin' do
diff --git a/spec/factories/equipment_models.rb b/spec/factories/equipment_models.rb
index e927042f1..26201dc0a 100644
--- a/spec/factories/equipment_models.rb
+++ b/spec/factories/equipment_models.rb
@@ -3,6 +3,7 @@
FactoryGirl.define do
sequence(:unique_id) { |n| n }
sequence(:ordering) { |n| n + 1 }
+
factory :equipment_model do
name
description 'This is a model'
@@ -14,6 +15,7 @@
max_renewal_length 10
renewal_days_before_due 10
ordering
+
factory :restricted_equipment_model do
category { FactoryGirl.create(:category, max_per_user: 1) }
end
diff --git a/spec/features/reservations_spec.rb b/spec/features/reservations_spec.rb
index d4e9fbbf0..76e4fba6a 100644
--- a/spec/features/reservations_spec.rb
+++ b/spec/features/reservations_spec.rb
@@ -384,7 +384,6 @@
expect(page.current_url).to \
eq(manage_reservations_for_user_url(@user))
end
-
it 'succeeds when the box is checked off' do
check 'terms_of_service_accepted'
select @eq_model.equipment_items.first.name.to_s,
diff --git a/spec/models/equipment_model_spec.rb b/spec/models/equipment_model_spec.rb
index 0db92171b..fc3b3a81c 100644
--- a/spec/models/equipment_model_spec.rb
+++ b/spec/models/equipment_model_spec.rb
@@ -23,7 +23,6 @@ def mock_eq_model(**attrs)
it { is_expected.to accept_nested_attributes_for(:checkout_procedures) }
it { is_expected.to have_and_belong_to_many(:associated_equipment_models) }
it { is_expected.to validate_presence_of(:name) }
- it { is_expected.to validate_uniqueness_of(:name) }
it { is_expected.to validate_presence_of(:description) }
it { is_expected.to belong_to(:category) }
it { is_expected.to validate_presence_of(:ordering) }
@@ -34,6 +33,11 @@ def mock_eq_model(**attrs)
end
end
+ describe 'validations requiring persistance' do
+ subject { FactoryGirl.build(:equipment_model) }
+ it { is_expected.to validate_uniqueness_of(:name) }
+ end
+
describe 'attribute-specific validations' do
shared_examples 'integer attribute' do |attr|
it 'is valid with an integer value' do