Files
openfoodnetwork/app/models/spree/adjustment.rb
Maikel Linke 8ef6966891 Declare old belongs_to default on remaining models
It would take ages to go through all files now and assess all belongs_to
associations. So I just declare the old default and then we can move on
and apply the new default for the application while these classes still
use the old one. All new models will then use the new default which is
the goal of this excercise and we can refactor old classes when we touch
them anyway.
2023-08-11 10:14:43 +10:00

159 lines
5.2 KiB
Ruby

# frozen_string_literal: true
require 'spree/localized_number'
require 'concerns/adjustment_scopes'
# Adjustments represent a change to the +item_total+ of an Order. Each adjustment
# has an +amount+ that can be either positive or negative.
#
# Adjustments can be open/closed/finalized
#
# Once an adjustment is finalized, it cannot be changed, but an adjustment can
# toggle between open/closed as needed
#
# Boolean attributes:
#
# +mandatory+
#
# If this flag is set to true then it means the the charge is required and will not
# be removed from the order, even if the amount is zero. In other words a record
# will be created even if the amount is zero. This is useful for representing things
# such as shipping and tax charges where you may want to make it explicitly clear
# that no charge was made for such things.
#
# +eligible?+
#
# This boolean attributes stores whether this adjustment is currently eligible
# for its order. Only eligible adjustments count towards the order's adjustment
# total. This allows an adjustment to be preserved if it becomes ineligible so
# it might be reinstated.
module Spree
class Adjustment < ApplicationRecord
extend Spree::LocalizedNumber
self.belongs_to_required_by_default = false
# Deletion of metadata is handled in the database.
# So we don't need the option `dependent: :destroy` as long as
# AdjustmentMetadata has no destroy logic itself.
has_one :metadata, class_name: 'AdjustmentMetadata'
has_many :adjustments, as: :adjustable, dependent: :destroy
belongs_to :adjustable, polymorphic: true
belongs_to :originator, -> { with_deleted }, polymorphic: true
belongs_to :order, class_name: "Spree::Order"
belongs_to :tax_category, class_name: 'Spree::TaxCategory'
validates :label, presence: true
validates :amount, numericality: true
after_create :update_adjustable_adjustment_total
state_machine :state, initial: :open do
event :close do
transition from: :open, to: :closed
end
event :open do
transition from: :closed, to: :open
end
event :finalize do
transition from: [:open, :closed], to: :finalized
end
end
scope :tax, -> { where(originator_type: 'Spree::TaxRate') }
scope :price, -> { where(adjustable_type: 'Spree::LineItem') }
scope :optional, -> { where(mandatory: false) }
scope :charge, -> { where('amount >= 0') }
scope :credit, -> { where('amount < 0') }
scope :return_authorization, -> { where(originator_type: "Spree::ReturnAuthorization") }
scope :voucher, -> { where(originator_type: "Voucher") }
scope :non_voucher, -> { where.not(originator_type: "Voucher") }
scope :inclusive, -> { where(included: true) }
scope :additional, -> { where(included: false) }
scope :legacy_tax, -> { additional.tax.where(adjustable_type: "Spree::Order") }
scope :enterprise_fee, -> { where(originator_type: 'EnterpriseFee') }
scope :admin, -> { where(originator_type: nil) }
scope :payment_fee, -> { where(AdjustmentScopes::PAYMENT_FEE_SCOPE) }
scope :shipping, -> { where(AdjustmentScopes::SHIPPING_SCOPE) }
scope :eligible, -> { where(AdjustmentScopes::ELIGIBLE_SCOPE) }
localize_number :amount
# Update both the eligibility and amount of the adjustment. Adjustments
# delegate updating of amount to their Originator when present, but only if
# +locked+ is false. Adjustments that are +locked+ will never change their amount.
#
# Adjustments delegate updating of amount to their Originator when present,
# but only if when they're in "open" state, closed or finalized adjustments
# are not recalculated.
#
# It receives +calculable+ as the updated source here so calculations can be
# performed on the current values of that source. If we used +source+ it
# could load the old record from db for the association. e.g. when updating
# more than on line items at once via accepted_nested_attributes the order
# object on the association would be in a old state and therefore the
# adjustment calculations would not performed on proper values
def update_adjustment!(calculable = nil, force: false)
return amount if immutable? && !force
if calculable.nil? && adjustable.nil?
delete
return 0.0
end
if originator.present?
amount = originator.compute_amount(calculable || adjustable)
update_columns(
amount: amount,
updated_at: Time.zone.now,
)
end
amount
end
def currency
adjustable ? adjustable.currency : Spree::Config[:currency]
end
def display_amount
Spree::Money.new(amount, currency: currency)
end
def admin?
originator_type.nil?
end
def immutable?
state != "open"
end
def has_tax?
tax_total.positive?
end
def included_tax_total
adjustments.tax.inclusive.sum(:amount)
end
def additional_tax_total
adjustments.tax.additional.sum(:amount)
end
private
def tax_total
adjustments.tax.sum(:amount)
end
def update_adjustable_adjustment_total
Spree::ItemAdjustments.new(adjustable).update if adjustable
end
end
end