Class: Partners::ReservationSummary

Inherits:
ApplicationRecord show all
Defined in:
app/models/partners/reservation_summary.rb

Overview

Streamlined summary table for Partner API reservations performance optimization. Stores exact values from serializer logic for fast querying and filtering.

Key principles:

  • No validation callbacks or complex business logic

  • No pre-computed formatted fields (use runtime formatting)

  • Stores exact values that match serializer output

  • Optimized for filtering and querying only

  • Tracks sync freshness via last_synced_at for monitoring and debugging

Examples:

Finding summaries for a restaurant

Partners::ReservationSummary.where(restaurant_id: 123)

Finding summaries by date range

Partners::ReservationSummary.where(date: Date.current..1.week.from_now)

Finding stale summaries (not synced in last hour)

Partners::ReservationSummary.stale_summaries(1.hour.ago)

Instance Attribute Summary collapse

Class Method Summary collapse

Methods inherited from ApplicationRecord

sync_carrierwave_url

Instance Attribute Details

#activeBoolean

Returns Whether reservation is active (not cancelled/deleted).

Returns:

  • (Boolean)

    Whether reservation is active (not cancelled/deleted)



# File 'app/models/partners/reservation_summary.rb', line 23

#adjustedBoolean

Returns Whether reservation was modified after creation.

Returns:

  • (Boolean)

    Whether reservation was modified after creation



# File 'app/models/partners/reservation_summary.rb', line 23

#adultInteger

Returns Number of adult guests.

Returns:

  • (Integer)

    Number of adult guests



# File 'app/models/partners/reservation_summary.rb', line 23

#created_atDateTime

Returns Record creation timestamp.

Returns:

  • (DateTime)

    Record creation timestamp



# File 'app/models/partners/reservation_summary.rb', line 23

#customer_emailString

Returns Customer email for search functionality.

Returns:

  • (String)

    Customer email for search functionality



# File 'app/models/partners/reservation_summary.rb', line 23

#customer_nameString

Returns Customer name for search functionality.

Returns:

  • (String)

    Customer name for search functionality



# File 'app/models/partners/reservation_summary.rb', line 23

#customer_phoneString

Returns Customer phone for search functionality.

Returns:

  • (String)

    Customer phone for search functionality



# File 'app/models/partners/reservation_summary.rb', line 23

#dateDate

Returns Reservation date for filtering and grouping.

Returns:

  • (Date)

    Reservation date for filtering and grouping



# File 'app/models/partners/reservation_summary.rb', line 23

#end_timeTime

Returns Reservation end time.

Returns:

  • (Time)

    Reservation end time



# File 'app/models/partners/reservation_summary.rb', line 23

#for_locking_systemBoolean

Returns Whether reservation is for table locking system.

Returns:

  • (Boolean)

    Whether reservation is for table locking system



# File 'app/models/partners/reservation_summary.rb', line 23

#is_temporaryBoolean

Returns Whether reservation is temporary (pending confirmation).

Returns:

  • (Boolean)

    Whether reservation is temporary (pending confirmation)



# File 'app/models/partners/reservation_summary.rb', line 23

#kidsInteger

Returns Number of child guests.

Returns:

  • (Integer)

    Number of child guests



# File 'app/models/partners/reservation_summary.rb', line 23

#last_synced_atDateTime?

Returns Timestamp of last sync for monitoring data freshness.

Returns:

  • (DateTime, nil)

    Timestamp of last sync for monitoring data freshness



# File 'app/models/partners/reservation_summary.rb', line 23

#party_sizeInteger

Returns Total number of guests (adult + kids).

Returns:

  • (Integer)

    Total number of guests (adult + kids)



# File 'app/models/partners/reservation_summary.rb', line 23

#payment_statusString

Returns Payment status for filtering.

Returns:

  • (String)

    Payment status for filtering



# File 'app/models/partners/reservation_summary.rb', line 23

#reservation_idInteger

Returns Original reservation ID (primary reference).

Returns:

  • (Integer)

    Original reservation ID (primary reference)



# File 'app/models/partners/reservation_summary.rb', line 23

#reservation_statusString

Returns Current reservation status.

Returns:

  • (String)

    Current reservation status



# File 'app/models/partners/reservation_summary.rb', line 23

#restaurant_idInteger

Returns Restaurant ID for filtering and associations.

Returns:

  • (Integer)

    Restaurant ID for filtering and associations



# File 'app/models/partners/reservation_summary.rb', line 23

#service_typeString

Returns Service type ('dine_in', 'delivery', etc.).

Returns:

  • (String)

    Service type ('dine_in', 'delivery', etc.)



# File 'app/models/partners/reservation_summary.rb', line 23

#start_timeTime

Returns Reservation start time.

Returns:

  • (Time)

    Reservation start time



# File 'app/models/partners/reservation_summary.rb', line 23

#updated_atDateTime

Returns Record last update timestamp.

Returns:

  • (DateTime)

    Record last update timestamp



# File 'app/models/partners/reservation_summary.rb', line 23

#user_idInteger?

Returns User ID (optional for guest bookings).

Returns:

  • (Integer, nil)

    User ID (optional for guest bookings)



# File 'app/models/partners/reservation_summary.rb', line 23

Class Method Details

.adjusted?Boolean

Check if reservation was adjusted (modified)

Returns:

  • (Boolean)

    true if reservation was adjusted



175
176
177
# File 'app/models/partners/reservation_summary.rb', line 175

def adjusted?
  adjusted == true
end

.by_date(date) ⇒ ActiveRecord::Relation<Partners::ReservationSummary>

Filter summaries by specific date

Parameters:

  • date (Date)

    Date to filter by

Returns:



118
# File 'app/models/partners/reservation_summary.rb', line 118

scope :by_date, ->(date) { where(date: date) }

.by_package_type(type) ⇒ ActiveRecord::Relation<Partners::ReservationSummary>

Filter summaries by package type through associated packages

Parameters:

  • type (String)

    Package type to filter by

Returns:



147
148
149
# File 'app/models/partners/reservation_summary.rb', line 147

scope :by_package_type, ->(type) {
  joins(:summary_packages).where(partners_reservation_summary_packages: { package_type: type })
}

.by_party_size(party_size) ⇒ ActiveRecord::Relation<Partners::ReservationSummary>

Filter summaries by party size

Parameters:

  • party_size (Integer)

    Party size to filter by

Returns:



134
# File 'app/models/partners/reservation_summary.rb', line 134

scope :by_party_size, ->(party_size) { where(party_size: party_size) }

.by_restaurant(restaurant_id) ⇒ ActiveRecord::Relation<Partners::ReservationSummary>

Filter summaries by restaurant

Parameters:

  • restaurant_id (Integer)

    Restaurant ID to filter by

Returns:



111
# File 'app/models/partners/reservation_summary.rb', line 111

scope :by_restaurant, ->(restaurant_id) { where(restaurant_id: restaurant_id) }

.by_service_type(service_type) ⇒ ActiveRecord::Relation<Partners::ReservationSummary>

Filter summaries by service type

Parameters:

  • service_type (String)

    Service type to filter by

Returns:



127
# File 'app/models/partners/reservation_summary.rb', line 127

scope :by_service_type, ->(service_type) { where(service_type: service_type) }

.delivery?Boolean

Check if reservation is for delivery service

Returns:

  • (Boolean)

    true if service type is delivery



182
183
184
# File 'app/models/partners/reservation_summary.rb', line 182

def delivery?
  service_type == 'delivery'
end

.dine_in?Boolean

Check if reservation is for dine-in service

Returns:

  • (Boolean)

    true if service type is dine_in



189
190
191
# File 'app/models/partners/reservation_summary.rb', line 189

def dine_in?
  service_type == 'dine_in'
end

.future?Boolean

Helper method to check if reservation is in the future

Returns:

  • (Boolean)

    true if reservation is in the future



235
236
237
# File 'app/models/partners/reservation_summary.rb', line 235

def future?
  !past?
end

.past?Boolean

Time-dependent method that cannot be stored in database This replaces the need to store is_past_reservation field

Returns:

  • (Boolean)

    true if reservation is in the past



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'app/models/partners/reservation_summary.rb', line 197

def past?
  return false unless date && start_time

  begin
    restaurant_timezone = restaurant&.time_zone || 'Asia/Bangkok'
    reservation_datetime = Time.zone.parse("#{date} #{start_time}")

    reservation_datetime_in_tz = reservation_datetime.in_time_zone(restaurant_timezone)
    current_time_in_tz = Time.current.in_time_zone(restaurant_timezone)

    reservation_datetime_in_tz < current_time_in_tz
  rescue StandardError => e
    BUSINESS_LOGGER.warn('Error calculating is_past for reservation summary', {
                           reservation_id: reservation_id,
                           error_message: e.message,
                         })
    false
  end
end

.reservation_timeTime

Calculate reservation time using exact same logic as Reservation model

This method replicates the reservation_time logic from the original Reservation model to avoid N+1 queries while maintaining exact compatibility. Memoized for performance since date/time fields rarely change for existing reservations.

Returns:

  • (Time)

    the reservation datetime in restaurant timezone



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'app/models/partners/reservation_summary.rb', line 248

def reservation_time
  return @reservation_time if defined?(@reservation_time)

  time_zone = restaurant&.time_zone || 'Asia/Bangkok'
  @reservation_time = Time.use_zone time_zone do
    if start_time.blank?
      Time.zone.at(0)
    else
      # Use start_time_format equivalent (start_time is already stored as string)
      start_time_formatted = start_time&.strftime('%H:%M')
      Time.zone.parse(start_time_formatted, date)
    end
  end
rescue StandardError => e
  BUSINESS_LOGGER.warn('Error calculating reservation_time for reservation summary', {
                         reservation_id: reservation_id,
                         error_message: e.message,
                       })
  @reservation_time = Time.zone.at(0)
end

.today?Boolean

Helper method to check if reservation is today

Returns:

  • (Boolean)

    true if reservation is today



220
221
222
223
224
225
226
227
228
229
230
# File 'app/models/partners/reservation_summary.rb', line 220

def today?
  return false unless date

  begin
    restaurant_timezone = restaurant&.time_zone || 'Asia/Bangkok'
    current_date_in_tz = Time.current.in_time_zone(restaurant_timezone).to_date
    date == current_date_in_tz
  rescue StandardError
    false
  end
end