Class: Discount

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable
Defined in:
app/models/discount.rb

Overview

== Schema Information

Table name: discounts
Database name: primary

id :integer not null, primary key
amount :decimal(10, 2)
blacklisted :boolean
effective_date :datetime
itemizable_type :string(50)
notes :text
user_amount :decimal(10, 2)
created_at :datetime
updated_at :datetime
coupon_id :integer not null
coupon_serial_number_id :uuid
itemizable_id :integer not null
legacy_coupon_id :integer

Indexes

by_itid_itt_csnid (itemizable_id,itemizable_type,coupon_serial_number_id)
idx_coupon_id (coupon_id)
index_discounts_unique_per_itemizable (itemizable_type,itemizable_id,coupon_id) UNIQUE

Foreign Keys

fk_rails_... (coupon_serial_number_id => coupon_serial_numbers.id)

Constant Summary collapse

ITEMIZABLE_TYPES =

Polymorphic owner allow-list. discounts.(itemizable_type, itemizable_id)
has no database FK, so pin the type here — a mistyped or injected
itemizable_type would corrupt order/quote money math
(see doc/tasks/202607041714_POLYMORPHIC_FK_INTEGRITY_AUDIT.md).

%w[Order Quote Invoice CreditMemo].freeze

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

#amountObject (readonly)

Validates amount.

Validations:



58
# File 'app/models/discount.rb', line 58

validates :amount, numericality: true

#coupon_descriptionObject

Returns the value of attribute coupon_description.



53
54
55
# File 'app/models/discount.rb', line 53

def coupon_description
  @coupon_description
end

#coupon_idObject (readonly)

Validates coupon id, itemizable id.

Validations:

  • Uniqueness ({ scope: %i[itemizable_type itemizable_id], if: :itemizable_id })


65
# File 'app/models/discount.rb', line 65

validates :coupon_id, uniqueness: { scope: %i[itemizable_type itemizable_id], if: :itemizable_id }

#coupon_serial_number_idObject (readonly)

Validates coupon serial number id, itemizable requires unique coupon serial.

Validations:

  • Uniqueness ({ allow_nil: true, if: :itemizable_requires_unique_coupon_serial? })


67
# File 'app/models/discount.rb', line 67

validates :coupon_serial_number_id, uniqueness: { allow_nil: true, if: :itemizable_requires_unique_coupon_serial? }

#itemizable_typeObject (readonly)

Validates itemizable type.

Validations:



60
# File 'app/models/discount.rb', line 60

validates :itemizable_type, inclusion: { in: ITEMIZABLE_TYPES }

#user_amountObject (readonly)

Validates user amount.

Validations:

  • Presence ({ if: ->(_d) { coupon&.adjustable } })


63
# File 'app/models/discount.rb', line 63

validates :user_amount, presence: { if: ->(_d) { coupon&.adjustable } }

Class Method Details

.activeActiveRecord::Relation<Discount>

A relation of Discounts that are active. Active Record Scope

Returns:

See Also:



71
# File 'app/models/discount.rb', line 71

scope :active, -> { joins(:coupon).merge(Coupon.active) }

.auto_appliedActiveRecord::Relation<Discount>

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

Returns:

See Also:



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

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

.excluding_shippingActiveRecord::Relation<Discount>

A relation of Discounts that are excluding shipping. Active Record Scope

Returns:

See Also:



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

scope :excluding_shipping, -> { joins(line_discounts: :line_item).where.not(line_item: { tax_class: 'shp' }) }

.fixed_dollarActiveRecord::Relation<Discount>

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

Returns:

See Also:



83
# File 'app/models/discount.rb', line 83

scope :fixed_dollar, -> { joins(:coupon).merge(Coupon.fixed_dollar) }

.free_online_shippingActiveRecord::Relation<Discount>

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

Returns:

See Also:



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

scope :free_online_shipping, -> { joins(:coupon).merge(Coupon.free_online_shipping) }

.manually_appliedActiveRecord::Relation<Discount>

A relation of Discounts that are manually applied. Active Record Scope

Returns:

See Also:



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

scope :manually_applied, -> { where.not(auto_apply: true) }

.non_blacklistedActiveRecord::Relation<Discount>

A relation of Discounts that are non blacklisted. Active Record Scope

Returns:

See Also:



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

scope :non_blacklisted, -> { where(blacklisted: [nil, false]) }

.non_zero_valueActiveRecord::Relation<Discount>

A relation of Discounts that are non zero value. Active Record Scope

Returns:

See Also:



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

scope :non_zero_value, -> { where.not(amount: 0) }

.sortedActiveRecord::Relation<Discount>

A relation of Discounts that are sorted. Active Record Scope

Returns:

See Also:



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

scope :sorted, -> { joins(:coupon).merge(Coupon.sorted) }

.tier1ActiveRecord::Relation<Discount>

A relation of Discounts that are tier1. Active Record Scope

Returns:

See Also:



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

scope :tier1, -> { sorted.merge(Coupon.tier1) }

.tier1and3ActiveRecord::Relation<Discount>

A relation of Discounts that are tier1and3. Active Record Scope

Returns:

See Also:



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

scope :tier1and3, -> { sorted.merge(Coupon.tier1and3) }

.tier2ActiveRecord::Relation<Discount>

A relation of Discounts that are tier2. Active Record Scope

Returns:

See Also:



80
# File 'app/models/discount.rb', line 80

scope :tier2, -> { sorted.merge(Coupon.tier2) }

.tier3ActiveRecord::Relation<Discount>

A relation of Discounts that are tier3. Active Record Scope

Returns:

See Also:



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

scope :tier3, -> { sorted.merge(Coupon.tier3) }

.visibleActiveRecord::Relation<Discount>

A relation of Discounts that are visible. Active Record Scope

Returns:

See Also:



72
# File 'app/models/discount.rb', line 72

scope :visible, -> { where(Discount[:amount].lt(0)) }

.with_economy_shipping_match_crmActiveRecord::Relation<Discount>

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

Returns:

See Also:



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

scope :with_economy_shipping_match_crm, -> { joins(:coupon).merge(Coupon.with_economy_shipping_match_crm) }

.zero_valueActiveRecord::Relation<Discount>

A relation of Discounts that are zero value. Active Record Scope

Returns:

See Also:



73
# File 'app/models/discount.rb', line 73

scope :zero_value, -> { where(amount: 0) }

Instance Method Details

#apply_sourceObject

Apply the source specified in the coupon if applicable.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/models/discount.rb', line 136

def apply_source
  return unless coupon && itemizable && coupon.source

  case itemizable_type.downcase.to_sym
  when :order
    coupon.apply_source_to_order(itemizable)
    coupon.apply_source_to_opportunity(itemizable.opportunity) if itemizable.opportunity
    coupon.apply_source_to_customer(itemizable.customer)
  when :quote
    return unless itemizable.opportunity

    coupon.apply_source_to_opportunity(itemizable.opportunity)
    itemizable.opportunity.orders.each { |order| coupon.apply_source_to_order(order) }
    coupon.apply_source_to_customer(itemizable.opportunity.customer)
  end
end

#auto_applyObject

Alias for Coupon#auto_apply

Returns:

  • (Object)

    Coupon#auto_apply

See Also:



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

delegate :blacklistable?, :auto_apply, :code, to: :coupon

#blacklistable?Object

Alias for Coupon#blacklistable?

Returns:

  • (Object)

    Coupon#blacklistable?

See Also:



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

delegate :blacklistable?, :auto_apply, :code, to: :coupon

#codeObject

Alias for Coupon#code

Returns:

  • (Object)

    Coupon#code

See Also:



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

delegate :blacklistable?, :auto_apply, :code, to: :coupon

#couponCoupon

Returns the coupon this record belongs to.

Returns:

  • (Coupon)

    the coupon this record belongs to



44
# File 'app/models/discount.rb', line 44

belongs_to :coupon, touch: :last_used_at

#coupon_serial_numberCouponSerialNumber?

Returns the coupon serial number this record belongs to.

Returns:



48
# File 'app/models/discount.rb', line 48

belongs_to :coupon_serial_number, optional: true

#discount_sum_by_tax_class(tax_class) ⇒ Object

Discount sum by tax class.

Parameters:

  • tax_class (Object)

    the tax class



97
98
99
# File 'app/models/discount.rb', line 97

def discount_sum_by_tax_class(tax_class)
  line_discounts.joins(:line_item).where(LineItem[:tax_class].eq(tax_class)).sum(:amount)
end

#goodsObject

Goods.



102
103
104
# File 'app/models/discount.rb', line 102

def goods
  discount_sum_by_tax_class 'g'
end

#invisible?Boolean

Returns whether the record invisible.

Returns:

  • (Boolean)

    whether the record invisible



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

def invisible?
  amount.zero? or blacklisted
end

#itemizableItemizable

Returns the itemizable this record belongs to.

Returns:

  • (Itemizable)

    the itemizable this record belongs to



46
# File 'app/models/discount.rb', line 46

belongs_to :itemizable, polymorphic: true

#line_discountsActiveRecord::Relation<LineDiscount>

Returns the associated line discounts.

Returns:

  • (ActiveRecord::Relation<LineDiscount>)

    the associated line discounts



51
# File 'app/models/discount.rb', line 51

has_many :line_discounts, dependent: :destroy, autosave: true

#mark_as_blacklistedObject

Mark as blacklisted.



117
118
119
120
121
122
123
# File 'app/models/discount.rb', line 117

def mark_as_blacklisted
  return if Coupon::SKIP_BLACKLISTING_COUPON_CODES.include?(code)

  self.amount = 0
  line_discounts.each(&:destroy)
  self.blacklisted = true
end

#preserve_amount?Boolean

Returns whether the record preserve amount.

Returns:

  • (Boolean)

    whether the record preserve amount



131
132
133
# File 'app/models/discount.rb', line 131

def preserve_amount?
  coupon.adjustable
end

#servicesObject

Services.



107
108
109
# File 'app/models/discount.rb', line 107

def services
  discount_sum_by_tax_class 'svc'
end

#shippingObject

Shipping.



112
113
114
# File 'app/models/discount.rb', line 112

def shipping
  discount_sum_by_tax_class 'shp'
end

#sticky?Boolean

Returns whether the record sticky.

Returns:

  • (Boolean)

    whether the record sticky



91
92
93
# File 'app/models/discount.rb', line 91

def sticky?
  blacklisted or coupon.sticky?
end