Module: ModelExt::Reservations::Callbacks
- Extended by:
- ActiveSupport::Concern
- Includes:
- ElasticAPM::SpanHelpers
- Included in:
- Reservation
- Defined in:
- lib/model_ext/reservations/callbacks.rb
Overview
callbacks stuff
Instance Method Summary collapse
- #push_booking_notification ⇒ Object
- #push_inventory_notification ⇒ Object
-
#trigger_creation_sync(sync_type = 'full') ⇒ void
Manual sync trigger for reservation creation (immediate for admin/staff, delayed for users).
-
#trigger_destroy_sync ⇒ void
Manual sync trigger for destruction (async).
-
#trigger_immediate_sync(sync_type = 'full') ⇒ void
Manual sync trigger for immediate updates.
-
#trigger_priority_sync(sync_type = 'full') ⇒ void
Manual sync trigger for priority updates (non-blocking but fast).
-
#trigger_summary_sync(sync_type = 'full') ⇒ void
Manual sync trigger for general updates (async).
- #update_inv_relations ⇒ Object
Instance Method Details
#push_booking_notification ⇒ Object
206 207 208 209 210 211 212 213 |
# File 'lib/model_ext/reservations/callbacks.rb', line 206 def push_booking_notification is_pending_confirmation = ack == false && confirmed_by.nil? && active == true not_temporary = is_temporary == false && for_locking_system == false return unless is_pending_confirmation && not_temporary NotificationWorkers::Partner::BookingWorker.perform_async(id) true end |
#push_inventory_notification ⇒ Object
215 216 217 218 |
# File 'lib/model_ext/reservations/callbacks.rb', line 215 def push_inventory_notification NotificationWorkers::Partner::InventoryWorker.perform_async(id) true end |
#trigger_creation_sync(sync_type = 'full') ⇒ void
This method returns an undefined value.
Manual sync trigger for reservation creation (immediate for admin/staff, delayed for users)
Call this method explicitly after reservation creation to ensure proper timing and avoid missed relations during the creation process.
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/model_ext/reservations/callbacks.rb', line 253 def trigger_creation_sync(sync_type = 'full') if created_by_admin_staff_or_owner? # Admin/Staff/Owner: Immediate sync for real-time partner portal updates begin Partner::ReservationSummarySyncWorker.new.perform(id, sync_type) rescue StandardError => e APMErrorHandler.report('Immediate creation sync failed', { reservation_id: id, sync_type: sync_type, created_by: created_by, error_message: e., exception: e, }) # Fallback to async Partner::ReservationSummarySyncWorker.perform_async(id, sync_type) end else # User bookings: Use delayed sync with protection against for_locking_system unless for_locking_system? Partner::ReservationSummarySyncWorker.perform_in(1.second, id, sync_type) end end rescue StandardError => e APMErrorHandler.report('Failed to trigger creation sync', { reservation_id: id, sync_type: sync_type, created_by: created_by, error_message: e., exception: e, }) end |
#trigger_destroy_sync ⇒ void
This method returns an undefined value.
Manual sync trigger for destruction (async)
Call this method when a reservation is being destroyed.
338 339 340 341 342 343 344 345 346 |
# File 'lib/model_ext/reservations/callbacks.rb', line 338 def trigger_destroy_sync Partner::ReservationSummarySyncWorker.perform_async(id, 'destroy') rescue StandardError => e APMErrorHandler.report('Failed to trigger destroy sync', { reservation_id: id, error_message: e., exception: e, }) end |
#trigger_immediate_sync(sync_type = 'full') ⇒ void
This method returns an undefined value.
Manual sync trigger for immediate updates
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/model_ext/reservations/callbacks.rb', line 309 def trigger_immediate_sync(sync_type = 'full') begin Partner::ReservationSummarySyncWorker.new.perform(id, sync_type) rescue StandardError => e APMErrorHandler.report('Immediate sync failed', { reservation_id: id, sync_type: sync_type, created_by: created_by, error_message: e., exception: e, }) # Fallback to async Partner::ReservationSummarySyncWorker.perform_async(id, sync_type) end rescue StandardError => e APMErrorHandler.report('Failed to trigger immediate sync', { reservation_id: id, sync_type: sync_type, created_by: created_by, error_message: e., exception: e, }) end |
#trigger_priority_sync(sync_type = 'full') ⇒ void
This method returns an undefined value.
Manual sync trigger for priority updates (non-blocking but fast)
Use this for critical updates that need fast processing but should not block the main application flow. Executes with minimal delay (0.1 seconds).
292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/model_ext/reservations/callbacks.rb', line 292 def trigger_priority_sync(sync_type = 'full') # Non-blocking but high priority sync with minimal delay Partner::ReservationSummarySyncWorker.perform_in(1.second, id, sync_type) rescue StandardError => e APMErrorHandler.report('Failed to trigger priority sync', { reservation_id: id, sync_type: sync_type, created_by: created_by, error_message: e., exception: e, }) end |
#trigger_summary_sync(sync_type = 'full') ⇒ void
This method returns an undefined value.
Manual sync trigger for general updates (async)
Use this for non-critical updates like package/add-on changes, general status updates, or when immediate sync is not required.
235 236 237 238 239 240 241 242 243 244 |
# File 'lib/model_ext/reservations/callbacks.rb', line 235 def trigger_summary_sync(sync_type = 'full') Partner::ReservationSummarySyncWorker.perform_async(id, sync_type) rescue StandardError => e APMErrorHandler.report('Failed to trigger summary sync', { reservation_id: id, sync_type: sync_type, error_message: e., exception: e, }) end |
#update_inv_relations ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/model_ext/reservations/callbacks.rb', line 52 def update_inv_relations # Guard against invalid states unless persisted? && !destroyed? APMErrorHandler.report("update_inv_relations called on invalid reservation state", context: { reservation_id: id, persisted: persisted?, destroyed: destroyed? }) return end # Ensure the reservation still exists in the database to avoid foreign key constraint violations unless self.class.exists?(id) APMErrorHandler.report("update_inv_relations called on non-existent reservation", context: { reservation_id: id }) return end changed_attribute_keys = transaction_changed_attributes.keys # Early return if no relevant attributes changed relevant_changed_attrs = %w[active ack adult date start_time end_time for_locking_system is_temporary restaurant_id service_type].select do |key| changed_attribute_keys.include?(key) end if relevant_changed_attrs.blank? return end affect_old_data = %w[service_type date start_time end_time].select do |key| changed_attribute_keys.include?(key) end.present? inv_reservation_class = nil inv_class = nil if affect_old_data old_service_type = service_type if changed_attribute_keys.include?('service_type') old_service_type = transaction_changed_attributes['service_type'] inv_reservation_class = inventory_reservation_klass(old_service_type) inv_class = inventory_klass(old_service_type) else inv_reservation_class = inventory_reservation_klass # Use inventory_klass for consistency, but fall back to restaurant.selected_inventory_model # since the current service_type might determine a different inventory class inv_class = inventory_klass || restaurant.selected_inventory_model end # Query inventory reservations for the current reservation inventory_reservations = inv_reservation_class.where(restaurant_id: restaurant_id, reservation_id: id) # collect inventory IDs from the reservations inventory_ids = inventory_reservations.distinct.pluck(inv_reservation_class.inventory_id_column) # Delete all inventory reservations for this reservation as they will be recreated with updated data inventory_reservations.delete_all # Sort inventory IDs to ensure consistent lock ordering sorted_inventory_ids = inventory_ids.sort # No transaction needed here since this is an after_commit callback inv_class.where(id: sorted_inventory_ids).order(:id).each do |inv| inv.with_lock do # recalculate total booked seats for the inventory # it is needed to restore the seat left quota after this reservation has been updated # or created inv.calc_total_booked_seat inv.save! end end else inv_reservation_class = inventory_reservation_klass # Use inventory_klass for consistency, but fall back to restaurant.selected_inventory_model # since the current service_type might determine a different inventory class inv_class = inventory_klass || restaurant.selected_inventory_model end invs = inv_class.where(date: date, restaurant_id: restaurant_id). where('start_time >= ? AND start_time <= ? AND end_time >= ? AND end_time <= ?', start_time_format, end_time_format, start_time_format, end_time_format) inventory_reservations = [] inventory_relation = if restaurant.use_third_party_inventory? 'inventory_id' else "#{inv_class.to_s.underscore}_id" end booked_seat = if active? if ack? || (is_temporary? || for_locking_system?) adult else 0 end else 0 end ir_attributes_base = { reservation_id: id, booked_seat: booked_seat, restaurant_id: restaurant.id, } inv_class_as_string = inv_class.to_s invs.each do |inv| ir_attributes = ir_attributes_base.dup if service_type == 'dine_in' ir_attributes[inventory_relation] = inv.id[0] ir_attributes[:inventory_type] = inv_class_as_string else ir_attributes[inventory_relation] = inv.id end inventory_reservations.push ir_attributes end if inventory_reservations.present? Retriable.retriable( on: ActiveRecord::Deadlocked, tries: 3, base_interval: 0.1, multiplier: 2, rand_factor: 0.5, on_retry: APMErrorHandler.report_retriable_event("reservation.inventory.update") ) do inv_reservation_class.import!(inventory_reservations, on_duplicate_key_update: [:booked_seat], raise_error: true, validate: false, batch_size: 1000) end end # Sort inventories by ID to ensure consistent lock ordering across transactions sorted_invs = invs.sort_by(&:id) Retriable.retriable( on: ActiveRecord::Deadlocked, tries: 3, base_interval: 0.1, multiplier: 2, rand_factor: 0.5, on_retry: APMErrorHandler.report_retriable_event("reservation.inventory.update") ) do sorted_invs.each do |inv| inv.with_lock do # recalculate total booked seats for the inventory # it is needed to consume the seat left quota after this reservation has been updated # or created inv.calc_total_booked_seat inv.save! end end end end |