Class: Coupon

Inherits:
ApplicationRecord show all
Includes:
Memery, Models::Auditable, Models::LiquidMethods
Defined in:
app/models/coupon.rb

Overview

== Schema Information

Table name: coupons
Database name: primary

id :integer not null, primary key
allow_non_domestic_shipping :boolean
allow_oversized_items :boolean default(FALSE), not null
amount_goods :decimal(10, 2)
amount_services :decimal(10, 2)
amount_services_ar :float is an Array
amount_shipping :decimal(10, 2)
auto_apply :boolean default(FALSE), not null
auto_buying_group_exclude :boolean
buying_group_ids_exclude :boolean default(FALSE), not null
calculation_type_goods :string(5)
calculation_type_services :string(5)
calculation_type_shipping :string(5)
code :string(255)
description :text
details :text
effective_date :date
exclude_opportunity_reception_types_filters :boolean default(FALSE), not null
exclude_opportunity_types_filters :boolean default(FALSE), not null
exclude_order_reception_types_filters :boolean default(FALSE), not null
exclude_order_types_filters :boolean default(FALSE), not null
exclude_state_codes :boolean default(FALSE), not null
exclusion_level :enum default("not_exclusive"), not null
expiration_date :date
first_time_customers_only :boolean default(FALSE), not null
is_inactive :boolean default(FALSE), not null
last_used_at :datetime
legacy_amount_goods :string(100)
legacy_amount_services :string(100)
legacy_amount_shipping :string(100)
opportunity_reception_types_filter :string default([]), is an Array
opportunity_types_filter :string default([]), is an Array
order_reception_types_filter :string(255) default([]), is an Array
order_types_filter :string(255) default([]), is an Array
p5 :string(100)
p6 :string(100)
position :integer
profile_ids_exclude :boolean
promo_page_content_html :text
promo_tracking :boolean default(FALSE), not null
role_ids_restriction :integer default([]), is an Array
sale_page_position :integer default(0), not null
serial_numbers_only :boolean default(FALSE), not null
ships_economy_only :boolean default(FALSE), not null
single_use_per_customer :boolean default(FALSE), not null
state_codes :string(255) default([]), is an Array
store_id_restriction :integer
tier :integer default(3), not null
title :text
type :string(50)
created_at :datetime
updated_at :datetime
auto_apply_customer_filter_id :integer
creator_id :integer
customer_filter_id :integer
image_id :integer
publication_id :integer
source_id :integer
updater_id :integer

Indexes

by_auto_ap_st_codes (auto_apply,state_codes)
idx_code (code)
idx_type_is_inactive (type,is_inactive)
index_coupons_on_auto_apply_customer_filter_id (auto_apply_customer_filter_id)
index_coupons_on_customer_filter_id (customer_filter_id)
index_coupons_on_image_id (image_id)
index_coupons_on_position (position)
index_coupons_on_single_use_per_customer (single_use_per_customer)
index_coupons_on_type_and_id (type,id)

Foreign Keys

coupons_auto_apply_customer_filter_id_fk (auto_apply_customer_filter_id => customer_filters.id)
coupons_customer_filter_id_fk (customer_filter_id => customer_filters.id)
fk_rails_... (image_id => digital_assets.id)

Defined Under Namespace

Classes: ApplyCoupon, CatalogItemPricing, DeleteDiscount, ItemizableDiscountCalculator, LineItemDiscountAllocator, MsrpAllocator, OverlappingPromoValidator, PromoItemSync, Qualifier, QueryBuilder, Tier1BaseItemPricing, Tier2ProgramPricing, Tier3PromotionalPricing

Constant Summary collapse

INSTRUCTIONS =

Instructions.

'Placeholder'.freeze
VALID_PRICE_MATRIX_CALCULATIONS =

Valid price matrix calculations.

['%', '$', 'DP', 'MP'].freeze
FIXED_DOLLAR_CALCULATIONS =

Fixed dollar calculations.

{
  'Flat Amount Off' => '$',
  'Adjustable (+ or -)' => '?',
  'Adjustable (Discount Only)' => '-?',
  'Adjustable (Charge Only)' => '+?'
}.freeze
FIXED_DOLLAR_CALCULATIONS_TYPES =

Recognised fixed dollar calculations types.

FIXED_DOLLAR_CALCULATIONS.map { |_k, v| v }
CORE_CALCULATIONS =

Core calculations.

FIXED_DOLLAR_CALCULATIONS.merge({
  'Percentage Off' => '%',
  'Flat Amount Off Per Unit' => '$1',
  'Percentage Off MSRP' => '%M',
  'Percentage Off Catalog Price with VAT' => '%V',
  'Percentage Off per Min Qty Met' => '%1',
  'Force Discount Unit Price' => 'DP',
  'Force MSRP' => 'MP',
  'Promo Matrix' => 'MX'
})
CALCULATION_TYPES_GOODS =

Calculation types goods.

CORE_CALCULATIONS.merge({
  'Sq.Ft Multiplier for MSRP' => 'SF',
  'Linear Ft Multiplier for MSRP' => 'LF',
  'Sq.Ft Multiplier after base discount' => 'SD',
  'Linear Ft Multiplier after base discount' => 'LD'
})
CALCULATION_TYPES_SERVICES =

Calculation types services.

CORE_CALCULATIONS
CALCULATION_TYPES_SHIPPING =

Calculation types shipping.

{ 'Percentage Off' => '%', 'Flat Amount Off' => '$',
'Flat Rate Shipping' => '_R', 'Adjustable (Discount or Charge)' => '?',
'Adjustable (Discount Only)' => '-?', 'Adjustable (Charge Only)' => '+?' }.freeze
ADJUSTABLE_CALCULATIONS =

Adjustable calculations.

['?', '-?', '+?'].freeze
FREE_ONLINE_SHIPPING_COUPON_CODES =

Free online shipping coupon codes.

%w[FREEGNDONLINE FREEGNDONLINECA FREE_ECONOMY_ONLINE_USA FREE_ECONOMY_ONLINE_CAN].freeze
FREE_ECONOMY_SHIPPING_ONLINE_COUPON_CODES_BY_COUNTRY =

Free economy shipping online coupon codes by country.

{ USA: 'FREE_ECONOMY_ONLINE_USA', CAN: 'FREE_ECONOMY_ONLINE_CAN' }.freeze
ECONOMY_SHIPPING_MATCH_CRM_COUPON_CODE =

Economy shipping match crm coupon code.

'ECONOMY_SHIPPING_MATCH_CRM'.freeze
SKIP_BLACKLISTING_COUPON_CODES =

this blacklisting just breaks things when holding/release. Just skip for these codes!

[ECONOMY_SHIPPING_MATCH_CRM_COUPON_CODE] + FREE_ECONOMY_SHIPPING_ONLINE_COUPON_CODES_BY_COUNTRY.values

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has many collapse

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

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 Models::Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#amount_goods_overrideObject

Returns the value of attribute amount_goods_override.



144
145
146
# File 'app/models/coupon.rb', line 144

def amount_goods_override
  @amount_goods_override
end

#amount_services_overrideObject

Returns the value of attribute amount_services_override.



144
145
146
# File 'app/models/coupon.rb', line 144

def amount_services_override
  @amount_services_override
end

#amount_shipping_overrideObject

Returns the value of attribute amount_shipping_override.



144
145
146
# File 'app/models/coupon.rb', line 144

def amount_shipping_override
  @amount_shipping_override
end

#serial_numberObject

Returns the value of attribute serial_number.



144
145
146
# File 'app/models/coupon.rb', line 144

def serial_number
  @serial_number
end

#ship_to_continental_regionsObject

Returns the value of attribute ship_to_continental_regions.



144
145
146
# File 'app/models/coupon.rb', line 144

def ship_to_continental_regions
  @ship_to_continental_regions
end

Class Method Details

.activeActiveRecord::Relation<Coupon>

A relation of Coupons that are active. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



182
# File 'app/models/coupon.rb', line 182

scope :active, -> { where.not(is_inactive: true) }

.active_and_futureActiveRecord::Relation<Coupon>

A relation of Coupons that are active and future. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



201
# File 'app/models/coupon.rb', line 201

scope :active_and_future, -> { active.where.not(effective_date: nil).where(Coupon[:effective_date].gt(Date.current)) }

.affect_goodsActiveRecord::Relation<Coupon>

A relation of Coupons that are affect goods. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



205
# File 'app/models/coupon.rb', line 205

scope :affect_goods, -> { where.not(calculation_type_goods: nil) }

.affect_servicesActiveRecord::Relation<Coupon>

A relation of Coupons that are affect services. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



206
# File 'app/models/coupon.rb', line 206

scope :affect_services, -> { where.not(calculation_type_services: nil) }

.affect_shippingActiveRecord::Relation<Coupon>

A relation of Coupons that are affect shipping. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



207
# File 'app/models/coupon.rb', line 207

scope :affect_shipping, -> { where.not(calculation_type_shipping: nil) }

.auto_appliedActiveRecord::Relation<Coupon>

A relation of Coupons that are auto applied. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



184
# File 'app/models/coupon.rb', line 184

scope :auto_applied, -> { where(auto_apply: true) }

.by_codeActiveRecord::Relation<Coupon>

A relation of Coupons that are by code. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



181
# File 'app/models/coupon.rb', line 181

scope :by_code, -> { order(:code) }

.calculation_types_for_goods_selectObject

Calculation types for goods select.



319
320
321
# File 'app/models/coupon.rb', line 319

def self.calculation_types_for_goods_select
  CALCULATION_TYPES_GOODS.to_a.sort
end

.calculation_types_for_services_selectObject

Calculation types for services select.



324
325
326
# File 'app/models/coupon.rb', line 324

def self.calculation_types_for_services_select
  CALCULATION_TYPES_SERVICES.to_a.sort
end

.calculation_types_for_shipping_selectObject

Calculation types for shipping select.



329
330
331
# File 'app/models/coupon.rb', line 329

def self.calculation_types_for_shipping_select
  CALCULATION_TYPES_SHIPPING.to_a.sort
end

.coupon_type_for_selectObject

Coupon type for select.



295
296
297
# File 'app/models/coupon.rb', line 295

def self.coupon_type_for_select
  coupon_types.invert
end

.coupon_typesObject

Coupon types.



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

def self.coupon_types
  ['Coupon::Tier1BaseItemPricing', 'Coupon::Tier2ProgramPricing', 'Coupon::Tier3PromotionalPricing'].index_with { |t| t.constantize.friendly_type_name }
end

.customer_accessibleActiveRecord::Relation<Coupon>

A relation of Coupons that are customer accessible. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



199
# File 'app/models/coupon.rb', line 199

scope :customer_accessible, -> { tier3.active.non_auto_applied }

.customer_filters_for_selectObject

Customer filters for select.



344
345
346
# File 'app/models/coupon.rb', line 344

def self.customer_filters_for_select
  CustomerFilter.joins(:coupons).order(CustomerFilter[:name]).distinct.pluck(:name, :id)
end

.discounted_priceActiveRecord::Relation<Coupon>

A relation of Coupons that are discounted price. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



187
# File 'app/models/coupon.rb', line 187

scope :discounted_price, -> { where(amount_goods: 'DP') }

.exclude_ships_economy_onlyActiveRecord::Relation<Coupon>

A relation of Coupons that are exclude ships economy only. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



211
# File 'app/models/coupon.rb', line 211

scope :exclude_ships_economy_only, -> { where.not(ships_economy_only: true) }

.exclusion_level_for_selectObject

Exclusion level for select.



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

def self.exclusion_level_for_select
  exclusion_levels.map { |k, _v| [k.humanize, k] }
end

.expiredActiveRecord::Relation<Coupon>

A relation of Coupons that are expired. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



191
# File 'app/models/coupon.rb', line 191

scope :expired, ->(date = Date.current) { where.not(expiration_date: nil).where(Coupon[:expiration_date].lteq(date)) }

.fixed_dollarActiveRecord::Relation<Coupon>

A relation of Coupons that are fixed dollar. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



202
203
204
# File 'app/models/coupon.rb', line 202

scope :fixed_dollar, -> {
  where(calculation_type_goods: Coupon::FIXED_DOLLAR_CALCULATIONS_TYPES).or(where(calculation_type_services: Coupon::FIXED_DOLLAR_CALCULATIONS_TYPES)).or(where(calculation_type_shipping: Coupon::FIXED_DOLLAR_CALCULATIONS_TYPES))
}

.for_publicActiveRecord::Relation<Coupon>

A relation of Coupons that are for public. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



193
# File 'app/models/coupon.rb', line 193

scope :for_public, -> { tier3.where(Coupon[:role_ids_restriction].contains([Role.find_by(name: 'customer').id]).or(Coupon[:role_ids_restriction].eq([])).or(Coupon[:role_ids_restriction].eq(nil))) }

.free_online_shippingActiveRecord::Relation<Coupon>

A relation of Coupons that are free online shipping. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



208
# File 'app/models/coupon.rb', line 208

scope :free_online_shipping, -> { where(code: Coupon::FREE_ONLINE_SHIPPING_COUPON_CODES) }

.friendly_calculation_name(calculation_type) ⇒ Object

Friendly calculation name.

Parameters:

  • calculation_type (Object)

    the calculation type



311
312
313
314
315
316
# File 'app/models/coupon.rb', line 311

def self.friendly_calculation_name(calculation_type)
  CORE_CALCULATIONS.invert[calculation_type] ||
    CALCULATION_TYPES_GOODS.invert[calculation_type] ||
    CALCULATION_TYPES_SERVICES.invert[calculation_type] ||
    CALCULATION_TYPES_SHIPPING.invert[calculation_type] || 'Unknown'
end

.friendly_price_matrix_calculation_namesObject

Friendly price matrix calculation names.



305
306
307
# File 'app/models/coupon.rb', line 305

def self.friendly_price_matrix_calculation_names
  Coupon::VALID_PRICE_MATRIX_CALCULATIONS.map { |c| Coupon.friendly_calculation_name(c) }.to_sentence
end

.in_effect_onActiveRecord::Relation<Coupon>

A relation of Coupons that are in effect on. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



189
# File 'app/models/coupon.rb', line 189

scope :in_effect_on, ->(date = Date.current) { active.where(Coupon[:effective_date].eq(nil).or(Coupon[:effective_date].lteq(date))).where(Coupon[:expiration_date].eq(nil).or(Coupon[:expiration_date].gteq(date))) }

.inactiveActiveRecord::Relation<Coupon>

A relation of Coupons that are inactive. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



183
# File 'app/models/coupon.rb', line 183

scope :inactive, -> { where(is_inactive: true) }

.instantiate_by_type(type) ⇒ Object

Instantiate by type.

Parameters:

  • type (String)

    the type



280
281
282
# File 'app/models/coupon.rb', line 280

def self.instantiate_by_type(type)
  (coupon_types.keys & [type])[0].constantize
end

.msrp_priceActiveRecord::Relation<Coupon>

A relation of Coupons that are msrp price. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



188
# File 'app/models/coupon.rb', line 188

scope :msrp_price, -> { where(amount_goods: %w[MP SF LF]) }

.non_auto_appliedActiveRecord::Relation<Coupon>

A relation of Coupons that are non auto applied. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



185
# File 'app/models/coupon.rb', line 185

scope :non_auto_applied, -> { where(auto_apply: false) }

.not_expiredActiveRecord::Relation<Coupon>

A relation of Coupons that are not expired. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



192
# File 'app/models/coupon.rb', line 192

scope :not_expired, ->(date = Date.current) { where.not(expiration_date: nil).where(Coupon[:expiration_date].gt(date)) }

.not_in_effect_onActiveRecord::Relation<Coupon>

A relation of Coupons that are not in effect on. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



190
# File 'app/models/coupon.rb', line 190

scope :not_in_effect_on, ->(date = Date.current) { where(Coupon[:effective_date].eq(nil).or(Coupon[:effective_date].gt(date))).where(Coupon[:expiration_date].eq(nil).or(Coupon[:expiration_date].lt(date))) }

.order_types_for_selectObject

Order types for select.



285
286
287
# File 'app/models/coupon.rb', line 285

def self.order_types_for_select
  %w[SO CO ST]
end

.promoActiveRecord::Relation<Coupon>

A relation of Coupons that are promo. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



212
# File 'app/models/coupon.rb', line 212

scope :promo, -> { where(promo_tracking: true) }

.promo_for_sale_pageActiveRecord::Relation<Coupon>

A relation of Coupons that are promo for sale page. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



213
214
215
216
217
218
219
# File 'app/models/coupon.rb', line 213

scope :promo_for_sale_page, -> {
  # A little hardcoding for now until the customer filter are better defined
  excluded_codes = []
  excluded_codes << 'FREE_ECONOMY_ONLINE_USA' unless I18n.locale == :'en-US'
  excluded_codes << 'FREE_ECONOMY_ONLINE_CAN' unless I18n.locale.in?(%i[en-CA fr-CA])
  active.in_effect_on.where.not(code: excluded_codes).where(Coupon[:sale_page_position].gt(0)).order(sale_page_position: :asc, created_at: :desc)
}

.promo_matrixActiveRecord::Relation<Coupon>

A relation of Coupons that are promo matrix. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



197
# File 'app/models/coupon.rb', line 197

scope :promo_matrix, -> { where(calculation_type_goods: 'MX').or(where(calculation_type_services: 'MX')).or(where(calculation_type_shipping: 'MX')) }

.select_optionsArray<Array>

Returns [label, id] pairs for select dropdowns.

Returns:

  • (Array<Array>)

    [label, id] pairs for select dropdowns



334
335
336
# File 'app/models/coupon.rb', line 334

def self.select_options
  order(:code).pluck(:code, :id)
end

.ships_economy_onlyActiveRecord::Relation<Coupon>

A relation of Coupons that are ships economy only. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



210
# File 'app/models/coupon.rb', line 210

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

.sortedActiveRecord::Relation<Coupon>

A relation of Coupons that are sorted. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



180
# File 'app/models/coupon.rb', line 180

scope :sorted, -> { order(Coupon[:type], Coupon[:position]) }

.syndicated_to_googleActiveRecord::Relation<Coupon>

A relation of Coupons that are syndicated to google. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



200
# File 'app/models/coupon.rb', line 200

scope :syndicated_to_google, -> { where(syndicate_to_google: true) }

.tier1ActiveRecord::Relation<Coupon>

A relation of Coupons that are tier1. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



194
# File 'app/models/coupon.rb', line 194

scope :tier1, -> { where(type: 'Coupon::Tier1BaseItemPricing') }

.tier1and3ActiveRecord::Relation<Coupon>

A relation of Coupons that are tier1and3. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



198
# File 'app/models/coupon.rb', line 198

scope :tier1and3, -> { where(type: ['Coupon::Tier1BaseItemPricing', 'Coupon::Tier3PromotionalPricing']) }

.tier2ActiveRecord::Relation<Coupon>

A relation of Coupons that are tier2. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



195
# File 'app/models/coupon.rb', line 195

scope :tier2, -> { where(type: 'Coupon::Tier2ProgramPricing') }

.tier2_select_optionsObject

Tier2 select options.



339
340
341
# File 'app/models/coupon.rb', line 339

def self.tier2_select_options
  tier2.order(:title).pluck(:title, :id)
end

.tier3ActiveRecord::Relation<Coupon>

A relation of Coupons that are tier3. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



196
# File 'app/models/coupon.rb', line 196

scope :tier3, -> { where(type: 'Coupon::Tier3PromotionalPricing') }

.with_economy_shipping_match_crmActiveRecord::Relation<Coupon>

A relation of Coupons that are with economy shipping match crm. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



209
# File 'app/models/coupon.rb', line 209

scope :with_economy_shipping_match_crm, -> { where(code: Coupon::ECONOMY_SHIPPING_MATCH_CRM_COUPON_CODE) }

.with_publicationActiveRecord::Relation<Coupon>

A relation of Coupons that are with publication. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Coupon>)

See Also:



186
# File 'app/models/coupon.rb', line 186

scope :with_publication, -> { where.not(publication_id: nil) }

Instance Method Details

#adjustableObject

Adjustable.



466
467
468
# File 'app/models/coupon.rb', line 466

def adjustable
  goods_adjustable or services_adjustable or shipping_adjustable
end

#all_applicable_catalog_itemsObject

All applicable catalog items.



501
502
503
504
505
506
507
508
# File 'app/models/coupon.rb', line 501

def all_applicable_catalog_items
  # Our items ids (real-time from ltree):
  item_ids = all_applicable_item_ids
  # Our catalogs:
  catalog_ids = all_catalog_ids || []
  # Intersect
  CatalogItem.joins(:item).where(items: { id: item_ids }, catalog_id: catalog_ids).available_for_edi_feeds
end

#all_applicable_item_idsObject

Returns all applicable item IDs using real-time ltree queries.
No caching needed - ltree queries are fast (~1-2ms per filter).



487
488
489
# File 'app/models/coupon.rb', line 487

def all_applicable_item_ids
  product_filters.where(apply_discount: true).flat_map { |pf| pf.applicable_item_ids_set.to_a }.uniq
end

#all_catalog_idsObject

Alias for Customer_filter#all_catalog_ids

Returns:

  • (Object)

    Customer_filter#all_catalog_ids

See Also:



258
# File 'app/models/coupon.rb', line 258

delegate :all_catalog_ids, to: :customer_filter, allow_nil: true

#apply_source_to_customer(customer) ⇒ Object

Apply source to customer.

Parameters:



667
668
669
670
671
672
# File 'app/models/coupon.rb', line 667

def apply_source_to_customer(customer)
  return unless source

  # For customer i do an update attribute to get a paper trail hit
  customer.update_attribute!(:source_id, source_id)
end

#apply_source_to_opportunity(opportunity) ⇒ Object

Apply source to opportunity.

Parameters:

  • opportunity (Object)

    the opportunity



688
689
690
691
692
# File 'app/models/coupon.rb', line 688

def apply_source_to_opportunity(opportunity)
  return unless source && opportunity.sales_opportunity?

  opportunity.update_column(:source_id, source_id)
end

#apply_source_to_order(order) ⇒ Object

Apply source to order.

Parameters:

  • order (Order)

    the order



676
677
678
679
680
681
682
683
684
# File 'app/models/coupon.rb', line 676

def apply_source_to_order(order)
  return unless source && order&.is_sales_order?

  if order.persisted?
    order.update_column(:source_id, source_id)
  else
    order.source_id = source_id
  end
end

#auto_apply_customer_filterCustomerFilter?

Returns the auto apply customer filter this record belongs to.

Returns:

  • (CustomerFilter, nil)

    the auto apply customer filter this record belongs to



156
# File 'app/models/coupon.rb', line 156

belongs_to :auto_apply_customer_filter, inverse_of: :auto_apply_coupons, class_name: 'CustomerFilter', optional: true

#blacklistable?Boolean

Every coupon by default is blacklistable if they are auto apply
This behavior can be overriden in each class of coupon
e.g. tier2 coupon is always true

Returns:

  • (Boolean)


457
458
459
460
461
462
463
# File 'app/models/coupon.rb', line 457

def blacklistable?
  if SKIP_BLACKLISTING_COUPON_CODES.include?(code)
    false
  else
    auto_apply
  end
end

#coupon_active?Boolean

Returns whether the record coupon active.

Returns:

  • (Boolean)

    whether the record coupon active



392
393
394
# File 'app/models/coupon.rb', line 392

def coupon_active?
  status == :active
end

#coupon_expired?Boolean

Returns whether the record coupon expired.

Returns:

  • (Boolean)

    whether the record coupon expired



382
383
384
# File 'app/models/coupon.rb', line 382

def coupon_expired?
  status == :expired
end

#coupon_inactive?Boolean

Returns whether the record coupon inactive.

Returns:

  • (Boolean)

    whether the record coupon inactive



377
378
379
# File 'app/models/coupon.rb', line 377

def coupon_inactive?
  status == :inactive
end

#coupon_not_yet_in_effect?Boolean

Returns whether the record coupon not yet in effect.

Returns:

  • (Boolean)

    whether the record coupon not yet in effect



387
388
389
# File 'app/models/coupon.rb', line 387

def coupon_not_yet_in_effect?
  status == :not_yet_in_effect
end

#coupon_promo_sync_alertObject

Coupon promo sync alert.



397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'app/models/coupon.rb', line 397

def coupon_promo_sync_alert
  msg = nil
  if coupon_active? || coupon_not_yet_in_effect?
    if (missing_catalog_items = missing_catalog_items_not_in_promotion_product_filter).present?
      msg = %(There are potentially #{missing_catalog_items.size} catalog items in your product filter that need to be synchronized,
      run Synchronize Promo Items to correct)
    elsif (missing_items = promotional_item_ids_not_in_product_filter).present?
      msg = %(There are #{missing_items.size} items in your promotional catalog that are not part of your filter,
        run Synchronize Promo Items to correct)
    end
  elsif (coupon_expired? || coupon_inactive?) && promotional_view_product_catalog_items.present?
    msg = %(Coupon is inactive or expired, synchronize promo items to cleanup catalog item references)
  end
  msg
end

#coupon_serial_numbersActiveRecord::Relation<CouponSerialNumber>

Returns the associated coupon serial numbers.

Returns:



169
# File 'app/models/coupon.rb', line 169

has_many :coupon_serial_numbers, dependent: :destroy, inverse_of: :coupon

#customer_can_apply?Boolean

Returns whether the record customer can apply.

Returns:

  • (Boolean)

    whether the record customer can apply



661
662
663
# File 'app/models/coupon.rb', line 661

def customer_can_apply?
  (is_inactive != true) and (auto_apply != true) and public? and tier3?
end

#customer_filterCustomerFilter?

Returns the customer filter this record belongs to.

Returns:

  • (CustomerFilter, nil)

    the customer filter this record belongs to



154
# File 'app/models/coupon.rb', line 154

belongs_to :customer_filter, inverse_of: :coupons, optional: true

#customer_ids_txtObject

Customer ids txt.



644
645
646
# File 'app/models/coupon.rb', line 644

def customer_ids_txt
  (customer_ids || []).join(',')
end

#customer_ids_txt=(val) ⇒ Object

Sets the customer ids txt.

Parameters:

  • val (Object)

    the val

  • value (Object)

    the new customer ids txt



639
640
641
# File 'app/models/coupon.rb', line 639

def customer_ids_txt=(val)
  self.customer_ids = val # attribute normalizer will clean this up.
end

#deactivateObject

Deactivate.



547
548
549
# File 'app/models/coupon.rb', line 547

def deactivate
  update({ is_inactive: true })
end

#deep_dupObject

Returns a deep-copied, unsaved duplicate of the record.

Returns:

  • (Object)

    a deep-copied, unsaved duplicate of the record



266
267
268
269
270
271
272
273
274
275
276
# File 'app/models/coupon.rb', line 266

def deep_dup
  deep_clone(
    include: :product_filters,
    except: %i[effective_date expiration_date]
  ) do |_original, copy|
    if copy.is_a?(Coupon)
      copy.code = "#{code}_COPY"
      copy.is_inactive = true
    end
  end
end

#discountsActiveRecord::Relation<Discount>

Returns the associated discounts.

Returns:

  • (ActiveRecord::Relation<Discount>)

    the associated discounts



163
# File 'app/models/coupon.rb', line 163

has_many :discounts, inverse_of: :coupon

#exclusive?Boolean

Returns whether the record exclusive.

Returns:

  • (Boolean)

    whether the record exclusive



261
262
263
# File 'app/models/coupon.rb', line 261

def exclusive?
  !not_exclusive?
end

#flat_amount_goodsObject

Flat amount goods.



607
608
609
# File 'app/models/coupon.rb', line 607

def flat_amount_goods
  (amount_goods_override || amount_goods).to_f.round(2)
end

#flat_amount_servicesObject

Flat amount services.



617
618
619
# File 'app/models/coupon.rb', line 617

def flat_amount_services
  (amount_services_override || amount_services).to_f.round(2)
end

#flat_amount_shippingObject

Flat amount shipping.



612
613
614
# File 'app/models/coupon.rb', line 612

def flat_amount_shipping
  (amount_shipping_override || amount_shipping).to_f.round(2)
end

#future_promotional_catalog_itemsActiveRecord::Relation<CatalogItem>

Returns the associated future promotional catalog items.

Returns:

  • (ActiveRecord::Relation<CatalogItem>)

    the associated future promotional catalog items



173
# File 'app/models/coupon.rb', line 173

has_many :future_promotional_catalog_items, class_name: 'CatalogItem', foreign_key: :new_coupon_id, dependent: :nullify, inverse_of: :new_coupon

#goods_adjustableObject

Goods adjustable.



471
472
473
# File 'app/models/coupon.rb', line 471

def goods_adjustable
  ADJUSTABLE_CALCULATIONS.include? calculation_type_goods
end

#imageImage?

Returns the image this record belongs to.

Returns:

  • (Image, nil)

    the image this record belongs to



160
# File 'app/models/coupon.rb', line 160

belongs_to :image, optional: true

#instructionsObject

Instructions.



542
543
544
# File 'app/models/coupon.rb', line 542

def instructions
  self.class::INSTRUCTIONS
end

#is_goods_price_forcing?Boolean

Returns whether the record is goods price forcing.

Returns:

  • (Boolean)

    whether the record is goods price forcing



592
593
594
# File 'app/models/coupon.rb', line 592

def is_goods_price_forcing?
  %w[DP MP].include? calculation_type_goods
end

#is_msrp_goods_based?Boolean

Returns whether the record is msrp goods based.

Returns:

  • (Boolean)

    whether the record is msrp goods based



602
603
604
# File 'app/models/coupon.rb', line 602

def is_msrp_goods_based?
  %w[SF LF].include? calculation_type_goods
end

#is_price_forcing?Boolean

Returns whether the record is price forcing.

Returns:

  • (Boolean)

    whether the record is price forcing



587
588
589
# File 'app/models/coupon.rb', line 587

def is_price_forcing?
  is_goods_price_forcing? or is_services_price_forcing?
end

#is_services_price_forcing?Boolean

Returns whether the record is services price forcing.

Returns:

  • (Boolean)

    whether the record is services price forcing



597
598
599
# File 'app/models/coupon.rb', line 597

def is_services_price_forcing?
  %w[DP MP].include? calculation_type_services
end

#label_for(n) ⇒ Object

Label for.

Parameters:

  • n (Object)

    the n



535
536
537
538
539
# File 'app/models/coupon.rb', line 535

def label_for(n)
  self.class.const_get("LABEL_P#{n}")
rescue NameError
  nil
end

#line_discountsActiveRecord::Relation<LineDiscount>

Returns the associated line discounts.

Returns:

  • (ActiveRecord::Relation<LineDiscount>)

    the associated line discounts



165
# File 'app/models/coupon.rb', line 165

has_many :line_discounts, inverse_of: :coupon

#localized_promo_page_content_html(locale = nil, preview_mode: false) ⇒ Object

Localized promo page content html.

Parameters:

  • locale (Object) (defaults to: nil)

    the locale

  • preview_mode (Object) (defaults to: false)

    the preview mode



429
430
431
432
433
434
435
436
437
438
439
# File 'app/models/coupon.rb', line 429

def localized_promo_page_content_html(locale = nil, preview_mode: false)
  return if promo_page_content_html.blank?

  locale ||= I18n.locale
  Liquid::ParseEnvironment.parse(promo_page_content_html).render(
    'preview_mode' => preview_mode,
    'locale' => locale.to_s,
    'canada' => %i[en-CA fr-CA].include?(locale.to_sym),
    'usa' => locale.to_sym == :'en-US'
  )
end

#missing_catalog_items_not_in_promotion_product_filterObject

Missing catalog items not in promotion product filter.



511
512
513
# File 'app/models/coupon.rb', line 511

def missing_catalog_items_not_in_promotion_product_filter
  all_applicable_catalog_items.where.not(id: promotional_view_product_catalog_items.pluck(:id))
end

#percentage_off_goodsObject

Percentage off goods.



622
623
624
# File 'app/models/coupon.rb', line 622

def percentage_off_goods
  (amount_goods_override || amount_goods).to_f / 100
end

#percentage_off_servicesObject

Percentage off services.



632
633
634
# File 'app/models/coupon.rb', line 632

def percentage_off_services
  (amount_services_override || amount_services).to_f / 100
end

#percentage_off_shippingObject

Percentage off shipping.



627
628
629
# File 'app/models/coupon.rb', line 627

def percentage_off_shipping
  (amount_shipping_override || amount_shipping).to_f / 100
end

#product_filter_item_ids_not_in_catalog_itemsObject

Product filter item ids not in catalog items.



516
517
518
# File 'app/models/coupon.rb', line 516

def product_filter_item_ids_not_in_catalog_items
  all_applicable_item_ids - promotional_view_product_catalog_items.pluck(:item_id)
end

#product_filtersActiveRecord::Relation<ProductFilter>

Returns the associated product filters.

Returns:

  • (ActiveRecord::Relation<ProductFilter>)

    the associated product filters



167
# File 'app/models/coupon.rb', line 167

has_many :product_filters, as: :resource, dependent: :destroy

#promotion_matrix_calculation?Boolean

Returns whether the record promotion matrix calculation.

Returns:

  • (Boolean)

    whether the record promotion matrix calculation



582
583
584
# File 'app/models/coupon.rb', line 582

def promotion_matrix_calculation?
  [calculation_type_goods, calculation_type_services, calculation_type_shipping].include?('MX')
end

#promotional_catalog_itemsActiveRecord::Relation<CatalogItem>

Returns the associated promotional catalog items.

Returns:

  • (ActiveRecord::Relation<CatalogItem>)

    the associated promotional catalog items



171
# File 'app/models/coupon.rb', line 171

has_many :promotional_catalog_items, class_name: 'CatalogItem', dependent: :nullify, inverse_of: :coupon

#promotional_item_ids_not_in_product_filterObject

Promotional item ids not in product filter.



521
522
523
# File 'app/models/coupon.rb', line 521

def promotional_item_ids_not_in_product_filter
  promotional_view_product_catalog_items.pluck(:item_id) - all_applicable_item_ids
end

#promotional_view_product_catalog_itemsObject

Returns the view product catalog items applicable for this coupon



492
493
494
495
496
497
498
# File 'app/models/coupon.rb', line 492

def promotional_view_product_catalog_items
  if persisted?
    ViewProductCatalog.where(ViewProductCatalog[:coupon_id].eq(id).or(ViewProductCatalog[:future_coupon_id].eq(id)))
  else
    ViewProductCatalog.none
  end
end

#public?Boolean

Returns whether the record public.

Returns:

  • (Boolean)

    whether the record public



649
650
651
652
653
654
655
656
657
658
# File 'app/models/coupon.rb', line 649

def public?
  roles = Array(role_ids_restriction)
  return true if roles.empty?

  # Guard against a missing 'customer' role (renamed/unseeded env) — otherwise
  # Role.find_by(...).id raises NoMethodError on nil. Now hit on every save
  # where syndicate_to_google? is true via syndicate_to_google_eligibility.
  customer_id = Role.find_by(name: 'customer')&.id
  customer_id.present? && roles.include?(customer_id)
end

#publicationItem?

Returns the publication this record belongs to.

Returns:

  • (Item, nil)

    the publication this record belongs to



152
# File 'app/models/coupon.rb', line 152

belongs_to :publication, class_name: 'Item', optional: true

#qualifying_line_items?(itemizable) ⇒ Boolean

Returns whether the record qualifying line items.

Parameters:

  • itemizable (Object)

    the itemizable

Returns:

  • (Boolean)

    whether the record qualifying line items



563
564
565
566
567
568
569
570
571
572
573
574
# File 'app/models/coupon.rb', line 563

def qualifying_line_items?(itemizable)
  # Preload associations to prevent N+1 queries on item, catalog_item, and item_specification
  # during qualification calculations (sqft, linear_ft, msrp, etc.)
  lines = itemizable.line_items.with_discount_preloads.active_parent_lines
  ProductFilter::LineQualifier.new(lines, product_filters,
                                   {
                                     code:,
                                     coupon_tier: tier,
                                     allow_non_domestic_shipping: allow_non_domestic_shipping?,
                                     exclusion_level:
                                   }).qualifying_line_items?
end

#reactivateObject

Reactivate.



552
553
554
# File 'app/models/coupon.rb', line 552

def reactivate
  update({ is_inactive: false })
end

#require_amount_goods?Boolean

Returns whether the record require amount goods.

Returns:

  • (Boolean)

    whether the record require amount goods



695
696
697
# File 'app/models/coupon.rb', line 695

def require_amount_goods?
  calculation_type_goods.present? && !goods_adjustable && !calculation_type_goods == 'MX'
end

#require_amount_services?Boolean

Returns whether the record require amount services.

Returns:

  • (Boolean)

    whether the record require amount services



700
701
702
# File 'app/models/coupon.rb', line 700

def require_amount_services?
  calculation_type_services.present? && !services_adjustable && !calculation_type_services == 'MX'
end

#require_amount_shipping?Boolean

Returns whether the record require amount shipping.

Returns:

  • (Boolean)

    whether the record require amount shipping



705
706
707
# File 'app/models/coupon.rb', line 705

def require_amount_shipping?
  calculation_type_shipping.present? && !shipping_adjustable && !calculation_type_shipping == 'MX'
end

#require_product_filter?Boolean

Returns whether the record require product filter.

Returns:

  • (Boolean)

    whether the record require product filter



577
578
579
# File 'app/models/coupon.rb', line 577

def require_product_filter?
  is_price_forcing? and code != 'SMARTPRESETFREEBG'
end

#select_options_for(n) ⇒ Object

Select options for.

Parameters:

  • n (Object)

    the n



527
528
529
530
531
# File 'app/models/coupon.rb', line 527

def select_options_for(n)
  self.class.const_get("OPTIONS_P#{n}")
rescue NameError
  nil
end

#services_adjustableObject

Services adjustable.



476
477
478
# File 'app/models/coupon.rb', line 476

def services_adjustable
  ADJUSTABLE_CALCULATIONS.include? calculation_type_services
end

#shipping_adjustableObject

Shipping adjustable.



481
482
483
# File 'app/models/coupon.rb', line 481

def shipping_adjustable
  ADJUSTABLE_CALCULATIONS.include? calculation_type_shipping
end

#sourceSource?

Returns the source this record belongs to.

Returns:

  • (Source, nil)

    the source this record belongs to



158
# File 'app/models/coupon.rb', line 158

belongs_to :source, optional: true

#statusObject

Status.



364
365
366
367
368
369
370
371
372
373
374
# File 'app/models/coupon.rb', line 364

def status
  if is_inactive
    :inactive
  elsif expiration_date && Date.current > expiration_date
    :expired
  elsif effective_date && Date.current < effective_date
    :not_yet_in_effect
  else
    :active
  end
end

#sticky?Boolean

Sticky coupons remain even when $0

Returns:

  • (Boolean)


557
558
559
# File 'app/models/coupon.rb', line 557

def sticky?
  is_price_forcing?
end

#tier1?Boolean

Returns whether the record tier1.

Returns:

  • (Boolean)

    whether the record tier1



349
350
351
# File 'app/models/coupon.rb', line 349

def tier1?
  type == 'Coupon::Tier1BaseItemPricing'
end

#tier2?Boolean

Returns whether the record tier2.

Returns:

  • (Boolean)

    whether the record tier2



354
355
356
# File 'app/models/coupon.rb', line 354

def tier2?
  type == 'Coupon::Tier2ProgramPricing'
end

#tier3?Boolean

Returns whether the record tier3.

Returns:

  • (Boolean)

    whether the record tier3



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

def tier3?
  type == 'Coupon::Tier3PromotionalPricing'
end

#to_sString

Returns string representation of the record.

Returns:

  • (String)

    string representation of the record



414
415
416
417
418
419
420
421
422
423
424
# File 'app/models/coupon.rb', line 414

def to_s
  [
    code,
    ("G(#{amount_goods}#{calculation_type_goods})" if calculation_type_goods),
    ("SV(#{amount_services}#{calculation_type_services})" if calculation_type_services),
    ("SH(#{amount_shipping}#{calculation_type_shipping})" if calculation_type_shipping),
    ("From #{effective_date || 'Anytime'}" if effective_date),
    ("Until #{expiration_date}" if expiration_date),
    title
  ].compact.join(' - ')
end

#versions_for_audit_trail(_params = {}) ⇒ Object

Versions for audit trail.

Parameters:

  • _params (Object) (defaults to: {})

    the params



443
444
445
446
447
448
449
450
451
452
# File 'app/models/coupon.rb', line 443

def versions_for_audit_trail(_params = {})
  query_sql = %q{
                  (item_type = 'Coupon' AND item_id = :id)
                  OR
                  (item_type = 'ProductFilter'
                    AND reference_data @> '{"resource_type": "Coupon"}'
                    AND reference_data @> :resource_id_json)
              }
  RecordVersion.where(query_sql, id:, resource_id_json: { resource_id: id }.to_json)
end