Module: MyCartHelper
- Defined in:
- app/helpers/my_cart_helper.rb
Instance Method Summary collapse
- #billing_zip_code_for_cc ⇒ Object
-
#calculate_tier_adjusted_total(itemizable) ⇒ Object
Calculate tier-adjusted total for an itemizable.
-
#calculate_tier_discount_total(itemizable) ⇒ Object
Calculate total tier discount for an itemizable (Quote/Order).
-
#calculate_tier_price_for_line_item(li) ⇒ Object
Calculate tier price for a line item if the current user has tier pricing.
- #cart_counter(cart = nil) ⇒ Object
-
#cart_sale_price_savings(cart) ⇒ Object
Calculate total savings from sale prices and tier pricing (used in "You're saving" display).
- #checkout_breadcrumb(show_billing, show_delivery, show_payment) ⇒ Object
- #display_discounted_line_price_for_catalog_item(ci, qty, target) ⇒ Object
- #display_discounted_line_total(li) ⇒ Object
- #display_discounted_shipping_price(order) ⇒ Object
- #display_discounted_unit_price(li) ⇒ Object
- #display_discounted_unit_price_for_catalog_item(ci, target) ⇒ Object
- #display_room_discounted_total(room) ⇒ Object
-
#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.
-
#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.
-
#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).
-
#effective_shipping_cost(cart) ⇒ Object
Calculate effective shipping cost considering free economy shipping.
- #expiration_month_for_cc ⇒ Object
- #expiration_year_for_cc ⇒ Object
- #format_cp_category(category) ⇒ Object
- #format_cp_hint(category) ⇒ Object
-
#has_tier_pricing? ⇒ Boolean
Check if current user has tier pricing.
- #name_for_cc ⇒ Object
- #number_for_cc ⇒ Object
- #payment_category_label(category) ⇒ Object
-
#safe_add_multiple_items_my_cart_path(items:) ⇒ Object
items is an array of "sku|qty" strings.
- #setup_account(customer) ⇒ Object
- #setup_cart_for_checkout(cart) ⇒ Object
- #setup_new_shipping_address(cart) ⇒ Object
- #state_definition(state_name) ⇒ Object
-
#tier_discount_percentage ⇒ Object
Get tier discount percentage for current user.
- #validate_email_inline(category) ⇒ Object
- #verification_value_for_cc ⇒ Object
Instance Method Details
#billing_zip_code_for_cc ⇒ Object
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 (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, (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') + content_tag(: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 content_tag(: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') + content_tag(:span, 'FREE', class: 'text-success fw-bold') else tag.del(number_to_currency(shipping_cost, unit: currency_symbol), class: 'text-muted small me-1') + content_tag(: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)) + ' ' + content_tag(: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)) + ' ' + content_tag(: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_cc ⇒ Object
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_cc ⇒ Object
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
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_cc ⇒ Object
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_cc ⇒ Object
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 setup_account(customer) customer.tap do |c| c.build_account if c.account.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 ||= current_account.email if cat == ContactPoint::EMAIL and account_signed_in? and current_account.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.build_account if o.customer.account.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_percentage ⇒ Object
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_cc ⇒ Object
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 |