Class: Edi::Amazon::JsonListingGenerator::BaseGenerator

Inherits:
BaseService
  • Object
show all
Includes:
Memery
Defined in:
app/services/edi/amazon/json_listing_generator/base_generator.rb

Defined Under Namespace

Classes: GenerateResult

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from BaseService

#log_debug, #log_error, #log_info, #log_warning, #logger, #options, #process, #tagged_logger

Constructor Details

#initialize(catalog_item, locale: nil, attribute_actions: nil, product_type: nil, logger: nil, variation: nil, marketplace_id: nil, fba: false, language_tag: nil, business_price_available: true) ⇒ BaseGenerator

Returns a new instance of BaseGenerator.

Raises:

  • (ArgumentError)


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

def initialize(catalog_item, locale: nil, attribute_actions: nil, product_type: nil, logger: nil, variation: nil, marketplace_id: nil, fba: false, language_tag: nil, business_price_available: true)
  raise ArgumentError, "catalog_item is required for #{self.class.name}" if catalog_item.nil?

  @catalog_item = catalog_item
  @variation = variation
  @item = catalog_item.item
  @product_type = product_type || @variation&.amazon_desired_product_type || catalog_item.amazon_product_type_in_effect
  @locale = locale || @catalog_item.amazon_locales.first
  # Normalize locale
  @language_tag = language_tag || @locale.to_s.tr('-', '_').presence # Ensure we use a normalized tag
  @business_price_available = business_price_available
  @marketplace_id = marketplace_id || @catalog_item.catalog.amazon_marketplace.marketplace_identifier # MUST be present
  @marketplace = AmazonMarketplace.find_by(marketplace_identifier: @marketplace_id)
  @marketplace_country_iso = @marketplace.country.iso
  raise ArgumentError, "Cannot initialize #{self.class.name} without locale defined, product_type: #{@product_type}, @marketplace&.id: #{@marketplace&.id}, catalog_item&.sku: #{@catalog_item&.sku}, variation&.id: #{@variation&.id}" if @locale.blank?
  raise ArgumentError, "Cannot initialize #{self.class.name} for CatalogItem[#{@catalog_item&.id}] (sku: #{@catalog_item&.sku}) — no product_type set. Assign an amazon_product_type on the catalog item or variation first." if @product_type.blank?
  @amazon_schema = AmazonSchema.amazon_channel_seller.where(product_type: @product_type).where(amazon_marketplace_id: @marketplace.id).first

  # If we don't have the schema, try to pull it
  if @amazon_schema.blank?
    @catalog_item.amazon_pull_listing_schema(@product_type)
    @amazon_schema = AmazonSchema.amazon_channel_seller.where(product_type: @product_type).where(amazon_marketplace_id: @marketplace.id).first
  end

  # If we still don't have the schema raise the error
  raise ArgumentError, "Cannot initialize #{self.class.name} for CatalogItem[#{@catalog_item&.id}] (sku: #{@catalog_item&.sku}) without #{@product_type} schema present for marketplace: #{@marketplace.name}" if @amazon_schema.blank?

  # Pass attributes and actions as a hash of symbols,.
  # For e.g. for inventory we pass:
  # see: https://developer-docs.amazon.com/sp-api/docs/listing-workflow-migration-tutorial#submitting-inventory-data
  # attribute_actions: {fulfillment_availability: :replace}
  # For e.g. for creating new listing offers we might pass:
  # see: https://developer-docs.amazon.com/sp-api/docs/listing-workflow-migration-tutorial
  # attribute_actions: {condition_type: :replace, merchant_suggested_asin: :replace, purchasable_offer: :replace}

  # If attributes actions are not specified, we use the combined defaults

  @attribute_actions = attribute_actions&.transform_keys(&:to_sym)&.transform_values(&:to_sym) || @amazon_schema.keys.reject{|k| k.to_s.include?('other_') && k.to_s.include?('image_locator')}.index_with { |_attribute| :replace } # here we exclude any other_*_image_locator attributes, because the only reliable way to set these is via bulk upload on Amazon Seller Central, per Christian Billen's (cbillen@warmlyyours.com) email of 11/21/25 with subject 'Amazon patch/put'

  # Until we refactor FBA into catalog_items, we need to pass this to generate the correct FBA logic for JSON feeds
  @fba = fba

  super(logger:)
end

Instance Attribute Details

#amazon_schemaObject (readonly)

Returns the value of attribute amazon_schema.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def amazon_schema
  @amazon_schema
end

#attribute_actionsObject (readonly)

Returns the value of attribute attribute_actions.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def attribute_actions
  @attribute_actions
end

#business_price_availableObject (readonly)

Returns the value of attribute business_price_available.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def business_price_available
  @business_price_available
end

#catalog_itemObject (readonly)

Returns the value of attribute catalog_item.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def catalog_item
  @catalog_item
end

#fbaObject (readonly)

Returns the value of attribute fba.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def fba
  @fba
end

#itemObject (readonly)

Returns the value of attribute item.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def item
  @item
end

#language_tagObject (readonly)

Returns the value of attribute language_tag.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def language_tag
  @language_tag
end

#localeObject (readonly)

Returns the value of attribute locale.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def locale
  @locale
end

#marketplace_country_isoObject (readonly)

Returns the value of attribute marketplace_country_iso.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def marketplace_country_iso
  @marketplace_country_iso
end

#marketplace_idObject (readonly)

Returns the value of attribute marketplace_id.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def marketplace_id
  @marketplace_id
end

#missing_attributesObject (readonly)

Returns the value of attribute missing_attributes.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def missing_attributes
  @missing_attributes
end

#product_typeObject (readonly)

Returns the value of attribute product_type.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def product_type
  @product_type
end

#variationObject (readonly)

Returns the value of attribute variation.



4
5
6
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 4

def variation
  @variation
end

Instance Method Details

#append_attribute(attributes_hash, attribute, include_nil: false) ⇒ Object

Appends an attribute to the given attributes hash based on the attribute name.



108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 108

def append_attribute(attributes_hash, attribute, include_nil: false)
  attribute_builder = Edi::Amazon::JsonListingGenerator::Attributes::AttributeFactory.build(attribute, catalog_item:, variation:, language_tag:, enum_mapper:, marketplace_id:, fba:) rescue nil
  return attributes_hash unless attribute_builder
  r = attribute_builder.build
  if r.nil? && !include_nil
    @missing_attributes << attribute
    logger.error "#{attribute_builder.class.name} returned nil"
  else
    attributes_hash[attribute] = r
  end
  attributes_hash
end

#build_attributes(include_nil: false) ⇒ Object

Builds a hash of attributes for a product listing.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 64

def build_attributes(include_nil: false)
  attributes_hash = {}
  @missing_attributes = []
  # To make everything with locale, we will wrap into a mobility call
  Mobility.with_locale(locale) do
    @attribute_actions.each do |attribute, attribute_action|
      next if attribute_action == :skip

      include_nil = true if attribute_action.to_sym == :delete
      append_attribute(attributes_hash, attribute, include_nil:)
    end
  end
  attributes_hash
end

#enum_mapperObject



58
59
60
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 58

def enum_mapper
  Edi::Amazon::EnumMapper.new(amazon_schema.schema)
end

#generateObject



79
80
81
82
83
84
85
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 79

def generate
  GenerateResult.new(
    attributes_hash: build_attributes,
    product_data_hash: product_data_hash,
    missing_attributes: @missing_attributes
  )
end

#get_patchesObject



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 87

def get_patches
  patches = generate.product_data_hash.values.flat_map do |locale_data|
    locale_data[:attributes].map do |attribute, value_arr|
      operation = attribute_actions[attribute]
      next if value_arr.blank? && operation.in?(%i[skip replace])

      # Delete operation value
      value_arr = [{ marketplace_id: }] if operation == :delete

      {
        op: operation,
        path: "/attributes/#{attribute}",
        value: value_arr
      }.compact
    end
  end

  patches.compact.uniq # we might have the same attribute generated for two different locales that are not locale dependent
end

#product_data_hashObject



121
122
123
124
125
126
127
128
# File 'app/services/edi/amazon/json_listing_generator/base_generator.rb', line 121

def product_data_hash
  {
    language_tag => {
      asin: @catalog_item.amazon_asin,
      attributes: build_attributes
    }
  }
end