2023-03-13 17:29:06 +01:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# Check if a user if allowed to book a reservation without exceeding the limits set by his plan
|
|
|
|
class ReservationLimitService
|
|
|
|
class << self
|
|
|
|
# @param plan [Plan,NilClass]
|
|
|
|
# @param customer [User]
|
|
|
|
# @param reservation [CartItem::Reservation]
|
|
|
|
# @param cart_items [Array<CartItem::BaseItem>]
|
|
|
|
# @return [Boolean]
|
|
|
|
def authorized?(plan, customer, reservation, cart_items)
|
|
|
|
return true if plan.nil? || !plan.limiting
|
|
|
|
|
|
|
|
return true if reservation.nil? || !reservation.is_a?(CartItem::Reservation)
|
|
|
|
|
2023-03-14 12:35:58 +01:00
|
|
|
limit = limit(plan, reservation.reservable)
|
|
|
|
return true if limit.nil?
|
|
|
|
|
|
|
|
reservation.cart_item_reservation_slots.group_by { |sr| sr.slot.start_at.to_date }.each_pair do |date, reservation_slots|
|
|
|
|
daily_duration = reservations_duration(customer, date, reservation, cart_items) +
|
|
|
|
(reservation_slots.map { |sr| sr.slot.duration }.reduce(:+) || 0)
|
2023-03-14 16:45:01 +01:00
|
|
|
return false if Rational(daily_duration / 3600).to_f > limit.limit
|
2023-03-13 17:29:06 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2023-03-14 16:45:01 +01:00
|
|
|
# @param reservation [Reservation]
|
|
|
|
# @return [Date,NilClass]
|
|
|
|
def reached_limit_date(reservation)
|
|
|
|
user = reservation.user
|
|
|
|
plan = user.subscribed_plan
|
|
|
|
return nil if plan.nil? || !plan.limiting
|
|
|
|
|
|
|
|
limit = limit(plan, reservation.reservable)
|
|
|
|
return nil if limit.nil?
|
|
|
|
|
|
|
|
reservation.slots_reservations.group_by { |sr| sr.slot.start_at.to_date }.each_pair do |date, reservation_slots|
|
|
|
|
daily_duration = saved_reservations_durations(user, reservation.reservable, date, reservation) +
|
|
|
|
(reservation_slots.map { |sr| sr.slot.duration }.reduce(:+) || 0)
|
|
|
|
return date if Rational(daily_duration / 3600).to_f >= limit.limit
|
|
|
|
end
|
|
|
|
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2023-03-13 17:29:06 +01:00
|
|
|
# @param plan [Plan,NilClass]
|
|
|
|
# @param reservable [Machine,Event,Space,Training]
|
2023-03-14 16:45:01 +01:00
|
|
|
# @return [PlanLimitation] in hours
|
2023-03-13 17:29:06 +01:00
|
|
|
def limit(plan, reservable)
|
|
|
|
return nil unless plan&.limiting
|
|
|
|
|
2023-03-14 12:35:58 +01:00
|
|
|
limitations = plan&.plan_limitations&.filter { |limit| limit.reservables.include?(reservable) }
|
2023-03-14 16:45:01 +01:00
|
|
|
limitations&.find { |limit| limit.limitable_type != 'MachineCategory' } || limitations&.first
|
2023-03-13 17:29:06 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
# @param customer [User]
|
|
|
|
# @param date [Date]
|
|
|
|
# @param reservation [CartItem::Reservation]
|
|
|
|
# @param cart_items [Array<CartItem::BaseItem>]
|
|
|
|
# @return [Integer] in seconds
|
|
|
|
def reservations_duration(customer, date, reservation, cart_items)
|
2023-03-14 16:45:01 +01:00
|
|
|
daily_reservations_hours = saved_reservations_durations(customer, reservation.reservable, date)
|
2023-03-13 17:29:06 +01:00
|
|
|
|
|
|
|
cart_daily_reservations = cart_items.filter do |item|
|
|
|
|
item.is_a?(CartItem::Reservation) &&
|
|
|
|
item != reservation &&
|
|
|
|
item.reservable == reservation.reservable &&
|
|
|
|
item.cart_item_reservation_slots
|
|
|
|
.includes(:slot)
|
|
|
|
.where("date_trunc('day', slots.start_at) = :date", date: date)
|
|
|
|
end
|
|
|
|
|
2023-03-14 16:45:01 +01:00
|
|
|
daily_reservations_hours +
|
2023-03-13 17:29:06 +01:00
|
|
|
(cart_daily_reservations.map { |r| r.cart_item_reservation_slots.map { |sr| sr.slot.duration } }.flatten.reduce(:+) || 0)
|
|
|
|
end
|
2023-03-14 16:45:01 +01:00
|
|
|
|
|
|
|
# @param customer [User]
|
|
|
|
# @param reservable [Machine,Event,Space,Training]
|
|
|
|
# @param date [Date]
|
|
|
|
# @param reservation [Reservation]
|
|
|
|
# @return [Integer] in seconds
|
|
|
|
def saved_reservations_durations(customer, reservable, date, reservation = nil)
|
|
|
|
daily_reservations = customer.reservations
|
|
|
|
.includes(slots_reservations: :slot)
|
|
|
|
.where(reservable: reservable)
|
|
|
|
.where(slots_reservations: { canceled_at: nil })
|
|
|
|
.where("date_trunc('day', slots.start_at) = :date", date: date)
|
|
|
|
|
|
|
|
daily_reservations = daily_reservations.where.not(id: reservation.id) unless reservation.nil?
|
|
|
|
|
|
|
|
(daily_reservations.map { |r| r.slots_reservations.map { |sr| sr.slot.duration } }.flatten.reduce(:+) || 0)
|
|
|
|
end
|
2023-03-13 17:29:06 +01:00
|
|
|
end
|
|
|
|
end
|