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

Constant Summary collapse

AMAZON_DEFAULT_BUSINESS_DISCOUNT =

%

5
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)



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

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:



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

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:



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

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:



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

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:



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

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:



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

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:



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

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

Instance Method Details

#amalytix_tagsObject



665
666
667
668
669
670
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 665

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



462
463
464
465
466
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 462

def amazon_asin
  return unless amazon_catalog_item?

  third_party_part_number.presence || item.amazon_asin
end

#amazon_business_priceObject



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

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



513
514
515
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 513

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

#amazon_business_price_fbaObject



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

def amazon_business_price_fba
  amazon_business_price&.-fba_discount
end

#amazon_business_price_with_taxObject

Returns the computer amazon business price



526
527
528
529
530
531
532
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 526

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



534
535
536
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 534

def amazon_business_price_with_tax_fba
  amazon_business_price_with_tax&.-fba_discount
end

#amazon_catalog_item?Boolean

Returns:

  • (Boolean)


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

def amazon_catalog_item?
  catalog.amazon_catalog?
end

#amazon_current_imagesObject



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

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



235
236
237
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
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 235

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? { |r| r == false }
               status = :partial_error if res.any? { |r| r == false || r.exception? } && res.any? { |ecl| ecl.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? { |ecl| ecl.processed? }
               status = :partial_error if res.any? { |ecl| ecl.processed? } && res.any? { |ecl| ecl.exception? }
               status = :error if res.all? { |ecl| ecl.exception? }
               { status: status, message: "Catalog Item ASIN #{amazon_asin} and FBA SKU #{amazon_fba_sku}: listings deleted from Amazon!", ecls: res }
             end # Here we essentially duplicate all DELETE listing data but use the FBA SKU so that we delete both listings in parallel
           else
             status = :success if res.all? { |ecl| ecl.processed? }
             status = :partial_error if res.any? { |ecl| ecl.processed? } && res.any? { |ecl| ecl.exception? }
             status = :error if res.all? { |ecl| ecl.exception? }
             { status: status, message: "Catalog Item SKU #{sku}: listing deleted from Amazon!", ecls: res }
           end

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

  result
end

#amazon_descriptionObject

Alias for
to: :item#amazon_description

Returns:

  • (Object)
             to: :item#amazon_description
    

See Also:



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

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



508
509
510
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 508

def amazon_effective_business_price_discount
  business_discount_override.presence || AMAZON_DEFAULT_BUSINESS_DISCOUNT
end

#amazon_effective_desired_product_typeObject



302
303
304
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 302

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:



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

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:



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

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:



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

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:



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

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:



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

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:



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

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:



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

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:



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

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:



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

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:



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

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



636
637
638
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 636

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



306
307
308
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 306

def amazon_json_generator(marketplace_id: nil, attribute_actions: nil, fba: false, language_tag: nil, business_price_available: true)
  Edi::Amazon::JsonListingGenerator::Factory.generator_for_catalog_item(self, attribute_actions:, fba:, language_tag:, business_price_available:)
end

#amazon_label_requirementsObject



496
497
498
499
500
501
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 496

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

#amazon_list_priceObject



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

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



281
282
283
284
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 281

def amazon_locales # Update with marketplace supported locales when available
  # 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



538
539
540
541
542
543
544
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 538

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



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

def amazon_lowest_quantity_discounted_price; end

#amazon_maximum_seller_allowed_price_with_taxObject



562
563
564
565
566
567
568
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 562

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



570
571
572
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 570

def amazon_maximum_seller_allowed_price_with_tax_fba
  amazon_maximum_seller_allowed_price_with_tax&.-fba_discount
end

#amazon_minimum_profit_marginObject



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

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)


580
581
582
583
584
585
586
587
588
589
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 580

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



591
592
593
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 591

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



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

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.map { |r| r[: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



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

def amazon_price_with_tax
  price_with_vat || amount
end

#amazon_price_with_tax_fbaObject



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

def amazon_price_with_tax_fba
  amazon_price_with_tax&.-fba_discount
end

#amazon_product_data(fba: false) ⇒ Object



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

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

#amazon_product_type_in_effectObject



286
287
288
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 286

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)


427
428
429
430
431
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 427

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



421
422
423
424
425
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 421

def amazon_product_url
  return unless amazon_marketplace && amazon_asin

  amazon_marketplace.url_for_asin(amazon_asin)
end

#amazon_pull_buy_box_statusObject



87
88
89
90
91
92
93
94
95
96
97
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 87

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



75
76
77
78
79
80
81
82
83
84
85
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 75

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



99
100
101
102
103
104
105
106
107
108
109
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 99

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



111
112
113
114
115
116
117
118
119
120
121
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 111

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



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

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.map { |r| r[: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



546
547
548
549
550
551
552
553
554
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 546

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



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

def amazon_sale_price_with_tax
  sale_price_with_vat || sale_price
end

#amazon_sale_price_with_tax_fbaObject



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

def amazon_sale_price_with_tax_fba
  amazon_sale_price_with_tax&.-fba_discount
end

#amazon_schemaObject



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

def amazon_schema
  return unless amazon_effective_desired_product_type.present?

  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)


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

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



123
124
125
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
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 123

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.map { |r| r[: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



152
153
154
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
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 152

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.map { |r| r[: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:



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

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:



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

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:



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

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



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

def amazon_vendor_code
  catalog.vendor_code.presence
end

#amazon_vendor_item?Boolean

Returns:

  • (Boolean)


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

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



398
399
400
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 398

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

#api_ready_state?Boolean

Returns:

  • (Boolean)


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

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)



675
676
677
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
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 675

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'
  }

  cells.each do |target, partial|
    Turbo::StreamsChannel.broadcast_update_to(
      'amazon_catalog_items',
      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



482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 482

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



468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 468

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



603
604
605
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 603

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



384
385
386
387
388
389
390
391
392
393
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 384

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)

Returns:

  • (Hash, Array, Object, nil)

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



330
331
332
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
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 330

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



413
414
415
416
417
418
419
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 413

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

  attribute_names.each_with_object({}) do |attr_name, result|
    result[attr_name.to_sym] = get_amz_attribute(attr_name, fba:, language_tag:, format:)
  end
end

#has_amazon_fba?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 55

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

#max_discount_allowedObject



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

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



657
658
659
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 657

def max_discount_allowed_dollars
  max_discount_allowed[:max_discount_dollars]
end

#max_discount_allowed_percentageObject



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

def max_discount_allowed_percentage
  max_discount_allowed[:max_discount_percentage]
end

#profit_margin_amazon_minimum_below_targetObject



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

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



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

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



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

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)


503
504
505
# File 'app/concerns/models/catalog_item_amazon_helper.rb', line 503

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

#retailer_information_first_localeObject



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

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:



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

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