Class: Retailer::WalmartItemIdDiscovery
- Inherits:
-
Object
- Object
- Retailer::WalmartItemIdDiscovery
- Defined in:
- app/services/retailer/walmart_item_id_discovery.rb
Overview
Backfills the Walmart product id onto CatalogItem#third_party_part_number
from Walmart's own Marketplace API — the system of record for our listings.
Walmart items need no stored url at all: UrlConstructor's
walmart_usa_url / walmart_canada_url synthesize /ip/product/{id} from the
part number. What was missing was the id. We had been sending inventory and
price feeds for 337 US items since November 2025 without ever reading the
resulting ids back, so every one of them had no probeable URL and silently
left the daily probe run (see ListingIssues::RetailerProbeAdapter's
probe_stale).
GET /v3/items returns wpid — Walmart's alphanumeric product id, e.g.
"4KRAWZGZCAO2". That is not the numeric usItemId in a canonical Walmart URL,
but walmart.com resolves the wpid in the same position (verified 2026-08-08:
/ip/product/4KRAWZGZCAO2 returns the right product page), so it is a valid
direct-PDP identifier and the existing builders need no change.
== Identity
Matching is by seller SKU — ours, echoed back by Walmart — which alone would
be self-referential. So every match is confirmed against the UPC Walmart
publishes for the item independently of us. A mismatch is never written; it is
reported, because a wrong id here puts a wrong competitor price into repricing.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- PROFILES =
Catalog => Heatwave::Configuration profile for that storefront's API.
{ CatalogConstants::WALMART_SELLER_USA => :walmart_seller_us_api, CatalogConstants::WALMART_SELLER_CANADA => :walmart_seller_ca_api }.freeze
Instance Method Summary collapse
-
#initialize(catalog_id: CatalogConstants::WALMART_SELLER_USA, logger: Rails.logger) ⇒ WalmartItemIdDiscovery
constructor
A new instance of WalmartItemIdDiscovery.
- #run(dry_run: false) ⇒ Result
Constructor Details
#initialize(catalog_id: CatalogConstants::WALMART_SELLER_USA, logger: Rails.logger) ⇒ WalmartItemIdDiscovery
Returns a new instance of WalmartItemIdDiscovery.
50 51 52 53 54 |
# File 'app/services/retailer/walmart_item_id_discovery.rb', line 50 def initialize(catalog_id: CatalogConstants::WALMART_SELLER_USA, logger: Rails.logger) @catalog_id = catalog_id @logger = logger @profile = PROFILES.fetch(catalog_id) end |
Instance Method Details
#run(dry_run: false) ⇒ Result
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 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 |
# File 'app/services/retailer/walmart_item_id_discovery.rb', line 58 def run(dry_run: false) updated = [] already = 0 unlisted = [] conflicts = [] candidates.each do |catalog_item| listing = remote_listing(catalog_item.sku.to_s) next unlisted << catalog_item.sku.to_s if listing.nil? wpid = listing['wpid'].to_s next unlisted << catalog_item.sku.to_s if wpid.blank? # Matching is by seller SKU, which is OUR string echoed back, so it proves # nothing on its own. A positive barcode match is REQUIRED: absent or # unreadable barcodes on either side mean we cannot confirm the listing is # this product, and an unconfirmed id is exactly what puts a wrong # competitor price into repricing. Unconfirmed is reported, never written. ours = normalize_gtin(catalog_item.item&.upc) theirs = (listing) unless ours.present? && theirs.include?(ours) conflicts << { sku: catalog_item.sku.to_s, ours: ours.presence || '(none)', theirs: theirs.presence&.join('/') || '(none)' } next end if catalog_item.third_party_part_number == wpid already += 1 next end updated << { catalog_item_id: catalog_item.id, sku: catalog_item.sku.to_s, wpid: wpid } next if dry_run # Conditional on the column still being blank: this walks hundreds of # items over a slow external API, and a concurrent EDI pull or a second # run of this service could have filled it meanwhile. Re-checking in the # UPDATE means the later writer never clobbers the earlier one. written = CatalogItem.where(id: catalog_item.id, third_party_part_number: [nil, '']) .update_all(third_party_part_number: wpid, updated_at: Time.current) if written.zero? @logger.info "[WalmartItemIdDiscovery] #{catalog_item.id} #{catalog_item.sku}: id set concurrently, left alone" updated.pop already += 1 next end @logger.info "[WalmartItemIdDiscovery] #{catalog_item.id} #{catalog_item.sku}: tppn -> #{wpid}" end Result.new(updated:, already_set: already, unlisted:, conflicts:, dry_run:) end |