Module: Models::CatalogItemAmazonHelper

Extended by:
ActiveSupport::Concern
Includes:
ActionView::Helpers::UrlHelper, Memery
Included in:
CatalogItem
Defined in:
app/concerns/models/catalog_item_amazon_helper.rb

Overview

ActiveSupport::Concern mixin: catalog item amazon helper.

Constant Summary collapse

AMAZON_DEFAULT_BUSINESS_DISCOUNT =

%

5
AMAZON_MIN_PROFIT_MARGIN =

Amazon min profit margin.

40

Instance Attribute Summary collapse

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#amazon_fnskuObject (readonly)



21
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 21

validates :amazon_fnsku, amazon_fnsku: true, uniqueness: true, if: :amazon_fba_labeling_fnsku?

Class Method Details

.amazonsActiveRecord::Relation<Models::CatalogItemAmazonHelper>

A relation of Models::CatalogItemAmazonHelpers that are amazons. Active Record Scope

Returns:

See Also:



29
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 29

scope :amazons, -> { where(catalog_id: CatalogConstants::AMAZON_CATALOG_IDS) }

.amazons_sellersActiveRecord::Relation<Models::CatalogItemAmazonHelper>

A relation of Models::CatalogItemAmazonHelpers that are amazons sellers. Active Record Scope

Returns:

See Also:



30
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 30

scope :amazons_sellers, -> { where(catalog_id: CatalogConstants::AMAZON_SELLER_IDS) }

.amazons_sellers_with_asinsActiveRecord::Relation<Models::CatalogItemAmazonHelper>

A relation of Models::CatalogItemAmazonHelpers that are amazons sellers with asins. Active Record Scope

Returns:

See Also:



32
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 32

scope :amazons_sellers_with_asins, -> { amazons.with_asin }

.amazons_with_asinsActiveRecord::Relation<Models::CatalogItemAmazonHelper>

A relation of Models::CatalogItemAmazonHelpers that are amazons with asins. Active Record Scope

Returns:

See Also:



31
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 31

scope :amazons_with_asins, -> { amazons.with_asin }

.by_asinsActiveRecord::Relation<Models::CatalogItemAmazonHelper>

A relation of Models::CatalogItemAmazonHelpers that are by asins. Active Record Scope

Returns:

See Also:



28
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 28

scope :by_asins, ->(*asins) { amazons_with_asins.where(items: { amazon_asin: [asins].flatten.uniq.compact }) }

.with_asinActiveRecord::Relation<Models::CatalogItemAmazonHelper>

A relation of Models::CatalogItemAmazonHelpers that are with asin. Active Record Scope

Returns:

See Also:



33
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 33

scope :with_asin, -> { amazons.joins(store_item: :item).where.not(items: { amazon_asin: [nil, ''] }) }

Instance Method Details

#amalytix_tagsObject



668
669
670
671
672
673
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 668

def amalytix_tags
  tags = []
  tags += item.primary_product_line.self_and_ancestors.select { |pl| pl.available_to_public || pl.show_in_sales_portal }.first(3).map { |pl| pl.slug_ltree.to_s }
  tags << item.amazon_variation.sku if item.amazon_variation.present?
  tags
end

#amazon_asinObject

If catalog item represents an amazon asin we return that value



465
466
467
468
469
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 465

def amazon_asin
  return unless amazon_catalog_item?

  third_party_part_number.presence || item.amazon_asin
end

#amazon_business_priceObject



520
521
522
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 520

def amazon_business_price
  (amount.to_f * amazon_business_price_factor).round(2)
end

#amazon_business_price_factorObject

Returns a ratio to apply to Amazon Catalog price to get the business price



516
517
518
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 516

def amazon_business_price_factor
  (100.0 - amazon_effective_business_price_discount.to_f) / 100.0
end

#amazon_business_price_fbaObject



524
525
526
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 524

def amazon_business_price_fba
  amazon_business_price&.-fba_discount
end

#amazon_business_price_with_taxObject

Returns the computer amazon business price



529
530
531
532
533
534
535
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 529

def amazon_business_price_with_tax
  price = amazon_business_price

  return price unless tax_rate

  ((tax_rate + 1) * (price || 0.0)).round(2)
end

#amazon_business_price_with_tax_fbaObject



537
538
539
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 537

def amazon_business_price_with_tax_fba
  amazon_business_price_with_tax&.-fba_discount
end

#amazon_catalog_item?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 62

def amazon_catalog_item?
  catalog.amazon_catalog?
end

#amazon_current_imagesObject



436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 436

def amazon_current_images
  # This differs if you're vendor or seller
  current_images = {}
  if amazon_vendor_item?
    current_images['MAIN'] = retailer_information_first_locale.dig(:catalog, :payload, :summaries, 0, :mainImage, :link)
    (1..9).to_a.each do |i|
      # Note that vendor does not give us the image type use, just a positional number so we guess and assign it PT0x
      media_url = retailer_information_first_locale.dig('attributes', "other_product_image_locator_#{i}", 0, 'media_location')
      current_images["PT0#{i}"] = media_url if media_url.present?
    end
  elsif amazon_seller_item?

    images_in_marketplace = retailer_information_first_locale.dig(:catalog, :payload, :images, 0, :images) || []
    largest_images = {}
    # On seller, it returns different sizes of the same image, 1600, 500, 75
    images_in_marketplace.each do |image|
      variant = image['variant']

      # If we haven't seen this variant yet, or if the current image is larger, update the hash
      largest_images[variant] = image if !largest_images[variant] || (image['width'] * image['height'] > largest_images[variant]['width'] * largest_images[variant]['height'])
    end

    # Get the resulting images as an array
    current_images = largest_images.transform_values { |value| value[:link] }
  end
  current_images
end

#amazon_delete_information(attribute_actions: nil) ⇒ Object



238
239
240
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/concerns/models/catalog_item_amazon_helper.rb', line 238

def amazon_delete_information(attribute_actions: nil)
  return { status: :skipped, message: 'Not an amazon item' } unless amazon_catalog_item?
  return { status: :skipped, message: 'Orchestrator could not be loaded' } unless (orchestrator = catalog.load_orchestrator)

  res = orchestrator.delete_listing_from_catalog_item(catalog_item: self, http_method: 'DELETE', attribute_actions:)
  result = if res.any?(false)
             { status: :error, message: "Catalog Item ASIN #{amazon_asin}: could not delete listing from Amazon! ASIN not found?" }
           elsif has_amazon_fba?
             res = orchestrator.delete_listing_from_catalog_item(catalog_item: self, http_method: 'DELETE', attribute_actions:, use_fba_sku: true)
             if res.any?(false)
               status = :error if res.all?(false)
               status = :partial_error if res.any? { |r| r == false || r.exception? } && res.any?(&:processed?)
               { status: status, message: "Catalog Item ASIN #{amazon_asin}: listing deleted from Amazon! but FBA SKU #{amazon_fba_sku}: listing could not be deleted from Amazon! ASIN not found?", ecls: res.reject { |r| r == false } }
             else
               status = :success if res.all?(&:processed?)
               status = :partial_error if res.any?(&:processed?) && res.any?(&:exception?)
               status = :error if res.all?(&:exception?)
               { status: status, message: "Catalog Item ASIN #{amazon_asin} and FBA SKU #{amazon_fba_sku}: listings deleted from Amazon!", ecls: res }
             end
           else
             status = :success if res.all?(&:processed?)
             status = :partial_error if res.any?(&:processed?) && res.any?(&:exception?)
             status = :error if res.all?(&:exception?)
             { status: status, message: "Catalog Item SKU #{sku}: listing deleted from Amazon!", ecls: res }
           end

  # After a successful DELETE, transition pending_discontinue -> discontinued
  discontinue! if result[:status] == :success && pending_discontinue? && can_discontinue?

  result
end

#amazon_descriptionObject

Alias for
to: :item#amazon_description

Returns:

  • (Object)
             to: :item#amazon_description
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_effective_business_price_discountObject

What is the effective business discount for amazon



511
512
513
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 511

def amazon_effective_business_price_discount
  business_discount_override.presence || AMAZON_DEFAULT_BUSINESS_DISCOUNT
end

#amazon_effective_desired_product_typeObject



304
305
306
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 304

def amazon_effective_desired_product_type
  amazon_desired_product_type.presence || amazon_variation&.amazon_desired_product_type
end

#amazon_feature_1Object

Alias for
to: :item#amazon_feature_1

Returns:

  • (Object)
             to: :item#amazon_feature_1
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_feature_2Object

Alias for
to: :item#amazon_feature_2

Returns:

  • (Object)
             to: :item#amazon_feature_2
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_feature_3Object

Alias for
to: :item#amazon_feature_3

Returns:

  • (Object)
             to: :item#amazon_feature_3
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_feature_4Object

Alias for
to: :item#amazon_feature_4

Returns:

  • (Object)
             to: :item#amazon_feature_4
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_feature_5Object

Alias for
to: :item#amazon_feature_5

Returns:

  • (Object)
             to: :item#amazon_feature_5
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_feature_6Object

Alias for
to: :item#amazon_feature_6

Returns:

  • (Object)
             to: :item#amazon_feature_6
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_feature_7Object

Alias for
to: :item#amazon_feature_7

Returns:

  • (Object)
             to: :item#amazon_feature_7
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_feature_8Object

Alias for
to: :item#amazon_feature_8

Returns:

  • (Object)
             to: :item#amazon_feature_8
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_feature_9Object

Alias for
to: :item#amazon_feature_9

Returns:

  • (Object)
             to: :item#amazon_feature_9
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_generic_keywordObject

Alias for
to: :item#amazon_generic_keyword

Returns:

  • (Object)
             to: :item#amazon_generic_keyword
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_item_costObject

This can be refined later to add referral fee and shipping cogs



639
640
641
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 639

def amazon_item_cost
  store_item.unit_cogs.round(4)
end

#amazon_json_generator(marketplace_id: nil, attribute_actions: nil, fba: false, language_tag: nil, business_price_available: true) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



308
309
310
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 308

def amazon_json_generator(marketplace_id: nil, attribute_actions: nil, fba: false, language_tag: nil, business_price_available: true) # rubocop:disable Lint/UnusedMethodArgument
  Edi::Amazon::JsonListingGenerator::Factory.generator_for_catalog_item(self, attribute_actions:, fba:, language_tag:, business_price_available:)
end

#amazon_label_requirementsObject



499
500
501
502
503
504
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 499

def amazon_label_requirements
  errors = []
  errors << 'ASIN number missing' if third_party_part_number.blank?
  errors << 'UPC missing' if item.upc.blank?
  errors
end

#amazon_list_priceObject



598
599
600
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 598

def amazon_list_price
  amazon_price_with_tax || msrp_with_vat || msrp # per https://www.youtube.com/watch?v=xir588FnHSI we are trying to get over the 'No Reference Price' error on promotions, so send list_price of 'Our Price' to see if that resolves this issue, fallback to MSRP
end

#amazon_localesObject

Update with marketplace supported locales when available



283
284
285
286
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 283

def amazon_locales
  # Include supported marketplace languages; keep two-letter language tags to match catalog.locales
  catalog.locales & %i[en-US en-CA fr de es nl it pl pt sv]
end

#amazon_lowest_quantity_discount_priceObject



541
542
543
544
545
546
547
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 541

def amazon_lowest_quantity_discount_price
  (1..5).to_a.reverse_each do |threshold_index|
    discounted_price = amazon_quantity_discount_price(threshold_index)
    return discounted_price if discounted_price.present?
  end
  nil
end

#amazon_lowest_quantity_discounted_priceObject



559
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 559

def amazon_lowest_quantity_discounted_price; end

#amazon_maximum_seller_allowed_price_with_taxObject



565
566
567
568
569
570
571
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 565

def amazon_maximum_seller_allowed_price_with_tax
  price = [msrp, amount].max

  return price unless tax_rate

  ((tax_rate + 1) * (price || 0.0)).round(2)
end

#amazon_maximum_seller_allowed_price_with_tax_fbaObject



573
574
575
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 573

def amazon_maximum_seller_allowed_price_with_tax_fba
  amazon_maximum_seller_allowed_price_with_tax&.-fba_discount
end

#amazon_minimum_profit_marginObject



561
562
563
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 561

def amazon_minimum_profit_margin
  AMAZON_MIN_PROFIT_MARGIN
end

#amazon_minimum_seller_allowed_price_with_taxObject

Creates the lower threshold for minimum price (in case of automated rule)
Take the smallest of

  • 40% of msrp
  • 80% of catalog item amount (our price)
  • Amazon business price
  • Effective price (in case a sale is in effect)


583
584
585
586
587
588
589
590
591
592
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 583

def amazon_minimum_seller_allowed_price_with_tax
  min_margin_price = calculate_minimum_price_for_margin(amazon_minimum_profit_margin)
  msap = [min_margin_price, amazon_lowest_quantity_discount_price, amount, amazon_business_price, effective_price, amz_min_seller_price_override].compact.min
  # Whatever our msap is because of another random rule on amazon, shave it by one percent, amazon price upload requires it
  price = (msap * 0.99).round(2)

  return price unless tax_rate

  ((tax_rate + 1) * (price || 0.0)).round(2)
end

#amazon_minimum_seller_allowed_price_with_tax_fbaObject



594
595
596
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 594

def amazon_minimum_seller_allowed_price_with_tax_fba
  amazon_minimum_seller_allowed_price_with_tax&.-fba_discount
end

#amazon_patch_information(attribute_actions: nil, use_fba_sku: false) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 184

def amazon_patch_information(attribute_actions: nil, use_fba_sku: false)
  return { status: :skipped, message: 'Not an amazon item' } unless amazon_catalog_item?
  return { status: :skipped, message: 'Orchestrator could not be loaded' } unless (orchestrator = catalog.load_orchestrator)
  return { status: :skipped, message: 'Not an FBA amazon catalog item' } if use_fba_sku && !has_amazon_fba?

  fbm_vs_fba_sku_snippet = (use_fba_sku ? "FBA SKU #{amazon_fba_sku}" : "SKU #{sku}")

  res = orchestrator.push_listing_from_catalog_item(self, http_method: 'PATCH', attribute_actions:, use_fba_sku:)

  # Separate error hashes from ECL objects
  error_results = res.select { |r| r.is_a?(Hash) && r[:error] }
  ecl_results = res.reject { |r| r.is_a?(Hash) && r[:error] }

  if error_results.any?
    error_messages = error_results.pluck(:message).join('; ')
    status = ecl_results.any?(&:processed?) ? :partial_error : :error
    { status: status, message: "#{fbm_vs_fba_sku_snippet}: #{error_messages}", ecls: ecl_results }
  elsif ecl_results.all?(&:processed?)
    { status: :success, message: "Catalog Item #{fbm_vs_fba_sku_snippet}: information patched to Amazon!", ecls: ecl_results }
  elsif ecl_results.any?(&:exception?)
    status = ecl_results.any?(&:processed?) ? :partial_error : :error
    { status: status, message: "Catalog Item #{fbm_vs_fba_sku_snippet}: some operations failed", ecls: ecl_results }
  else
    { status: :error, message: "Catalog Item #{fbm_vs_fba_sku_snippet}: unknown state", ecls: ecl_results }
  end
end

#amazon_price_with_taxObject



610
611
612
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 610

def amazon_price_with_tax
  price_with_vat || amount
end

#amazon_price_with_tax_fbaObject



614
615
616
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 614

def amazon_price_with_tax_fba
  amazon_price_with_tax&.-fba_discount
end

#amazon_product_data(fba: false) ⇒ Object



278
279
280
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 278

def amazon_product_data(fba: false)
  amazon_json_generator(fba: fba).generate
end

#amazon_product_type_in_effectObject



288
289
290
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 288

def amazon_product_type_in_effect
  amazon_effective_desired_product_type || amazon_listing_reported_product_type.presence || amazon_reported_product_type.presence
end

#amazon_product_type_incoherent?Boolean

Returns:

  • (Boolean)


430
431
432
433
434
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 430

def amazon_product_type_incoherent?
  return false unless amazon_reported_product_type && amazon_effective_desired_product_type

  amazon_reported_product_type != amazon_effective_desired_product_type
end

#amazon_product_urlObject



424
425
426
427
428
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 424

def amazon_product_url
  return unless amazon_marketplace && amazon_asin

  amazon_marketplace.url_for_asin(amazon_asin)
end

#amazon_pull_buy_box_statusObject



90
91
92
93
94
95
96
97
98
99
100
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 90

def amazon_pull_buy_box_status
  return { status: :skipped, message: 'Not an amazon item or no asin present' } unless amazon_catalog_item? && amazon_asin.present?
  return { status: :skipped, message: 'Orchestrator could not be loaded' } unless (orchestrator = catalog.load_orchestrator)

  res = orchestrator.pull_buy_box_status(self)
  if res.any? { |h| h.values.any?(false) }
    { status: :error, message: "Catalog Item ASIN #{amazon_asin}: could not pull buy box status from Amazon! ASIN not found?" }
  else
    { status: :success, message: "Catalog Item ASIN #{amazon_asin}: buy box status pulled from Amazon!" }
  end
end

#amazon_pull_catalog_informationObject



78
79
80
81
82
83
84
85
86
87
88
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 78

def amazon_pull_catalog_information
  return { status: :skipped, message: 'Not an amazon item or no asin present' } unless amazon_catalog_item? && amazon_asin.present?
  return { status: :skipped, message: 'Orchestrator could not be loaded' } unless (orchestrator = catalog.load_orchestrator)

  res = orchestrator.pull_catalog_information(self)
  if res.any? { |h| h.values.any?(false) }
    { status: :error, message: "Catalog Item ASIN #{amazon_asin}: could not pull information from Amazon! ASIN not found?" }
  else
    { status: :success, message: "Catalog Item ASIN #{amazon_asin}: information pulled from Amazon!" }
  end
end

#amazon_pull_listing_informationObject



102
103
104
105
106
107
108
109
110
111
112
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 102

def amazon_pull_listing_information
  return { status: :skipped, message: 'Not an amazon item' } unless amazon_catalog_item?
  return { status: :skipped, message: 'Orchestrator could not be loaded' } unless (orchestrator = catalog.load_orchestrator)

  res = orchestrator.pull_listing_information(self)
  if res.any? { |h| h.values.any?(false) }
    { status: :error, message: "Catalog Item ID #{id}: could not pull information from Amazon!" }
  else
    { status: :success, message: "Catalog Item ID #{id}: information pulled from Amazon!" }
  end
end

#amazon_pull_listing_schema(product_type = nil) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 114

def amazon_pull_listing_schema(product_type = nil)
  return { status: :skipped, message: 'Not an amazon item' } unless amazon_catalog_item?
  return { status: :skipped, message: 'Orchestrator could not be loaded' } unless (orchestrator = catalog.load_orchestrator)

  res = orchestrator.pull_listing_schema(self, product_type)
  if res.any? { |h| h.values.any?(false) }
    { status: :error, message: "Catalog Item ID #{id}: could not pull schema from Amazon!" }
  else
    { status: :success, message: "Catalog Item ID #{id}: schema pulled from Amazon!" }
  end
end

#amazon_put_information(attribute_actions: nil, use_fba_sku: false) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 211

def amazon_put_information(attribute_actions: nil, use_fba_sku: false)
  return { status: :skipped, message: 'Not an amazon item' } unless amazon_catalog_item?
  return { status: :skipped, message: 'Orchestrator could not be loaded' } unless (orchestrator = catalog.load_orchestrator)
  return { status: :skipped, message: 'Not an FBA amazon catalog item' } if use_fba_sku && !has_amazon_fba?

  fbm_vs_fba_sku_snippet = (use_fba_sku ? "FBA SKU #{amazon_fba_sku}" : "SKU #{sku}")

  res = orchestrator.push_listing_from_catalog_item(self, http_method: 'PUT', attribute_actions:, use_fba_sku:)

  # Separate error hashes from ECL objects
  error_results = res.select { |r| r.is_a?(Hash) && r[:error] }
  ecl_results = res.reject { |r| r.is_a?(Hash) && r[:error] }

  if error_results.any?
    error_messages = error_results.pluck(:message).join('; ')
    status = ecl_results.any?(&:processed?) ? :partial_error : :error
    { status: status, message: "#{fbm_vs_fba_sku_snippet}: #{error_messages}", ecls: ecl_results }
  elsif ecl_results.all?(&:processed?)
    { status: :success, message: "Catalog Item #{fbm_vs_fba_sku_snippet}: information put to Amazon!", ecls: ecl_results }
  elsif ecl_results.any?(&:exception?)
    status = ecl_results.any?(&:processed?) ? :partial_error : :error
    { status: status, message: "Catalog Item #{fbm_vs_fba_sku_snippet}: some operations failed", ecls: ecl_results }
  else
    { status: :error, message: "Catalog Item #{fbm_vs_fba_sku_snippet}: unknown state", ecls: ecl_results }
  end
end

#amazon_quantity_discount_price(threshold_index) ⇒ Object



549
550
551
552
553
554
555
556
557
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 549

def amazon_quantity_discount_price(threshold_index)
  return unless (discount = send(:"quantity_#{threshold_index}_price_discount"))

  if quantity_discount_price_type_percent?
    (amount * ((100.0 - discount.to_f) / 100.0)).round(2)
  elsif quantity_discount_price_type_fixed?
    (amount - discount)
  end
end

#amazon_sale_price_with_taxObject



618
619
620
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 618

def amazon_sale_price_with_tax
  sale_price_with_vat || sale_price
end

#amazon_sale_price_with_tax_fbaObject



622
623
624
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 622

def amazon_sale_price_with_tax_fba
  amazon_sale_price_with_tax&.-fba_discount
end

#amazon_schemaObject



292
293
294
295
296
297
298
299
300
301
302
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 292

def amazon_schema
  return if amazon_effective_desired_product_type.blank?

  AmazonSchema
    .amazon_channel_seller
    .where(
      amazon_marketplace: catalog&.amazon_marketplace&.id,
      product_type: amazon_effective_desired_product_type
    )
    .first
end

#amazon_seller_item?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 70

def amazon_seller_item?
  catalog_id.in?(CatalogConstants::AMAZON_SC_CATALOG_IDS + CatalogConstants::AMAZON_EU_CATALOG_IDS)
end

#amazon_send_patch_listing_information(attribute_actions: nil, fbm_vs_fba: 'Both') ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 126

def amazon_send_patch_listing_information(attribute_actions: nil, fbm_vs_fba: 'Both')
  if api_ready_state?
    res_arr = []
    sku_labels = ['', '-FBA'] if %w[Both].include?(fbm_vs_fba)
    sku_labels = [''] if %w[FBM].include?(fbm_vs_fba)
    sku_labels = ['-FBA'] if %w[FBA].include?(fbm_vs_fba)
    res_arr << amazon_patch_information(attribute_actions:, use_fba_sku: false) if %w[Both FBM].include?(fbm_vs_fba)
    res_arr << amazon_patch_information(attribute_actions:, use_fba_sku: true) if %w[Both FBA].include?(fbm_vs_fba)
    status = :success if res_arr.all? { |r| r[:status] == :success }
    status = :partial_skipped if res_arr.any? { |r| r[:status] == :skipped }
    status = :skipped if res_arr.all? { |r| r[:status] == :skipped }
    status = :partial_error if res_arr.any? { |r| r[:status] == :error }
    status = :error if res_arr.all? { |r| r[:status] == :error }

    ecl_links = res_arr.map.with_index do |r, i|
      Array(r[:ecls]).map do |ecl|
        link_to("#{sku}#{sku_labels[i]} (#{ecl.state})", edi_communication_log_path(ecl), target: '_blank', rel: 'noopener noreferrer')
      end
    end.flatten.compact

    message_parts = res_arr.pluck(:message).join(' ')
    message_suffix = ecl_links.any? ? " See: #{ecl_links.join(', ')}" : ''

    { status: status, message: "#{message_parts}#{message_suffix}".html_safe }
  else
    { status: :skipped, message: 'Not an active or pending onboarding item' }
  end
end

#amazon_send_put_listing_information(attribute_actions: nil, fbm_vs_fba: 'Both') ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 155

def amazon_send_put_listing_information(attribute_actions: nil, fbm_vs_fba: 'Both')
  if api_ready_state?
    res_arr = []
    sku_labels = ['', '-FBA'] if %w[Both].include?(fbm_vs_fba)
    sku_labels = [''] if %w[FBM].include?(fbm_vs_fba)
    sku_labels = ['-FBA'] if %w[FBA].include?(fbm_vs_fba)
    res_arr << amazon_put_information(attribute_actions:, use_fba_sku: false) if %w[Both FBM].include?(fbm_vs_fba)
    res_arr << amazon_put_information(attribute_actions:, use_fba_sku: true) if %w[Both FBA].include?(fbm_vs_fba)
    status = :success if res_arr.all? { |r| r[:status] == :success }
    status = :partial_skipped if res_arr.any? { |r| r[:status] == :skipped }
    status = :skipped if res_arr.all? { |r| r[:status] == :skipped }
    status = :partial_error if res_arr.any? { |r| r[:status] == :error }
    status = :error if res_arr.all? { |r| r[:status] == :error }

    ecl_links = res_arr.map.with_index do |r, i|
      Array(r[:ecls]).map do |ecl|
        link_to("#{sku}#{sku_labels[i]} (#{ecl.state})", edi_communication_log_path(ecl), target: '_blank', rel: 'noopener noreferrer')
      end
    end.flatten.compact

    message_parts = res_arr.pluck(:message).join(' ')
    message_suffix = ecl_links.any? ? " See: #{ecl_links.join(', ')}" : ''

    { status: status, message: "#{message_parts}#{message_suffix}".html_safe }
  else
    { status: :skipped, message: 'Not an active or pending onboarding item' }
  end
end

#amazon_target_keywordsObject

Alias for
to: :item#amazon_target_keywords

Returns:

  • (Object)
             to: :item#amazon_target_keywords
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_titleObject

Alias for
to: :item#amazon_title

Returns:

  • (Object)
             to: :item#amazon_title
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_variationObject

Alias for
to: :item#amazon_variation

Returns:

  • (Object)
             to: :item#amazon_variation
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item

#amazon_vendor_codeObject



274
275
276
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 274

def amazon_vendor_code
  catalog.vendor_code.presence
end

#amazon_vendor_item?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 74

def amazon_vendor_item?
  catalog_id.in?(CatalogConstants::AMAZON_VC_CATALOG_IDS)
end

#amz_available_attributesArray<String>

List all available Amazon attribute names that can be used with get_amz_attribute

Returns:

  • (Array<String>)

    Sorted list of attribute names



401
402
403
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 401

def amz_available_attributes
  Edi::Amazon::JsonListingGenerator::Attributes::AttributeFactory.attributes_available
end

#api_ready_state?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 66

def api_ready_state?
  active? || active_hidden? || pending_onboarding? || require_vendor_update? || pending_vendor_update?
end

#broadcast_amazon_dashboard_update(skip_amazon_check: false) ⇒ Object

Broadcast updates to the Amazon Catalog Items dashboard via Turbo Streams
Called after Amazon operations complete (pull buy box, pull catalog info, etc.)

Parameters:

  • skip_amazon_check (Boolean) (defaults to: false)

    Skip the amazon_catalog_item? check (useful when caller already verified)



678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 678

def broadcast_amazon_dashboard_update(skip_amazon_check: false)
  return unless skip_amazon_check || amazon_catalog_item?

  Rails.logger.info "[CatalogItem] Broadcasting Amazon dashboard update for #{id}"

  # Broadcast updates to each cell that may have changed
  cells = {
    "amazon_item_status_#{id}" => 'crm/amazon_products/status_cell',
    "amazon_item_price_#{id}" => 'crm/amazon_products/price_cell',
    "amazon_item_minmax_#{id}" => 'crm/amazon_products/minmax_cell',
    "amazon_item_comp_price_#{id}" => 'crm/amazon_products/competitive_price_cell',
    "amazon_item_buybox_#{id}" => 'crm/amazon_products/buybox_cell',
    "amazon_item_fm_#{id}" => 'crm/amazon_products/fm_cell',
    "amazon_item_issues_#{id}" => 'crm/amazon_products/issues_cell',
    "amazon_item_price_updated_#{id}" => 'crm/amazon_products/price_updated_cell',
    "amazon_item_data_#{id}" => 'crm/amazon_products/data_freshness_cell'
  }

  stream = "amazon_catalog_items:catalog_#{catalog_id}"
  cells.each do |target, partial|
    Turbo::StreamsChannel.broadcast_update_to(
      stream,
      target: target,
      partial: partial,
      locals: { catalog_item: self }
    )
  end
rescue StandardError => e
  Rails.logger.warn "[CatalogItem] Amazon dashboard broadcast failed: #{e.message}"
end

#extract_amazon_procurement_cost_priceObject



485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 485

def extract_amazon_procurement_cost_price
  return unless amazon_catalog_item?

  amz_locale = retailer_information&.keys&.first
  return unless amz_locale

  begin
    hsh = retailer_information&.fetch(amz_locale)&.fetch('procurement')&.first&.fetch('costPrice')
    hsh&.fetch('amount').to_f
  rescue KeyError
    hsh&.to_s
  end
end

#extract_retailer_information(amz_attribute_sym) ⇒ Object



471
472
473
474
475
476
477
478
479
480
481
482
483
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 471

def extract_retailer_information(amz_attribute_sym)
  return unless amazon_catalog_item?

  amz_locale = retailer_information&.keys&.first
  return unless amz_locale

  begin
    hsh = retailer_information&.fetch(amz_locale)&.fetch('attributes')&.fetch(amz_attribute_sym.to_s)&.first
    hsh&.fetch('value')
  rescue KeyError
    hsh&.to_s
  end
end

#fba_discountObject

This allows you to control the discount applied to the FBA price
However I noticed that if you have no FBA stock, the FBM offer doesn't automatically
take over and detects the FBA as a competitive price, so it is better to match
them and let amazon decides which to present



606
607
608
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 606

def fba_discount
  0.0
end

#get_amz_all_patches(fba: false, language_tag: nil) ⇒ Array<Hash>

Returns all Amazon attribute patches for this catalog item
Useful for debugging to see what would be sent to Amazon

Parameters:

  • fba (Boolean) (defaults to: false)

    Whether to use FBA logic

  • language_tag (String, nil) (defaults to: nil)

    Override language tag

Returns:

  • (Array<Hash>)

    Array of patch objects



387
388
389
390
391
392
393
394
395
396
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 387

def get_amz_all_patches(fba: false, language_tag: nil)
  return [] unless amazon_catalog_item?

  begin
    amazon_json_generator(fba:, language_tag:).get_patches
  rescue StandardError => e
    Rails.logger.error "[CatalogItem] Failed to get Amazon patches: #{e.class} - #{e.message}"
    []
  end
end

#get_amz_attribute(attribute_name, fba: false, language_tag: nil, format: :patch, operation: :replace) ⇒ Hash, ...

Retrieves a single Amazon attribute's PATCH JSON through the full listing generator stack.

Examples:

Get the wattage patch JSON

catalog_item.get_amz_attribute('wattage')
# => { op: :replace, path: "/attributes/wattage", value: [{ value: 150, unit: "watts", marketplace_id: "ATVPDKIKX0DER" }] }

Get just the built value array

catalog_item.get_amz_attribute('item_name', format: :value)
# => [{ value: "Floor Heating Mat 120V", language_tag: "en_US", marketplace_id: "ATVPDKIKX0DER" }]

Get all available attributes

catalog_item.amz_available_attributes
# => ["wattage", "voltage", "item_name", ...]

Parameters:

  • attribute_name (String, Symbol)

    The attribute name (e.g., 'wattage', 'item_name', 'voltage')

  • fba (Boolean) (defaults to: false)

    Whether to use FBA logic (default: false)

  • language_tag (String, nil) (defaults to: nil)

    Override language tag (default: uses catalog's amazon_locales)

  • format (Symbol) (defaults to: :patch)

    Output format - :patch (full patch JSON), :value (just the built value array), :raw (raw value before Amazon formatting)

  • operation (Symbol) (defaults to: :replace)

Returns:

  • (Hash, Array, Object, nil)

    The attribute data in the requested format, or nil if unavailable



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 333

def get_amz_attribute(attribute_name, fba: false, language_tag: nil, format: :patch, operation: :replace)
  return nil unless amazon_catalog_item?

  attribute_name = attribute_name.to_s.underscore.to_sym
  attribute_actions = { attribute_name => operation }

  begin
    generator = amazon_json_generator(attribute_actions:, fba:, language_tag:)
    attribute_builder = Edi::Amazon::JsonListingGenerator::Attributes::AttributeFactory.build(
      attribute_name,
      catalog_item: self,
      variation: nil,
      language_tag: generator.language_tag,
      enum_mapper: generator.enum_mapper,
      marketplace_id: generator.marketplace_id,
      fba: fba
    )

    built_value = attribute_builder.build

    case format
    when :raw
      attribute_builder.value
    when :value
      built_value
    when :patch
      return nil if built_value.blank? && operation != :delete

      {
        op: operation,
        path: "/attributes/#{attribute_name}",
        value: operation == :delete ? [{ marketplace_id: generator.marketplace_id }] : built_value
      }
    else
      built_value
    end
  rescue NameError => e
    Rails.logger.warn "[CatalogItem] Amazon attribute '#{attribute_name}' not found: #{e.message}"
    nil
  rescue ArgumentError => e
    Rails.logger.warn "[CatalogItem] Amazon attribute '#{attribute_name}' failed to initialize: #{e.message}"
    nil
  rescue StandardError => e
    Rails.logger.error "[CatalogItem] Amazon attribute '#{attribute_name}' error: #{e.class} - #{e.message}"
    nil
  end
end

#get_amz_attributes(attribute_names, fba: false, language_tag: nil, format: :patch) ⇒ Hash

Retrieves multiple Amazon attributes at once

Examples:

Get multiple attributes

catalog_item.get_amz_attributes(['wattage', 'voltage', 'item_name'])
# => { wattage: {...}, voltage: {...}, item_name: {...} }

Parameters:

  • attribute_names (Array<String, Symbol>)

    List of attribute names to retrieve

  • fba (Boolean) (defaults to: false)

    Whether to use FBA logic

  • language_tag (String, nil) (defaults to: nil)

    Override language tag

  • format (Symbol) (defaults to: :patch)

    Output format - :patch, :value, or :raw

Returns:

  • (Hash)

    Hash of attribute_name => result



416
417
418
419
420
421
422
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 416

def get_amz_attributes(attribute_names, fba: false, language_tag: nil, format: :patch)
  return {} unless amazon_catalog_item?

  attribute_names.to_h do |attr_name|
    [attr_name.to_sym, get_amz_attribute(attr_name, fba:, language_tag:, format:)]
  end
end

#has_amazon_fba?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 58

def has_amazon_fba?
  amazon_fba_sku.present? && (amazon_fba_labeling_upc? || amazon_fba_labeling_fnsku?)
end

#max_discount_allowedObject



643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 643

def max_discount_allowed
  # Calculate the minimum sale price required to meet the minimum profit margin
  minimum_sale_price = amazon_item_cost / (1 - (amazon_minimum_profit_margin / 100.0))

  # Calculate the maximum discount in dollars, 0 being the maximum
  max_discount = [msrp - minimum_sale_price, 0.0].max

  # Calculate the maximum discount as a percentage of MSRP
  max_discount_percentage = ((max_discount / msrp) * 100).round(2)

  # Return both dollar and percentage values
  {
    max_discount_dollars: max_discount.round(2),
    max_discount_percentage: max_discount_percentage
  }
end

#max_discount_allowed_dollarsObject



660
661
662
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 660

def max_discount_allowed_dollars
  max_discount_allowed[:max_discount_dollars]
end

#max_discount_allowed_percentageObject



664
665
666
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 664

def max_discount_allowed_percentage
  max_discount_allowed[:max_discount_percentage]
end

#profit_margin_amazon_minimum_below_targetObject



630
631
632
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 630

def profit_margin_amazon_minimum_below_target
  profit_marging_amazon_minimum_seller_allowed_price < amazon_minimum_profit_margin
end

#profit_marging_amazon_maximum_seller_allowed_priceObject



634
635
636
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 634

def profit_marging_amazon_maximum_seller_allowed_price
  calculate_profit_margin amazon_maximum_seller_allowed_price_with_tax
end

#profit_marging_amazon_minimum_seller_allowed_priceObject



626
627
628
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 626

def profit_marging_amazon_minimum_seller_allowed_price
  calculate_profit_margin amazon_minimum_seller_allowed_price_with_tax
end

#ready_to_print_amazon_fba_labels?Boolean

Returns:

  • (Boolean)


506
507
508
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 506

def ready_to_print_amazon_fba_labels?
  catalog.amazon_catalog? && amazon_label_requirements.blank?
end

#retailer_information_first_localeObject



270
271
272
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 270

def retailer_information_first_locale
  retailer_information&.with_indifferent_access&.values&.first || {}
end

#title_for_amazonObject

Alias for
to: :item#title_for_amazon

Returns:

  • (Object)
             to: :item#title_for_amazon
    

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 39

delegate :title_for_amazon,
:amazon_title,
:amazon_variation,
:amazon_description,
:amazon_generic_keyword,
:amazon_target_keywords,
:amazon_feature_1,
:amazon_feature_2,
:amazon_feature_3,
:amazon_feature_4,
:amazon_feature_5,
:amazon_feature_6,
:amazon_feature_7,
:amazon_feature_8,
:amazon_feature_9,
allow_nil: true,
to: :item