Class: Channel

Inherits:
ApplicationRecord show all
Includes:
IdentityCache
Defined in:
app/models/channel.rb

Constant Summary collapse

DEFAULT =
'default'
AUTO_ACK =
'auto_ack'
CHARGE_CC_DIRECTLY =
'charge_cc_directly'
HOLD_CC =
'credit_card'
EXTERNAL_BOOKING =
'external_booking'
MANUAL =
'manual'
ACCEPT_BIG_GROUP =
'big_group'
SHOW_IN_OWNER_DASHBOARD =
'show_in_owner_dashboard'
PHONE_TYPE =
%w[restaurant custom default].freeze
VENDOR_CHANNEL_URI_LIST =
ApiVendorV1::Constants.constants.select do |constant|
  constant.to_s.include?('_CHANNEL_URI')
end.map do |constant|
  ApiVendorV1::Constants.const_get(constant)
end.freeze
VENDOR_NAME_LIST =
ApiVendorV1::Constants.constants.select do |constant|
  constant.to_s.include?('_VENDOR_NAME')
end.map do |constant|
  ApiVendorV1::Constants.const_get(constant)
end.freeze
VENDOR_NAME_LIST_COMPLETE_INTEGRATIONS =

Always update this constant when new vendor has been integrated with HungryHub with end to end integration

[
  ApiVendorV1::Constants::OPEN_RICE_VENDOR_NAME,
  ApiVendorV1::Constants::TAG_THAI_VENDOR_NAME,
  ApiVendorV1::Constants::GOOGLE_RESERVE_VENDOR_NAME,
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

sync_carrierwave_url

Class Method Details

.[](channel_id) ⇒ Object



177
178
179
# File 'app/models/channel.rb', line 177

def self.[](channel_id)
  fetch_by_channel_id(channel_id)
end

.accept_big_groupObject



164
165
166
# File 'app/models/channel.rb', line 164

def self.accept_big_group
  Channel.tagged_with(ACCEPT_BIG_GROUP).pluck(:channel_id)
end

.adjust_package_qty_for_vendor?(vendor_name) ⇒ Boolean

Returns:

  • (Boolean)


419
420
421
422
423
424
425
426
427
# File 'app/models/channel.rb', line 419

def self.adjust_package_qty_for_vendor?(vendor_name)
  # need to adjust package qty if vendor requires package qty to be adjusted as per pax
  [
    ApiVendorV1::Constants::GLOBALTIX_VENDOR_NAME,
    ApiVendorV1::Constants::ROYAL_ORCHID_PLUS_VENDOR_NAME,
    ApiVendorV1::Constants::SPARK_LOVE_VENDOR_NAME,
    ApiVendorV1::Constants::GOOGLE_RESERVE_VENDOR_NAME,
  ].include?(vendor_name)
end

.adjust_pax_to_max_per_pack_qty?(vendor_name) ⇒ Boolean

Returns:

  • (Boolean)


429
430
431
432
433
434
435
436
437
438
439
# File 'app/models/channel.rb', line 429

def self.adjust_pax_to_max_per_pack_qty?(vendor_name)
  # need to adjust pax qty if vendor do not send correct adult/kids,
  # so we need to block restaurant inventory as per max per_pack qty for package
  [
    ApiVendorV1::Constants::GET_YOUR_GUIDE_VENDOR_NAME,
    ApiVendorV1::Constants::ROYAL_ORCHID_PLUS_VENDOR_NAME,
    ApiVendorV1::Constants::KLOOK_VENDOR_NAME,
    ApiVendorV1::Constants::KKDAY_VENDOR_NAME,
    ApiVendorV1::Constants::DIANPING_VENDOR_NAME,
  ].include?(vendor_name)
end

Used by external web booking Currently it is for footer banner only



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'app/models/channel.rb', line 232

def self.assign_footer_ad(ad_channel_params)
  ad_channel_params[:footer_banner].each do |ad|
    ad = ad.last
    channel = Channel.find_by channel_id: ad[:channel_id]
    channel_ad = ChannelAd.find_by id: ad[:ad_id]
    if channel_ad.nil?
      manager = ChannelAdManager.find_by ad_type: :footer_banner, channel_id: channel.id
      next if manager.nil?
      return false unless manager.destroy
    else
      manager = ChannelAdManager.create ad_type: :footer_banner, channel_id: channel.id, channel_ad_id: channel_ad.id
      unless manager.save
        dup_manager = ChannelAdManager.where ad_type: :footer_banner, channel_id: channel.id
        dup_manager.each(&:destroy)
        return false unless manager.save!
      end
    end
  end
  true
rescue ActiveRecord::ActiveRecordError => e
  APMErrorHandler.report e
  false
end

.auto_ackObject



141
142
143
# File 'app/models/channel.rb', line 141

def self.auto_ack
  Channel.tagged_with(AUTO_ACK).pluck(:channel_id)
end

.charge_credit_card_directlyObject



124
125
126
# File 'app/models/channel.rb', line 124

def self.charge_credit_card_directly
  Channel.tagged_with(CHARGE_CC_DIRECTLY).pluck(:channel_id)
end

.chatbotObject



271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'app/models/channel.rb', line 271

def self.chatbot
  Rails.cache.fetch 'Channel:chatbot:instance', expires_in: CACHEFLOW.generate_expiry do
    instance = Channel.find_or_initialize_by name: 'Chatbot',
                                             group_name: 'hungry_hub',
                                             uri_name: 'chatbot'
    if instance.new_record?
      instance.tag_list.add('auto_ack')
      instance.channel_id = 101
      instance.save!
    end
    instance
  end
end

.credit_cardObject



120
121
122
# File 'app/models/channel.rb', line 120

def self.credit_card
  Channel.tagged_with(HOLD_CC).pluck(:channel_id)
end

.default_channelChannel

Returns channel instance.

Returns:



146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'app/models/channel.rb', line 146

def self.default_channel
  default = Channel.tagged_with(Channel::DEFAULT)
  if default.present?
    default.first
  else
    channel = Channel.find_or_initialize_by(name: 'Hungry Hub', group_name: 'hungry_hub', channel_id: 0)
    return channel if channel.persisted?

    channel.tag_list.add(DEFAULT)
    channel.save!
    channel
  end
end

.default_vendor_email_if_skippable?(vendor_name) ⇒ Boolean

Returns:

  • (Boolean)


370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'app/models/channel.rb', line 370

def self.default_vendor_email_if_skippable?(vendor_name)
  return '' if vendor_name.blank?

  # Add new vendor names here if there is a new vendor that skips the email for CREATE reservation
  vendors_skipping_email = [
    ApiVendorV1::Constants::OPEN_RICE_VENDOR_NAME,
  ]

  return '' unless vendors_skipping_email.include?(vendor_name)

  # Use each vendor support email so when restaurant reply back, it goes to them and they know which one to handle
  if vendor_name == ApiVendorV1::Constants::OPEN_RICE_VENDOR_NAME
    return ApiVendorV1::Constants::OPEN_RICE_SUPPORT_EMAIL
  end

  "#{vendor_name.strip.downcase.gsub(' ', '')}-booking-#{Time.thai_time.to_i}@hungryhub.com"
end

.default_vendor_phone_if_skippable?(vendor_name) ⇒ Boolean

Returns:

  • (Boolean)


395
396
397
398
# File 'app/models/channel.rb', line 395

def self.default_vendor_phone_if_skippable?(vendor_name)
  # Vendors that are allowed to skip sending phone number in reservation params
  VENDOR_NAME_LIST.include?(vendor_name) ? AdminSetting.support_phone : ''
end

.external_bookingObject



160
161
162
# File 'app/models/channel.rb', line 160

def self.external_booking
  Channel.tagged_with(EXTERNAL_BOOKING).pluck(:channel_id)
end

.external_booking_with_nameObject



168
169
170
171
# File 'app/models/channel.rb', line 168

def self.external_booking_with_name
  names = Channel.tagged_with(EXTERNAL_BOOKING).pluck(:uri_name)
  HashWithIndifferentAccess.new(names.zip(external_booking).to_h)
end

.grouped_channelObject



181
182
183
# File 'app/models/channel.rb', line 181

def self.grouped_channel
  all.group_by(&:group_name)
end

.hh_android_appObject



298
299
300
301
302
303
304
305
306
307
308
309
# File 'app/models/channel.rb', line 298

def self.hh_android_app
  Rails.cache.fetch 'Channel:hh_android_app:instance', expires_in: CACHEFLOW.generate_expiry do
    instance = Channel.find_or_initialize_by name: 'HH Android app',
                                             group_name: 'hungry_hub'
    if instance.new_record?
      instance.tag_list.add('auto_ack')
      instance.channel_id = 301
      instance.save!
    end
    instance
  end
end

.hh_ios_appObject



285
286
287
288
289
290
291
292
293
294
295
296
# File 'app/models/channel.rb', line 285

def self.hh_ios_app
  Rails.cache.fetch 'Channel:hh_ios_app:instance', expires_in: CACHEFLOW.generate_expiry do
    instance = Channel.find_or_initialize_by name: 'HH iOS app',
                                             group_name: 'hungry_hub'
    if instance.new_record?
      instance.tag_list.add('auto_ack')
      instance.channel_id = 302
      instance.save!
    end
    instance
  end
end

.idsObject



173
174
175
# File 'app/models/channel.rb', line 173

def self.ids
  pluck(:channel_id)
end

.manualArray

Returns:

  • (Array)


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'app/models/channel.rb', line 95

def self.manual
  manual_channel = Rails.cache.fetch(MANUAL_CHANNEL_CACHE_KEY, expires_in: CACHEFLOW.generate_expiry) do
    Channel.tagged_with('manual').pluck(:channel_id)
  end
  return manual_channel if manual_channel.present?

  manual_channel = Channel.find_by(name: 'Manual', channel_id: 5, group_name: 'manual')
  return [manual_channel.channel_id] if manual_channel.present?

  if manual_channel.blank?
    manual_channel = Channel.new(name: 'Manual', channel_id: 5, group_name: 'manual', phone_type: 'default')
    manual_channel.tag_list.add('manual')
    manual_channel.save!
    return [manual_channel.channel_id]
  end
  raise 'Manual channel is empty'
end

.manual_packageObject

reservation created by owner dashboard uses this channel



257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'app/models/channel.rb', line 257

def self.manual_package
  Rails.cache.fetch 'Channel:manual_package:instance', expires_in: CACHEFLOW.generate_expiry do
    instance = Channel.find_or_initialize_by name: 'Manual package by Owner',
                                             group_name: 'hungry_hub',
                                             uri_name: 'ownerdashboard'
    if instance.new_record?
      instance.tag_list.add('auto_ack')
      instance.channel_id = 100
      instance.save!
    end
    instance
  end
end

.manual_record_idObject



193
194
195
# File 'app/models/channel.rb', line 193

def self.manual_record_id
  Channel.tagged_with(MANUAL).pluck(:id).join(', ')
end

.manual_to_sqlObject



137
138
139
# File 'app/models/channel.rb', line 137

def self.manual_to_sql
  '(' + manual.join(', ') + ')'
end

.names_to_reservation_report_by_channelObject



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'app/models/channel.rb', line 213

def self.names_to_reservation_report_by_channel
  sql = ''
  grouped_channel.each do |group|
    group[1].each_with_index do |channel, index|
      sql += "SUM(channel = #{channel.channel_id}) AS #{channel.group_name}_#{index}, "
    end
  end

  sql += 'SUM('
  ids.each do |id|
    sql += "(channel = #{id})"
    sql += ' + ' unless id == ids.last
  end
  sql += ') AS grand_total'
  sql
end

.names_to_reservation_report_by_restaurantObject



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'app/models/channel.rb', line 197

def self.names_to_reservation_report_by_restaurant
  sql = ''
  grouped_channel.each do |group|
    group[1].each_with_index do |channel, index|
      sql += "SUM(#{Reservation.table_name}.channel = #{channel.channel_id}) AS #{channel.group_name}_#{index}, "
    end
  end
  sql += 'SUM('
  ids.each do |id|
    sql += "(#{Reservation.table_name}.channel = #{id})"
    sql += ' + ' unless id == ids.last
  end
  sql += ') AS grand_total'
  sql
end

.names_to_xls_reportObject



185
186
187
188
189
190
191
# File 'app/models/channel.rb', line 185

def self.names_to_xls_report
  sql = ''
  all.find_each do |channel|
    sql += " when #{channel.channel_id} THEN '#{channel.name}' "
  end
  sql
end

.require_cc?(channel_id) ⇒ Boolean

Returns:

  • (Boolean)


128
129
130
131
132
133
134
135
# File 'app/models/channel.rb', line 128

def self.require_cc?(channel_id)
  channel = Channel.fetch_by_channel_id(channel_id)
  [HOLD_CC, CHARGE_CC_DIRECTLY].each do |cc_tag|
    return false unless channel
    return true if channel.cached_tag_list.include? cc_tag
  end
  false
end

Instance Method Details

#aoa?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'app/models/channel.rb', line 68

def aoa?
  uri_name == 'aoa'
end

#cached_tag_listObject



76
77
78
79
80
# File 'app/models/channel.rb', line 76

def cached_tag_list
  @cached_tag_list ||= Rails.cache.fetch cache_key, expires_in: CACHEFLOW.generate_expiry do
    ActsAsTaggableOn::GenericParser.new(self[:cached_tag_list].to_s).parse
  end
end

#cc_charge_typeObject



113
114
115
116
117
118
# File 'app/models/channel.rb', line 113

def cc_charge_type
  return HOLD_CC if cached_tag_list.include? HOLD_CC
  return CHARGE_CC_DIRECTLY if cached_tag_list.include? CHARGE_CC_DIRECTLY

  nil
end

Returns:

  • (Boolean)


388
389
390
391
392
393
# File 'app/models/channel.rb', line 388

def hide_edit_booking_link_for_vendor_customer_email?
  # use only if return false from skip_send_notifications? for vendor

  # we remove modify button for google reserve postpaid bookings only to avoid kids mismatch qty
  [ApiVendorV1::Constants::GOOGLE_RESERVE_CHANNEL_URI].include?(uri_name)
end

#payment_handled_by_vendor?(vendor_payment: nil, vendor_name: nil) ⇒ Boolean

Returns:

  • (Boolean)


343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'app/models/channel.rb', line 343

def payment_handled_by_vendor?(vendor_payment: nil, vendor_name: nil)
  # Add new vendor names in the list if there is a new vendor that handles the payment from the vendor side.
  # We only allow 100% prepaid bookings for these vendors
  # however some vendors like Openrice also send pay-at-restaurant reservations for postpaid packages
  vendor_name = vendor_payment&.oauth_application&.name if vendor_payment.present?
  return true if [ApiVendorV1::Constants::OPEN_RICE_VENDOR_NAME,
                  ApiVendorV1::Constants::GLOBALTIX_VENDOR_NAME,
                  ApiVendorV1::Constants::KKDAY_VENDOR_NAME,
                  ApiVendorV1::Constants::GET_YOUR_GUIDE_VENDOR_NAME,
                  ApiVendorV1::Constants::ROYAL_ORCHID_PLUS_VENDOR_NAME,
                  ApiVendorV1::Constants::SPARK_LOVE_VENDOR_NAME,
                  ApiVendorV1::Constants::LINKTIVITY_VENDOR_NAME,
                  ApiVendorV1::Constants::DIANPING_VENDOR_NAME,
                  ApiVendorV1::Constants::KLOOK_VENDOR_NAME,
                  ApiVendorV1::Constants::ONE_WALLET_VENDOR_NAME,
                  ApiVendorV1::Constants::TRIP_VENDOR_NAME,
                  ApiVendorV1::Constants::POINTX_VENDOR_NAME].include?(vendor_name)

  false
end

#show_in_owner_dashboard?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'app/models/channel.rb', line 72

def show_in_owner_dashboard?
  cached_tag_list.include? SHOW_IN_OWNER_DASHBOARD
end

#skip_give_rewards?Boolean

Returns:

  • (Boolean)


311
312
313
# File 'app/models/channel.rb', line 311

def skip_give_rewards?
  VENDOR_CHANNEL_URI_LIST.include?(uri_name)
end

#skip_run_tier_qualification?Boolean

Returns:

  • (Boolean)


339
340
341
# File 'app/models/channel.rb', line 339

def skip_run_tier_qualification?
  VENDOR_CHANNEL_URI_LIST.include?(uri_name)
end

#skip_send_notifications?(actor:) ⇒ Boolean

Skip send notifications to spesific actor (user, admin, or owner) also responsible to skip review requests Based on channel uri_name

Parameters:

  • actor (String)

    actor type

Returns:

  • (Boolean)

    Returns true if notifications is skipped; false otherwise.



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'app/models/channel.rb', line 321

def skip_send_notifications?(actor:)
  return false if actor.blank?

  case actor.to_s
  when 'user'
    return false if [ApiVendorV1::Constants::GOOGLE_RESERVE_CHANNEL_URI].include?(uri_name)

    # even if admin is manually creating reservation for any vendor, we skip emails or else user can cancel paid bookings
    VENDOR_CHANNEL_URI_LIST.include?(uri_name)
  when 'owner'
    false
  when 'admin'
    false
  else
    raise NotImplementedError, "Unsupported actor type: #{actor}"
  end
end

#skip_vendor_phone_validation?Boolean

Returns:

  • (Boolean)


400
401
402
403
# File 'app/models/channel.rb', line 400

def skip_vendor_phone_validation?
  # Vendors for which we don't validate phone numbers provided in reservation params
  group_name == 'vendor'
end

#using_custom_phone?Boolean

Returns:

  • (Boolean)


86
87
88
# File 'app/models/channel.rb', line 86

def using_custom_phone?
  phone_type == PHONE_TYPE[1].to_s
end

#using_default_phone?Boolean

Returns:

  • (Boolean)


90
91
92
# File 'app/models/channel.rb', line 90

def using_default_phone?
  phone_type == PHONE_TYPE[2].to_s
end

#using_restaurant_phone?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'app/models/channel.rb', line 82

def using_restaurant_phone?
  phone_type == PHONE_TYPE[0].to_s
end

#vendor_reservations_cancellation_allowed?(vendor_name) ⇒ Boolean

Returns:

  • (Boolean)


405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'app/models/channel.rb', line 405

def vendor_reservations_cancellation_allowed?(vendor_name)
  # Currently for all bookings that are prepaid, HungryHub doesn't allow cancellation.
  # Customers have to contact HungryHub Customer Service for cancellation
  # and the only case we accept cancellation is if there is severe sickness
  # or any special case that the Merchant/Restaurant has to approve first

  vendors_allowed_cancellation = [
    # GYG Team will restrict reservation cancellations from their Backend for restaurants that don't allow cancellations of prepaid reservations.
    # ApiVendorV1::Constants::GET_YOUR_GUIDE_VENDOR_NAME,
    ApiVendorV1::Constants::DIANPING_VENDOR_NAME,
  ]
  vendors_allowed_cancellation.include?(vendor_name)
end

#vendor_skips_payment_check_for_multiple_bookings?(vendor_name) ⇒ Boolean

Returns:

  • (Boolean)


364
365
366
367
368
# File 'app/models/channel.rb', line 364

def vendor_skips_payment_check_for_multiple_bookings?(vendor_name)
  return true if [ApiVendorV1::Constants::GOOGLE_RESERVE_VENDOR_NAME].include?(vendor_name)

  false
end