2020-02-11 13:22:20 +01:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2020-03-25 17:45:53 +01:00
|
|
|
# Reservation is a Slot or a Ticket booked by a member.
|
|
|
|
# Slots are for Machine, Space and Training reservations.
|
|
|
|
# Tickets are for Event reservations.
|
2020-03-25 10:16:47 +01:00
|
|
|
class Reservation < ApplicationRecord
|
2016-03-23 18:39:41 +01:00
|
|
|
include NotifyWith::NotificationAttachedObject
|
|
|
|
|
2019-06-04 16:50:23 +02:00
|
|
|
belongs_to :statistic_profile
|
2017-02-28 13:23:31 +01:00
|
|
|
|
|
|
|
has_many :slots_reservations, dependent: :destroy
|
|
|
|
has_many :slots, through: :slots_reservations
|
|
|
|
|
2016-03-23 18:39:41 +01:00
|
|
|
accepts_nested_attributes_for :slots, allow_destroy: true
|
|
|
|
belongs_to :reservable, polymorphic: true
|
|
|
|
|
2016-08-25 18:41:33 +02:00
|
|
|
has_many :tickets
|
|
|
|
accepts_nested_attributes_for :tickets, allow_destroy: false
|
|
|
|
|
2018-12-11 17:27:25 +01:00
|
|
|
has_one :invoice, -> { where(type: nil) }, as: :invoiced, dependent: :destroy
|
2020-11-12 16:44:55 +01:00
|
|
|
has_one :payment_schedule, as: :scheduled, dependent: :destroy
|
2016-03-23 18:39:41 +01:00
|
|
|
|
|
|
|
validates_presence_of :reservable_id, :reservable_type
|
2018-12-11 17:27:25 +01:00
|
|
|
validate :machine_not_already_reserved, if: -> { reservable.is_a?(Machine) }
|
|
|
|
validate :training_not_fully_reserved, if: -> { reservable.is_a?(Training) }
|
2020-02-11 13:22:20 +01:00
|
|
|
validates_with ReservationSlotSubscriptionValidator
|
2016-03-23 18:39:41 +01:00
|
|
|
|
2019-09-10 17:57:46 +02:00
|
|
|
attr_accessor :plan_id, :subscription
|
2016-03-23 18:39:41 +01:00
|
|
|
|
|
|
|
after_commit :notify_member_create_reservation, on: :create
|
|
|
|
after_commit :notify_admin_member_create_reservation, on: :create
|
2018-12-11 17:27:25 +01:00
|
|
|
after_save :update_event_nb_free_places, if: proc { |reservation| reservation.reservable_type == 'Event' }
|
2016-03-23 18:39:41 +01:00
|
|
|
|
2016-08-10 17:37:17 +02:00
|
|
|
##
|
2020-12-16 18:33:43 +01:00
|
|
|
# These checks will run before the invoice/payment-schedule is generated
|
2016-08-10 17:37:17 +02:00
|
|
|
##
|
2020-12-16 18:33:43 +01:00
|
|
|
def pre_check
|
2017-09-06 17:10:10 +02:00
|
|
|
# check that none of the reserved availabilities was locked
|
|
|
|
slots.each do |slot|
|
2018-12-11 15:07:21 +01:00
|
|
|
raise LockedError if slot.availability.lock
|
2017-09-06 17:10:10 +02:00
|
|
|
end
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
2020-12-16 18:33:43 +01:00
|
|
|
## Generate the subscription associated with for the current reservation
|
|
|
|
def generate_subscription
|
|
|
|
return unless plan_id
|
2017-06-08 19:38:19 +02:00
|
|
|
|
2020-12-30 10:16:39 +01:00
|
|
|
self.subscription = Subscription.new(plan_id: plan_id, statistic_profile_id: statistic_profile_id, expiration_date: nil)
|
2020-12-16 18:33:43 +01:00
|
|
|
subscription.init_save
|
|
|
|
subscription
|
2017-06-08 19:38:19 +02:00
|
|
|
end
|
2016-03-23 18:39:41 +01:00
|
|
|
|
2020-12-16 18:33:43 +01:00
|
|
|
##
|
|
|
|
# These actions will be realized after the reservation is initially saved (on creation)
|
|
|
|
##
|
|
|
|
def post_save
|
2018-12-11 15:07:21 +01:00
|
|
|
UsersCredits::Manager.new(reservation: self).update_credits
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
2019-11-25 10:45:54 +01:00
|
|
|
# @param canceled if true, count the number of seats for this reservation, including canceled seats
|
|
|
|
def total_booked_seats(canceled: false)
|
2019-11-25 10:30:03 +01:00
|
|
|
# cases:
|
|
|
|
# - machine/training/space reservation => 1 slot = 1 seat (currently not covered by this function)
|
|
|
|
# - event reservation => seats = nb_reserve_place (normal price) + tickets.booked (other prices)
|
2019-11-25 10:45:54 +01:00
|
|
|
return 0 if slots.first.canceled_at && !canceled
|
|
|
|
|
2019-11-25 10:30:03 +01:00
|
|
|
total = nb_reserve_places
|
2019-01-10 16:50:54 +01:00
|
|
|
total += tickets.map(&:booked).map(&:to_i).reduce(:+) if tickets.count.positive?
|
|
|
|
|
2016-08-30 09:47:03 +02:00
|
|
|
total
|
|
|
|
end
|
|
|
|
|
2019-06-04 16:50:23 +02:00
|
|
|
def user
|
|
|
|
statistic_profile.user
|
|
|
|
end
|
|
|
|
|
2019-11-20 17:06:42 +01:00
|
|
|
def update_event_nb_free_places
|
|
|
|
return unless reservable_type == 'Event'
|
|
|
|
|
|
|
|
reservable.update_nb_free_places
|
|
|
|
reservable.save!
|
|
|
|
end
|
|
|
|
|
2016-08-10 16:33:26 +02:00
|
|
|
private
|
2019-01-10 16:50:54 +01:00
|
|
|
|
2016-03-23 18:39:41 +01:00
|
|
|
def machine_not_already_reserved
|
|
|
|
already_reserved = false
|
2019-01-10 16:50:54 +01:00
|
|
|
slots.each do |slot|
|
2017-02-28 13:23:31 +01:00
|
|
|
same_hour_slots = Slot.joins(:reservations).where(
|
2019-01-10 16:50:54 +01:00
|
|
|
reservations: { reservable_type: reservable_type, reservable_id: reservable_id },
|
|
|
|
start_at: slot.start_at,
|
|
|
|
end_at: slot.end_at,
|
|
|
|
availability_id: slot.availability_id,
|
|
|
|
canceled_at: nil
|
|
|
|
)
|
2016-03-23 18:39:41 +01:00
|
|
|
if same_hour_slots.any?
|
|
|
|
already_reserved = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
2019-01-10 16:50:54 +01:00
|
|
|
errors.add(:machine, 'already reserved') if already_reserved
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def training_not_fully_reserved
|
2019-01-10 16:50:54 +01:00
|
|
|
slot = slots.first
|
|
|
|
errors.add(:training, 'already fully reserved') if Availability.find(slot.availability_id).completed?
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def notify_member_create_reservation
|
|
|
|
NotificationCenter.call type: 'notify_member_create_reservation',
|
|
|
|
receiver: user,
|
|
|
|
attached_object: self
|
|
|
|
end
|
|
|
|
|
|
|
|
def notify_admin_member_create_reservation
|
|
|
|
NotificationCenter.call type: 'notify_admin_member_create_reservation',
|
2020-04-29 15:34:30 +02:00
|
|
|
receiver: User.admins_and_managers,
|
2016-03-23 18:39:41 +01:00
|
|
|
attached_object: self
|
|
|
|
end
|
|
|
|
end
|