Class: Edi::BaseOrchestrator

Inherits:
Object
  • Object
show all
Extended by:
Memery
Defined in:
app/services/edi/base_orchestrator.rb

Overview

Service object: base 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
[1, 2, 3, 4, 6, 8, 12, 24].freeze
DEFAULT_PENDING_DISCONTINUE_LIFETIME =

Default pending discontinue lifetime.

1.day

Instance Attribute Summary collapse

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(partner, options = {}) ⇒ BaseOrchestrator

Returns a new instance of BaseOrchestrator.

Parameters:

  • partner (Symbol, String)

    partner key from the partners config

  • options (Hash) (defaults to: {})

    orchestrator options

Options Hash (options):

  • logger (Logger)

    logger to use (defaults to Rails.logger)

Raises:

  • (ArgumentError)


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, options = {})
  # `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 = options
  @logger = options[:logger] || Rails.logger
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



6
7
8
# File 'app/services/edi/base_orchestrator.rb', line 6

def config
  @config
end

#loggerObject (readonly)

Returns the value of attribute logger.



6
7
8
# File 'app/services/edi/base_orchestrator.rb', line 6

def logger
  @logger
end

#optionsObject (readonly)

Returns the value of attribute options.



6
7
8
# File 'app/services/edi/base_orchestrator.rb', line 6

def options
  @options
end

Class Method Details

.all_orchestrators_classObject



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.

Parameters:

  • partner_config_key (Symbol, String)

    partner key from the partners config

  • options (Hash) (defaults to: {})

    orchestrator options (passed to the orchestrator initializer)

Options Hash (options):

  • logger (Logger)

    logger to use (defaults to Rails.logger)

Returns:



83
84
85
86
87
88
89
# File 'app/services/edi/base_orchestrator.rb', line 83

def build(partner_config_key, options = {})
  # 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, options) 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.

Parameters:

  • partners (Hash)

    partner-key => config (mirrors partners)

Returns:

  • (Hash)

    customer_id => partner key



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

Parameters:

  • partner_config_key (Symbol, String)

    partner key from the partners config

  • options (Hash) (defaults to: {})

    orchestrator options (part of the cache key; see build)

Options Hash (options):

  • logger (Logger)

    logger to use (defaults to Rails.logger)

Returns:



108
109
110
111
# File 'app/services/edi/base_orchestrator.rb', line 108

def cached_build(partner_config_key, options = {})
  cache_key = [partner_config_key.to_sym, options.hash].join('_')
  cached_orchestrators[cache_key] ||= build(partner_config_key, options)
end

.cached_orchestratorsObject

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_lifetimeObject

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_enabledObject



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_mapObject

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_enabledObject



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_enabledObject



141
142
143
144
145
146
147
148
149
150
# File 'app/services/edi/base_orchestrator.rb', line 141

def customer_ids_with_invoice_message_enabled
  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.invoice_message_enabled?

      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.

Raises:

  • (ArgumentError)


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.message,
                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.message,
                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.execute_listing_message_feed_flow(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.

Parameters:

  • options (Hash) (defaults to: {})

    orchestrator options (passed to orchestrators)

Options Hash (options):

  • logger (Logger)

    logger to use (defaults to Rails.logger)



281
282
283
284
285
# File 'app/services/edi/base_orchestrator.rb', line 281

def self.execute_order_flow(options = {})
  # Queue for Ship confirm what can be confirmed right away
  # Edi::ShipConfirm.new.process # NO MORE AUTO SHIP CONFIRM
  orchestrators(options).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.

Parameters:

  • options (Hash) (defaults to: {})

    orchestrator options (passed to orchestrators)

Options Hash (options):

  • logger (Logger)

    logger to use (defaults to Rails.logger)



364
365
366
# File 'app/services/edi/base_orchestrator.rb', line 364

def self.execute_product_data_flow(options = {})
  orchestrators(options).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.

Parameters:

  • options (Hash) (defaults to: {})

    orchestrator options (passed to each orchestrator)

Options Hash (options):

  • logger (Logger)

    logger to use (defaults to Rails.logger)

Returns:



132
133
134
# File 'app/services/edi/base_orchestrator.rb', line 132

def orchestrators(options = {})
  partners.keys.map { |partner| new(partner, options) }
end

.partnersObject



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)

Returns:

  • (Boolean)


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_catalogObject

Alias for Customer#catalog

Returns:

  • (Object)

    Customer#customer_catalog

See Also:



15
# File 'app/services/edi/base_orchestrator.rb', line 15

delegate :catalog, to: :customer, prefix: true

#customer_idsObject



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

#customersObject

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_flowObject



372
373
374
# File 'app/services/edi/base_orchestrator.rb', line 372

def execute_inventory_flow
  # Implement me in subclass
end

#execute_order_flowObject



368
369
370
# File 'app/services/edi/base_orchestrator.rb', line 368

def execute_order_flow
  # Implement me in subclass
end

#execute_price_flowObject



376
377
378
# File 'app/services/edi/base_orchestrator.rb', line 376

def execute_price_flow
  # Implement me in subclass
end

#execute_product_data_flowObject



380
381
382
# File 'app/services/edi/base_orchestrator.rb', line 380

def execute_product_data_flow
  # Implement me in subclass
end

#ignore_back_ordersObject

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

Returns:

  • (Boolean)


305
306
307
# File 'app/services/edi/base_orchestrator.rb', line 305

def inventory_message_enabled?
  try(:inventory_message_enabled).to_b
end

#pending_discontinue_lifetimeObject



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

Returns:

  • (Boolean)


297
298
299
# File 'app/services/edi/base_orchestrator.rb', line 297

def price_message_enabled?
  try(:price_message_enabled).to_b
end

#product_data_enabled?Boolean

Returns:

  • (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).

Returns:

  • (Boolean)


326
327
328
# File 'app/services/edi/base_orchestrator.rb', line 326

def return_notification_message_enabled?
  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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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).

Parameters:

  • flow (Symbol)

    one of the execute_*_flow methods

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


402
403
404
# File 'app/services/edi/base_orchestrator.rb', line 402

def test_mode?
  Rails.env.development?
end