Class: SevenRooms::Availabilities::Availability

Inherits:
Object
  • Object
show all
Includes:
DefaultErrorContainer, ElasticAPM::SpanHelpers, SevenRooms::ApiHelpers
Defined in:
app/services/seven_rooms/availabilities/availability.rb

Overview

Availability service for fetching SevenRooms timeslot availabilities.

Fetches availability data from SevenRooms API for specified dates and party size, and aggregates results.

Error handling and logging are performed using APMErrorHandler and BUSINESS_LOGGER. Supplier validation is handled by the calling service, not in this class.

Constant Summary collapse

DEFAULT_START_TIME =
'00:00'
DEFAULT_END_TIME =
'23:59'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from DefaultErrorContainer

#error, #error_message_simple, #merge_errors

Methods included from SevenRooms::ApiHelpers

#api_uri, #auth_headers, #common_headers, #find_seven_rooms_vendor_application, #parse_user_or_guest_full_name, #parsed_vendor_name, #send_error_availability_notification_to_staff, #sevenrooms_credit_card_error?, #standardize_or_default_phone

Constructor Details

#initialize(restaurant, dates, party_size, start_time = nil) ⇒ Availability

Initialize Availability service.

Parameters:

  • restaurant (Restaurant)

    Restaurant object (must be validated by caller)

  • dates (Array<String>)

    Dates to check availability for (must be 'YYYY-MM-DD' strings)

  • party_size (Integer)

    Party size for availability (optional, defaults to restaurant min_seat)

  • start_time (String, nil) (defaults to: nil)

    Optional start time for single timeslot calls (nil for multiple timeslots)



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'app/services/seven_rooms/availabilities/availability.rb', line 27

def initialize(restaurant, dates, party_size, start_time = nil)
  @restaurant = restaurant
  @restaurant_id = restaurant.id
  @restaurant_tz = restaurant.time_zone || 'Asia/Bangkok'
  @dates = dates
  @party_size = party_size || restaurant&.seven_rooms_restaurant&.min_seat.to_i
  @venue_id = restaurant&.seven_rooms_restaurant&.venue_id.to_s
  @concierge_id = AdminSetting.seven_rooms_concierge_id.to_s
  @start_time = start_time

  # Only initialize fetcher_service when start_time is nil (for multiple timeslots)
  if start_time.nil?
    @fetcher_service = VendorsService::InventorySync::InventoryFetcherService.new(
      restaurant: restaurant,
      supplier: :sevenrooms,
    )
  end
end

Instance Attribute Details

#concierge_idObject (readonly)

Returns the value of attribute concierge_id.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def concierge_id
  @concierge_id
end

#datesObject (readonly)

Returns the value of attribute dates.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def dates
  @dates
end

#fetcher_serviceObject (readonly)

Returns the value of attribute fetcher_service.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def fetcher_service
  @fetcher_service
end

#party_sizeObject (readonly)

Returns the value of attribute party_size.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def party_size
  @party_size
end

#restaurantObject (readonly)

Returns the value of attribute restaurant.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def restaurant
  @restaurant
end

#restaurant_idObject (readonly)

Returns the value of attribute restaurant_id.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def restaurant_id
  @restaurant_id
end

#restaurant_tzObject (readonly)

Returns the value of attribute restaurant_tz.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def restaurant_tz
  @restaurant_tz
end

#start_timeObject (readonly)

Returns the value of attribute start_time.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def start_time
  @start_time
end

#venue_idObject (readonly)

Returns the value of attribute venue_id.



18
19
20
# File 'app/services/seven_rooms/availabilities/availability.rb', line 18

def venue_id
  @venue_id
end

Instance Method Details

#fetch_single_timeslot(date, time_override = nil) ⇒ ServiceResult

Fetch availability for a single timeslot without using fetcher service. Makes direct API call and returns the raw response.

Parameters:

  • date (String)

    Date to check availability for ('YYYY-MM-DD' format)

  • time_override (String, nil) (defaults to: nil)

    Optional time override (uses instance start_time if not provided)

Returns:

  • (ServiceResult)

    Service result with data containing API response or errors



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'app/services/seven_rooms/availabilities/availability.rb', line 75

def fetch_single_timeslot(date, time_override = nil)
  raise ArgumentError, "venue_id is blank for restaurant_id=#{restaurant_id}" if venue_id.blank?
  raise ArgumentError, 'party_size must be present and > 0' if party_size.nil? || party_size <= 0
  raise ArgumentError, 'date must be present' if date.blank?

  BUSINESS_LOGGER.set_business_context({ restaurant_id: restaurant_id })

  auth_token = SevenRooms::Authentication.new.auth_request
  if auth_token.success?
    auth_token = auth_token.data['token']
  else
    error_message = auth_token.message
    BUSINESS_LOGGER.error('SEVENROOM: Authentication failed', error_message: error_message)
    return ServiceResult.new errors: { base: error_message }, message: error_message
  end

  # Use time_override if provided, otherwise use instance start_time, otherwise default to DEFAULT_START_TIME
  time_to_use = time_override || start_time || DEFAULT_START_TIME
  url = build_url(date, time_to_use)

  res = DefaultFaradayClient.create_faraday_connection.get do |req|
    req.url url
    req.headers['Authorization'] = auth_token
    req.headers['Accept'] = 'application/json'
  end

  res_json = JSON.parse(res.body)
  if res.status != 200 || res_json['status'] != 200
    error_message = res_json['msg'] || 'Unknown API error'
    error_data = {
      class: self.class.to_s,
      restaurant_id: restaurant_id,
      request: {
        method: res.env.method.upcase,
        url: res.env.url.to_s,
      },
      response: {
        status: res.status,
        body: res.body,
      },
    }
    BUSINESS_LOGGER.error('SEVENROOM: Failed to fetch single timeslot', error_data)
    return ServiceResult.new errors: { base: error_message }, message: error_message
  end

  # BUSINESS_LOGGER.info('SEVENROOM: Single timeslot fetched successfully', { date: date, start_time: time_to_use })
  # Return the entire response for the blockage service to process
  ServiceResult.new data: res_json
rescue StandardError => e
  error_message = e.message
  APMErrorHandler.report("SEVENROOM: Failed to fetch single timeslot: #{self.class} #{error_message}",
                         restaurant_id: restaurant_id, date: date, start_time: time_to_use)
  ServiceResult.new errors: { base: error_message }, message: error_message
end

#fetch_timeslots_with_availabilityArray<Hash>

Fetch availability for the specified dates. Uses the shared fetcher service for common HTTP handling logic.

Returns:

  • (Array<Hash>)

    Array of hashes: { datetime: <String>, quantity_available: <Integer> }

Raises:

  • (ArgumentError)


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/services/seven_rooms/availabilities/availability.rb', line 50

def fetch_timeslots_with_availability
  raise ArgumentError, "venue_id is blank for restaurant_id=#{restaurant_id}" if venue_id.blank?
  raise ArgumentError, 'party_size must be present and > 0' if party_size.nil? || party_size <= 0

  if fetcher_service.nil?
    raise ArgumentError,
          'fetcher_service not initialized - initialize without start_time for multiple timeslots'
  end

  auth_token = authenticate!

  BUSINESS_LOGGER.set_business_context({ restaurant_id: restaurant_id })

  fetcher_service.execute_parallel_requests(dates) do |block, hydra, availabilities|
    queue_request(block, auth_token, hydra, availabilities)
  end
end