Class: Edi::BaseOrchestrator
- Inherits:
-
Object
- Object
- Edi::BaseOrchestrator
- Extended by:
- Memery
- Defined in:
- app/services/edi/base_orchestrator.rb
Overview
Service object: base orchestrator.
Direct Known Subclasses
Amazon::Orchestrator, AmazonVc::Orchestrator, Commercehub::Orchestrator, Google::Orchestrator, Menard::Orchestrator, MftGateway::Orchestrator, MiraklSeller::Orchestrator, Openai::Orchestrator, ResellerInventory::Orchestrator, Walmart::Orchestrator, Wayfair::Orchestrator
Constant Summary collapse
- ORCHESTRATORS =
Orchestrators.
['Edi::Amazon::Orchestrator', 'Edi::Commercehub::Orchestrator', 'Edi::Wayfair::Orchestrator', 'Edi::AmazonVc::Orchestrator', 'Edi::MiraklSeller::Orchestrator', 'Edi::MftGateway::Orchestrator', 'Edi::Walmart::Orchestrator', 'Edi::Menard::Orchestrator', 'Edi::Openai::Orchestrator', 'Edi::Google::Orchestrator', 'Edi::ResellerInventory::Orchestrator'].freeze
- RECOMMENDED_EXECUTE_FLOW_EVERY_X_HOUR =
Recommended execute flow every x hour.
[1, 2, 3, 4, 6, 8, 12, 24].freeze
- DEFAULT_PENDING_DISCONTINUE_LIFETIME =
Default pending discontinue lifetime.
1.day
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Delegated Instance Attributes collapse
-
#customer_catalog ⇒ Object
Alias for Customer#catalog.
Class Method Summary collapse
- .all_orchestrators_class ⇒ Object
-
.build(partner_config_key, options = {}) ⇒ Edi::BaseOrchestrator
The orchestrator for the partner.
-
.build_customer_id_to_partner_key_map(partners) ⇒ Hash
Pure two-pass builder split out of BaseOrchestrator.customer_id_to_partner_key_map so tests can drive it with a fixture WITHOUT stubbing the memoized
partners(see the note above). -
.cached_build(partner_config_key, options = {}) ⇒ Edi::BaseOrchestrator
Returns a cached orchestrator instance for the given partner key This avoids expensive repeated instantiation of orchestrators with dynamic accessors.
-
.cached_orchestrators ⇒ Object
Request/job-scoped cache of orchestrator instances by partner key.
-
.catalog_id_to_pending_discontinue_lifetime ⇒ Object
Returns a hash of { catalog_id => ActiveSupport::Duration } for all active orchestrators that define a custom pending_discontinue_lifetime.
- .catalog_ids_edi_enabled ⇒ Object
-
.customer_id_to_partner_key_map ⇒ Object
Builds a lookup hash from customer_id to partner config key for fast lookups Handles both single customer_id values and multi-customer partner configurations.
- .customer_ids_edi_enabled ⇒ Object
- .customer_ids_with_invoice_message_enabled ⇒ Object
-
.execute_discontinue_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) ⇒ Object
Executes the discontinue flow for EDI orchestrators.
-
.execute_flow(flow, orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false, raise_on_error: false) ⇒ Object
Executes the specified flow (inventory, order, or product data) for the given orchestrator(s).
-
.execute_inventory_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) ⇒ Object
Executes the inventory flow for EDI orchestrators.
-
.execute_listing_message_feed_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) ⇒ Object
Executes the listing message flow for EDI orchestrators.
- .execute_order_flow(options = {}) ⇒ void
-
.execute_price_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) ⇒ Object
Executes the price flow for EDI orchestrators.
- .execute_product_data_flow(options = {}) ⇒ void
- .orchestrator_for_customer_id(customer_id, use_cache: true) ⇒ Object
-
.orchestrators(options = {}) ⇒ Array<Edi::BaseOrchestrator>
One orchestrator per partner.
- .partners ⇒ Object
Instance Method Summary collapse
-
#confirm_outbound_processing? ⇒ Boolean
By default we don't require a two stage processing (ready -> processing -> complete).
- #customer(segment = nil) ⇒ Object
- #customer_ids ⇒ Object
-
#customers ⇒ Object
Returns customers (or single customer) associated with an orchestrator as an active relation.
- #execute_inventory_flow ⇒ Object
- #execute_order_flow ⇒ Object
- #execute_price_flow ⇒ Object
- #execute_product_data_flow ⇒ Object
-
#ignore_back_orders ⇒ Object
By default back orders are not ignored.
-
#initialize(partner, options = {}) ⇒ BaseOrchestrator
constructor
A new instance of BaseOrchestrator.
- #inventory_message_enabled? ⇒ Boolean
- #pending_discontinue_lifetime ⇒ Object
- #price_message_enabled? ⇒ Boolean
- #product_data_enabled? ⇒ Boolean
-
#return_notification_message_enabled? ⇒ Boolean
Default false for orchestrators that don't handle inbound return notifications (inventory-only feeds, push-only partners).
-
#should_execute_flow?(flow) ⇒ Boolean
flow is in the format of execute_inventory_flow or execute_price_flow.
-
#should_execute_order_flow? ⇒ Boolean
Determines if the order flow should run.
-
#should_execute_product_data_flow? ⇒ Boolean
Determines if the product data flow should run.
-
#supports_flow?(flow) ⇒ Boolean
Whether this orchestrator actually implements the given flow: it either overrides the BaseOrchestrator no-op stub (order/inventory/price/ product_data) or defines a flow base doesn't (listing feed/discontinue).
- #test_mode? ⇒ Boolean
Constructor Details
#initialize(partner, options = {}) ⇒ BaseOrchestrator
Returns a new instance of BaseOrchestrator.
387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'app/services/edi/base_orchestrator.rb', line 387 def initialize(partner, = {}) # `try` guards against a non-symbolizable partner (e.g. a Hash passed by a # mis-invoked `rails runner` one-liner) — fail fast with a clear message # instead of `NoMethodError: undefined method 'to_sym'` (AppSignal #5014). @config = self.class.partners[partner.try(:to_sym)] raise ArgumentError, "Unrecognized EDI partner: #{partner.inspect}" unless @config @config.each do |name, val| singleton_class.send :attr_accessor, name.to_sym public_send :"#{name}=", val end @options = @logger = [:logger] || Rails.logger end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
6 7 8 |
# File 'app/services/edi/base_orchestrator.rb', line 6 def config @config end |
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
6 7 8 |
# File 'app/services/edi/base_orchestrator.rb', line 6 def logger @logger end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
6 7 8 |
# File 'app/services/edi/base_orchestrator.rb', line 6 def @options end |
Class Method Details
.all_orchestrators_class ⇒ Object
20 21 22 |
# File 'app/services/edi/base_orchestrator.rb', line 20 def all_orchestrators_class ORCHESTRATORS.map(&:constantize) end |
.build(partner_config_key, options = {}) ⇒ Edi::BaseOrchestrator
Returns the orchestrator for the partner.
83 84 85 86 87 88 89 |
# File 'app/services/edi/base_orchestrator.rb', line 83 def build(partner_config_key, = {}) # Find the orchestrator for this partner key orchestrator_class = all_orchestrators_class.find { |o| o.partners.key?(partner_config_key.to_sym) } return orchestrator_class.new(partner_config_key, ) if orchestrator_class raise "Cannot determine orchestrator class for partner #{partner_config_key}" end |
.build_customer_id_to_partner_key_map(partners) ⇒ Hash
Pure two-pass builder split out of customer_id_to_partner_key_map so
tests can drive it with a fixture WITHOUT stubbing the memoized
partners (see the note above). Not memoized — safe to call directly.
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 |
# File 'app/services/edi/base_orchestrator.rb', line 49 def build_customer_id_to_partner_key_map(partners) map = {} # The two passes are deliberately separate and MUST NOT be combined: # collapsing them is exactly the PR #480 regression that made every # Hash-style multi-customer partner (RONA: Rona.ca/Lowes.ca/Reno) invisible, because # the first pass `next if ...is_a?(Hash)` and the second pass # `next unless ...is_a?(Hash)` cannot share one iteration. # rubocop:disable Style/CombinableLoops # First pass: single customer_id partners (higher priority) partners.each do |key, config| next unless config[:active] next if config[:inventory_feed_only] # push-only inventory feeds never answer a customer_id lookup next if config[:customer_id].is_a?(Hash) # Skip multi-customer partners in first pass map[config[:customer_id]] = key if config[:customer_id] end # Second pass: multi-customer partners (lower priority, only if not already mapped) partners.each do |key, config| next unless config[:active] next if config[:inventory_feed_only] next unless config[:customer_id].is_a?(Hash) config[:customer_id].values.each do |cid| map[cid] ||= key # Only set if not already mapped by single-customer partner end end # rubocop:enable Style/CombinableLoops map end |
.cached_build(partner_config_key, options = {}) ⇒ Edi::BaseOrchestrator
Returns a cached orchestrator instance for the given partner key
This avoids expensive repeated instantiation of orchestrators with dynamic accessors
108 109 110 111 |
# File 'app/services/edi/base_orchestrator.rb', line 108 def cached_build(partner_config_key, = {}) cache_key = [partner_config_key.to_sym, .hash].join('_') cached_orchestrators[cache_key] ||= build(partner_config_key, ) end |
.cached_orchestrators ⇒ Object
Request/job-scoped cache of orchestrator instances by partner key.
Backed by CurrentScope so it is automatically reset between web
requests (Rails) and Sidekiq jobs (Sidekiq::CurrentAttributes
middleware). The previous implementation used a class-level instance
variable (@cached_orchestrators ||= {}) which is shared across
threads and never reset -- so it both leaked memory unboundedly and
held stale partner config across deploys/reloads.
98 99 100 |
# File 'app/services/edi/base_orchestrator.rb', line 98 def cached_orchestrators CurrentScope.edi_orchestrator_cache ||= {} end |
.catalog_id_to_pending_discontinue_lifetime ⇒ Object
Returns a hash of { catalog_id => ActiveSupport::Duration } for all active
orchestrators that define a custom pending_discontinue_lifetime. Used by
Maintenance::ItemMaintenance to apply per-partner wait times.
450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'app/services/edi/base_orchestrator.rb', line 450 def self.catalog_id_to_pending_discontinue_lifetime map = {} orchestrators.each do |o| next unless o.active lifetime = o.pending_discontinue_lifetime next if lifetime == DEFAULT_PENDING_DISCONTINUE_LIFETIME catalog_id = o.try(:catalog_id) map[catalog_id] = lifetime if catalog_id end map end |
.catalog_ids_edi_enabled ⇒ Object
153 154 155 |
# File 'app/services/edi/base_orchestrator.rb', line 153 def catalog_ids_edi_enabled Customer.where(id: customer_ids_edi_enabled).pluck(:catalog_id).uniq.sort end |
.customer_id_to_partner_key_map ⇒ Object
Builds a lookup hash from customer_id to partner config key for fast lookups
Handles both single customer_id values and multi-customer partner configurations
38 39 40 |
# File 'app/services/edi/base_orchestrator.rb', line 38 def customer_id_to_partner_key_map build_customer_id_to_partner_key_map(partners) end |
.customer_ids_edi_enabled ⇒ Object
136 137 138 |
# File 'app/services/edi/base_orchestrator.rb', line 136 def customer_ids_edi_enabled partners.values.select { |v| v[:customer_id].try(:values) || v[:customer_id] }.map { |v| v[:customer_id].try(:values) || v[:customer_id] }.flatten.uniq.sort end |
.customer_ids_with_invoice_message_enabled ⇒ Object
141 142 143 144 145 146 147 148 149 150 |
# File 'app/services/edi/base_orchestrator.rb', line 141 def Rails.cache.fetch('edi/customer_ids_with_invoice_message_enabled', expires_in: 1.hour) do partners.keys.filter_map do |key| o = cached_build(key) next unless o.respond_to?(:invoice_message_enabled?) && o. Array(o.config[:customer_id].is_a?(Hash) ? o.config[:customer_id].values : o.config[:customer_id]) end.flatten.compact.uniq.sort end end |
.execute_discontinue_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) ⇒ Object
Executes the discontinue flow for EDI orchestrators.
Picks up pending_discontinue catalog items and sends DELETE via SP-API.
orchestrator_name - The name of a specific orchestrator to run, optional.
partner - The partner key to run for, optional.
logger - The logger to use.
trial_run - If true, will not send real requests.
207 208 209 |
# File 'app/services/edi/base_orchestrator.rb', line 207 def self.execute_discontinue_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) execute_flow(:execute_discontinue_flow, orchestrator_name:, partner:, logger:, trial_run:) end |
.execute_flow(flow, orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false, raise_on_error: false) ⇒ Object
Executes the specified flow (inventory, order, or product data)
for the given orchestrator(s). Allows filtering by orchestrator name and partner.
Logs execution and returns results.
When raise_on_error is true, callers must supply one orchestrator_name
and partner; otherwise the first failure aborts the multi-target loop and
discards any results already collected for earlier targets.
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 |
# File 'app/services/edi/base_orchestrator.rb', line 217 def self.execute_flow(flow, orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false, raise_on_error: false) valid_flows = %i[execute_inventory_flow execute_order_flow execute_product_data_flow execute_price_flow execute_listing_message_feed_flow execute_discontinue_flow] raise "Invalid flow option, must be one of #{valid_flows.join(', ')}" unless flow.in?(valid_flows) raise ArgumentError, 'raise_on_error requires orchestrator_name and partner' if raise_on_error && (orchestrator_name.blank? || partner.blank?) results = [] all_orchestrators_class.each do |oc| next if orchestrator_name.present? && oc.name != orchestrator_name logger.tagged oc.name do oc.orchestrators.each do |orchestrator| next if partner.present? && orchestrator.partner.to_s != partner logger.tagged orchestrator.partner do logger.tagged flow do logger.info 'started' begin result = if orchestrator.should_execute_flow?(flow) && orchestrator.respond_to?(flow) trial_run ? :trial_run : orchestrator.send(flow) else :scheduled_skip end logger.info "Result: #{loggable_result(result)}" results << { orchestrator_class: oc.name, partner: orchestrator.partner, flow:, result: } rescue StandardError => e msg = "#{oc.name} #{orchestrator.partner} #{flow} exception. #{e}" # Enhanced error logging with detailed context ErrorReporting.error(e, { orchestrator_class: oc.name, partner: orchestrator.partner, flow: flow, error_type: 'orchestrator_execution_error', orchestrator_name: orchestrator.class.name, flow_method: flow, exception_class: e.class.name, exception_message: e., backtrace: e.backtrace&.first(10), message: msg }) logger.error msg raise if raise_on_error # Add error result to results array instead of failing silently results << { orchestrator_class: oc.name, partner: orchestrator.partner, flow:, result: :error, error: e., error_class: e.class.name } end logger.info 'completed' end end end end end results end |
.execute_inventory_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) ⇒ Object
Executes the inventory flow for EDI orchestrators.
orchestrator_name - The name of a specific orchestrator to run, optional.
partner - The partner key to run for, optional.
logger - The logger to use.
trial_run - If true, will not send real requests.
176 177 178 |
# File 'app/services/edi/base_orchestrator.rb', line 176 def self.execute_inventory_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) execute_flow(:execute_inventory_flow, orchestrator_name:, partner:, logger:, trial_run:) end |
.execute_listing_message_feed_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) ⇒ Object
Executes the listing message flow for EDI orchestrators.
orchestrator_name - The name of a specific orchestrator to run, optional.
partner - The partner key to run for, optional.
logger - The logger to use.
trial_run - If true, will not send real requests.
196 197 198 |
# File 'app/services/edi/base_orchestrator.rb', line 196 def self.(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) execute_flow(:execute_listing_message_feed_flow, orchestrator_name:, partner:, logger:, trial_run:) end |
.execute_order_flow(options = {}) ⇒ void
This method returns an undefined value.
281 282 283 284 285 |
# File 'app/services/edi/base_orchestrator.rb', line 281 def self.execute_order_flow( = {}) # Queue for Ship confirm what can be confirmed right away # Edi::ShipConfirm.new.process # NO MORE AUTO SHIP CONFIRM orchestrators().each(&:execute_order_flow) end |
.execute_price_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) ⇒ Object
Executes the price flow for EDI orchestrators.
orchestrator_name - The name of a specific orchestrator to run, optional.
partner - The partner key to run for, optional.
logger - The logger to use.
trial_run - If true, will not send real requests.
186 187 188 |
# File 'app/services/edi/base_orchestrator.rb', line 186 def self.execute_price_flow(orchestrator_name: nil, partner: nil, logger: Rails.logger, trial_run: false) execute_flow(:execute_price_flow, orchestrator_name:, partner:, logger:, trial_run:) end |
.execute_product_data_flow(options = {}) ⇒ void
This method returns an undefined value.
364 365 366 |
# File 'app/services/edi/base_orchestrator.rb', line 364 def self.execute_product_data_flow( = {}) orchestrators().each(&:execute_product_data_flow) end |
.orchestrator_for_customer_id(customer_id, use_cache: true) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'app/services/edi/base_orchestrator.rb', line 113 def orchestrator_for_customer_id(customer_id, use_cache: true) # 041619 Ramie: match first on single customer id partners, then dig into multi customer partners # this is necessary to properly match Amazon Vendor Central vendor partners: there is a single EDI entity partner: # :amazon_vendor_central_direct_fulfillment (for both WAX7V and WAT4D) # but two single partners for vendors WAX7V and WAT4D: # :amazon_vendor_central_direct_fulfillment_us_WAX7V and # :amazon_vendor_central_direct_fulfillment_us_WAT4D # and we want to drill down to one of the correct single customer id partners above, not the multi customer :amazon_vendor_central_direct_fulfillment partner (which is really only set up for inventory) # Use the cached lookup map for O(1) partner key lookups instead of O(n) detect operations partner_config_key = customer_id_to_partner_key_map[customer_id] return unless partner_config_key use_cache ? cached_build(partner_config_key) : build(partner_config_key) end |
.orchestrators(options = {}) ⇒ Array<Edi::BaseOrchestrator>
Returns one orchestrator per partner.
132 133 134 |
# File 'app/services/edi/base_orchestrator.rb', line 132 def orchestrators( = {}) partners.keys.map { |partner| new(partner, ) } end |
.partners ⇒ Object
25 26 27 |
# File 'app/services/edi/base_orchestrator.rb', line 25 def partners all_orchestrators_class.map(&:partners).reduce({}, :merge) end |
Instance Method Details
#confirm_outbound_processing? ⇒ Boolean
By default we don't require a two stage processing (ready -> processing -> complete)
465 466 467 |
# File 'app/services/edi/base_orchestrator.rb', line 465 def confirm_outbound_processing? false end |
#customer(segment = nil) ⇒ Object
406 407 408 409 410 411 412 413 414 415 416 417 418 419 |
# File 'app/services/edi/base_orchestrator.rb', line 406 def customer(segment = nil) raise 'Orchestrator requires a segment for this partner to determine customer' if customer_id.is_a?(Hash) && segment.nil? if segment.present? && customer_id.is_a?(Hash) segment_cust_id = customer_id[segment.to_s.downcase.to_sym] segment_cust_id = customer_id[segment.to_s.to_sym] if segment_cust_id.nil? # In case we use uppercase keys cust = Customer.where(id: segment_cust_id).first elsif customer_id cust = Customer.where(id: customer_id).first end return cust if cust raise 'Orchestrator is unable to find a customer' end |
#customer_catalog ⇒ Object
Alias for Customer#catalog
15 |
# File 'app/services/edi/base_orchestrator.rb', line 15 delegate :catalog, to: :customer, prefix: true |
#customer_ids ⇒ Object
427 428 429 430 431 432 433 434 435 436 437 |
# File 'app/services/edi/base_orchestrator.rb', line 427 def customer_ids if respond_to?(:customer_id) if customer_id.respond_to?(:values) customer_id.values.uniq else [customer_id] end else [] end end |
#customers ⇒ Object
Returns customers (or single customer) associated with an orchestrator
as an active relation
423 424 425 |
# File 'app/services/edi/base_orchestrator.rb', line 423 def customers Customer.where(id: customer_ids) end |
#execute_inventory_flow ⇒ Object
372 373 374 |
# File 'app/services/edi/base_orchestrator.rb', line 372 def execute_inventory_flow # Implement me in subclass end |
#execute_order_flow ⇒ Object
368 369 370 |
# File 'app/services/edi/base_orchestrator.rb', line 368 def execute_order_flow # Implement me in subclass end |
#execute_price_flow ⇒ Object
376 377 378 |
# File 'app/services/edi/base_orchestrator.rb', line 376 def execute_price_flow # Implement me in subclass end |
#execute_product_data_flow ⇒ Object
380 381 382 |
# File 'app/services/edi/base_orchestrator.rb', line 380 def execute_product_data_flow # Implement me in subclass end |
#ignore_back_orders ⇒ Object
By default back orders are not ignored
470 471 472 |
# File 'app/services/edi/base_orchestrator.rb', line 470 def ignore_back_orders false end |
#inventory_message_enabled? ⇒ Boolean
305 306 307 |
# File 'app/services/edi/base_orchestrator.rb', line 305 def try(:inventory_message_enabled).to_b end |
#pending_discontinue_lifetime ⇒ Object
442 443 444 445 |
# File 'app/services/edi/base_orchestrator.rb', line 442 def pending_discontinue_lifetime val = try(:pending_discontinue_lifetime_duration) val.is_a?(ActiveSupport::Duration) ? val : DEFAULT_PENDING_DISCONTINUE_LIFETIME end |
#price_message_enabled? ⇒ Boolean
297 298 299 |
# File 'app/services/edi/base_orchestrator.rb', line 297 def try(:price_message_enabled).to_b end |
#product_data_enabled? ⇒ Boolean
301 302 303 |
# File 'app/services/edi/base_orchestrator.rb', line 301 def product_data_enabled? try(:product_data_enabled).to_b end |
#return_notification_message_enabled? ⇒ Boolean
Default false for orchestrators that don't handle inbound return notifications
(inventory-only feeds, push-only partners). Returns-capable orchestrators
(Amazon, Walmart, Wayfair, …) override this. Prevents a NoMethodError when a
customer_id resolves to a non-returns orchestrator (AppSignal #6069).
326 327 328 |
# File 'app/services/edi/base_orchestrator.rb', line 326 def try(:return_notification_message_enabled).to_b end |
#should_execute_flow?(flow) ⇒ Boolean
flow is in the format of execute_inventory_flow or execute_price_flow
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 |
# File 'app/services/edi/base_orchestrator.rb', line 332 def should_execute_flow?(flow) flow_every_x_hour_sym = :"#{flow}_every_x_hour" return true unless respond_to? flow_every_x_hour_sym every_x_hour = send(flow_every_x_hour_sym).to_i current_hour = Time.current.hour if every_x_hour > 0 && every_x_hour <= 24 # valid integral value, let's use it # warn if it's not exactly one of the recommended value, i.e. integral factors of 24 unless RECOMMENDED_EXECUTE_FLOW_EVERY_X_HOUR.include?(every_x_hour) msg = "EDI #{self.class} partner: #{partner} has a #{flow_every_x_hour_sym} value of #{every_x_hour} which is not one of the recommended values: #{RECOMMENDED_EXECUTE_FLOW_EVERY_X_HOUR}, inventory may not be sent at exactly the desired frequency" ErrorReporting.warning(msg) Rails.logger.info(msg) end # but do go ahead and send per the every_x_hour integral value return true if current_hour.modulo(every_x_hour) == 0 else # invalid value, error but do send it at least once a day as a fall back msg = "EDI #{self.class} partner: #{partner} has an invalid #{flow_every_x_hour_sym} value of #{every_x_hour}. Valid values are between #{RECOMMENDED_EXECUTE_FLOW_EVERY_X_HOUR.min} and #{RECOMMENDED_EXECUTE_FLOW_EVERY_X_HOUR.max}, as a failsafe fallback, inventory will only be sent once per day!" ErrorReporting.error(msg) Rails.logger.error(msg) return true if current_hour.modulo(24) == 0 end false end |
#should_execute_order_flow? ⇒ Boolean
Determines if the order flow should run
288 289 290 |
# File 'app/services/edi/base_orchestrator.rb', line 288 def should_execute_order_flow? true end |
#should_execute_product_data_flow? ⇒ Boolean
Determines if the product data flow should run
293 294 295 |
# File 'app/services/edi/base_orchestrator.rb', line 293 def should_execute_product_data_flow? true end |
#supports_flow?(flow) ⇒ Boolean
Whether this orchestrator actually implements the given flow: it either
overrides the Edi::BaseOrchestrator no-op stub (order/inventory/price/
product_data) or defines a flow base doesn't (listing feed/discontinue).
Drives which flow buttons the CRM orchestrators dashboard shows —
execute_flow silently no-ops an unimplemented flow, so a button for one
is just noise (e.g. OpenAI only does product data).
318 319 320 |
# File 'app/services/edi/base_orchestrator.rb', line 318 def supports_flow?(flow) respond_to?(flow) && method(flow).owner != Edi::BaseOrchestrator end |
#test_mode? ⇒ Boolean
402 403 404 |
# File 'app/services/edi/base_orchestrator.rb', line 402 def test_mode? Rails.env.development? end |