Module: MyCartHelper

Defined in:
app/helpers/my_cart_helper.rb

Instance Method Summary collapse

Instance Method Details

#billing_zip_code_for_ccObject



170
171
172
173
174
# File 'app/helpers/my_cart_helper.rb', line 170

def billing_zip_code_for_cc
  @cart.billing_address.zip
rescue StandardError
  nil
end

#calculate_tier_adjusted_total(itemizable) ⇒ Object

Calculate tier-adjusted total for an itemizable



271
272
273
274
275
276
# File 'app/helpers/my_cart_helper.rb', line 271

def calculate_tier_adjusted_total(itemizable)
  tier_discount = calculate_tier_discount_total(itemizable)
  return nil unless tier_discount

  (itemizable.total - tier_discount).round(2)
end

#calculate_tier_discount_total(itemizable) ⇒ Object

Calculate total tier discount for an itemizable (Quote/Order)



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'app/helpers/my_cart_helper.rb', line 241

def calculate_tier_discount_total(itemizable)
  return nil unless @context_user.respond_to?(:pricing_program_discount)

  tier_discount_pct = @context_user.pricing_program_discount.to_f
  return nil if tier_discount_pct.zero?

  total_discount = 0.0
  itemizable.line_items.non_shipping.each do |li|
    original_price = li.price.to_f
    next if original_price.zero?

    max_discount = li.catalog_item&.max_discount || 100
    effective_discount_pct = [tier_discount_pct, max_discount].min
    effective_discount_factor = effective_discount_pct / 100.0

    tier_price = (original_price * (1 - effective_discount_factor)).round(2)

    # Check if sale price is better
    sale_price = li.catalog_item&.sale_price_in_effect? ? li.catalog_item.sale_price.to_f : nil
    next if sale_price.present? && sale_price < tier_price

    # Add the discount for this item (original - tier) * quantity
    discount_per_unit = original_price - tier_price
    total_discount += discount_per_unit * li.quantity
  end

  total_discount.positive? ? total_discount.round(2) : nil
end

#calculate_tier_price_for_line_item(li) ⇒ Object

Calculate tier price for a line item if the current user has tier pricing



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'app/helpers/my_cart_helper.rb', line 215

def calculate_tier_price_for_line_item(li)
  return nil unless @context_user.respond_to?(:pricing_program_discount)

  tier_discount_pct = @context_user.pricing_program_discount.to_f
  return nil if tier_discount_pct.zero?

  original_price = li.price.to_f
  return nil if original_price.zero?

  # Get max discount cap for this item
  max_discount = li.catalog_item&.max_discount || 100
  effective_discount_pct = [tier_discount_pct, max_discount].min
  effective_discount_factor = effective_discount_pct / 100.0

  tier_price = (original_price * (1 - effective_discount_factor)).round(2)

  # Check if sale price is better
  sale_price = li.catalog_item&.sale_price_in_effect? ? li.catalog_item.sale_price.to_f : nil
  if sale_price.present? && sale_price < tier_price
    return nil # Sale price is better, don't show tier pricing
  end

  tier_price
end

#cart_counter(cart = nil) ⇒ Object



417
418
419
# File 'app/helpers/my_cart_helper.rb', line 417

def cart_counter(cart = nil)
  @context_user&.cart_info.try(:[], :quantities) || 0
end

#cart_sale_price_savings(cart) ⇒ Object

Calculate total savings from sale prices and tier pricing (used in "You're saving" display)



403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'app/helpers/my_cart_helper.rb', line 403

def cart_sale_price_savings(cart)
  savings = 0.0
  cart.line_items.active_non_shipping_lines.each do |li|
    if li.is_discounted?
      savings += li.price_total - li.discounted_total
    elsif li.catalog_item&.sale_price_in_effect?
      savings += li.price_total - (li.catalog_item.sale_price * li.quantity)
    elsif (tier_price = calculate_tier_price_for_line_item(li))
      savings += li.price_total - (tier_price * li.quantity)
    end
  end
  savings
end

#checkout_breadcrumb(show_billing, show_delivery, show_payment) ⇒ Object



421
422
423
424
425
426
427
428
# File 'app/helpers/my_cart_helper.rb', line 421

def checkout_breadcrumb(show_billing, show_delivery, show_payment)
  links = [{ name: 'Shopping Cart', url: cms_link('/my_cart') }]
  links << { name: 'Billing Details', url: show_billing ? cms_link('/my_cart/customer_info') : nil }
  links << { name: 'Delivery Options', url: show_delivery ? cms_link('/my_cart/shipping') : nil }
  links << { name: 'Payment', url: show_payment ? cms_link('/my_cart/payment') : nil }
  # Standardize on formatted_breadcrumb for consistent rendering across www
  content_for(:formatted_breadcrumb, formatted_breadcrumb(links))
end

#display_discounted_line_price_for_catalog_item(ci, qty, target) ⇒ Object



360
361
362
363
364
365
366
367
368
# File 'app/helpers/my_cart_helper.rb', line 360

def display_discounted_line_price_for_catalog_item(ci, qty, target)
  currency_symbol = begin
    target.currency_symbol
  rescue StandardError
    Money::Currency.new('USD').symbol
  end
  discounted_unit_price = ci.discounted_price(target.customer, target)
  number_to_currency(discounted_unit_price * qty, unit: currency_symbol)
end

#display_discounted_line_total(li) ⇒ Object



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'app/helpers/my_cart_helper.rb', line 328

def display_discounted_line_total(li)
  currency_symbol = begin
    li.resource.currency_symbol
  rescue StandardError
    Money::Currency.new('USD').symbol
  end
  # Only show discounts that have been applied through the coupon system
  # Do NOT calculate display-only tier pricing here - the coupon system handles that
  if li.is_discounted?
    tag.del(number_to_currency(li.price_total, unit: currency_symbol), class: 'text-muted') +
      (:span, number_to_currency(li.discounted_total, unit: currency_symbol), class: 'text-danger fw-bold')
  else
    # Wrap in span for consistent alignment with discounted prices
    (:span, number_to_currency(li.total, unit: currency_symbol), class: 'fw-bold')
  end
end

#display_discounted_shipping_price(order) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'app/helpers/my_cart_helper.rb', line 292

def display_discounted_shipping_price(order)
  cart = order.is_a?(Order) ? order : @cart
  shipping_cost = cart.line_items.shipping_only.to_a.sum { |li| li.price_total }
  discounted_shipping_cost = cart.discounted_shipping_total
  currency_symbol = begin
    order.currency_symbol
  rescue StandardError
    Money::Currency.new('USD').symbol
  end

  if shipping_cost == discounted_shipping_cost
    number_to_currency(shipping_cost, unit: currency_symbol)
  elsif discounted_shipping_cost.zero?
    tag.del(number_to_currency(shipping_cost, unit: currency_symbol), class: 'text-muted small me-1') +
      (:span, 'FREE', class: 'text-success fw-bold')
  else
    tag.del(number_to_currency(shipping_cost, unit: currency_symbol), class: 'text-muted small me-1') +
      (:span, number_to_currency(discounted_shipping_cost, unit: currency_symbol), class: 'text-danger fw-bold')
  end
end

#display_discounted_unit_price(li) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'app/helpers/my_cart_helper.rb', line 198

def display_discounted_unit_price(li)
  currency_symbol = begin
    li.resource.currency_symbol
  rescue StandardError
    Money::Currency.new('USD').symbol
  end
  # Only show discounts that have been applied through the coupon system
  # Do NOT calculate display-only tier pricing here - the coupon system handles that
  if li.is_discounted?
    tag.del(number_to_currency(li.price, unit: currency_symbol)) + ' ' +
      (:span, number_to_currency(li.discounted_price, unit: currency_symbol), class: 'text-danger fw-bold')
  else
    number_to_currency(li.price, unit: currency_symbol)
  end
end

#display_discounted_unit_price_for_catalog_item(ci, target) ⇒ Object



345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'app/helpers/my_cart_helper.rb', line 345

def display_discounted_unit_price_for_catalog_item(ci, target)
  currency_symbol = begin
    target.currency_symbol
  rescue StandardError
    Money::Currency.new('USD').symbol
  end
  discounted_unit_price = ci.discounted_price(target.customer, target)
  if discounted_unit_price < ci.amount
    tag.del(number_to_currency(ci.amount, unit: currency_symbol)) + ' ' +
      (:span, number_to_currency(discounted_unit_price, unit: currency_symbol), class: 'text-danger fw-bold')
  else
    number_to_currency(ci.amount, unit: currency_symbol)
  end
end

#display_room_discounted_total(room) ⇒ Object



370
371
372
373
374
375
376
377
378
379
380
# File 'app/helpers/my_cart_helper.rb', line 370

def display_room_discounted_total(room)
  tot = 0.0
  room.line_items.each do |li|
    tot += li.quantity * li.catalog_item.discounted_price(room.customer, room)
  end
  number_to_currency(tot, unit: begin
    room.currency_symbol
  rescue StandardError
    Money::Currency.new('USD').symbol
  end)
end

#effective_cart_subtotal(cart) ⇒ Object

Calculate effective subtotal for cart display that accounts for sale prices
This ensures customers see promotional pricing in cart totals immediately



398
399
400
# File 'app/helpers/my_cart_helper.rb', line 398

def effective_cart_subtotal(cart)
  cart.line_items.active_non_shipping_lines.sum { |li| effective_line_total(li) }
end

#effective_cart_total(cart) ⇒ Object

Calculate effective total considering free economy shipping
This adjusts the total when economy shipping is selected but the discount hasn't been applied yet



320
321
322
323
324
325
326
# File 'app/helpers/my_cart_helper.rb', line 320

def effective_cart_total(cart)
  actual_shipping = cart.discounted_shipping_total
  effective_shipping = effective_shipping_cost(cart)
  shipping_adjustment = actual_shipping - effective_shipping

  cart.total - shipping_adjustment
end

#effective_line_total(li) ⇒ Object

Calculate effective line total considering sale prices and tier pricing
Returns the best price (lowest of: coupon-discounted, sale price, tier price, or regular)



384
385
386
387
388
389
390
391
392
393
394
# File 'app/helpers/my_cart_helper.rb', line 384

def effective_line_total(li)
  if li.is_discounted?
    li.discounted_total
  elsif li.catalog_item&.sale_price_in_effect?
    li.catalog_item.sale_price * li.quantity
  elsif (tier_price = calculate_tier_price_for_line_item(li))
    tier_price * li.quantity
  else
    li.total
  end
end

#effective_shipping_cost(cart) ⇒ Object

Calculate effective shipping cost considering free economy shipping



314
315
316
# File 'app/helpers/my_cart_helper.rb', line 314

def effective_shipping_cost(cart)
  cart.discounted_shipping_total
end

#expiration_month_for_ccObject



158
159
160
161
162
# File 'app/helpers/my_cart_helper.rb', line 158

def expiration_month_for_cc
  @cart.payments.last.credit_card.month
rescue StandardError
  nil
end

#expiration_year_for_ccObject



164
165
166
167
168
# File 'app/helpers/my_cart_helper.rb', line 164

def expiration_year_for_cc
  @cart.payments.last.credit_card.year
rescue StandardError
  nil
end

#format_cp_category(category) ⇒ Object



110
111
112
# File 'app/helpers/my_cart_helper.rb', line 110

def format_cp_category(category)
  "#{category.capitalize}"
end

#format_cp_hint(category) ⇒ Object



114
115
116
117
118
119
120
121
122
123
# File 'app/helpers/my_cart_helper.rb', line 114

def format_cp_hint(category)
  case category
  when 'phone'
    '(___) ___-____'
  when 'fax'
    '(___) ___-____'
  when 'email'
    'e.g. JohnSmith@gmail.com'
  end
end

#has_tier_pricing?Boolean

Check if current user has tier pricing

Returns:

  • (Boolean)


279
280
281
282
283
# File 'app/helpers/my_cart_helper.rb', line 279

def has_tier_pricing?
  return false unless @context_user.respond_to?(:pricing_program_discount)

  @context_user.pricing_program_discount.to_f.positive?
end

#name_for_ccObject



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'app/helpers/my_cart_helper.rb', line 136

def name_for_cc
  n = o.payments.last&.credit_card&.name
  n = nil if n.blank?
  n ||= begin
    @cart.billing_address.person_name
  rescue StandardError
    nil
  end
  n ||= begin
    @cart.customer.contacts.last.full_name
  rescue StandardError
    nil
  end
  n
end

#number_for_ccObject



152
153
154
155
156
# File 'app/helpers/my_cart_helper.rb', line 152

def number_for_cc
  @cart.payments.last.credit_card.number
rescue StandardError
  nil
end

#payment_category_label(category) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'app/helpers/my_cart_helper.rb', line 182

def payment_category_label(category)
  label_msg = nil
  additional_info = nil
  case category
  when Payment::CREDIT_CARD
    label_msg = 'Pay using a Credit Card'
  when Payment::PO
    label_msg = 'Pay using a Purchase Order'
  when Payment::CHECK
    label_msg = 'I will send a check'
  when Payment::VPO
    label_msg = 'Pay using Verbal Purchase Order'
  end
  [label_msg, additional_info]
end

#safe_add_multiple_items_my_cart_path(items:) ⇒ Object

items is an array of "sku|qty" strings. If a SKU isn't orderable in the
current locale, fall back to its successor item's SKU when that one is.

Resolves orderability in 1-2 queries against the view_product_catalogs
materialized view (instead of N Item.find_by(sku:) + N
ViewProductCatalog Load round trips). For showcase/quote callers with 17+
SKUs this collapses ~34 queries into 1-2.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'app/helpers/my_cart_helper.rb', line 9

def safe_add_multiple_items_my_cart_path(items:)
  parsed = items.filter_map do |sku_qty|
    sku, qty = sku_qty.to_s.split('|', 2)
    next if sku.blank? || qty.blank?

    [sku, qty]
  end
  skus = parsed.map(&:first).uniq
  return add_multiple_items_my_cart_path(items: []) if skus.empty?

  catalog_id = Catalog.locale_to_catalog_id

  # Single batched fetch across all catalogs. `successor_item_sku` is
  # denormalized identically on every row for a given `item_sku`, so any
  # row tells us the successor; the (catalog_id, item_is_web_accessible)
  # pair tells us whether the SKU itself is orderable in this locale.
  rows = ViewProductCatalog
           .where(item_sku: skus)
           .pluck(:item_sku, :catalog_id, :item_is_web_accessible, :successor_item_sku)

  orderable_skus = rows.each_with_object(Set.new) do |(sku, cid, accessible, _), set|
    set << sku if cid == catalog_id && accessible
  end
  successor_for = rows.each_with_object({}) do |(sku, _, _, succ_sku), hash|
    hash[sku] ||= succ_sku if succ_sku.present?
  end

  needed_successors = (skus - orderable_skus.to_a).filter_map { |s| successor_for[s] }.uniq
  successor_orderable = if needed_successors.any?
                          ViewProductCatalog
                            .where(item_sku: needed_successors,
                                   catalog_id: catalog_id,
                                   item_is_web_accessible: true)
                            .pluck(:item_sku).to_set
                        else
                          Set.new
                        end

  safe_items = parsed.filter_map do |sku, qty|
    if orderable_skus.include?(sku)
      "#{sku}|#{qty}"
    elsif (succ = successor_for[sku]) && successor_orderable.include?(succ)
      "#{succ}|#{qty}"
    end
  end

  add_multiple_items_my_cart_path(items: safe_items)
end

#setup_account(customer) ⇒ Object



97
98
99
100
101
# File 'app/helpers/my_cart_helper.rb', line 97

def (customer)
  customer.tap do |c|
    c. if c..nil?
  end
end

#setup_cart_for_checkout(cart) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'app/helpers/my_cart_helper.rb', line 81

def setup_cart_for_checkout(cart)
  cart.tap do |o|
    [ContactPoint::PHONE, ContactPoint::EMAIL].each do |cat|
      if o.customer.contact_points.select { |cp| cp.category == cat }.empty?
        cp = o.customer.contact_points.build({ category: cat })
        cp.detail ||= .email if cat == ContactPoint::EMAIL and  and .email
      end
    end
    # puts "!!!Contact Points in setup_cart_for_checkout, o.customer.contact_points: #{o.customer.contact_points.inspect}"
    o.customer.contacts.build if o.customer.contacts.empty?
    o.customer.build_billing_address(country_iso3: cart.catalog.store.country_iso3) unless o.customer.billing_address
    o.customer.clear_names if o.customer.has_guest_name?
    o.customer. if o.customer..nil?
  end
end

#setup_new_shipping_address(cart) ⇒ Object



103
104
105
106
107
108
# File 'app/helpers/my_cart_helper.rb', line 103

def setup_new_shipping_address(cart)
  cart.tap do |o|
    o.shipping_address = Address.new(party: o.customer, country_iso3: o.store.country_iso3) unless o.shipping_address and o.shipping_address.new_record?
    o.shipping_address.person_name ||= o.customer.contacts.first&.full_name if o.customer.is_organization?
  end
end

#state_definition(state_name) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'app/helpers/my_cart_helper.rb', line 58

def state_definition(state_name)
  case state_name
  when :pending, :in_cr_hold, :in_management_hold
    'Your order was received and is awaiting review by our staff.'
  when :pending_payment
    'Your order is currently awaiting processing of payment information.'
  when :pending_release_authorization
    'Your order was received and is currently awaiting release by our finance department.'
  when :at_warehouse
    'Your order has been sent to our warehouse.'
  when :picking, :awaiting_deliveries
    'Your order was received by our warehouse staff and is currently being processed.'
  when :processing_deliveries
    'Your order has is partially shipped.'
  when :invoiced
    'Your order has been shipped.'
  when :cancelled
    'Your order was cancelled.'
  when :crm_back_order
    'Your order has been back ordered.'
  end
end

#tier_discount_percentageObject

Get tier discount percentage for current user



286
287
288
289
290
# File 'app/helpers/my_cart_helper.rb', line 286

def tier_discount_percentage
  return 0 unless @context_user.respond_to?(:pricing_program_discount)

  @context_user.pricing_program_discount.to_f
end

#validate_email_inline(category) ⇒ Object



125
126
127
128
129
130
131
132
133
134
# File 'app/helpers/my_cart_helper.rb', line 125

def validate_email_inline(category)
  case category
  when 'phone'
    ''
  when 'fax'
    ''
  when 'email'
    'required'
  end
end

#verification_value_for_ccObject



176
177
178
179
180
# File 'app/helpers/my_cart_helper.rb', line 176

def verification_value_for_cc
  @cart.payments.last.credit_card.verification_value
rescue StandardError
  nil
end