module ActsAsBookable::Bookable::Core::InstanceMethods

Public Instance Methods

be_booked!(booker, opts={}) click to toggle source

Accept a booking by a booker. This is an alias method, equivalent to @booker.book!(@bookable, opts)

@param booker The booker model @param opts The booking options

Example:

@room.be_booked!(@user, from: Date.today, to: Date.tomorrow, amount: 2)
# File lib/acts_as_bookable/bookable/core.rb, line 269
def be_booked!(booker, opts={})
  booker.book!(self, opts)
end
booker?() click to toggle source
# File lib/acts_as_bookable/bookable/core.rb, line 283
def booker?
  self.class.booker?
end
check_availability(opts) click to toggle source

Check availability of current bookable

@param opts The booking options @return true if the bookable is available for given options, otherwise return false

Example:

@room.check_availability!(from: Date.today, to: Date.tomorrow, amount: 2)
# File lib/acts_as_bookable/bookable/core.rb, line 252
def check_availability(opts)
  begin
    check_availability!(opts)
  rescue ActsAsBookable::AvailabilityError
    false
  end
end
check_availability!(opts) click to toggle source

Check availability of current bookable, raising an error if the bookable is not available

@param opts The booking options @return true if the bookable is available for given options @raise ActsAsBookable::AvailabilityError if the bookable is not available for given options

Example:

@room.check_availability!(from: Date.today, to: Date.tomorrow, amount: 2)
# File lib/acts_as_bookable/bookable/core.rb, line 164
def check_availability!(opts)
  # validates options
  self.validate_booking_options!(opts)

  # Capacity check (done first because it doesn't require additional queries)
  if self.booking_opts[:capacity_type] != :none
    # Amount > capacity
    if opts[:amount] > self.capacity
      raise ActsAsBookable::AvailabilityError.new ActsAsBookable::T.er('.availability.amount_gt_capacity', model: self.class.to_s)
    end
  end

  ##
  # Time check
  #
  if self.booking_opts[:time_type] == :range
    time_check_ok = true
    # If it's bookable across recurrences, just check start time and end time
    if self.booking_opts[:bookable_across_occurrences]
      # Check start time
      if !(ActsAsBookable::TimeUtils.time_in_schedule?(self.schedule, opts[:time_start]))
        time_check_ok = false
      end
      # Check end time
      if !(ActsAsBookable::TimeUtils.time_in_schedule?(self.schedule, opts[:time_end]))
        time_check_ok = false
      end
    # If it's not bookable across recurrences, check if the whole interval is included in an occurrence
    else
      # Check the whole interval
      if !(ActsAsBookable::TimeUtils.interval_in_schedule?(self.schedule, opts[:time_start], opts[:time_end]))
        time_check_ok = false
      end
    end
    # If something went wrong
    unless time_check_ok
      raise ActsAsBookable::AvailabilityError.new ActsAsBookable::T.er('.availability.unavailable_interval', model: self.class.to_s, time_start: opts[:time_start], time_end: opts[:time_end])
    end
  end
  if self.booking_opts[:time_type] == :fixed
    if !(ActsAsBookable::TimeUtils.time_in_schedule?(self.schedule, opts[:time]))
      raise ActsAsBookable::AvailabilityError.new ActsAsBookable::T.er('.availability.unavailable_time', model: self.class.to_s, time: opts[:time])
    end
  end

  ##
  # Real capacity check (calculated with overlapped bookings)
  #
  overlapped = ActsAsBookable::Booking.overlapped(self, opts)
  # If capacity_type is :closed cannot book if already booked (no matter if amount < capacity)
  if (self.booking_opts[:capacity_type] == :closed && !overlapped.empty?)
    raise ActsAsBookable::AvailabilityError.new ActsAsBookable::T.er('.availability.already_booked', model: self.class.to_s)
  end
  # if capacity_type is :open, check if amount <= maximum amount of overlapped booking
  if (self.booking_opts[:capacity_type] == :open && !overlapped.empty?)
    # if time_type is :range, split in sub-intervals and check the maximum sum of amounts against capacity for each sub-interval
    if (self.booking_opts[:time_type] == :range)
      # Map overlapped bookings to a set of intervals with amount
      intervals = overlapped.map { |e| {time_start: e.time_start, time_end: e.time_end, amount: e.amount} }
      # Make subintervals from overlapped bookings and check capacity for each of them
      ActsAsBookable::TimeUtils.subintervals(intervals) do |a,b,op|
        case op
        when :open
          res = {amount: a[:amount] + b[:amount]}
        when :close
          res = {amount: a[:amount] - b[:amount]}
        end
        raise ActsAsBookable::AvailabilityError.new ActsAsBookable::T.er('.availability.already_booked', model: self.class.to_s) if (res[:amount] > self.capacity)
        res
      end
    # else, just sum the amounts (fixed times are not intervals and they overlap if are the same)
    else
      if(overlapped.sum(:amount) + opts[:amount] > self.capacity)
        raise ActsAsBookable::AvailabilityError.new ActsAsBookable::T.er('.availability.already_booked', model: self.class.to_s)
      end
    end
  end
  true
end
validate_booking_options!(opts) click to toggle source

Check if options passed for booking this Bookable are valid

@raise ActsAsBookable::OptionsInvalid if options are not valid @param opts The booking options

# File lib/acts_as_bookable/bookable/core.rb, line 279
def validate_booking_options!(opts)
  self.class.validate_booking_options!(opts)
end