Class: VendorsService::SupplierWebhookService

Inherits:
BaseOperationService show all
Includes:
DefaultErrorContainer, ElasticAPM::SpanHelpers
Defined in:
app/services/vendors_service/supplier_webhook_service.rb

Overview

SupplierWebhookService processes vendor webhook data for reservations. Fetches vendor-specific reservation data internally based on the supplier argument.

Business Rules:

  • Blocks webhook processing if restaurant is not integrated with the supplier (uses HungryHub inventory)

  • Blocks webhook processing if reservation dining time has passed by more than 24 hours

  • Raises ArgumentError if reservation_time is blank (data integrity issue)

  • Raises StandardError if supplier reservation is not found (integration issue)

Instance Attribute Summary collapse

Attributes inherited from BaseOperationService

#outcome

Instance Method Summary collapse

Methods included from DefaultErrorContainer

#error, #error_message_simple, #merge_errors

Methods inherited from BaseOperationService

#success?

Constructor Details

#initialize(reservation_id:, supplier:, supplier_payload: nil, **_unused_args) ⇒ SupplierWebhookService

Returns a new instance of SupplierWebhookService.



20
21
22
23
24
# File 'app/services/vendors_service/supplier_webhook_service.rb', line 20

def initialize(reservation_id:, supplier:, supplier_payload: nil, **_unused_args)
  @reservation_id = reservation_id
  @supplier = supplier.to_sym
  @supplier_payload = supplier_payload
end

Instance Attribute Details

#reservationObject (readonly)

Returns the value of attribute reservation.



18
19
20
# File 'app/services/vendors_service/supplier_webhook_service.rb', line 18

def reservation
  @reservation
end

#reservation_idObject (readonly)

Returns the value of attribute reservation_id.



18
19
20
# File 'app/services/vendors_service/supplier_webhook_service.rb', line 18

def reservation_id
  @reservation_id
end

#reservation_paramsObject (readonly)

Returns the value of attribute reservation_params.



18
19
20
# File 'app/services/vendors_service/supplier_webhook_service.rb', line 18

def reservation_params
  @reservation_params
end

#supplierObject (readonly)

Returns the value of attribute supplier.



18
19
20
# File 'app/services/vendors_service/supplier_webhook_service.rb', line 18

def supplier
  @supplier
end

#supplier_payloadObject (readonly)

Returns the value of attribute supplier_payload.



18
19
20
# File 'app/services/vendors_service/supplier_webhook_service.rb', line 18

def supplier_payload
  @supplier_payload
end

#supplier_reservationObject (readonly)

Returns the value of attribute supplier_reservation.



18
19
20
# File 'app/services/vendors_service/supplier_webhook_service.rb', line 18

def supplier_reservation
  @supplier_reservation
end

Instance Method Details

#executeServiceResult

Executes the webhook sync for the supplier reservation.

Flow:

  1. Sets up logging and context (BUSINESS_LOGGER, RequestStore)

  2. Validates restaurant is integrated with the supplier (skips if not)

  3. Validates dining time is within 24 hours (blocks if past 24 hours)

  4. Fetches supplier reservation data (raises if not found)

  5. Processes webhook based on supplier and event/status type

For SevenRooms, handles event_type logic (created, updated, deleted) as per SevenRooms::WebhookService. For other suppliers, uses status-based logic.

All error handling is reported to APM and business logs are used for traceability.

Returns:

  • (ServiceResult)

    Success result with nil data, or error result with errors

Raises:

  • (ArgumentError)

    if reservation_time is blank

  • (StandardError)

    if supplier reservation not found

  • (NotImplementedError)

    if unknown event_type or status encountered



44
45
46
47
48
49
50
51
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
# File 'app/services/vendors_service/supplier_webhook_service.rb', line 44

def execute
  setup_logging_and_context

  @reservation = Reservation.includes(:vendor_reservation).find_by(id: reservation_id)
  raise ActiveRecord::RecordNotFound, "Reservation #{reservation_id} not found" if @reservation.nil?

  @supplier_reservation = fetch_supplier_reservation(supplier, reservation_id)

  if supplier_reservation.blank?
    error_message = 'Supplier reservation not found'
    raise StandardError, error_message
  end

  @reservation_params = build_reservation_params

  # Check if restaurant is integrated with the supplier
  unless reservation.use_third_party_reservation?
    error_message = "Restaurant is not integrated with #{supplier}, skipping webhook processing"
    raise StandardError, error_message
  end

  # Check if reservation_time is blank
  if reservation.reservation_time.blank?
    error_message = "Reservation #{reservation.id} has blank reservation_time"
    raise ArgumentError, error_message
  end

  return if should_skip_processing_for_no_changes?

  # Check if dining time has passed by more than 24 hours
  if reservation.reservation_time_24h_passed?
    error_message = 'Reservation dining time has passed by more than 24 hours, skipping webhook processing'
    raise StandardError, error_message
  end

  BUSINESS_LOGGER.info("#{logger_prefix}: Executing sync")

  if supplier == ApiVendorV1::Constants::SEVEN_ROOMS_INV_SOURCE_NAME.to_sym

    case event_type
    when 'deleted'
      BUSINESS_LOGGER.info('SevenRooms: deleted event received, processing cancel_reservation')
      cancel_reservation if reservation.active?
    when 'updated'
      BUSINESS_LOGGER.info('SevenRooms: updated event received, processing update_reservation')
      update_seven_rooms_reservation(status)
    when 'created'
      BUSINESS_LOGGER.info('SevenRooms: created event received, skipping this event')
      # No need to handle this event type since we are not creating reservations from SevenRooms
      ServiceResult.new data: nil
    else
      error_message = "Unknown event_type #{event_type} for SevenRooms"
      raise NotImplementedError, error_message
    end
  else
    case supplier_reservation['status']
    when *cancel_statuses
      cancel_reservation if reservation.active?
    when *arrived_status
      mark_reservation_arrived
    when no_show_status
      mark_reservation_as_no_show
    when *update_statuses
      update_reservation_date_time_pax_or_status
    else
      error_message = "Unknown status #{supplier_reservation['status']} for #{supplier}"
      raise NotImplementedError, error_message
    end
  end

  true
rescue StandardError, ActiveRecord::RecordNotFound => e
  handle_logs_and_errors(e.message)
end