Class: Edi::Amazon::Orchestrator
- Inherits:
-
BaseOrchestrator
- Object
- BaseOrchestrator
- Edi::Amazon::Orchestrator
- Defined in:
- app/services/edi/amazon/orchestrator.rb
Overview
Service object: orchestrator.
Constant Summary collapse
- GOLIVE_DATE_STR =
Golive date str.
'October 11, 2024'
Constants inherited from BaseOrchestrator
BaseOrchestrator::DEFAULT_PENDING_DISCONTINUE_LIFETIME, BaseOrchestrator::ORCHESTRATORS, BaseOrchestrator::RECOMMENDED_EXECUTE_FLOW_EVERY_X_HOUR
Instance Attribute Summary
Attributes inherited from BaseOrchestrator
Class Method Summary collapse
- .execute_feed_submission_result_processor(options = {}) ⇒ Object
- .partners ⇒ Object
- .populate_items_from_all_partners(limit: nil, skus: nil, force: false) ⇒ Object
Instance Method Summary collapse
- #a_plus_content_processor ⇒ Object
- #a_plus_content_remote_path ⇒ Object
- #a_plus_content_retriever ⇒ Object
- #acknowledge_message_enabled? ⇒ Boolean
- #acknowledge_message_sender ⇒ Object
- #buy_box_status_enabled? ⇒ Boolean
- #buy_box_status_retriever ⇒ Object
- #buy_shipping_client ⇒ Object
- #buy_shipping_enabled? ⇒ Boolean
- #catalog_item_information_enabled? ⇒ Boolean
- #catalog_item_information_processor ⇒ Object
- #catalog_item_information_retriever ⇒ Object
- #confirm_message_enabled? ⇒ Boolean
- #confirm_message_processor ⇒ Object
- #confirm_message_sender ⇒ Object
-
#confirm_outbound_processing? ⇒ Boolean
Amazon is a two stage process on , processing.
- #connection_info ⇒ Object
-
#delete_listing_from_catalog_item(catalog_item: nil, amazon_variation: nil, http_method: 'DELETE', attribute_actions: nil, use_fba_sku: false) ⇒ Array
Builds and sends an Amazon listing delete message for a catalog item or variation.
- #delete_listing_message_processor ⇒ Object
- #discontinue_flow_processor ⇒ Object
- #execute_discontinue_flow ⇒ Object
- #execute_fba_order_flow ⇒ Object
- #execute_inventory_flow(use_delta_since_last_message: false) ⇒ Object
- #execute_listing_message_feed_flow ⇒ Object
- #execute_order_flow ⇒ Object
- #execute_order_status_verification ⇒ Object
- #execute_price_flow(use_delta_since_last_message: false) ⇒ Object
- #fba_order_message_enabled? ⇒ Boolean
- #fba_order_message_processor ⇒ Object
- #fba_order_message_retriever ⇒ Object
- #fba_pull_orders_by_po_numbers(po_numbers) ⇒ Object
- #feed_submission_result_processor ⇒ Object
- #inventory_message_enabled? ⇒ Boolean
- #inventory_message_processor ⇒ Object
- #inventory_message_sender ⇒ Object
- #invoice_message_enabled? ⇒ Boolean
- #invoice_message_processor ⇒ Object
- #listing_item_information_processor ⇒ Object
- #listing_item_information_retriever ⇒ Object
- #listing_item_schema_processor ⇒ Object
- #listing_item_schema_retriever ⇒ Object
- #listing_message_enabled? ⇒ Boolean
- #listing_message_feed_enabled? ⇒ Boolean
- #listing_message_feed_processor ⇒ Object
- #listing_message_feed_sender ⇒ Object
- #listing_message_processor ⇒ Object
- #listing_message_sender ⇒ Object
- #listing_schema_message_enabled? ⇒ Boolean
- #order_message_enabled? ⇒ Boolean
- #order_message_processor ⇒ Object
- #order_message_retriever ⇒ Object
- #order_status_verification_enabled? ⇒ Boolean
- #order_status_verifier ⇒ Object
- #orders_with_amazon_id_po(order_id) ⇒ Object
-
#populate_items_from_catalog(limit: nil, skus: nil, force: false) ⇒ Object
A method to populate our item data with amazon's catalog data limit: how many records to process skus: specific skus to process force: normally we only will pull if the information is not present, use this to force a reset.
- #price_message_enabled? ⇒ Boolean
- #price_message_processor ⇒ Object
- #price_message_sender ⇒ Object
- #pull_amazon_variation_listing_information(amazon_variation) ⇒ Object
- #pull_buy_box_status(catalog_item) ⇒ Object
- #pull_catalog_information(catalog_item) ⇒ Object
- #pull_listing_information(catalog_item) ⇒ Object
- #pull_listing_schema(catalog_item, product_type = nil) ⇒ Object
- #pull_schema(product_type) ⇒ Object
- #pull_schema_for_locale(product_type, locale, amazon_schema: nil) ⇒ Object
-
#push_listing_from_amazon_variation(amazon_variation) ⇒ Array
Builds and sends an Amazon listing PUT message for a single variation.
-
#push_listing_from_catalog_item(catalog_item, http_method: 'PATCH', attribute_actions: nil, use_fba_sku: false) ⇒ Array
Builds and sends an Amazon listing-items message for a catalog item.
- #return_notification_message_enabled? ⇒ Boolean
- #return_notification_message_processor ⇒ Object
- #ship_code_mapper ⇒ Object
-
#store_competitor_seller_ids(catalog_item, offers, our_merchant_id) ⇒ Object
Store competitor seller IDs from Buy Box status for discovery Creates AmazonCompetitor records (without names) for any new seller IDs found.
Methods inherited from BaseOrchestrator
all_orchestrators_class, build, build_customer_id_to_partner_key_map, cached_build, cached_orchestrators, catalog_id_to_pending_discontinue_lifetime, catalog_ids_edi_enabled, #customer, #customer_catalog, customer_id_to_partner_key_map, #customer_ids, customer_ids_edi_enabled, customer_ids_with_invoice_message_enabled, #customers, execute_discontinue_flow, execute_flow, execute_inventory_flow, execute_listing_message_feed_flow, execute_order_flow, execute_price_flow, execute_product_data_flow, #execute_product_data_flow, #ignore_back_orders, #initialize, orchestrator_for_customer_id, orchestrators, #pending_discontinue_lifetime, #product_data_enabled?, #should_execute_flow?, #should_execute_order_flow?, #should_execute_product_data_flow?, #test_mode?
Constructor Details
This class inherits a constructor from Edi::BaseOrchestrator
Class Method Details
.execute_feed_submission_result_processor(options = {}) ⇒ Object
375 376 377 |
# File 'app/services/edi/amazon/orchestrator.rb', line 375 def self.execute_feed_submission_result_processor( = {}) orchestrators().each { |o| o.feed_submission_result_processor.process } end |
.partners ⇒ Object
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 57 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 110 111 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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'app/services/edi/amazon/orchestrator.rb', line 13 def self.partners { amazon_seller_central_us: { active: true, partner: :amazon_seller_central_us, customer_id: 5_322_434, fba_store_id: 3, marketplace: 'ATVPDKIKX0DER', merchant_id: 'A1ALB70A8YMUCH', locales: ['en_US'], # aws_access_key_id: Heatwave::Configuration.fetch(:amazon_mws, :aws_access_key_id), # aws_secret_access_key: Heatwave::Configuration.fetch(:amazon_mws, :aws_secret_access_key), order_status_verification_enabled: true, inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: true, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: true, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/catalog/2022-04-01/items/", product_pricing_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/products/pricing/v0/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/aplus/2020-11-01", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/feeds/2021-06-30", buy_shipping_enabled: true, buy_shipping_business_id: 'AmazonShipping_US', failure_timeout_in_minutes: 1440, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'USD' }, amazon_seller_central_ca: { active: true, partner: :amazon_seller_central_ca, customer_id: 5_768_174, fba_store_id: 5, marketplace: 'A2EUQ1WTGCTBG2', merchant_id: 'A1ALB70A8YMUCH', locales: %w[en_CA fr_CA], # aws_access_key_id: Heatwave::Configuration.fetch(:amazon_mws, :aws_access_key_id), # aws_secret_access_key: Heatwave::Configuration.fetch(:amazon_mws, :aws_secret_access_key), order_status_verification_enabled: true, inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: true, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: true, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/catalog/2022-04-01/items/", product_pricing_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/products/pricing/v0/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/aplus/2020-11-01", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/feeds/2021-06-30", buy_shipping_enabled: false, failure_timeout_in_minutes: 1440, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'CAD' }, # North-American (Mexico) — amazon.com.mx. Order-pull only (FBM/MFN): # products list via Build International Listings (US FBA / Remote # Fulfillment), so no product/price/inventory push here. Orders arrive # in MXN; customer_id 26215516 points at catalog 76 (US), so # `order.currency` resolves to USD and OrderMessageProcessor converts # the MXN amounts at the order-date FX rate. First pull validated # 2026-05-29 (ECL 3510368); flipped active here so the scheduled # order flow picks up subsequent MX orders. amazon_seller_central_mx: { active: true, partner: :amazon_seller_central_mx, customer_id: 26_215_516, marketplace: 'A1AM78C64UM0Y8', merchant_id: 'A1ALB70A8YMUCH', locales: ['es_MX'], order_status_verification_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_api, :api_host)}/orders/v0/orders", failure_timeout_in_minutes: 1440, currency: 'USD', # marketplace transacts in MXN; orders book USD via catalog 76, processor converts MXN→USD inventory_message_enabled: false, # MX orders book through catalog 76 (US); no separate MX inventory feed price_message_enabled: false, # ditto — no MX-specific price feed listing_message_feed_enabled: false # ditto — listings live under the US channel via Build International Listings }, amazon_seller_central_fr: { active: true, partner: :amazon_seller_central_fr, customer_id: 22_447_665, marketplace: 'A13V1IB3VIYZZH', merchant_id: 'A1U07I6I50BN2R', locales: ['fr'], language_tag: 'fr_FR', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: true, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'EUR' }, amazon_seller_central_be: { active: true, partner: :amazon_seller_central_be, customer_id: 23_325_148, marketplace: 'AMEN7PMS3EDWL', merchant_id: 'A1U07I6I50BN2R', locales: ['fr'], language_tag: 'fr_FR', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: false, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'EUR' }, amazon_seller_central_de: { active: true, partner: :amazon_seller_central_de, customer_id: 18_007_732, marketplace: 'A1PA6795UKMFR9', merchant_id: 'A1U07I6I50BN2R', locales: ['de'], language_tag: 'de_DE', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: true, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'EUR' }, amazon_seller_central_es: { active: true, partner: :amazon_seller_central_es, customer_id: 22_308_935, marketplace: 'A1RKKUPIHCS9HS', merchant_id: 'A1U07I6I50BN2R', locales: ['es'], language_tag: 'es_ES', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: true, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'EUR' }, amazon_seller_central_it: { active: true, partner: :amazon_seller_central_it, customer_id: 22_788_055, marketplace: 'APJ6JRA9NG5V4', merchant_id: 'A1U07I6I50BN2R', locales: ['it'], language_tag: 'it_IT', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: true, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'EUR' }, amazon_seller_central_nl: { active: true, partner: :amazon_seller_central_nl, customer_id: 23_325_157, marketplace: 'A1805IZSGTT6HS', merchant_id: 'A1U07I6I50BN2R', locales: ['de'], language_tag: 'nl_NL', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: false, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'EUR' }, amazon_seller_central_pl: { active: true, partner: :amazon_seller_central_pl, customer_id: 23_325_160, marketplace: 'A1C3SOZRARQ6R3', merchant_id: 'A1U07I6I50BN2R', locales: ['pl'], language_tag: 'pl_PL', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: false, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'PLN' }, amazon_seller_central_se: { active: true, partner: :amazon_seller_central_se, customer_id: 23_325_161, marketplace: 'A2NODRKZP88ZB9', merchant_id: 'A1U07I6I50BN2R', locales: ['se'], language_tag: 'sv_SE', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: false, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'SEK' }, amazon_seller_central_uk: { active: false, partner: :amazon_seller_central_uk, customer_id: 23_325_163, marketplace: 'A1F83G8C2ARO7P', merchant_id: 'A1U07I6I50BN2R', locales: ['en-UK'], language_tag: 'en_UK', inventory_message_enabled: true, support_contact: 'https://sellercentral.amazon.com/gp/mws/contactus.html', price_message_enabled: true, business_price_available: true, listing_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/listings/2021-08-01/items/", listing_message_feed_enabled: false, product_type_schema_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/definitions/2020-09-01/productTypes/", listing_schema_message_enabled: true, transporter: :http_seller_api, transporter_profile: :amazon_sc_seller_eu_api, acknowledge_only_on_pending_ship_confirm: true, order_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/orders/v0/orders", catalog_item_information_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/catalog/2022-04-01/items/", a_plus_content_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/aplus/2020-11-01/", feed_message_remote_path: "https://#{Heatwave::Configuration.fetch(:amazon_sc_seller_eu_api, :api_host)}/feeds/2021-06-30", failure_timeout_in_minutes: 480, execute_inventory_flow_every_x_hour: 24, # once a day for full inventory feed execute_price_flow_every_x_hour: 24, # once a day for full price feed execute_listing_message_feed_flow_every_x_hour: 24, # once a day for full feed currency: 'GBP' } } end |
.populate_items_from_all_partners(limit: nil, skus: nil, force: false) ⇒ Object
1133 1134 1135 1136 1137 1138 1139 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1133 def self.populate_items_from_all_partners(limit: nil, skus: nil, force: false) res = {} orchestrators.each do |orchestrator| res[orchestrator.partner] = orchestrator.populate_items_from_catalog(limit:, skus:, force:) end res end |
Instance Method Details
#a_plus_content_processor ⇒ Object
578 579 580 581 582 |
# File 'app/services/edi/amazon/orchestrator.rb', line 578 def a_plus_content_processor return Edi::NullProcessor.new(self, :a_plus_content_processor, ) if a_plus_content_remote_path.blank? APlusContentProcessor.new(self, ) end |
#a_plus_content_remote_path ⇒ Object
568 569 570 |
# File 'app/services/edi/amazon/orchestrator.rb', line 568 def a_plus_content_remote_path try(:a_plus_content_remote_path) end |
#a_plus_content_retriever ⇒ Object
572 573 574 575 576 |
# File 'app/services/edi/amazon/orchestrator.rb', line 572 def a_plus_content_retriever return Edi::NullProcessor.new(self, :a_plus_content_retriever, ) if a_plus_content_remote_path.blank? APlusContentRetriever.new(self, ) end |
#acknowledge_message_enabled? ⇒ Boolean
411 412 413 |
# File 'app/services/edi/amazon/orchestrator.rb', line 411 def try(:order_message_remote_path).present? end |
#acknowledge_message_sender ⇒ Object
415 416 417 418 419 |
# File 'app/services/edi/amazon/orchestrator.rb', line 415 def return Edi::NullProcessor.new(self, :acknowledge_message_sender, ) unless AcknowledgeMessageSender.new(self, ) end |
#buy_box_status_enabled? ⇒ Boolean
520 521 522 |
# File 'app/services/edi/amazon/orchestrator.rb', line 520 def buy_box_status_enabled? try(:product_pricing_remote_path).present? end |
#buy_box_status_retriever ⇒ Object
524 525 526 527 528 |
# File 'app/services/edi/amazon/orchestrator.rb', line 524 def buy_box_status_retriever return Edi::NullProcessor.new(self, :buy_box_status_retriever, ) unless buy_box_status_enabled? BuyBoxStatusRetriever.new(self, ) end |
#buy_shipping_client ⇒ Object
1193 1194 1195 1196 1197 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1193 def buy_shipping_client return nil unless buy_shipping_enabled? ShipWithAmazon.new(self) end |
#buy_shipping_enabled? ⇒ Boolean
1189 1190 1191 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1189 def buy_shipping_enabled? try(:buy_shipping_enabled) == true end |
#catalog_item_information_enabled? ⇒ Boolean
510 511 512 |
# File 'app/services/edi/amazon/orchestrator.rb', line 510 def catalog_item_information_enabled? try(:catalog_item_information_remote_path).present? end |
#catalog_item_information_processor ⇒ Object
554 555 556 557 558 |
# File 'app/services/edi/amazon/orchestrator.rb', line 554 def catalog_item_information_processor return Edi::NullProcessor.new(self, :catalog_item_information_processor, ) unless catalog_item_information_enabled? CatalogItemInformationProcessor.new(self, ) end |
#catalog_item_information_retriever ⇒ Object
514 515 516 517 518 |
# File 'app/services/edi/amazon/orchestrator.rb', line 514 def catalog_item_information_retriever return Edi::NullProcessor.new(self, :catalog_item_information_retriever, ) unless catalog_item_information_enabled? CatalogItemInformationRetriever.new(self, ) end |
#confirm_message_enabled? ⇒ Boolean
395 396 397 |
# File 'app/services/edi/amazon/orchestrator.rb', line 395 def try(:order_message_remote_path).present? end |
#confirm_message_processor ⇒ Object
399 400 401 402 403 |
# File 'app/services/edi/amazon/orchestrator.rb', line 399 def return Edi::NullProcessor.new(self, :confirm_message_processor, ) unless ConfirmMessageProcessor.new(self, ) end |
#confirm_message_sender ⇒ Object
405 406 407 408 409 |
# File 'app/services/edi/amazon/orchestrator.rb', line 405 def return Edi::NullProcessor.new(self, :confirm_message_sender, ) unless ConfirmMessageSender.new(self, ) end |
#confirm_outbound_processing? ⇒ Boolean
Amazon is a two stage process on , processing
652 653 654 |
# File 'app/services/edi/amazon/orchestrator.rb', line 652 def confirm_outbound_processing? true end |
#connection_info ⇒ Object
501 502 503 504 505 506 507 508 |
# File 'app/services/edi/amazon/orchestrator.rb', line 501 def connection_info { marketplace:, merchant_id:, aws_access_key_id:, aws_secret_access_key: } end |
#delete_listing_from_catalog_item(catalog_item: nil, amazon_variation: nil, http_method: 'DELETE', attribute_actions: nil, use_fba_sku: false) ⇒ Array
Builds and sends an Amazon listing delete message for a catalog item or variation.
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1064 def delete_listing_from_catalog_item(catalog_item: nil, amazon_variation: nil, http_method: 'DELETE', attribute_actions: nil, use_fba_sku: false) raise 'Must provide either a catalog_item or an amazon_variation' unless catalog_item || amazon_variation raise 'Catalog Item not an amazon item or not in the right state' if catalog_item && !catalog_item.amazon_catalog_item? listing = catalog_item || amazon_variation res = [] result = .process(listing, http_method:, attribute_actions:, use_fba_sku:) if result.try(:success?) Rails.logger.debug { "orchestrator#delete_listing_from_catalog_item for #{listing}, ecl: #{result.data}" } res << .process(result.data, catalog_item_or_variation: listing, http_method:, use_fba_sku:) else Rails.logger.error "orchestrator#delete_listing_from_catalog_item for #{listing} failed: #{result.try(:error) || 'listing messages not enabled for this partner'}" res << false end res end |
#delete_listing_message_processor ⇒ Object
596 597 598 599 600 |
# File 'app/services/edi/amazon/orchestrator.rb', line 596 def return Edi::NullProcessor.new(self, :delete_listing_message_processor, ) unless DeleteListingMessageProcessor.new(self, ) end |
#discontinue_flow_processor ⇒ Object
602 603 604 605 606 |
# File 'app/services/edi/amazon/orchestrator.rb', line 602 def discontinue_flow_processor return Edi::NullProcessor.new(self, :discontinue_flow_processor, ) unless DiscontinueFlowProcessor.new(self, ) end |
#execute_discontinue_flow ⇒ Object
645 646 647 648 649 |
# File 'app/services/edi/amazon/orchestrator.rb', line 645 def execute_discontinue_flow return false unless active discontinue_flow_processor.process end |
#execute_fba_order_flow ⇒ Object
1178 1179 1180 1181 1182 1183 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1178 def execute_fba_order_flow return unless active .process .process end |
#execute_inventory_flow(use_delta_since_last_message: false) ⇒ Object
622 623 624 625 626 627 628 629 |
# File 'app/services/edi/amazon/orchestrator.rb', line 622 def execute_inventory_flow(use_delta_since_last_message: false) return false unless active .process(use_delta_since_last_message:) # Generate an inventory feed message .process # Send it sleep(1) execute_fba_order_flow # put it here for daily processing for now: need this to run less frequently or to remove ECLs where we skip all the processed fba_orders end |
#execute_listing_message_feed_flow ⇒ Object
638 639 640 641 642 643 |
# File 'app/services/edi/amazon/orchestrator.rb', line 638 def return false unless active .process # Generate a listing message feed message .process # Send it end |
#execute_order_flow ⇒ Object
608 609 610 611 612 613 614 615 616 617 618 619 620 |
# File 'app/services/edi/amazon/orchestrator.rb', line 608 def execute_order_flow return unless active .process sleep(1) .process sleep(1) .process sleep(1) .process # sleep(1) # execute_fba_order_flow # need this to run less frequently or to remove ECLs where we skip all the processed fba_orders end |
#execute_order_status_verification ⇒ Object
1156 1157 1158 1159 1160 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1156 def execute_order_status_verification return unless active && order_status_verification_enabled? order_status_verifier.process end |
#execute_price_flow(use_delta_since_last_message: false) ⇒ Object
631 632 633 634 635 636 |
# File 'app/services/edi/amazon/orchestrator.rb', line 631 def execute_price_flow(use_delta_since_last_message: false) return false unless active .process(use_delta_since_last_message:) # Generate a price feed message .process # Send it end |
#fba_order_message_enabled? ⇒ Boolean
1162 1163 1164 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1162 def try(:order_message_remote_path).present? && try(:fba_store_id).present? end |
#fba_order_message_processor ⇒ Object
1172 1173 1174 1175 1176 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1172 def return Edi::NullProcessor.new(self, :fba_order_message_processor, ) unless FbaOrderMessageProcessor.new(self, ) end |
#fba_order_message_retriever ⇒ Object
1166 1167 1168 1169 1170 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1166 def return Edi::NullProcessor.new(self, :fba_order_message_retriever, ) unless FbaOrderMessageRetriever.new(self, ) end |
#fba_pull_orders_by_po_numbers(po_numbers) ⇒ Object
1185 1186 1187 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1185 def fba_pull_orders_by_po_numbers(po_numbers) .process(po_numbers:) end |
#feed_submission_result_processor ⇒ Object
496 497 498 499 |
# File 'app/services/edi/amazon/orchestrator.rb', line 496 def feed_submission_result_processor # Its amazon its always enabled FeedSubmissionResultProcessor.new(self, ) end |
#inventory_message_enabled? ⇒ Boolean
437 438 439 440 441 442 443 |
# File 'app/services/edi/amazon/orchestrator.rb', line 437 def # `try` (not bare call) because per-partner attr_accessors are only # defined for keys actually present in the partner config hash — # a partner added without an explicit `inventory_message_enabled:` # key would otherwise raise NameError here (AppSignal #5595). try(:inventory_message_enabled).present? end |
#inventory_message_processor ⇒ Object
445 446 447 448 449 |
# File 'app/services/edi/amazon/orchestrator.rb', line 445 def return Edi::NullProcessor.new(self, :inventory_message_processor, ) unless InventoryMessageProcessor.new(self, ) end |
#inventory_message_sender ⇒ Object
451 452 453 454 455 |
# File 'app/services/edi/amazon/orchestrator.rb', line 451 def return Edi::NullProcessor.new(self, :inventory_message_sender, ) unless InventoryMessageSender.new(self, ) end |
#invoice_message_enabled? ⇒ Boolean
421 422 423 |
# File 'app/services/edi/amazon/orchestrator.rb', line 421 def false end |
#invoice_message_processor ⇒ Object
425 426 427 |
# File 'app/services/edi/amazon/orchestrator.rb', line 425 def Edi::NullProcessor.new(self, :invoice_message_processor, ) end |
#listing_item_information_processor ⇒ Object
536 537 538 539 540 |
# File 'app/services/edi/amazon/orchestrator.rb', line 536 def listing_item_information_processor return Edi::NullProcessor.new(self, :listing_item_information_processor, ) unless ListingItemInformationProcessor.new(self, ) end |
#listing_item_information_retriever ⇒ Object
530 531 532 533 534 |
# File 'app/services/edi/amazon/orchestrator.rb', line 530 def listing_item_information_retriever return Edi::NullProcessor.new(self, :listing_item_information_retriever, ) unless ListingItemInformationRetriever.new(self, ) end |
#listing_item_schema_processor ⇒ Object
548 549 550 551 552 |
# File 'app/services/edi/amazon/orchestrator.rb', line 548 def listing_item_schema_processor return Edi::NullProcessor.new(self, :listing_item_schema_processor, ) unless ListingItemSchemaProcessor.new(self, ) end |
#listing_item_schema_retriever ⇒ Object
542 543 544 545 546 |
# File 'app/services/edi/amazon/orchestrator.rb', line 542 def listing_item_schema_retriever return Edi::NullProcessor.new(self, :listing_item_schema_retriever, ) unless ListingItemSchemaRetriever.new(self, ) end |
#listing_message_enabled? ⇒ Boolean
560 561 562 |
# File 'app/services/edi/amazon/orchestrator.rb', line 560 def try(:listing_message_remote_path).present? end |
#listing_message_feed_enabled? ⇒ Boolean
477 478 479 480 481 482 |
# File 'app/services/edi/amazon/orchestrator.rb', line 477 def # Same NameError-safety pattern as `inventory_message_enabled?` / # `price_message_enabled?`. Partners without the key in their config # hash return false instead of raising. AppSignal #5867. try(:listing_message_feed_enabled).present? end |
#listing_message_feed_processor ⇒ Object
490 491 492 493 494 |
# File 'app/services/edi/amazon/orchestrator.rb', line 490 def return Edi::NullProcessor.new(self, :listing_message_feed_processor, ) unless ListingMessageFeedProcessor.new(self, ) end |
#listing_message_feed_sender ⇒ Object
484 485 486 487 488 |
# File 'app/services/edi/amazon/orchestrator.rb', line 484 def return Edi::NullProcessor.new(self, :listing_message_feed_sender, ) unless ListingMessageFeedSender.new(self, ) end |
#listing_message_processor ⇒ Object
590 591 592 593 594 |
# File 'app/services/edi/amazon/orchestrator.rb', line 590 def return Edi::NullProcessor.new(self, :listing_message_processor, ) unless ListingMessageProcessor.new(self, ) end |
#listing_message_sender ⇒ Object
584 585 586 587 588 |
# File 'app/services/edi/amazon/orchestrator.rb', line 584 def return Edi::NullProcessor.new(self, :listing_message_sender, ) unless ListingMessageSender.new(self, ) end |
#listing_schema_message_enabled? ⇒ Boolean
564 565 566 |
# File 'app/services/edi/amazon/orchestrator.rb', line 564 def try(:listing_schema_message_enabled).present? end |
#order_message_enabled? ⇒ Boolean
379 380 381 |
# File 'app/services/edi/amazon/orchestrator.rb', line 379 def try(:order_message_remote_path).present? end |
#order_message_processor ⇒ Object
389 390 391 392 393 |
# File 'app/services/edi/amazon/orchestrator.rb', line 389 def return Edi::NullProcessor.new(self, :order_message_processor, ) unless OrderMessageProcessor.new(self, ) end |
#order_message_retriever ⇒ Object
383 384 385 386 387 |
# File 'app/services/edi/amazon/orchestrator.rb', line 383 def return Edi::NullProcessor.new(self, :order_message_retriever, ) unless OrderMessageRetriever.new(self, ) end |
#order_status_verification_enabled? ⇒ Boolean
1146 1147 1148 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1146 def order_status_verification_enabled? !!try(:order_status_verification_enabled) && try(:order_message_remote_path).present? end |
#order_status_verifier ⇒ Object
1150 1151 1152 1153 1154 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1150 def order_status_verifier return Edi::NullProcessor.new(self, :order_status_verifier, ) unless order_status_verification_enabled? OrderStatusVerifier.new(self, ) end |
#orders_with_amazon_id_po(order_id) ⇒ Object
1141 1142 1143 1144 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1141 def orders_with_amazon_id_po(order_id) # Here we look for either EDI orders with edi_po_number matching order_id or manual orders with payments with the po_number matching order_id (customer.orders.where(edi_po_number: order_id).to_ary + customer.orders.with_payments.where(payments: { po_number: order_id }).to_ary).uniq end |
#populate_items_from_catalog(limit: nil, skus: nil, force: false) ⇒ Object
A method to populate our item data with amazon's catalog data
limit: how many records to process
skus: specific skus to process
force: normally we only will pull if the information is not present, use this to force a reset
1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1121 def populate_items_from_catalog(limit: nil, skus: nil, force: false) catalog = customer_catalog catalog_items = catalog.catalog_items.amazons_with_asins.available_for_edi_feeds # Note that this should probably be specific to the locale so leverage mobility TODO catalog_items = catalog_items.where(items: { amazon_title: [nil, ''] }) unless force catalog_items = catalog_items.limit(limit) if limit catalog_items = catalog_items.where(items: { sku: skus }) if skus.present? catalog_items.map do |catalog_item| { name: catalog_item.to_s, catalog_item_id: catalog_item.id, res: pull_catalog_information(catalog_item) } end end |
#price_message_enabled? ⇒ Boolean
457 458 459 460 461 462 463 |
# File 'app/services/edi/amazon/orchestrator.rb', line 457 def # Use `try(:price_message_enabled).present?` so partners whose config # hash does not carry the key (no attr_accessor for `price_message_enabled` # exists on that instance) return false instead of raising NameError. # Same pattern as `inventory_message_enabled?` after AppSignal #5595. try(:price_message_enabled).present? end |
#price_message_processor ⇒ Object
471 472 473 474 475 |
# File 'app/services/edi/amazon/orchestrator.rb', line 471 def return Edi::NullProcessor.new(self, :price_message_processor, ) unless PriceMessageProcessor.new(self, ) end |
#price_message_sender ⇒ Object
465 466 467 468 469 |
# File 'app/services/edi/amazon/orchestrator.rb', line 465 def return Edi::NullProcessor.new(self, :price_message_sender, ) unless PriceMessageSender.new(self, ) end |
#pull_amazon_variation_listing_information(amazon_variation) ⇒ Object
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 |
# File 'app/services/edi/amazon/orchestrator.rb', line 939 def pull_amazon_variation_listing_information(amazon_variation) sku = amazon_variation.reported_vendor_sku(partner) # Listing info is marketplace-specific, not locale-specific. # The API call doesn't use the locale parameter, so we only need one call per marketplace. primary_locale = locales.first ecl = listing_item_information_retriever.process( sku: sku, locale: primary_locale, file_info: { sku: sku, locale: primary_locale } ) if ecl Rails.logger.debug { "orchestrator#pull_amazon_variation_listing_information, ecl: #{ecl}" } [{ primary_locale => listing_item_information_processor.process(ecl, catalog_item_or_amazon_variation: amazon_variation, locale: primary_locale) }] else Rails.logger.error "orchestrator#pull_amazon_variation_listing_information for amazon variation #{amazon_variation.id} yielded no ecl (sku #{sku} not found?)" [{ primary_locale => false }] end end |
#pull_buy_box_status(catalog_item) ⇒ Object
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 |
# File 'app/services/edi/amazon/orchestrator.rb', line 684 def pull_buy_box_status(catalog_item) asin = catalog_item.amazon_asin raise "No ASIN for catalog_item #{catalog_item.id}" if asin.blank? locale = 'en_US' # just put something in here item = catalog_item.item sku = catalog_item.reported_vendor_sku res = [] ecl = buy_box_status_retriever.process( asin: asin, file_info: { sku: sku, item_id: item.id, catalog_item_id: catalog_item.id } ) if ecl&.try(:data).present? && (json_hash = JSON.parse(ecl.data).with_indifferent_access).present? Rails.logger.debug { "orchestrator#pull_buy_box_status, catalog_item.id: #{catalog_item.id}, ecl: #{ecl}" } Rails.logger.debug { "orchestrator#pull_buy_box_status, json_hash: #{json_hash}" } ecl.edi_documents.create(catalog_item: catalog_item) status = json_hash.dig(:payload, :status).to_s.downcase if status == 'success' offers = json_hash.dig(:payload, :Offers) || [] # Convert each offer to indifferent access for consistent key handling # (nested hashes in arrays from JSON.parse don't inherit indifferent access) offers = offers.map(&:with_indifferent_access) # Find ALL offers from our merchant (there may be multiple: FBA vs MFN/FBM) our_offers = offers.select { |o| o[:SellerId] == merchant_id } if our_offers.any? # Separate FBA and MFN offers our_fba_offers = our_offers.select { |o| o[:IsFulfilledByAmazon] } our_mfn_offers = our_offers.reject { |o| o[:IsFulfilledByAmazon] } # Check if ANY of our offers is the buy box winner (FBA offer may win while MFN doesn't) is_buy_box_winner = our_offers.any? { |o| o[:IsBuyBoxWinner] } is_featured_merchant = our_offers.any? { |o| o[:IsFeaturedMerchant] } # Detect which fulfillment type is winning the buy box fba_winning = our_fba_offers.any? { |o| o[:IsBuyBoxWinner] } mfn_winning = our_mfn_offers.any? { |o| o[:IsBuyBoxWinner] } # Log the situation for visibility if our_fba_offers.any? && our_mfn_offers.any? Rails.logger.info { "Catalog item #{catalog_item.id}: Multiple offer types detected - FBA: #{our_fba_offers.count}, MFN: #{our_mfn_offers.count}" } if fba_winning Rails.logger.info { "Catalog item #{catalog_item.id}: FBA offer is the Buy Box winner" } elsif mfn_winning Rails.logger.info { "Catalog item #{catalog_item.id}: MFN offer is the Buy Box winner" } end end Rails.logger.debug { "Found #{our_offers.count} offer(s) for merchant #{merchant_id}: IsBuyBoxWinner=#{is_buy_box_winner}, IsFeaturedMerchant=#{is_featured_merchant}, FBA_winning=#{fba_winning}, MFN_winning=#{mfn_winning}" } else # No offer found for our merchant, set defaults is_buy_box_winner = false is_featured_merchant = false Rails.logger.debug { "No offer found for merchant #{merchant_id}, using defaults: IsBuyBoxWinner=#{is_buy_box_winner}, IsFeaturedMerchant=#{is_featured_merchant}" } end # Track buy box winner changes previous_buy_box_winner = catalog_item.is_amz_buy_box_winner new_buy_box_winner = is_buy_box_winner.nil? ? false : !!is_buy_box_winner # Ensure we never set nil values to prevent database constraint violations catalog_item.is_amz_buy_box_winner = new_buy_box_winner catalog_item.is_amz_featured_merchant = is_featured_merchant.nil? ? false : !!is_featured_merchant # Update change timestamp if buy box status changed catalog_item.amz_last_buy_box_winner_change = Time.current if previous_buy_box_winner != new_buy_box_winner # Check if we have an active offer on Amazon we_have_offer = our_offers.present? if we_have_offer # We have an offer - resolve any out_of_stock/no_buyable_offers flags catalog_item.amazon_catalog_item_flags .active .where(flag_type: %w[no_buyable_offers out_of_stock]) .find_each { |flag| flag.update!(resolved_at: Time.current) } # Auto-resolve "cannot_automatically_win_buy_box" flags when we become the Buy Box winner if is_buy_box_winner catalog_item.amazon_catalog_item_flags .active .where(flag_type: 'cannot_automatically_win_buy_box') .find_each { |flag| flag.update!(resolved_at: Time.current) } end else # We don't have an offer on Amazon despite successful status check # This likely means we're out of stock - create appropriate flags Rails.logger.info { "Catalog item #{catalog_item.id}: No offer found for merchant #{merchant_id} despite successful status. Creating out_of_stock flag." } # Create out_of_stock flag if not already present unless catalog_item.amazon_catalog_item_flags.active.by_flag_type('out_of_stock').exists? catalog_item.amazon_catalog_item_flags.create!( flag_type: 'out_of_stock', issues: 'Out of stock! No active offer found on Amazon for this listing.', our_price_at_flagging: catalog_item.amazon_price_with_tax ) end # Also create no_buyable_offers flag (we have no buyable offer) unless catalog_item.amazon_catalog_item_flags.active.by_flag_type('no_buyable_offers').exists? catalog_item.amazon_catalog_item_flags.create!( flag_type: 'no_buyable_offers', issues: 'No Buyable Offers from us! We have no active offer on Amazon.', our_price_at_flagging: catalog_item.amazon_price_with_tax ) end # Resolve any "cannot_automatically_win_buy_box" flags - the issue isn't pricing catalog_item.amazon_catalog_item_flags .active .where(flag_type: 'cannot_automatically_win_buy_box') .find_each { |flag| flag.update!(resolved_at: Time.current) } end # Store all competitor seller IDs for discovery (excluding our own offers) store_competitor_seller_ids(catalog_item, offers, merchant_id) Rails.logger.debug { "Setting catalog_item #{catalog_item.id}: is_amz_buy_box_winner=#{catalog_item.is_amz_buy_box_winner}, is_amz_featured_merchant=#{catalog_item.is_amz_featured_merchant}" } catalog_item.save! ecl.complete! if ecl.can_complete? elsif status == 'nobuyableoffers' # just mark it as not buy box because there are no offers previous_buy_box_winner = catalog_item.is_amz_buy_box_winner is_buy_box_winner = false is_featured_merchant = false # Ensure we never set nil values to prevent database constraint violations catalog_item.is_amz_buy_box_winner = is_buy_box_winner.nil? ? false : !!is_buy_box_winner catalog_item.is_amz_featured_merchant = is_featured_merchant.nil? ? false : !!is_featured_merchant # Update change timestamp if buy box status changed catalog_item.amz_last_buy_box_winner_change = Time.current if previous_buy_box_winner != catalog_item.is_amz_buy_box_winner # Check if this is an out-of-stock situation vs a real "no buyable offers" issue # Look at the stored retailer_information for fulfillmentAvailability quantity is_out_of_stock = false begin amazon_info = catalog_item.retailer_information&.with_indifferent_access || {} # Check en_US listing first, then try other locales listing_info = amazon_info.dig(:en_US, :listing, :payload) || amazon_info.dig('en_US', 'listing', 'payload') || amazon_info.values.first&.dig(:listing, :payload) || amazon_info.values.first&.dig('listing', 'payload') if listing_info.present? fulfillment_availability = listing_info[:fulfillmentAvailability] || listing_info['fulfillmentAvailability'] || [] # Check if all fulfillment channels report 0 quantity if fulfillment_availability.any? quantities = fulfillment_availability.map { |fa| fa[:quantity] || fa['quantity'] || 0 } is_out_of_stock = quantities.all?(&:zero?) end end rescue StandardError => e Rails.logger.warn { "Error checking fulfillment availability for catalog_item #{catalog_item.id}: #{e.}" } end # Always create no_buyable_offers flag when status is NoBuyableOffers # This ensures the item appears in "No Buyable Offers" filter unless catalog_item.amazon_catalog_item_flags.active.by_flag_type('no_buyable_offers').exists? catalog_item.amazon_catalog_item_flags.create!( flag_type: 'no_buyable_offers', issues: 'No Buyable Offers! This listing has no active offers on Amazon.', our_price_at_flagging: catalog_item.amazon_price_with_tax ) Rails.logger.info { "Created no_buyable_offers flag for catalog_item #{catalog_item.id}" } end if is_out_of_stock # Also create out_of_stock flag to indicate WHY there are no buyable offers # This allows the item to appear in both "No Buyable Offers" AND "Out of Stock" filters unless catalog_item.amazon_catalog_item_flags.active.by_flag_type('out_of_stock').exists? catalog_item.amazon_catalog_item_flags.create!( flag_type: 'out_of_stock', issues: 'Out of stock! This listing has no inventory reported to Amazon.', our_price_at_flagging: catalog_item.amazon_price_with_tax ) Rails.logger.info { "Created out_of_stock flag for catalog_item #{catalog_item.id}" } end else # Not out of stock - resolve any stale out_of_stock flags catalog_item.amazon_catalog_item_flags .active .by_flag_type('out_of_stock') .find_each { |flag| flag.update!(resolved_at: Time.current) } end Rails.logger.debug { "Setting default values for catalog_item #{catalog_item.id} due to no buyable offers" } catalog_item.save! ecl.complete! if ecl.can_complete? else Rails.logger.error "Failed to retrieve buy box status! Status: #{status}" ecl.notes = "Failed to retrieve buy box status! Status: #{status}" # Set default values to prevent database constraint violations previous_buy_box_winner = catalog_item.is_amz_buy_box_winner catalog_item.is_amz_buy_box_winner = false catalog_item.is_amz_featured_merchant = false # Update change timestamp if buy box status changed catalog_item.amz_last_buy_box_winner_change = Time.current if previous_buy_box_winner != catalog_item.is_amz_buy_box_winner Rails.logger.debug { "Setting default values for catalog_item #{catalog_item.id} due to unexpected status: #{status}" } catalog_item.save! if ecl.can_error? ecl.error! else ecl.save end res << { locale => false } end else Rails.logger.error "orchestrator#pull_buy_box_status for catalog_item.id: #{catalog_item.id} yielded no ecl!" # Set default values to prevent database constraint violations previous_buy_box_winner = catalog_item.is_amz_buy_box_winner catalog_item.is_amz_buy_box_winner = false catalog_item.is_amz_featured_merchant = false # Update change timestamp if buy box status changed catalog_item.amz_last_buy_box_winner_change = Time.current if previous_buy_box_winner != catalog_item.is_amz_buy_box_winner Rails.logger.debug { "Setting default values for catalog_item #{catalog_item.id} due to no ECL data" } catalog_item.save! res << { locale => false } end res << { locale => true } end |
#pull_catalog_information(catalog_item) ⇒ Object
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 |
# File 'app/services/edi/amazon/orchestrator.rb', line 656 def pull_catalog_information(catalog_item) item = catalog_item.item asin = catalog_item.amazon_asin raise "No ASIN for catalog_item #{catalog_item.id}" if asin.blank? res = [] locales.each do |locale| # en_US en_CA fr_CA ecl = catalog_item_information_retriever.process( asin: asin, locale:, file_info: { asin: asin, item_id: item.id, locale: } ) if ecl Rails.logger.debug { "orchestrator#pull_catalog_information, ecl: #{ecl}" } res << { locale => catalog_item_information_processor.process(ecl, catalog_item:, locale:) } else Rails.logger.error "orchestrator#pull_catalog_information for catalog item #{catalog_item.id} yielded no ecl (asin #{asin} not found?)" res << { locale => false } end end res end |
#pull_listing_information(catalog_item) ⇒ Object
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 |
# File 'app/services/edi/amazon/orchestrator.rb', line 913 def pull_listing_information(catalog_item) item = catalog_item.item sku = catalog_item.reported_vendor_sku # Listing info is marketplace-specific, not locale-specific. # The API call doesn't use the locale parameter, so we only need one call per marketplace. primary_locale = locales.first ecl = listing_item_information_retriever.process( sku: sku, locale: primary_locale, file_info: { sku: sku, item_id: item.id, catalog_item_id: catalog_item.id, locale: primary_locale } ) if ecl Rails.logger.debug { "orchestrator#pull_listing_information, ecl: #{ecl}" } [{ primary_locale => listing_item_information_processor.process(ecl, catalog_item_or_amazon_variation: catalog_item, locale: primary_locale) }] else Rails.logger.error "orchestrator#pull_listing_information for catalog item #{catalog_item.id} yielded no ecl (sku #{sku} not found?)" [{ primary_locale => false }] end end |
#pull_listing_schema(catalog_item, product_type = nil) ⇒ Object
1014 1015 1016 1017 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1014 def pull_listing_schema(catalog_item, product_type = nil) product_type ||= catalog_item.amazon_effective_desired_product_type pull_schema(product_type) end |
#pull_schema(product_type) ⇒ Object
962 963 964 965 966 |
# File 'app/services/edi/amazon/orchestrator.rb', line 962 def pull_schema(product_type) locales.map do |locale| # en_US en_CA fr_CA { locale => pull_schema_for_locale(product_type, locale) } end end |
#pull_schema_for_locale(product_type, locale, amazon_schema: nil) ⇒ Object
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 |
# File 'app/services/edi/amazon/orchestrator.rb', line 968 def pull_schema_for_locale(product_type, locale, amazon_schema: nil) amazon_schema = nil if product_type.blank? Rails.logger.error "orchestrator#pull_schema_for_locale called with blank product_type for partner #{partner}, locale #{locale} — skipping" return amazon_schema end ErrorReporting.scoped(partner: partner, product_type: product_type, locale: locale) do ecl = listing_item_schema_retriever.process( product_type: product_type, locale: locale, file_info: { product_type: product_type, locale: locale } ) if ecl ErrorReporting.scoped(edi_commmunication_log_id: ecl.id) do Rails.logger.debug { "orchestrator#pull_listing_schema, ecl: #{ecl}" } json_hash = JSON.parse(ecl.data).with_indifferent_access schema_authenticated_url = json_hash.dig(:schema, :link, :resource) if schema_authenticated_url && (response = HTTP.get(schema_authenticated_url))&.status&.success? json_schema = response.body.to_s catalog = customer_catalog amazon_marketplace = catalog.amazon_marketplace amazon_schema ||= AmazonSchema.amazon_channel_seller.where(amazon_marketplace: amazon_marketplace.id, locale: locale, product_type: product_type).first_or_initialize amazon_schema.schema = json_schema amazon_schema.save! ecl.complete! if ecl.can_complete? else Rails.logger.error "Failed to retrieve data from #{schema_authenticated_url}: #{response&.status}" ecl.notes = "Failed to retrieve data from #{schema_authenticated_url}: #{response&.status}" if ecl.can_error? ecl.error! else ecl.save end end end else Rails.logger.error "orchestrator#pull_listing_schema for product_type #{product_type} yielded no ecl (schema #{product_type} not found?)" end end amazon_schema end |
#push_listing_from_amazon_variation(amazon_variation) ⇒ Array
Builds and sends an Amazon listing PUT message for a single variation.
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1085 def push_listing_from_amazon_variation(amazon_variation) res = [] http_method = 'PUT' result = .process(amazon_variation, http_method:) if result.try(:success?) Rails.logger.debug { "orchestrator#push_listing_from_amazon_variation for amazon variation #{amazon_variation.id}, ecl: #{result.data}" } res << .process(result.data, catalog_item_or_variation: amazon_variation, http_method:) else Rails.logger.error "orchestrator#push_listing_from_amazon_variation for amazon variation #{amazon_variation.id} failed: #{result.try(:error) || 'listing messages not enabled for this partner'}" res << false end res end |
#push_listing_from_catalog_item(catalog_item, http_method: 'PATCH', attribute_actions: nil, use_fba_sku: false) ⇒ Array
Builds and sends an Amazon listing-items message for a catalog item.
Runs the listing message through the processor, then hands the resulting
ECL to the sender. When the partner has no listing-message path configured
the processor is an NullProcessor whose #process returns a falsy
non-Result, so the success check is guarded with #try.
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1032 def push_listing_from_catalog_item(catalog_item, http_method: 'PATCH', attribute_actions: nil, use_fba_sku: false) item = catalog_item.item raise 'Catalog item not an amazon item or not in the right state' unless catalog_item.amazon_catalog_item? && catalog_item.api_ready_state? res = [] result = .process(catalog_item, http_method:, attribute_actions:, use_fba_sku:) if result.try(:success?) Rails.logger.debug { "orchestrator#push_listing_from_catalog_item for catalog item #{catalog_item.id}, ecl: #{result.data}" } res << .process(result.data, catalog_item_or_variation: catalog_item, http_method:, use_fba_sku:) else # result is a non-Result (NullProcessor#process returns logger.debug => false) when listing # messages aren't configured for the partner; #try keeps that from raising NoMethodError. fba_label = use_fba_sku ? ' (FBA)' : ' (FBM)' error = result.try(:error) || 'listing messages not enabled for this partner' Rails.logger.error "orchestrator#push_listing_from_catalog_item for catalog item #{catalog_item.id}#{fba_label} failed: #{error}. " \ "ASIN: #{item.amazon_asin}, SKU: #{catalog_item.sku}, Partner: #{partner}." res << { error: true, message: error } end res end |
#return_notification_message_enabled? ⇒ Boolean
429 430 431 |
# File 'app/services/edi/amazon/orchestrator.rb', line 429 def false end |
#return_notification_message_processor ⇒ Object
433 434 435 |
# File 'app/services/edi/amazon/orchestrator.rb', line 433 def Edi::NullProcessor.new(self, :return_notification_message_processor, ) end |
#ship_code_mapper ⇒ Object
1199 1200 1201 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1199 def ship_code_mapper ShipCodeMapper.new(self) end |
#store_competitor_seller_ids(catalog_item, offers, our_merchant_id) ⇒ Object
Store competitor seller IDs from Buy Box status for discovery
Creates AmazonCompetitor records (without names) for any new seller IDs found
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 |
# File 'app/services/edi/amazon/orchestrator.rb', line 1205 def store_competitor_seller_ids(catalog_item, offers, our_merchant_id) return if offers.blank? competitor_offers = offers.reject { |o| o[:SellerId] == our_merchant_id } return if competitor_offers.empty? competitor_offers.each do |offer| seller_id = offer[:SellerId] next if seller_id.blank? # Create a placeholder record if this seller doesn't exist yet # Name will be nil until someone identifies them AmazonCompetitor.find_or_create_by(seller_id: seller_id) do |competitor| competitor.notes = "Discovered on #{Time.current.strftime('%Y-%m-%d')} from ASIN #{catalog_item.amazon_asin}" end rescue ActiveRecord::RecordNotUnique # Race condition - record already exists, which is fine end end |