Class: SpreeSquare::CatalogObjectMapper
- Inherits:
-
Object
- Object
- SpreeSquare::CatalogObjectMapper
- Defined in:
- app/services/spree_square/catalog_object_mapper.rb
Overview
Maps single Square catalog objects (CATEGORY, ITEM, ITEM_VARIATION) onto Spree records. Square is the source of truth for the menu — this is a one-way mirror, never the reverse.
related_objects_by_id is a lookup (built by CatalogImporter from a
SearchCatalogObjects response's related_objects) used to resolve
references like an item's category_id to the full CatalogObjectCategory,
or an image id to its CatalogObjectImage.
Constant Summary collapse
- VARIATION_OPTION_TYPE_NAME =
One shared OptionType for any item that has more than one variation (e.g. sizes). Single-variation items skip this entirely and use the product's master variant — avoids OptionType/OptionValue bookkeeping for the common case, since most menu items don't have real variations.
'square_variation'.freeze
Instance Method Summary collapse
-
#initialize(related_objects_by_id: {}) ⇒ CatalogObjectMapper
constructor
A new instance of CatalogObjectMapper.
- #map_category(square_object) ⇒ Object
- #map_item(square_object) ⇒ Object
- #map_modifier(square_object, modifier_list) ⇒ Object
-
#map_modifier_list(square_object) ⇒ Object
A Square MODIFIER_LIST embeds its full MODIFIER objects inline (same pattern as ITEM#variations) — no follow-up API call needed.
- #map_variation(square_object, product) ⇒ Object
Constructor Details
#initialize(related_objects_by_id: {}) ⇒ CatalogObjectMapper
Returns a new instance of CatalogObjectMapper.
19 20 21 22 23 24 |
# File 'app/services/spree_square/catalog_object_mapper.rb', line 19 def initialize(related_objects_by_id: {}) @related_objects_by_id = @store = Spree::Store.default @shipping_category = Spree::ShippingCategory.find_by(name: 'Default') || Spree::ShippingCategory.first @channel = Spree::Channel.find_by(code: 'online') || Spree::Channel.first end |
Instance Method Details
#map_category(square_object) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'app/services/spree_square/catalog_object_mapper.rb', line 26 def map_category(square_object) data = square_object.category_data name = data.name.presence || 'Uncategorized' mapping = SpreeSquare::TaxonMapping.find_or_initialize_by(square_category_id: square_object.id) # Square's SearchCatalogObjects index is only eventually consistent — # a category deleted and recreated (or, as observed in practice, a # stale duplicate momentarily reappearing alongside the fresh one) can # surface a second CatalogObject with a different id but the same # name before a TaxonMapping exists for it. Category name+store is # unique in Spree (see Spree::Category#requires_taxonomy? — no # taxonomy, so uniqueness is scoped to store_id), so falling back to # an existing same-named category avoids a hard crash on that race # instead of trying (and failing) to create a duplicate. taxon = mapping.taxon || Spree::Category.find_by(store: @store, name: name) || Spree::Category.new(store: @store) taxon.name = name taxon.save! mapping.taxon = taxon mapping.square_version = square_object.version mapping.save! taxon end |
#map_item(square_object) ⇒ Object
78 79 80 81 82 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 |
# File 'app/services/spree_square/catalog_object_mapper.rb', line 78 def map_item(square_object) data = square_object.item_data mapping = SpreeSquare::CatalogMapping.find_or_initialize_by( square_catalog_object_id: square_object.id, square_object_type: SpreeSquare::CatalogMapping::ITEM ) return mapping.product if mapping.persisted? && mapping.stale?(square_object.version) product = mapping.product || Spree::Product.new( store: @store, shipping_category: @shipping_category, status: 'active', slug: generate_slug(data.name, square_object.id) ) product.name = data.name.presence || 'Untitled item' product.description = data.description product.save! publish!(product) assign_taxons!(product, data) assign_modifier_lists!(product, data) mapping.product = product mapping.square_version = square_object.version mapping.last_synced_at = Time.current mapping.save! Array(data.variations).each { |variation| map_variation(variation, product) } import_primary_image(product, data.image_ids&.first) if product.images.empty? revalidate!(product) product end |
#map_modifier(square_object, modifier_list) ⇒ Object
67 68 69 70 71 72 73 74 75 76 |
# File 'app/services/spree_square/catalog_object_mapper.rb', line 67 def map_modifier(square_object, modifier_list) data = square_object.modifier_data modifier = SpreeSquare::Modifier.find_or_initialize_by(square_modifier_id: square_object.id) modifier.modifier_list = modifier_list modifier.name = data.name.presence || 'Option' modifier.price_cents = data.price_money&.amount || 0 modifier.square_version = square_object.version modifier.save! modifier end |
#map_modifier_list(square_object) ⇒ Object
A Square MODIFIER_LIST embeds its full MODIFIER objects inline (same pattern as ITEM#variations) — no follow-up API call needed.
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'app/services/spree_square/catalog_object_mapper.rb', line 53 def map_modifier_list(square_object) data = square_object.modifier_list_data list = SpreeSquare::ModifierList.find_or_initialize_by(square_modifier_list_id: square_object.id) list.name = data.name.presence || 'Options' list.selection_type = data.selection_type.presence || SpreeSquare::ModifierList::SINGLE list.min_selected_modifiers = data.min_selected_modifiers list.max_selected_modifiers = data.max_selected_modifiers list.square_version = square_object.version list.save! Array(data.modifiers).each { |modifier| map_modifier(modifier, list) } list end |
#map_variation(square_object, product) ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'app/services/spree_square/catalog_object_mapper.rb', line 112 def map_variation(square_object, product) data = square_object.item_variation_data mapping = SpreeSquare::CatalogMapping.find_or_initialize_by( square_catalog_object_id: square_object.id, square_object_type: SpreeSquare::CatalogMapping::ITEM_VARIATION ) return mapping.variant if mapping.persisted? && mapping.stale?(square_object.version) variant = mapping.variant || pick_variant(product, data) variant.sku = square_object.id if variant.sku.blank? variant.save! amount = (data.price_money&.amount || 0) / 100.0 variant.set_price(data.price_money&.currency || @store.default_currency, amount) mapping.variant = variant mapping.square_version = square_object.version mapping.last_synced_at = Time.current mapping.save! # A variation's own price/sku change touches the product without # firing Spree's product.updated event (see Revalidator) — the # storefront's product cache needs telling explicitly either way. revalidate!(product) variant end |