Class: Edi::Wayfair::ListingGenerator::ListingGenerator

Inherits:
Object
  • Object
show all
Defined in:
app/services/edi/wayfair/listing_generator/listing_generator.rb

Overview

Generates Wayfair product update payloads using taxonomy-based attributes.

Similar to Amazon's JsonListingGenerator but for Wayfair's
updateMarketSpecificCatalogItems mutation.

Usage:
generator = ListingGenerator.new(catalog_item)
payload = generator.generate

=> { supplierPartNumber: "SKU123", attributes: [...] }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(catalog_item, wayfair_schema: nil) ⇒ ListingGenerator

Returns a new instance of ListingGenerator.

Parameters:

  • catalog_item (CatalogItem)

    the catalog item to build a payload for

  • wayfair_schema (WayfairSchema, nil) (defaults to: nil)

    taxonomy schema to use; fetched for
    the item's Wayfair taxonomy category when omitted



31
32
33
34
35
# File 'app/services/edi/wayfair/listing_generator/listing_generator.rb', line 31

def initialize(catalog_item, wayfair_schema: nil)
  @catalog_item = catalog_item
  @wayfair_schema = wayfair_schema || fetch_schema
  @errors = []
end

Instance Attribute Details

#catalog_itemCatalogItem (readonly)

Returns the catalog item being mapped.

Returns:



26
27
28
# File 'app/services/edi/wayfair/listing_generator/listing_generator.rb', line 26

def catalog_item
  @catalog_item
end

#errorsObject (readonly)

Returns the value of attribute errors.



26
# File 'app/services/edi/wayfair/listing_generator/listing_generator.rb', line 26

attr_reader :catalog_item, :wayfair_schema, :errors

#wayfair_schemaWayfairSchema? (readonly)

Returns the resolved taxonomy schema for the item's class.

Returns:

  • (WayfairSchema, nil)

    the resolved taxonomy schema for the item's class



26
# File 'app/services/edi/wayfair/listing_generator/listing_generator.rb', line 26

attr_reader :catalog_item, :wayfair_schema, :errors

Instance Method Details

#attribute_coverageHash

Check which attributes can be mapped for this item's class.

Returns:

  • (Hash)

    { mappable: [names], missing: [names], unmapped_required: [{id, title}] }

    • +mappable+: attribute mappers that produced a value valid for this class
    • +missing+: attribute mappers that produced no value for this class
    • +unmapped_required+: REQUIRED class specs (numeric taxonomy ids) we have
      no value for — the specs a human still has to supply. Driven off the live
      schema's required attributes, so it reports the right specs per class
      rather than only the handful of hardcoded (heater) mapper ids.


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'app/services/edi/wayfair/listing_generator/listing_generator.rb', line 83

def attribute_coverage
  return { mappable: [], missing: [], unmapped_required: [] } unless wayfair_schema

  mappable = []
  missing = []
  mapped_ids = []

  Attributes::AttributeFactory.attributes_available.each do |attr_name|
    mapper = Attributes::AttributeFactory.build(attr_name, catalog_item: catalog_item, wayfair_schema: wayfair_schema)
    if mapper.build.present?
      mappable << attr_name
      mapped_ids << mapper.taxonomy_attribute_id.to_s
    else
      missing << attr_name
    end
  rescue StandardError => e
    Rails.logger.debug { "ListingGenerator: Error checking #{attr_name}: #{e.message}" }
    missing << attr_name
  end

  unmapped_required = wayfair_schema.required_attributes.filter_map do |attr|
    id = attr['taxonomyAttributeId'].to_s
    # Only class-specific product specs, identified by a bare numeric id (any
    # datatype — Material/Thermostat Type are MULTI_CHOICE/SINGLE_CHOICE). The
    # namespaced core::/price::/shippingAndFulfillment::/propSixtyFive:: platform
    # fields and the structural Class ID (1) are supplied by other systems, not
    # these attribute mappers.
    next unless id.match?(/\A\d+\z/) && id != '1'
    next if mapped_ids.include?(id)

    { id: id, title: attr['title'] }
  end

  { mappable: mappable, missing: missing, unmapped_required: unmapped_required }
end

#generate(attributes: nil) ⇒ Hash

Generate the full update payload

Parameters:

  • attributes (Array<String>, nil) (defaults to: nil)

    Optional list of attribute names to include (nil = all available)

Returns:

  • (Hash)

    The update payload for updateMarketSpecificCatalogItems



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'app/services/edi/wayfair/listing_generator/listing_generator.rb', line 40

def generate(attributes: nil)
  @errors = []

  unless wayfair_schema
    @errors << 'No Wayfair schema available for this catalog item'
    return nil
  end

  attribute_payloads = build_attribute_payloads(attributes)

  if attribute_payloads.empty?
    @errors << 'No attributes could be mapped'
    return nil
  end

  {
    supplierPartNumber: supplier_part_number,
    attributes: attribute_payloads
  }
end

#generate_required_onlyHash?

Generate payload for only the attributes the class marks REQUIRED that we
can map. Resolves each mapper against the live schema (by title) — the same
schema-driven resolution #attribute_coverage uses — so class-specific
required ids (e.g. floor-heating Wattage 383497) select the right mapper
instead of being missed by the legacy hardcoded-id registry.

Returns:

  • (Hash, nil)

    the update payload, or nil when nothing could be mapped



68
69
70
71
72
73
# File 'app/services/edi/wayfair/listing_generator/listing_generator.rb', line 68

def generate_required_only
  required_ids = wayfair_schema&.required_attributes&.pluck('taxonomyAttributeId')&.map(&:to_s) || []
  required_names = required_ids.empty? ? [] : mappers_for_required(required_ids)

  generate(attributes: required_names)
end

#previewHash

Preview what would be sent (for debugging)

Returns:

  • (Hash)

    preview data with supplier part number, attribute count, and
    per-attribute id/title/value, or an { error: ... } hash when generation failed



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/services/edi/wayfair/listing_generator/listing_generator.rb', line 123

def preview
  payload = generate
  return { error: errors.first } unless payload

  {
    supplier_part_number: payload[:supplierPartNumber],
    attribute_count: payload[:attributes].size,
    attributes: payload[:attributes].map do |attr|
      {
        id: attr[:taxonomyAttributeId],
        title: wayfair_schema.find_attribute(attr[:taxonomyAttributeId])&.dig('title'),
        value: attr[:value] || attr[:values]
      }
    end
  }
end