Merge decorators with original code from spree_core

This commit is contained in:
Luis Ramos
2020-08-07 18:54:23 +01:00
parent aa46a4b5da
commit da683e3ecf
6 changed files with 119 additions and 147 deletions

View File

@@ -1,3 +1,6 @@
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.
#
@@ -24,9 +27,18 @@
# it might be reinstated.
module Spree
class Adjustment < ActiveRecord::Base
extend Spree::LocalizedNumber
# 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'
belongs_to :adjustable, polymorphic: true
belongs_to :source, polymorphic: true
belongs_to :originator, polymorphic: true
belongs_to :tax_rate, -> { where spree_adjustments: { originator_type: 'Spree::TaxRate' } },
foreign_key: 'originator_id'
validates :label, presence: true
validates :amount, numericality: true
@@ -50,14 +62,26 @@ module Spree
scope :tax, -> { where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::Order') }
scope :price, -> { where(adjustable_type: 'Spree::LineItem') }
scope :shipping, -> { where(originator_type: 'Spree::ShippingMethod') }
scope :optional, -> { where(mandatory: false) }
scope :eligible, -> { where(eligible: true) }
scope :charge, -> { where('amount >= 0') }
scope :credit, -> { where('amount < 0') }
scope :promotion, -> { where(originator_type: 'Spree::PromotionAction') }
scope :return_authorization, -> { where(source_type: "Spree::ReturnAuthorization") }
scope :enterprise_fee, -> { where(originator_type: 'EnterpriseFee') }
scope :admin, -> { where(source_type: nil, originator_type: nil) }
scope :included_tax, -> {
where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::LineItem')
}
scope :with_tax, -> { where('spree_adjustments.included_tax <> 0') }
scope :without_tax, -> { where('spree_adjustments.included_tax = 0') }
scope :payment_fee, -> { where(AdjustmentScopes::PAYMENT_FEE_SCOPE) }
scope :shipping, -> { where(AdjustmentScopes::SHIPPING_SCOPE) }
scope :eligible, -> { where(AdjustmentScopes::ELIGIBLE_SCOPE) }
localize_number :amount
def promotion?
originator_type == 'Spree::PromotionAction'
end
@@ -112,6 +136,39 @@ module Spree
state != "open"
end
def set_included_tax!(rate)
tax = amount - (amount / (1 + rate))
set_absolute_included_tax! tax
end
def set_absolute_included_tax!(tax)
# This rubocop issue can now fixed by renaming Adjustment#update! to something else,
# then AR's update! can be used instead of update_attributes!
# rubocop:disable Rails/ActiveRecordAliases
update_attributes! included_tax: tax.round(2)
# rubocop:enable Rails/ActiveRecordAliases
end
def display_included_tax
Spree::Money.new(included_tax, currency: currency)
end
def has_tax?
included_tax > 0
end
def self.without_callbacks
skip_callback :save, :after, :update_adjustable
skip_callback :destroy, :after, :update_adjustable
result = yield
ensure
set_callback :save, :after, :update_adjustable
set_callback :destroy, :after, :update_adjustable
result
end
private
def update_adjustable

View File

@@ -1,62 +0,0 @@
require 'spree/localized_number'
require 'concerns/adjustment_scopes'
module Spree
Adjustment.class_eval do
extend Spree::LocalizedNumber
# 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'
belongs_to :tax_rate, -> { where spree_adjustments: { originator_type: 'Spree::TaxRate' } },
foreign_key: 'originator_id'
scope :enterprise_fee, -> { where(originator_type: 'EnterpriseFee') }
scope :admin, -> { where(source_type: nil, originator_type: nil) }
scope :included_tax, -> {
where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::LineItem')
}
scope :with_tax, -> { where('spree_adjustments.included_tax <> 0') }
scope :without_tax, -> { where('spree_adjustments.included_tax = 0') }
scope :payment_fee, -> { where(AdjustmentScopes::PAYMENT_FEE_SCOPE) }
scope :shipping, -> { where(AdjustmentScopes::SHIPPING_SCOPE) }
scope :eligible, -> { where(AdjustmentScopes::ELIGIBLE_SCOPE) }
localize_number :amount
def set_included_tax!(rate)
tax = amount - (amount / (1 + rate))
set_absolute_included_tax! tax
end
def set_absolute_included_tax!(tax)
# This rubocop issue can only be fixed when Adjustment#update! is brought from Spree to OFN
# and renamed to something else, then AR's update! can be used instead of update_attributes!
# rubocop:disable Rails/ActiveRecordAliases
update_attributes! included_tax: tax.round(2)
# rubocop:enable Rails/ActiveRecordAliases
end
def display_included_tax
Spree::Money.new(included_tax, currency: currency)
end
def has_tax?
included_tax > 0
end
def self.without_callbacks
skip_callback :save, :after, :update_adjustable
skip_callback :destroy, :after, :update_adjustable
result = yield
ensure
set_callback :save, :after, :update_adjustable
set_callback :destroy, :after, :update_adjustable
result
end
end
end

View File

@@ -35,5 +35,21 @@ module Spree
def available?(object)
true
end
private
# Given an object which might be an Order or a LineItem (amongst
# others), return a collection of line items.
def line_items_for(object)
if object.is_a?(Spree::LineItem)
[object]
elsif object.respond_to? :line_items
object.line_items
elsif object.respond_to?(:order) && object.order.present?
object.order.line_items
else
[object]
end
end
end
end

View File

@@ -1,19 +0,0 @@
module Spree
Calculator.class_eval do
private
# Given an object which might be an Order or a LineItem (amongst
# others), return a collection of line items.
def line_items_for(object)
if object.is_a?(Spree::LineItem)
[object]
elsif object.respond_to? :line_items
object.line_items
elsif object.respond_to?(:order) && object.order.present?
object.order.line_items
else
[object]
end
end
end
end

View File

@@ -22,11 +22,12 @@ module Spree
scope :by_zone, ->(zone) { where(zone_id: zone) }
# Gets the array of TaxRates appropriate for the specified order
def self.match(order)
def match(order)
return [] if order.distributor && !order.distributor.charges_sales_tax
return [] unless order.tax_zone
all.select do |rate|
(!rate.included_in_price && (rate.zone == order.tax_zone || rate.zone.contains?(order.tax_zone) || (order.tax_address.nil? && rate.zone.default_tax))) ||
(rate.included_in_price && !order.tax_address.nil? && !rate.zone.contains?(order.tax_zone) && rate.zone.default_tax)
rate.zone == order.tax_zone || rate.zone.contains?(order.tax_zone) || rate.zone.default_tax
end
end
@@ -73,6 +74,32 @@ module Spree
else
create_adjustment(label, order, order)
end
order.adjustments(:reload)
order.line_items(:reload)
# TaxRate adjustments (order.adjustments.tax) and price adjustments (tax included on line items) consist of 100% tax
(order.adjustments.tax + order.price_adjustments).each do |adjustment|
adjustment.set_absolute_included_tax! adjustment.amount
end
end
# Manually apply a TaxRate to a particular amount. TaxRates normally compute against
# LineItems or Orders, so we mock out a line item here to fit the interface
# that our calculator (usually DefaultTax) expects.
def compute_tax(amount)
line_item = LineItem.new quantity: 1
line_item.tax_category = tax_category
line_item.define_singleton_method(:price) { amount }
# Tax on adjustments (represented by the included_tax field) is always inclusive of
# tax. However, there's nothing to stop an admin from setting one up with a tax rate
# that's marked as not inclusive of tax, and that would result in the DefaultTax
# calculator generating a slightly incorrect value. Therefore, we treat the tax
# rate as inclusive of tax for the calculations below, regardless of its original
# setting.
with_tax_included_in_price do
calculator.compute line_item
end
end
private
@@ -82,5 +109,19 @@ module Spree
label << (name.present? ? name : tax_category.name) + " "
label << (show_rate_in_label? ? "#{amount * 100}%" : "")
end
def with_tax_included_in_price
old_included_in_price = included_in_price
self.included_in_price = true
calculator.calculable.included_in_price = true
result = yield
ensure
self.included_in_price = old_included_in_price
calculator.calculable.included_in_price = old_included_in_price
result
end
end
end

View File

@@ -1,61 +0,0 @@
module Spree
TaxRate.class_eval do
class << self
def match(order)
return [] if order.distributor && !order.distributor.charges_sales_tax
return [] unless order.tax_zone
all.select do |rate|
rate.zone == order.tax_zone || rate.zone.contains?(order.tax_zone) || rate.zone.default_tax
end
end
end
def adjust_with_included_tax(order)
adjust_without_included_tax(order)
order.adjustments(:reload)
order.line_items(:reload)
# TaxRate adjustments (order.adjustments.tax) and price adjustments (tax included on line items) consist of 100% tax
(order.adjustments.tax + order.price_adjustments).each do |adjustment|
adjustment.set_absolute_included_tax! adjustment.amount
end
end
alias_method_chain :adjust, :included_tax
# Manually apply a TaxRate to a particular amount. TaxRates normally compute against
# LineItems or Orders, so we mock out a line item here to fit the interface
# that our calculator (usually DefaultTax) expects.
def compute_tax(amount)
line_item = LineItem.new quantity: 1
line_item.tax_category = tax_category
line_item.define_singleton_method(:price) { amount }
# Tax on adjustments (represented by the included_tax field) is always inclusive of
# tax. However, there's nothing to stop an admin from setting one up with a tax rate
# that's marked as not inclusive of tax, and that would result in the DefaultTax
# calculator generating a slightly incorrect value. Therefore, we treat the tax
# rate as inclusive of tax for the calculations below, regardless of its original
# setting.
with_tax_included_in_price do
calculator.compute line_item
end
end
private
def with_tax_included_in_price
old_included_in_price = included_in_price
self.included_in_price = true
calculator.calculable.included_in_price = true
result = yield
ensure
self.included_in_price = old_included_in_price
calculator.calculable.included_in_price = old_included_in_price
result
end
end
end