Class: Catalog

Inherits:
ApplicationRecord show all
Includes:
CatalogConstants, Models::Auditable, Models::Lineage
Defined in:
app/models/catalog.rb

Overview

== Schema Information

Table name: catalogs
Database name: primary

id :integer not null, primary key
alternate_warehouse_stock_fraction :integer default(25), not null
bom_enabled :boolean default(FALSE), not null
country_iso3 :string(3) not null
currency :string(255)
currency_symbol :string(255)
default_catalog_item_state :string(30)
external_price_check_enabled :boolean default(FALSE), not null
for_admin :boolean default(FALSE), not null
is_active_third_party_sku_filter :boolean default(FALSE), not null
is_discontinued :boolean default(FALSE), not null
locales :string default([]), not null, is an Array
map_percentage :decimal(5, 4) default(0.8), not null
name :string(255)
orchestrator_key :string
parent_catalog_discount :decimal(5, 4)
parent_catalog_discount_refurb :decimal(5, 4) default(0.0)
price_sync_delay :integer
price_sync_policy :enum default("price_sync_manual"), not null
probe_cadence_days :integer default(1), not null
public_name :string
require_upc :boolean default(FALSE), not null
retailer_part_number_label :string
retailer_type :enum default("marketplace"), not null
shipping_preferences :jsonb
third_party_name_en_required :boolean default(FALSE), not null
third_party_name_fr_required :boolean default(FALSE), not null
third_party_part_number_required :boolean default(FALSE), not null
third_party_sku_filter_regex :string
third_party_sku_required :boolean default(FALSE), not null
url_template :string
vendor_code :string
website :string
created_at :datetime
updated_at :datetime
amazon_marketplace_id :integer
company_id :integer
parent_catalog_id :integer
sales_manager_employee_record_id :integer
store_id :integer

Indexes

catalogs_company_id_idx (company_id)
catalogs_parent_catalog_id_idx (parent_catalog_id)
catalogs_sales_manager_employee_record_id_idx (sales_manager_employee_record_id)
index_catalogs_on_alternate_warehouse_stock_fraction (alternate_warehouse_stock_fraction)
index_catalogs_on_amazon_marketplace_id (amazon_marketplace_id)
index_catalogs_on_bom_enabled (bom_enabled)
index_catalogs_on_name (name)
index_catalogs_on_store_id (store_id)

Foreign Keys

catalogs_company_id_fkey (company_id => companies.id)
catalogs_sales_manager_employee_record_id_fk (sales_manager_employee_record_id => employee_records.id)
catalogs_store_id_fkey (store_id => stores.id)
fk_rails_... (amazon_marketplace_id => amazon_marketplaces.id)
fk_rails_... (country_iso3 => countries.iso3)
fk_rails_... (parent_catalog_id => catalogs.id)

Defined Under Namespace

Classes: AmazonBuyBoxRecoveryService, AmazonImageBackfillService, AmazonPriceLoweringService, AmazonPriceRaisingService, AmazonPricingAutomationService, AssignCatalogItem, AssignCatalogToCustomer, CatalogCloner, CreateCatalogItem, PullAmazonCatalogSuppressionData, PullAmazonCatalogsListingsData, PushCatalogItemPrice, SynchronizeCatalogPrices, UpdateCatalog, UpdateCatalogItem, ValidateCatalogItems

Constant Summary collapse

EXCLUDABLE_DIRECT_CARRIERS =

Convenience: all carrier exclusion options for select dropdowns

%w[SpeedeeDelivery USPS UPS FedEx Canadapost Purolator Canpar RlCarriers Freightquote].freeze
EXCLUDABLE_MARKETPLACE_CARRIERS =

Excludable marketplace carriers.

%w[USPS FEDEX UPS SPEEDEE ONTRAC LASERSHIP DHL AMAZON_SHIPPING].freeze

Constants included from CatalogConstants

CatalogConstants::ALL_MAIN_CATALOG_IDS, CatalogConstants::AMAZON_CATALOG_IDS, CatalogConstants::AMAZON_CA_CATALOG_IDS, CatalogConstants::AMAZON_EU_CATALOG_IDS, CatalogConstants::AMAZON_NA_SELLER_IDS, CatalogConstants::AMAZON_SC_BE_CATALOG_ID, CatalogConstants::AMAZON_SC_CATALOG_IDS, CatalogConstants::AMAZON_SC_CA_CATALOG_ID, CatalogConstants::AMAZON_SC_DE_CATALOG_ID, CatalogConstants::AMAZON_SC_ES_CATALOG_ID, CatalogConstants::AMAZON_SC_FR_CATALOG_ID, CatalogConstants::AMAZON_SC_IT_CATALOG_ID, CatalogConstants::AMAZON_SC_NL_CATALOG_ID, CatalogConstants::AMAZON_SC_PL_CATALOG_ID, CatalogConstants::AMAZON_SC_SE_CATALOG_ID, CatalogConstants::AMAZON_SC_UK_CATALOG_ID, CatalogConstants::AMAZON_SC_US_CATALOG_ID, CatalogConstants::AMAZON_SELLER_IDS, CatalogConstants::AMAZON_US_CATALOG_IDS, CatalogConstants::AMAZON_VC_CATALOG_IDS, CatalogConstants::AMAZON_VC_CA_CATALOG_ID, CatalogConstants::AMAZON_VC_CA_CATALOG_IDS, CatalogConstants::AMAZON_VC_DIRECT_FULFILLMENT_CATALOG_IDS, CatalogConstants::AMAZON_VC_US_CATALOG_IDS, CatalogConstants::AMAZON_VC_US_WASN4_CATALOG_ID, CatalogConstants::AMAZON_VC_US_WAX7V_CATALOG_ID, CatalogConstants::AMAZON_VC_WAT0F_CA_CATALOG_ID, CatalogConstants::AMAZON_VC_WAT4D_CA_CATALOG_ID, CatalogConstants::AMAZON_VENDOR_CODE_TO_CATALOG_ID, CatalogConstants::BESTBUY_CANADA, CatalogConstants::BUILD_COM, CatalogConstants::CANADIAN_TIRE, CatalogConstants::CA_CATALOG_ID, CatalogConstants::COSTCO_CANADA, CatalogConstants::COSTCO_CATALOGS, CatalogConstants::COSTCO_USA, CatalogConstants::EU_CATALOG_ID, CatalogConstants::HOME_DEPOT_CANADA, CatalogConstants::HOME_DEPOT_CATALOGS, CatalogConstants::HOME_DEPOT_USA, CatalogConstants::HOUZZ, CatalogConstants::LOCALE_TO_CATALOG, CatalogConstants::LOWES_CANADA, CatalogConstants::LOWES_USA, CatalogConstants::RONA_CANADA, CatalogConstants::US_CATALOG_ID, CatalogConstants::WALMART_CATALOGS, CatalogConstants::WALMART_SELLER_CANADA, CatalogConstants::WALMART_SELLER_USA, CatalogConstants::WAYFAIR_CANADA, CatalogConstants::WAYFAIR_CATALOGS, CatalogConstants::WAYFAIR_GERMANY, CatalogConstants::WAYFAIR_USA

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has and belongs to many collapse

Has many collapse

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CatalogConstants

amazon_catalog?, amazon_seller_catalog?, costco_catalog?, home_depot_catalog?, walmart_catalog?, wayfair_catalog?

Methods included from Models::Lineage

#ancestors, #ancestors_ids, #children_and_roots, #descendants, #descendants_ids, #ensure_non_recursive_lineage, #family_members, #generate_full_name, #generate_full_name_array, #lineage, #lineage_array, #lineage_simple, #root, #root_id, #self_ancestors_and_descendants, #self_ancestors_and_descendants_ids, #self_and_ancestors, #self_and_ancestors_ids, #self_and_children, #self_and_descendants, #self_and_descendants_ids, #self_and_siblings, #self_and_siblings_ids, #siblings, #siblings_ids

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#currencyObject (readonly)



92
# File 'app/models/catalog.rb', line 92

validates :name, :currency, :currency_symbol, presence: true

#currency_symbolObject (readonly)



92
# File 'app/models/catalog.rb', line 92

validates :name, :currency, :currency_symbol, presence: true

#default_catalog_item_stateObject (readonly)



94
# File 'app/models/catalog.rb', line 94

validates :default_catalog_item_state, presence: true

#explode_product_linesObject

Returns the value of attribute explode_product_lines.



151
152
153
# File 'app/models/catalog.rb', line 151

def explode_product_lines
  @explode_product_lines
end

#nameObject (readonly)



92
# File 'app/models/catalog.rb', line 92

validates :name, :currency, :currency_symbol, presence: true

#parent_catalog_idObject (readonly)



95
# File 'app/models/catalog.rb', line 95

validates :parent_catalog_id, presence: { if: :price_sync_timed?, message: 'When price sync timed is set, a parent catalog is required' }

#price_sync_delayObject (readonly)



93
# File 'app/models/catalog.rb', line 93

validates :price_sync_delay, presence: { if: :price_sync_timed? }

#price_sync_policyObject (readonly)



96
97
# File 'app/models/catalog.rb', line 96

validates :price_sync_policy, inclusion: { in: %w[price_sync_timed], message: 'must be timed if a price sync delay is specified',
if: :price_sync_delay }

Class Method Details

.activeActiveRecord::Relation<Catalog>

A relation of Catalogs that are active. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



111
# File 'app/models/catalog.rb', line 111

scope :active, -> { where(is_discontinued: false) }

.amazon_catalog_orderActiveRecord::Relation<Catalog>

A relation of Catalogs that are amazon catalog order. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



125
126
127
128
# File 'app/models/catalog.rb', line 125

scope :amazon_catalog_order, -> {
  ids = AMAZON_SC_CATALOG_IDS + AMAZON_VC_CATALOG_IDS
  where(id: ids).in_order_of(:id, ids)
}

.amazon_sellersActiveRecord::Relation<Catalog>

A relation of Catalogs that are amazon sellers. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



119
# File 'app/models/catalog.rb', line 119

scope :amazon_sellers, -> { where(id: AMAZON_SELLER_IDS) }

.amazonsActiveRecord::Relation<Catalog>

A relation of Catalogs that are amazons. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



118
# File 'app/models/catalog.rb', line 118

scope :amazons, -> { where(id: AMAZON_CATALOG_IDS) }

.bom_enabledActiveRecord::Relation<Catalog>

A relation of Catalogs that are bom enabled. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



113
# File 'app/models/catalog.rb', line 113

scope :bom_enabled, -> { where(bom_enabled: true) }

.ca_catalogObject



169
170
171
# File 'app/models/catalog.rb', line 169

def self.ca_catalog
  Catalog.find(CA_CATALOG_ID).freeze
end

.catalog_for_party(party) ⇒ Object



191
192
193
# File 'app/models/catalog.rb', line 191

def self.catalog_for_party(party)
  party&.catalog || Catalog.locale_to_catalog(I18n.locale)
end

.catalog_id_to_locale(catalog_id) ⇒ Object



187
188
189
# File 'app/models/catalog.rb', line 187

def self.catalog_id_to_locale(catalog_id)
  { US_CATALOG_ID => 'en-US', CA_CATALOG_ID => 'en-CA' }[catalog_id] || 'en-US'
end

.default_for_country(country_iso3_or_iso2) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
# File 'app/models/catalog.rb', line 205

def self.default_for_country(country_iso3_or_iso2)
  # TODO: I18n: Add other countries here or store catalog in country table
  case country_iso3_or_iso2&.upcase
  when 'CA', 'CAN'
    Catalog.ca_catalog
  when *Country::EU_COUNTRIES_ISO
    Catalog.eu_catalog
  else
    Catalog.us_catalog
  end
end

.eu_catalogObject



173
174
175
# File 'app/models/catalog.rb', line 173

def self.eu_catalog
  Catalog.find(EU_CATALOG_ID).freeze
end

.for_amazon_seller_marketplace_identifier(marketplace_identifier) ⇒ Object



231
232
233
234
235
236
# File 'app/models/catalog.rb', line 231

def self.for_amazon_seller_marketplace_identifier(marketplace_identifier)
  amz_mp = AmazonMarketplace.find_by(marketplace_identifier: marketplace_identifier)
  return nil unless amz_mp

  Catalog.amazon_sellers.where(amazon_marketplace_id: amz_mp.id).first
end

.for_countryActiveRecord::Relation<Catalog>

A relation of Catalogs that are for country. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



114
# File 'app/models/catalog.rb', line 114

scope :for_country, ->(country) { includes(:store).where(stores: { country_iso3: country }) }

.for_google_feedActiveRecord::Relation<Catalog>

A relation of Catalogs that are for google feed. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



117
# File 'app/models/catalog.rb', line 117

scope :for_google_feed, -> { main_catalogs }

.locale_to_catalog(locale = nil) ⇒ Object



182
183
184
185
# File 'app/models/catalog.rb', line 182

def self.locale_to_catalog(locale = nil)
  locale ||= I18n.locale
  Catalog.find(locale_to_catalog_id(locale))
end

.locale_to_catalog_id(locale = nil) ⇒ Object



177
178
179
180
# File 'app/models/catalog.rb', line 177

def self.locale_to_catalog_id(locale = nil)
  locale ||= I18n.locale
  LOCALE_TO_CATALOG[locale.to_sym] || US_CATALOG_ID
end

.main_catalog_idsObject

These are the catalog that are offered on our site



257
258
259
# File 'app/models/catalog.rb', line 257

def self.main_catalog_ids
  [US_CATALOG_ID, CA_CATALOG_ID]
end

.main_catalogsActiveRecord::Relation<Catalog>

A relation of Catalogs that are main catalogs. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



115
# File 'app/models/catalog.rb', line 115

scope :main_catalogs, -> { where(id: Catalog.main_catalog_ids) }

.non_adminActiveRecord::Relation<Catalog>

A relation of Catalogs that are non admin. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



112
# File 'app/models/catalog.rb', line 112

scope :non_admin, -> { where(for_admin: false) }

.options_for_select(default_scope = nil, prioritize_main_catalogs: true, show_store_info: false) ⇒ Object



195
196
197
198
199
200
201
202
203
# File 'app/models/catalog.rb', line 195

def self.options_for_select(default_scope = nil, prioritize_main_catalogs: true, show_store_info: false)
  catalogs = (default_scope || Catalog.active.non_admin).includes(:store)
  catalogs = catalogs.order("catalogs.id NOT IN (#{Catalog.main_catalog_ids.join(',')})".sql_safe) if prioritize_main_catalogs
  catalogs.order(:name).map do |c|
    n = c.name
    n << " (#{c.store.short_name})" if show_store_info
    [n, c.id]
  end
end

.price_alert_catalog_idsObject

Catalogs kept out of the "other catalogs" summary bucket in the daily
Catalog Price Changes email: the main WarmlyYours catalogs (reported
line-by-line in that email) plus the Amazon Seller Central catalogs
(omitted there and reported separately in the Amazon Repricing Report).



265
266
267
# File 'app/models/catalog.rb', line 265

def self.price_alert_catalog_ids
  main_catalog_ids + AMAZON_SC_CATALOG_IDS
end

.seller_na_catalogsActiveRecord::Relation<Catalog>

A relation of Catalogs that are seller na catalogs. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



120
# File 'app/models/catalog.rb', line 120

scope :seller_na_catalogs, -> { where(id: AMAZON_NA_SELLER_IDS) }

.target_catalog_options_for_selectObject

Used in mass action assignment, showing the % off from the master catalog if any



218
219
220
221
222
223
224
225
226
227
228
229
# File 'app/models/catalog.rb', line 218

def self.target_catalog_options_for_select
  # where.not(id: main_catalog_ids).where.not(parent_catalog_id: nil)
  Catalog.order(:name).map do |catalog|
    n = catalog.name
    if catalog.parent_catalog
      n << " - #{catalog.parent_catalog_discount * 100} % off" if catalog.parent_catalog_discount&.positive?
      n << " - #{catalog.parent_catalog_discount_refurb * 100} % off (refurb)" if catalog.parent_catalog_discount_refurb&.positive?
    end

    [n, catalog.id]
  end
end

.us_catalogObject



165
166
167
# File 'app/models/catalog.rb', line 165

def self.us_catalog
  Catalog.find(US_CATALOG_ID).freeze
end

.vendorsActiveRecord::Relation<Catalog>

A relation of Catalogs that are vendors. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



121
# File 'app/models/catalog.rb', line 121

scope :vendors, -> { where(retailer_type: :vendor) }

.with_orchestrator_keyActiveRecord::Relation<Catalog>

A relation of Catalogs that are with orchestrator key. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Catalog>)

See Also:



116
# File 'app/models/catalog.rb', line 116

scope :with_orchestrator_key, -> { where.not(orchestrator_key: [nil, '']) }

Instance Method Details

#amazon_catalog?Boolean

Returns:

  • (Boolean)


277
278
279
280
# File 'app/models/catalog.rb', line 277

def amazon_catalog?
  # id.in?(AMAZON_CATALOG_IDS)
  amazon_marketplace.present?
end

#amazon_fulfillable?Boolean

Returns:

  • (Boolean)


282
283
284
# File 'app/models/catalog.rb', line 282

def amazon_fulfillable?
  id.in?(AMAZON_SELLER_IDS)
end

#amazon_marketplaceAmazonMarketplace



79
# File 'app/models/catalog.rb', line 79

belongs_to :amazon_marketplace, optional: true, inverse_of: :catalogs

#apply_default_countryObject



463
464
465
466
467
# File 'app/models/catalog.rb', line 463

def apply_default_country
  return unless store

  self.country_iso3 ||= store.country.iso3
end

#apply_default_localesObject



454
455
456
457
458
459
460
461
# File 'app/models/catalog.rb', line 454

def apply_default_locales
  return if locales.present?

  default_locales = []
  default_locales = store.country.locales_for_country if store
  default_locales += parent_catalog.locales || [] if parent_catalog
  self.locales = default_locales & I18n.available_locales
end

#carrier_excluded?(carrier_name) ⇒ Boolean

Returns true if the given direct carrier name is excluded for this catalog.
Carrier names match ShippingOption#carrier values (e.g. "SpeedeeDelivery", "USPS", "FedEx").

Returns:

  • (Boolean)


419
420
421
422
423
# File 'app/models/catalog.rb', line 419

def carrier_excluded?(carrier_name)
  return false if excluded_carriers.blank?

  excluded_carriers.any? { |ec| ec.casecmp?(carrier_name.to_s) }
end

#catalog_itemsActiveRecord::Relation<CatalogItem>

Returns:

See Also:



86
# File 'app/models/catalog.rb', line 86

has_many :catalog_items, dependent: :destroy, inverse_of: :catalog

#check_for_item_restrictions?Boolean

This is for channel specific items (custom catalog)
Should this catalog requires check in pick item to make sure they don't pick
a restricted catalog item

Returns:

  • (Boolean)


241
242
243
# File 'app/models/catalog.rb', line 241

def check_for_item_restrictions?
  is_main_catalog?
end

#companyCompany

Returns:

See Also:



75
# File 'app/models/catalog.rb', line 75

belongs_to :company, inverse_of: :catalogs

#countryCountry

Returns:

See Also:



78
# File 'app/models/catalog.rb', line 78

belongs_to :country, foreign_key: 'country_iso3', primary_key: 'iso3'

#couponsActiveRecord::Relation<Coupon>

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



89
# File 'app/models/catalog.rb', line 89

has_many :coupons, through: :customer_filters

#customer_filtersActiveRecord::Relation<CustomerFilter>

Returns:

See Also:



81
# File 'app/models/catalog.rb', line 81

has_and_belongs_to_many :customer_filters

#customer_service_managerObject

Alias for Employee#customer_service_manager

Returns:

  • (Object)

    Employee#customer_service_manager

See Also:



363
# File 'app/models/catalog.rb', line 363

delegate :customer_service_manager, to: :Employee

#customersActiveRecord::Relation<Customer>

Returns:

See Also:



84
# File 'app/models/catalog.rb', line 84

has_many :customers

#deep_dupObject



156
157
158
159
160
161
162
163
# File 'app/models/catalog.rb', line 156

def deep_dup
  deep_clone(
    include: :catalog_items,
    except: %i[created_at updated_at]
  ) do |original, copy|
    copy.name = "#{original.name} copy" if copy.is_a?(Catalog)
  end
end

#determine_retailer_part_number_labelObject



365
366
367
# File 'app/models/catalog.rb', line 365

def determine_retailer_part_number_label
  retailer_part_number_label || 'Third Party Part Number'
end

#discontinue_catalog_itemsObject



353
354
355
356
357
# File 'app/models/catalog.rb', line 353

def discontinue_catalog_items
  return unless is_discontinued_changed? && is_discontinued?

  catalog_items.each(&:discontinue)
end

#is_amazon?Boolean

Returns:

  • (Boolean)


375
376
377
# File 'app/models/catalog.rb', line 375

def is_amazon?
  CatalogConstants::AMAZON_CATALOG_IDS.include?(id)
end

#is_amazon_seller_central?Boolean

Returns:

  • (Boolean)


379
380
381
# File 'app/models/catalog.rb', line 379

def is_amazon_seller_central?
  CatalogConstants::AMAZON_SC_CATALOG_IDS.include?(id)
end

#is_amazon_vendor_central?Boolean

Returns:

  • (Boolean)


383
384
385
# File 'app/models/catalog.rb', line 383

def is_amazon_vendor_central?
  CatalogConstants::AMAZON_VC_CATALOG_IDS.include?(id)
end

#is_costco?Boolean

True for Costco catalogs, whose prices are probed per-variant via
Costco's JSON APIs rather than an Oxylabs page scrape.

Returns:

  • (Boolean)

See Also:



390
391
392
# File 'app/models/catalog.rb', line 390

def is_costco?
  CatalogConstants::COSTCO_CATALOGS.include?(id)
end

#is_main_catalog?Boolean

Returns:

  • (Boolean)


273
274
275
# File 'app/models/catalog.rb', line 273

def is_main_catalog?
  Catalog.main_catalog_ids.include?(id)
end

#itemsActiveRecord::Relation<Item>

Returns:

  • (ActiveRecord::Relation<Item>)

See Also:



88
# File 'app/models/catalog.rb', line 88

has_many :items, through: :catalog_items

#load_orchestratorEdi::BaseOrchestrator?

Returns the EDI orchestrator that manages this catalog's listings.

A catalog can be shared by several EDI customers — e.g. the US seller
account plus a satellite marketplace (Amazon Mexico books its orders
through the US catalog 76). Only the primary channel manages
listings/inventory/pricing; satellite partners disable those feeds and
exist for order capture only (resolved separately via
Edi::OrderEventProcessor#load_orchestrator by the order's customer).

customers has no inherent order, so the previous .first picked an
arbitrary partner from the heap. Once the MX satellite was attached to
catalog 76 (2026-05-29) a listing PUT could non-deterministically resolve
to MX — whose listing feed is disabled — and crash (AppSignal #5904).
Resolve deterministically: prefer a partner that actually manages
listings, falling back to the lowest customer id for stability.

Returns:

  • (Edi::BaseOrchestrator, nil)

    the managing orchestrator, or nil if
    no customer maps to an EDI partner



412
413
414
415
# File 'app/models/catalog.rb', line 412

def load_orchestrator
  orchestrators = customers.sort_by(&:id).filter_map { |c| Edi::BaseOrchestrator.orchestrator_for_customer_id(c.id) }
  orchestrators.find { |o| o.try(:listing_message_enabled?) } || orchestrators.first
end

#locale_for_catalogObject



316
317
318
319
320
321
322
323
324
# File 'app/models/catalog.rb', line 316

def locale_for_catalog
  # TODO: I18n: multi countries and multi locale
  case currency
  when 'CAD'
    :'en-CA'
  else
    :'en-US'
  end
end

#locales_for_catalogObject

Better method which takes into consideration that certain country will have multiple locales



327
328
329
# File 'app/models/catalog.rb', line 327

def locales_for_catalog
  (locales.presence || country&.locales_for_country || store.country.locales_for_country) & I18n.available_locales
end

#marketplace_catalog?Boolean

Returns:

  • (Boolean)


252
253
254
# File 'app/models/catalog.rb', line 252

def marketplace_catalog?
  is_main_catalog?
end

#marketplace_rate_excluded?(service_code) ⇒ Boolean

Returns true if a marketplace rate's service code should be excluded.
Checks both the general excluded_marketplace_carriers list and the
independent sww_usps_excluded toggle for SWW USPS specifically.

Parameters:

  • service_code (String)

    e.g. "SWW_USPS_GROUND_ADVANTAGE", "AMZBS_SPEEDEE_DELIVERY"

Returns:

  • (Boolean)


431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'app/models/catalog.rb', line 431

def marketplace_rate_excluded?(service_code)
  code = service_code.to_s.upcase
  is_sww = code.start_with?('SWW_')
  is_amzbs = code.start_with?('AMZBS_')
  return false unless is_sww || is_amzbs

  # Independent SWW USPS toggle
  return true if is_sww && sww_usps_excluded? && code.start_with?('SWW_USPS')

  # General marketplace carrier exclusions
  return false if excluded_marketplace_carriers.blank?

  prefix = is_sww ? 'SWW_' : 'AMZBS_'
  excluded_marketplace_carriers.any? do |carrier|
    code.start_with?("#{prefix}#{carrier.upcase}")
  end
end

#ok_to_destroy?Boolean

Returns:

  • (Boolean)


359
360
361
# File 'app/models/catalog.rb', line 359

def ok_to_destroy?
  !LineItem.joins(:catalog_item).merge(catalog_items).exists?
end

#ordersActiveRecord::Relation<Order>

Returns:

  • (ActiveRecord::Relation<Order>)

See Also:



85
# File 'app/models/catalog.rb', line 85

has_many :orders, through: :customers

#parent_catalogCatalog

Returns:

See Also:



77
# File 'app/models/catalog.rb', line 77

belongs_to :parent_catalog, class_name: 'Catalog', optional: true

#parent_catalog_options_for_selectObject



310
311
312
313
314
# File 'app/models/catalog.rb', line 310

def parent_catalog_options_for_select
  catalogs = Catalog.main_catalogs
  catalogs = catalogs.where(store_id:) if store_id
  catalogs.pluck(:name, :id)
end

#product_line_ids_for_analysisObject



347
348
349
# File 'app/models/catalog.rb', line 347

def product_line_ids_for_analysis
  (product_lines_ids_from_catalog_items + product_line_ids).uniq.sort
end

#product_linesActiveRecord::Relation<ProductLine>

Returns:

See Also:



82
# File 'app/models/catalog.rb', line 82

has_and_belongs_to_many :product_lines

#product_lines_defined?Boolean

Returns:

  • (Boolean)


296
297
298
# File 'app/models/catalog.rb', line 296

def product_lines_defined?
  product_line_ids.present?
end

#product_lines_for_sales_portalObject

This method is used to render a list of product line publicly available on /all_products
We look at all product line available to sales portal, then remove any that don't have any
items returned by the standard product page api query



248
249
250
# File 'app/models/catalog.rb', line 248

def product_lines_for_sales_portal
  ProductLine.for_index
end

#product_lines_from_catalog_itemsObject



331
332
333
334
335
336
337
338
339
340
341
# File 'app/models/catalog.rb', line 331

def product_lines_from_catalog_items
  raw_sql = %{
    exists(select 1 from item_product_lines ipl
           inner join items i on i.id = ipl.item_id
           inner join store_items si on si.item_id = i.id
           inner join catalog_items ci on ci.store_item_id = si.id and ci.catalog_id = ?
           where ipl.product_line_id = product_lines.id and ipl.position = 1
         )
  }
  ProductLine.where(raw_sql, id).order(:lineage_expanded)
end

#product_lines_ids_from_catalog_itemsObject



343
344
345
# File 'app/models/catalog.rb', line 343

def product_lines_ids_from_catalog_items
  product_lines_from_catalog_items.ids
end

#public_catalog_itemsObject

Alias for Catalog_items#public_catalog_items

Returns:

  • (Object)

    Catalog_items#public_catalog_items

See Also:



351
# File 'app/models/catalog.rb', line 351

delegate :public_catalog_items, to: :catalog_items

#reported_nameObject



269
270
271
# File 'app/models/catalog.rb', line 269

def reported_name
  public_name.presence || name.presence
end

#sales_managerEmployeeRecord



76
# File 'app/models/catalog.rb', line 76

belongs_to :sales_manager, foreign_key: 'sales_manager_employee_record_id', class_name: 'EmployeeRecord', optional: true

#storeStore

Returns:

See Also:



74
# File 'app/models/catalog.rb', line 74

belongs_to :store, inverse_of: :catalogs

#store_items_for_selectObject



300
301
302
# File 'app/models/catalog.rb', line 300

def store_items_for_select
  store.store_items.active.available.joins(:item).includes(:item).order('items.sku').map { |si| ["#{si.item.sku} - #{si.item.name}", si.id] }
end

#storesActiveRecord::Relation<Store>

Returns:

  • (ActiveRecord::Relation<Store>)

See Also:



90
# File 'app/models/catalog.rb', line 90

has_many :stores, -> { distinct }, through: :catalog_items

#stores_for_selectObject



304
305
306
307
308
# File 'app/models/catalog.rb', line 304

def stores_for_select
  return Store.none unless company

  company.stores.order(:name).pluck(:name, :id)
end

#stripe_keyObject



369
370
371
372
373
# File 'app/models/catalog.rb', line 369

def stripe_key
  Payment::Apis::Stripe.publishable_key(currency)
rescue ArgumentError
  nil
end

#tax_rateObject



286
287
288
# File 'app/models/catalog.rb', line 286

def tax_rate
  country&.tax_rate&.goods
end

#tax_rate_stringObject



290
291
292
# File 'app/models/catalog.rb', line 290

def tax_rate_string
  "#{tax_rate * 100}%" if tax_rate.present?
end

#to_sObject



294
# File 'app/models/catalog.rb', line 294

def to_s = "#{name} [#{id}]"

#view_product_catalogsActiveRecord::Relation<ViewProductCatalog>

Returns:

See Also:



87
# File 'app/models/catalog.rb', line 87

has_many :view_product_catalogs