Module: ModelExt::Reservations::CreditCard

Included in:
Reservation
Defined in:
lib/model_ext/reservations/credit_card.rb

Instance Method Summary collapse

Instance Method Details

#change_payment_type_provider!(new_payment_type_provider) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
# File 'lib/model_ext/reservations/credit_card.rb', line 89

def change_payment_type_provider!(new_payment_type_provider)
  return if payment_type_provider == new_payment_type_provider

  if payment_type_provider == 'promptpay' && new_payment_type_provider == 'credit_card'
    update! cc_provider: promptpay_provider, promptpay_provider: nil
  elsif payment_type_provider == 'credit_card' && new_payment_type_provider == 'promptpay'
    update! promptpay_provider: cc_provider, cc_provider: nil
  else
    raise NotImplementedError
  end
end

#change_promptpay_provider(provider:, merchant: nil) ⇒ Object

Can be one of the following Reservation.promptpay_provider.values:

  • omise

  • gb_primepay

Parameters:

Raises:

  • (ArgumentError)


13
14
15
16
17
18
19
# File 'lib/model_ext/reservations/credit_card.rb', line 13

def change_promptpay_provider(provider:, merchant: nil)
  raise ArgumentError, 'Invalid provider' unless Reservation.promptpay_provider.values.include?(provider)

  self.promptpay_provider = provider
  self.gb_merchant = merchant if merchant.present?
  save!
end

#charge_amountObject



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/model_ext/reservations/credit_card.rb', line 257

def charge_amount
  charge_amount = if package.present?
                    if require_prepayment?
                      total_charge_price = charge_price
                      total_charge_price += delivery_fee.to_d * 100 if service_type == :delivery
                      total_charge_price
                    else
                      0
                    end
                  elsif using_on_hold_channel?
                    on_hold_formula
                  elsif using_charge_channel? # charge immediately
                    immediate_charge_formula
                  else
                    0
                  end

  charge_amount.to_d
end

#charged_amount(perspective: nil) ⇒ Object

Returns:

  • nil if there is no charge

  • (Integer)

    charge amount in cents



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/model_ext/reservations/credit_card.rb', line 279

def charged_amount(perspective: nil)
  # make sure to return nil to prevent UI bug if the charge is 0

  return nil if perspective == :user && corporate_order?

  total_amount = 0

  # REMOVED: Double counting issue
  # The following code was causing charges to be counted twice:
  # if customer.present?
  #   amount = customer.charged_amount(self)
  #   return nil if amount.to_i.zero?
  #   total_amount += amount
  # end
  #
  # Reason for removal:
  # - customer.charged_amount(self) sums charges.where(reservation: self).map(&:amount)
  # - charges.success_scope.map(&:amount).sum below does the same calculation
  # - This resulted in doubled amounts being displayed in V2 booking list
  # - The direct charges relationship is sufficient and more accurate

  if charges.present?
    total_amount += charges.success_scope.map(&:amount).compact.sum
  end

  if property&.is_prepayment && property&.prepayment_cents.present?
    total_amount += property.prepayment_cents
  end

  return nil if total_amount.zero?

  total_amount
end

#charged_amount_in_baht(perspective: nil) ⇒ Object Also known as: format_charged_amount

TODO: rename to be format_charged_amount



324
325
326
327
328
329
330
331
332
333
334
# File 'lib/model_ext/reservations/credit_card.rb', line 324

def charged_amount_in_baht(perspective: nil)
  amount = charged_amount(perspective: perspective)
  return nil if amount.nil?

  country_code = begin
    restaurant.currency_code
  rescue StandardError
    'THB'
  end
  HhMoney.new(amount, country_code).default_format
end

#charged_by_gb_primepay?Boolean

Returns:

  • (Boolean)


183
184
185
# File 'lib/model_ext/reservations/credit_card.rb', line 183

def charged_by_gb_primepay?
  cc_provider == :gb_primepay && charges.success_scope.present?
end

#charged_by_omise?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/model_ext/reservations/credit_card.rb', line 187

def charged_by_omise?
  cc_provider == :omise && charges.success_scope.present?
end

#could_charge_again?Boolean

Returns:

  • (Boolean)


175
176
177
178
179
180
181
# File 'lib/model_ext/reservations/credit_card.rb', line 175

def could_charge_again?
  return false unless using_on_hold_channel?
  return true if cc_provider == :gb_primepay && gb_primepay_card.present?
  return true if cc_provider == :omise && customer.present?

  false
end


40
41
42
43
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
# File 'lib/model_ext/reservations/credit_card.rb', line 40

def paid_amount
  prepayments = []
  target_currency = restaurant.default_currency

  if property&.is_prepayment && property&.prepayment_cents
    prepayments.push(Money.new(property.prepayment_cents, target_currency))
  end

  if paid_charges.present?
    paid_charges.each do |charge|
      charge_amount = charge.amount_v2
      # Ensure currency consistency - all amounts must be in the same currency for proper summation
      if charge_amount.currency.iso_code != target_currency
        # This indicates a data inconsistency issue that should be investigated
        # Rather than performing incorrect currency conversion, we skip mismatched amounts
        # and log the issue for investigation
        APMErrorHandler.report(
          'Currency mismatch detected in reservation charges',
          context: {
            reservation_id: id,
            charge_currency: charge_amount.currency.iso_code,
            target_currency: target_currency,
            charge_amount_cents: charge_amount.cents,
          },
        )
        next
      end

      prepayments.push(charge_amount)
    end
  end

  # Return zero amount in target currency if no valid prepayments found
  prepayments.empty? ? Money.new(0, target_currency) : prepayments.sum
end


134
135
136
137
138
139
# File 'lib/model_ext/reservations/credit_card.rb', line 134

def paid_at
  return nil if paid_charges.blank?

  paid_charge = paid_charges.where.not(paid_at: nil).last
  paid_charge.paid_at.in_time_zone restaurant.time_zone if paid_charge
end

Returns:

  • (Boolean)


167
168
169
# File 'lib/model_ext/reservations/credit_card.rb', line 167

def paid_cc?
  cc_provider.present? && paid_charges.present?
end


337
338
339
340
341
# File 'lib/model_ext/reservations/credit_card.rb', line 337

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

  @paid_charges = charges.success_scope
end

Returns:

  • (Boolean)


158
159
160
161
162
163
164
165
# File 'lib/model_ext/reservations/credit_card.rb', line 158

def paid_non_cc?
  (
    shopee_pay_provider.present? ||
    promptpay_provider.present? ||
    alipay_provider.present? ||
    wechat_pay_provider.present?
  ) && paid_charges.present?
end

#payment_gatewayObject



141
142
143
144
145
# File 'lib/model_ext/reservations/credit_card.rb', line 141

def payment_gateway
  return payment_provider.humanize if paid_charges.present?

  nil
end

#payment_gateway_accountObject



147
148
149
150
151
152
153
154
155
156
# File 'lib/model_ext/reservations/credit_card.rb', line 147

def 
  case payment_provider.to_sym
  when :gb_primepay
    gb_merchant.present? ? gb_merchant.name : 'Hungry Hub'
  when :omise
    'Hungry Hub'
  else
    ''
  end
end

#payment_providerObject



313
314
315
316
317
318
319
320
321
# File 'lib/model_ext/reservations/credit_card.rb', line 313

def payment_provider
  promptpay_provider.presence ||
    cc_provider.presence ||
    true_wallet_provider.presence ||
    shopee_pay_provider.presence ||
    alipay_provider.presence ||
    wechat_pay_provider.presence ||
    'no_provider'
end

#payment_typeObject



125
126
127
128
129
130
131
132
# File 'lib/model_ext/reservations/credit_card.rb', line 125

def payment_type
  payment_types = []
  payment_types.push(payment_type_record.humanize_name) if payment_type_record.present?

  payment_types.push('Manual by admin') if property&.is_prepayment && property&.prepayment_cents

  payment_types.to_sentence
end

#payment_type_as_symbolObject



121
122
123
# File 'lib/model_ext/reservations/credit_card.rb', line 121

def payment_type_as_symbol
  payment_type_record&.name&.to_sym
end

#payment_type_providerObject



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/model_ext/reservations/credit_card.rb', line 76

def payment_type_provider
  return 'promptpay' if promptpay_provider.present?
  return 'credit_card' if cc_provider.present?
  return 'true_wallet' if true_wallet_provider.present?
  return 'shopee_pay' if shopee_pay_provider.present?
  return 'alipay_plus' if alipay_provider.present?
  return 'wechat_pay' if wechat_pay_provider.present?

  if vendor_payment.present? && booking_channel&.payment_handled_by_vendor?(vendor_payment: vendor_payment)
    channel_uri_name
  end
end

#payment_type_recordObject



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/model_ext/reservations/credit_card.rb', line 101

def payment_type_record
  if paid_charges.present?
    if promptpay_provider.present?
      PaymentType.promptpay
    elsif cc_provider.present?
      PaymentType.credit_card
    elsif true_wallet_provider.present?
      PaymentType.true_wallet
    elsif shopee_pay_provider.present?
      PaymentType.shopee_pay
    elsif alipay_provider.present?
      PaymentType.alipay_plus
    elsif wechat_pay_provider.present?
      PaymentType.wechat_pay
    else
      ''
    end
  end
end

#pending_payment_non_cc?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/model_ext/reservations/credit_card.rb', line 171

def pending_payment_non_cc?
  charges.present? && charges.pending_scope.promptpay_source.present?
end

#prepayment_percentnil, Float

if the user paid the prepayment using voucher, then it means that the booking has been paid return average of prepayment percent from all package bought

Returns:

  • (nil)

    if the package does not require prepayment

  • (Float)

    0-100.0 range as percent



26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/model_ext/reservations/credit_card.rb', line 26

def prepayment_percent
  return 0 unless package?

  prepayment_percents = package[:package_data].map do |pack|
    package_prepayment_percent = pack[:prepayment_percent]
    if package_prepayment_percent.is_a?(Integer)
      package_prepayment_percent
    else
      0
    end
  end
  prepayment_percents.inject { |sum, el| sum + el }.to_f / prepayment_percents.size
end

#require_prepayment?Boolean

Returns:

  • (Boolean)


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/model_ext/reservations/credit_card.rb', line 191

def require_prepayment?
  @require_prepayment ||= begin
    if package.present?
      if vouchers_require_cc(vouchers)
        :true
      elsif pay_now?
        :true
      elsif package[:package_data].select do |pack|
              if pack['type'].present? && pack['id'].present?
                constantize_pack(pack['type'], pack['id']).require_cc?
              else
                false
              end
            end.present?
        :true
      else
        :false
      end
    elsif Channel.require_cc?(channel) && restaurant.require_credit_card? &&
        party_size >= restaurant.cc_min_party_size
      :true
    else
      :false
    end
  rescue ActiveRecord::RecordNotFound => e
    APMErrorHandler.report(e, id: id)
    :false
  end
  @require_prepayment == :true
end

#using_charge_channel?Boolean Also known as: using_charge_directly_channel?

User will be charge upon booking

Returns:

  • (Boolean)


238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/model_ext/reservations/credit_card.rb', line 238

def using_charge_channel?
  if package.present? && require_prepayment?
    return true if vouchers_require_cc(vouchers)

    return package[:package_data].select do |pack|
      if pack['type'].present? && pack['id'].present?
        constantize_pack(pack['type'], pack['id']).charge_type == HhPackage::Package::CHARGE_TYPE_ON_CHARGE
      else
        false
      end
    end.present?
  end

  channel_instance = ::Channel.fetch_by_channel_id channel
  channel_instance.cached_tag_list.include?(::Channel::CHARGE_CC_DIRECTLY)
end

#using_on_hold_channel?Boolean

Returns:

  • (Boolean)


222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/model_ext/reservations/credit_card.rb', line 222

def using_on_hold_channel?
  if package.present? && require_prepayment?
    return package[:package_data].select do |pack|
      if pack['type'].present? && pack['id'].present?
        constantize_pack(pack['type'], pack['id']).charge_type == HhPackage::Package::CHARGE_TYPE_ON_HOLD
      else
        false
      end
    end.present?
  end

  channel_instance = ::Channel.fetch_by_channel_id channel
  channel_instance.cached_tag_list.include?(::Channel::HOLD_CC)
end