Class: Edi::MiraklSeller::BestbuyCa::ProductDataPresenter
- Inherits:
-
Edi::MiraklSeller::BaseMiraklPresenter
- Object
- SimpleDelegator
- BasePresenter
- Edi::MiraklSeller::BaseMiraklPresenter
- Edi::MiraklSeller::BestbuyCa::ProductDataPresenter
- Defined in:
- app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb
Overview
Service object: product data presenter.
Direct Known Subclasses
Constant Summary collapse
- HEATER_CATEGORY =
BBYCat is the literal Best Buy category code (
CAT_NNNNN), not the human
path you see in their portal. Verified via live/api/hierarchiesagainst
the Outlet shop. Sending the path returns1001|The category … is unknown. 'CAT_32872'- BATHROOM_CATEGORY =
Heaters & Fireplaces
'CAT_328963'- DEFAULT_PRODUCT_CONDITION =
Brand Newfor the marketplace storefront. Outlet subclass overrides
this with a per-item lookup against item.condition. 'Brand New'
Instance Attribute Summary
Attributes inherited from Edi::MiraklSeller::BaseMiraklPresenter
Attributes inherited from BasePresenter
#current_account, #options, #url_helper
Instance Method Summary collapse
-
#accessory? ⇒ Boolean
Ember Towel Warmer Bars, stands, and other parts marketed as accessories in our catalog don't map to a Best Buy product category — they're not sold as standalone listings on BB.
- #bathroom_material_family ⇒ Object
-
#bathroom_mirror? ⇒ Boolean
Heatwave's
is_mirror_defogger?covers anti-fog defoggers; LED backlit mirrors are a separate product line — detect by SKU prefix as a fallback until/unless anis_led_backlit_mirror?predicate lands on Item. - #bathroom_mirror_data ⇒ Object
-
#best_buy_category ⇒ Object
─── Common value helpers ────────────────────────────────────────────.
-
#colour_family ⇒ Object
Best Buy LOV — accepts a small set of strings.
- #country_of_origin ⇒ Object
- #dim_cm(field) ⇒ Object
- #dim_inches(field) ⇒ Object
- #electric_heater_technology ⇒ Object
-
#family ⇒ Object
─── Family dispatch ──────────────────────────────────────────────────.
- #family_data ⇒ Object
- #french_compliant_flag ⇒ Object
-
#generic_data ⇒ Object
─── Generic (cross-category) required + optional fields ──────────────.
-
#heater_type_label ⇒ Object
Indoor-heater-specific LOV mappings — placeholder defaults that match the actual values seen in the manual export.
-
#images ⇒ Object
Up to 10 images for Best Buy (base presenter caps at 9).
- #indoor_heater? ⇒ Boolean
- #indoor_heater_data ⇒ Object
- #outdoor_heater? ⇒ Boolean
- #outdoor_heater_data ⇒ Object
- #product_condition ⇒ Object
-
#short_description_for_bb ⇒ Object
Best Buy requires a short description on every product (REQUIRED for all categories).
- #spec_color ⇒ Object
- #spec_finish ⇒ Object
- #spec_material ⇒ Object
- #thermostat_label ⇒ Object
-
#to_h ⇒ Object
Override the base — Best Buy has four (Outlet: five) product families, not the two-way towel-warmer / infrared-heating-panel split the base assumes.
-
#towel_warmer_data ⇒ Object
─── Per-family data ────────────────────────────────────────────────.
- #weight_kg ⇒ Object
- #yes_no(value) ⇒ Object
Methods inherited from Edi::MiraklSeller::BaseMiraklPresenter
Methods inherited from BasePresenter
#can?, #capture, #concat, #content_tag, #fa_icon, #h, #initialize, #link_to, #number_to_currency, #present, presents, #r, #safe_present, #simple_format, #u
Constructor Details
This class inherits a constructor from Edi::MiraklSeller::BaseMiraklPresenter
Instance Method Details
#accessory? ⇒ Boolean
Ember Towel Warmer Bars, stands, and other parts marketed as accessories
in our catalog don't map to a Best Buy product category — they're not
sold as standalone listings on BB. Returning nil from family (via this
predicate) tells the configurator to skip them.
107 108 109 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 107 def accessory? item.sku.to_s.start_with?('IP-EM-ACC-') end |
#bathroom_material_family ⇒ Object
328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 328 def bathroom_material_family raw = spec_material.to_s case raw when /stainless|steel|metal/i then 'Stainless Steel' when /aluminium|aluminum/i then 'Aluminum' when /glass/i then 'Glass' when /ceramic|porcelain/i then 'Ceramic' when /plastic|acrylic/i then 'Plastic' when /brass/i then 'Brass' else @error_list << "Best Buy bathroom_material_family unmapped for #{item.sku}: #{raw.inspect}" nil end end |
#bathroom_mirror? ⇒ Boolean
Heatwave's is_mirror_defogger? covers anti-fog defoggers; LED backlit
mirrors are a separate product line — detect by SKU prefix as a fallback
until/unless an is_led_backlit_mirror? predicate lands on Item. The
-MIR- infix catches Ember infrared-heating-panel/mirror hybrids
(e.g. IP-EM-GLS-MIR-0600).
84 85 86 87 88 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 84 def bathroom_mirror? item.is_mirror_defogger? || item.sku.to_s.start_with?('MR-') || item.sku.to_s.include?('-MIR-') end |
#bathroom_mirror_data ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 180 def bathroom_mirror_data { required: { _ProductCondition_20257570_CAT_328963_EN: product_condition, _Type_17305230_CAT_328963_EN: 'Bathroom Mirror' }, optional: { _Height_15404_CAT_35719_EN: dim_cm(:height), _HeightInches_25131_CAT_35719_EN: dim_inches(:height), _Width_6823_CAT_35719_EN: dim_cm(:width), _WidthInches_25132_CAT_35719_EN: dim_inches(:width), _Depth_14236_CAT_35719_EN: dim_cm(:depth), _DepthInches_25133_CAT_35719_EN: dim_inches(:depth), _Weight_5302_CAT_35719_EN: weight_kg, _Colour_5105_CAT_35719_EN: spec_color, _ColourFamily_30576_CAT_328963_EN: colour_family, _Finish_9214_CAT_35719_EN: spec_finish, _Material_23642_CAT_35719_EN: spec_material, _BathroomMaterialFamily_14021032_CAT_328963_EN: bathroom_material_family, _MountingHardwareIncluded_29139_CAT_35719_EN: yes_no(true), _CountryofOrigin_26726_CAT_35719_EN: country_of_origin } } end |
#best_buy_category ⇒ Object
─── Common value helpers ────────────────────────────────────────────
245 246 247 248 249 250 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 245 def best_buy_category case family when :indoor_heater, :outdoor_heater then HEATER_CATEGORY when :towel_warmer, :bathroom_mirror then BATHROOM_CATEGORY end end |
#colour_family ⇒ Object
Best Buy LOV — accepts a small set of strings. Map our raw finish/color
text into the canonical option. Unknowns surface in the error_list so
they can be added explicitly without breaking the upload silently.
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 310 def colour_family raw = (spec_color || spec_finish).to_s case raw when /white|ivory|cream/i then 'White' when /black|matte/i then 'Black' when /silver|chrome|stainless/i then 'Silver' when /gold|brass/i then 'Gold' when /bronze|copper/i then 'Bronze' when /grey|gray/i then 'Grey' when /beige|tan|natural/i then 'Beige' when /brown/i then 'Brown' when /blue/i then 'Blue' else @error_list << "Best Buy colour_family unmapped for #{item.sku}: #{raw.inspect}" nil end end |
#country_of_origin ⇒ Object
343 344 345 346 347 348 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 343 def country_of_origin # Manufacturer origin isn't currently exposed on Item; leave nil and # rely on the optional-flag in Best Buy's schema. Wire in once # `item.country_of_manufacture` (or similar) lands. nil end |
#dim_cm(field) ⇒ Object
281 282 283 284 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 281 def dim_cm(field) v = item.spec_value(field, output_unit: 'cm') v.presence && v.to_f.round(2) end |
#dim_inches(field) ⇒ Object
286 287 288 289 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 286 def dim_inches(field) v = item.spec_value(field, output_unit: 'inch') v.presence && v.to_f.round(2) end |
#electric_heater_technology ⇒ Object
358 359 360 361 362 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 358 def electric_heater_technology # LOV: Infrared, Convection, Radiant, etc. Default to nil rather than # guess wrong; Best Buy allows the field empty for non-applicable items. nil end |
#family ⇒ Object
─── Family dispatch ──────────────────────────────────────────────────
60 61 62 63 64 65 66 67 68 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 60 def family return nil if accessory? return :towel_warmer if item.is_towel_warmer? return :bathroom_mirror if bathroom_mirror? return :outdoor_heater if outdoor_heater? return :indoor_heater if indoor_heater? nil end |
#family_data ⇒ Object
70 71 72 73 74 75 76 77 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 70 def family_data case family when :indoor_heater then indoor_heater_data when :outdoor_heater then outdoor_heater_data when :towel_warmer then towel_warmer_data when :bathroom_mirror then bathroom_mirror_data end end |
#french_compliant_flag ⇒ Object
270 271 272 273 274 275 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 270 def french_compliant_flag # Heatwave doesn't track French-compliance per SKU yet. Default to 'Y' # (compliant) for newly-shipped marketplace items; the validation team # can override per-SKU once the data lands on Item. 'Y' end |
#generic_data ⇒ Object
─── Generic (cross-category) required + optional fields ──────────────
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 138 139 140 141 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 113 def generic_data { required: { BBYCat: best_buy_category, shop_sku: item.sku.to_s[0, 30], _Title_BB_Category_Root_EN: name(locale: :en).to_s[0, 200], _Short_Description_BB_Category_Root_EN: short_description_for_bb.to_s[0, 400], _Brand_Name_Category_Root_EN: 'WarmlyYours', _Primary_UPC_Category_Root_EN: item.ean13, _Model_Number_Category_Root_EN: item.sku.to_s[0, 20], _Manufacturers_Part_Number_Category_Root_EN: item.sku.to_s[0, 30], _MP_Source_Image_URL_01_Category_Root_EN: images[0] }, optional: { _Long_Description_BB_Category_Root_EN: detailed_description_html(locale: :en), _MP_Source_Image_URL_02_Category_Root_EN: images[1], _MP_Source_Image_URL_03_Category_Root_EN: images[2], _MP_Source_Image_URL_04_Category_Root_EN: images[3], _MP_Source_Image_URL_05_Category_Root_EN: images[4], _MP_Source_Image_URL_06_Category_Root_EN: images[5], _MP_Source_Image_URL_07_Category_Root_EN: images[6], _MP_Source_Image_URL_08_Category_Root_EN: images[7], _MP_Source_Image_URL_09_Category_Root_EN: images[8], _MP_Source_Image_URL_10_Category_Root_EN: images[9], _French_Compliant_Category_Root_EN: french_compliant_flag, _Energy_Star_Indicator_Category_Root_EN: nil # TODO: spec_value(:energy_star) once exposed on Item } } end |
#heater_type_label ⇒ Object
Indoor-heater-specific LOV mappings — placeholder defaults that match
the actual values seen in the manual export. Refine when item specs
expose a real value.
354 355 356 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 354 def heater_type_label 'Radiant' # The LOV includes Fan / Fireplace / Radiant / etc. end |
#images ⇒ Object
Up to 10 images for Best Buy (base presenter caps at 9).
144 145 146 147 148 149 150 151 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 144 def images @images ||= item.image_profiles .website_image_profiles .image_order .includes(:image) .limit(10) .map { |p| p.image.image_url(width: 2000, height: 2000, encode_format: :jpeg) } end |
#indoor_heater? ⇒ Boolean
97 98 99 100 101 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 97 def indoor_heater? item.is_snow_melting_product? || item.is_pipe_freeze_protection? || item.is_infrared_heating_panel? end |
#indoor_heater_data ⇒ Object
205 206 207 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/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 205 def indoor_heater_data { required: { _ProductCondition_20257570_CAT_32872_EN: product_condition, _HeaterFireplaceType_11251344_CAT_32872_EN: 'Indoor Heater' }, optional: { _HeatMethodType_12058619_CAT_32872_EN: 'Electric', _HeaterType_5644_CAT_32872_EN: heater_type_label, _ElectricHeaterTechnology_18431374_CAT_32872_EN: electric_heater_technology, # _HeaterDesign_18431376_CAT_32872_EN omitted: Best Buy CA's value # list on CAT_32872 doesn't include any of "Wall/Tabletop/Hanging/ # Floor"; sending "Floor" produced 37 warnings/upload (one per SKU) # and the value was dropped on Mirakl's side anyway. Leave empty # until we map real values from BB's hierarchy API. _Portable_7310_CAT_32872_EN: yes_no(false), _Thermostat_5646_CAT_32872_EN: thermostat_label, _Height_15404_CAT_32872_EN: dim_cm(:height), _HeightInches_25131_CAT_32872_EN: dim_inches(:height), _Width_6823_CAT_32872_EN: dim_cm(:width), _WidthInches_25132_CAT_32872_EN: dim_inches(:width), _Depth_14236_CAT_32872_EN: dim_cm(:depth), _DepthInches_25133_CAT_32872_EN: dim_inches(:depth), _Weight_5302_CAT_32872_EN: weight_kg, _Colour_5105_CAT_32872_EN: spec_color, _Material_23642_CAT_32872_EN: spec_material } } end |
#outdoor_heater? ⇒ Boolean
90 91 92 93 94 95 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 90 def outdoor_heater? # De-icing / roof+gutter / ice-shield products. SKU prefix `ETC` covers # the Electric Trace Cable de-icing line; tighten when an # `is_de_icing_product?` predicate is available. item.sku.to_s.start_with?('ETC') end |
#outdoor_heater_data ⇒ Object
235 236 237 238 239 240 241 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 235 def outdoor_heater_data # Same Best Buy category as indoor (CAT_32872), only HeaterFireplaceType # differs. Reuse the indoor block + flip the required type label. indoor_heater_data.tap do |data| data[:required][:_HeaterFireplaceType_11251344_CAT_32872_EN] = 'Outdoor Heater' end end |
#product_condition ⇒ Object
252 253 254 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 252 def product_condition DEFAULT_PRODUCT_CONDITION end |
#short_description_for_bb ⇒ Object
Best Buy requires a short description on every product (REQUIRED for all
categories). Some Heatwave items legitimately don't have one populated —
mirror Item#default_short_description and fall back through seo_description
then the item name so the integration stays unblocked instead of failing
the whole upload over a single empty column.
261 262 263 264 265 266 267 268 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 261 def short_description_for_bb raw = (item.try(:short_description).presence || item.try(:seo_description).presence || item.name).to_s # Strip simple HTML so a name like "WarmlyYours<sup>®</sup> …" doesn't # land in BB's plain-text column with stray markup. raw.gsub(/<[^>]+>/, '').squish end |
#spec_color ⇒ Object
295 296 297 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 295 def spec_color item.spec_value(:color) end |
#spec_finish ⇒ Object
299 300 301 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 299 def spec_finish item.spec_value(:finish) end |
#spec_material ⇒ Object
303 304 305 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 303 def spec_material item.spec_value(:material) end |
#thermostat_label ⇒ Object
364 365 366 367 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 364 def thermostat_label # LOV: Digital, Analog, Programmable, None nil end |
#to_h ⇒ Object
Override the base — Best Buy has four (Outlet: five) product families,
not the two-way towel-warmer / infrared-heating-panel split the base assumes.
Returns nil for items we can't classify so the configurator can skip
them instead of aborting the entire feed.
52 53 54 55 56 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 52 def to_h return nil if family.nil? generic_data.deep_merge(family_data) end |
#towel_warmer_data ⇒ Object
─── Per-family data ────────────────────────────────────────────────
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 155 def towel_warmer_data { required: { _ProductCondition_20257570_CAT_328963_EN: product_condition, _Type_17305230_CAT_328963_EN: 'Towel Warmer' }, optional: { _Height_15404_CAT_35719_EN: dim_cm(:height), _HeightInches_25131_CAT_35719_EN: dim_inches(:height), _Width_6823_CAT_35719_EN: dim_cm(:width), _WidthInches_25132_CAT_35719_EN: dim_inches(:width), _Depth_14236_CAT_35719_EN: dim_cm(:depth), _DepthInches_25133_CAT_35719_EN: dim_inches(:depth), _Weight_5302_CAT_35719_EN: weight_kg, _Colour_5105_CAT_35719_EN: spec_color, _ColourFamily_30576_CAT_328963_EN: colour_family, _Finish_9214_CAT_35719_EN: spec_finish, _Material_23642_CAT_35719_EN: spec_material, _BathroomMaterialFamily_14021032_CAT_328963_EN: bathroom_material_family, _MountingHardwareIncluded_29139_CAT_35719_EN: yes_no(true), _CountryofOrigin_26726_CAT_35719_EN: country_of_origin } } end |
#weight_kg ⇒ Object
291 292 293 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 291 def weight_kg item.base_weight_converted(unit: 'kg')&.round(2) end |
#yes_no(value) ⇒ Object
277 278 279 |
# File 'app/services/edi/mirakl_seller/bestbuy_ca/product_data_presenter.rb', line 277 def yes_no(value) value ? 'Yes' : 'No' end |