Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tax extension point and merge tax updating code #1479

Merged
merged 5 commits into from
Oct 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
## Solidus 2.1.0 (master, unreleased)

* The OrderUpdater (as used by `order.update!`) now fully updates taxes.

Previously there were two different ways taxes were calculated: a "full"
and a "quick" calculation. The full calculation was performed with
`order.create_tax_charge!` and would determine which tax rates applied and
add taxes to items. The "quick" calculation was performed as part of an
order update, and would only update the tax amounts on existing line items
with taxes.

Now `order.update!` will perform the full calculation every time.
`order.create_tax_charge!` is now deprecated and has been made equivalent
to `order.update!`.

https://github.com/solidusio/solidus/pull/1479

* Added Spree::Config.tax_adjuster_class

To allow easier customization of tax calculation in extensions or
applications.

https://github.com/solidusio/solidus/pull/1479

* Add `Spree::Promotion#remove_from` and `Spree::PromotionAction#remove_from`

This will allow promotions to be removed from orders and allows promotion
Expand All @@ -18,7 +40,7 @@
* Remove callback `Spree::LineItem.after_create :update_tax_charge`

Any code that creates `LineItem`s outside the context of OrderContents
should ensure that it calls `order.create_tax_charge!` after doing so.
should ensure that it calls `order.update!` after doing so.

* Mark `Spree::Tax::ItemAdjuster` as api-private

Expand All @@ -39,7 +61,7 @@
* Removals

* Removed deprecated method `Spree::TaxRate.adjust` (not to be confused with
Spree::TaxRate#adjust) in favor of `Spree::Tax::OrderAdjuster`.
Spree::TaxRate#adjust) in favor of `Spree::Config.tax_adjuster_class`.

https://github.com/solidusio/solidus/pull/1462

Expand Down
53 changes: 28 additions & 25 deletions backend/spec/features/admin/orders/adjustments_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,26 @@
describe "Adjustments", type: :feature do
stub_authorization!

let!(:order) { create(:completed_order_with_totals, line_items_count: 5) }
let!(:line_item) do
line_item = order.line_items.first
# so we can be sure of a determinate price in our assertions
line_item.update_column(:price, 10)
line_item
let!(:ship_address) { create(:address) }
let!(:tax_zone) { create(:global_zone) } # will include the above address
let!(:tax_rate) { create(:tax_rate, amount: 0.20, zone: tax_zone, tax_category: tax_category) }

let!(:order) do
create(
:completed_order_with_totals,
line_items_attributes: [{ price: 10, variant: variant }] * 5,
ship_address: ship_address,
)
end
let!(:line_item) { order.line_items[0] }

let!(:tax_adjustment) do
create(:tax_adjustment,
adjustable: line_item,
finalized: true,
order: order,
label: "VAT 5%",
amount: 10)
end
let(:tax_category) { create(:tax_category) }
let(:variant) { create(:variant, tax_category: tax_category) }

let!(:adjustment) { order.adjustments.create!(order: order, label: 'Rebate', amount: 10) }

before(:each) do
# To ensure the order totals are correct
order.update_totals
order.persist_totals
order.update!

visit spree.admin_path
click_link "Orders"
Expand All @@ -35,9 +32,9 @@

context "admin managing adjustments" do
it "should display the correct values for existing order adjustments" do
within_row(1) do
expect(column_text(2)).to eq("VAT 5%")
expect(column_text(3)).to eq("$10.00")
within first('table tr', text: 'Tax') do
expect(column_text(2)).to match(/TaxCategory - \d+ 20\.000%/)
expect(column_text(3)).to eq("$2.00")
end
end

Expand Down Expand Up @@ -76,7 +73,9 @@

context "admin editing an adjustment" do
before(:each) do
within_row(2) { click_icon :edit }
within('table tr', text: 'Rebate') do
click_icon :edit
end
end

context "successfully" do
Expand Down Expand Up @@ -106,15 +105,19 @@
end

context "deleting an adjustment" do
it "should not be possible if adjustment is closed" do
within_row(1) do
expect(page).not_to have_css('.fa-trash')
context 'when the adjustment is finalized' do
let!(:adjustment) { super().tap(&:finalize!) }

it 'should not be possible' do
within('table tr', text: 'Rebate') do
expect(page).not_to have_css('.fa-trash')
end
end
end

it "should update the total", js: true do
accept_alert do
within_row(2) do
within('table tr', text: 'Rebate') do
click_icon(:trash)
end
end
Expand Down
10 changes: 10 additions & 0 deletions core/app/models/spree/app_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,16 @@ def add_payment_sources_to_wallet_class
@add_payment_sources_to_wallet_class ||= Spree::Wallet::AddPaymentSourcesToWallet
end

# Allows providing your own class for calculating taxes on an order.
#
# @!attribute [rw] tax_adjuster_class
# @return [Class] a class with the same public interfaces as
# Spree::Tax::OrderAdjuster
attr_writer :tax_adjuster_class
def tax_adjuster_class
@tax_adjuster_class ||= Spree::Tax::OrderAdjuster
end

def static_model_preferences
@static_model_preferences ||= Spree::Preferences::StaticModelPreferences.new
end
Expand Down
6 changes: 4 additions & 2 deletions core/app/models/spree/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module Spree
# `checkout_allowed?` or `payment_required?`.
#
# * Implements an interface for mutating the order with methods like
# `create_tax_charge!` and `fulfill!`.
# `empty!` and `fulfill!`.
#
class Order < Spree::Base
ORDER_NUMBER_LENGTH = 9
Expand Down Expand Up @@ -348,9 +348,11 @@ def line_item_options_match(line_item, options)

# Creates new tax charges if there are any applicable rates. If prices already
# include taxes then price adjustments are created instead.
# @deprecated This now happens during #update!
def create_tax_charge!
Spree::Tax::OrderAdjuster.new(self).adjust!
Spree::Config.tax_adjuster_class.new(self).adjust!
end
deprecate create_tax_charge!: :update!, deprecator: Spree::Deprecation

def outstanding_balance
# If reimbursement has happened add it back to total to prevent balance_due payment state
Expand Down
2 changes: 0 additions & 2 deletions core/app/models/spree/order/checkout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ def self.define_state_machine!

after_transition to: :complete, do: :add_payment_sources_to_wallet
before_transition to: :payment, do: :set_shipments_cost
before_transition to: :payment, do: :create_tax_charge!
before_transition to: :payment, do: :assign_default_credit_card

before_transition to: :confirm, do: :add_store_credit_payments
Expand All @@ -90,7 +89,6 @@ def self.define_state_machine!
before_transition from: :cart, do: :ensure_line_items_present

if states[:address]
before_transition from: :address, do: :create_tax_charge!
before_transition to: :address, do: :assign_default_addresses!
before_transition from: :address, do: :persist_user_address!
end
Expand Down
3 changes: 0 additions & 3 deletions core/app/models/spree/order_contents.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ def remove_line_item(line_item, options = {})

def update_cart(params)
if order.update_attributes(params)
order.create_tax_charge!

unless order.completed?
order.line_items = order.line_items.select { |li| li.quantity > 0 }
# Update totals, then check if the order is eligible for any cart promotions.
Expand Down Expand Up @@ -77,7 +75,6 @@ def after_add_or_remove(line_item, options = {})
shipment = options[:shipment]
shipment.present? ? shipment.update_amounts : order.ensure_updated_shipments
PromotionHandler::Cart.new(order, line_item).activate
order.create_tax_charge!
reload_totals
line_item
end
Expand Down
4 changes: 2 additions & 2 deletions core/app/models/spree/order_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,10 @@ def update_order_promotions
end

def update_taxes
Spree::Config.tax_adjuster_class.new(order).adjust!

[*line_items, *shipments].each do |item|
tax_adjustments = item.adjustments.select(&:tax?)

tax_adjustments.each(&:update!)
# Tax adjustments come in not one but *two* exciting flavours:
# Included & additional

Expand Down
1 change: 0 additions & 1 deletion core/lib/spree/core/unreturned_item_charger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def charge_for_items
new_order.associate_user!(@original_order.user) if @original_order.user

add_exchange_variants_to_order
new_order.create_tax_charge!
set_shipment_for_new_order

new_order.update!
Expand Down
1 change: 0 additions & 1 deletion core/lib/spree/testing_support/factories/order_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
create(:line_item, attributes)
end
order.line_items.reload
order.create_tax_charge!

create(:shipment, order: order, cost: evaluator.shipment_cost, shipping_method: evaluator.shipping_method, stock_location: evaluator.stock_location)
order.shipments.reload
Expand Down
1 change: 0 additions & 1 deletion core/spec/lib/spree/core/unreturned_item_charger_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@

it "applies tax" do
exchange_order = exchange_shipment.order
exchange_order.create_tax_charge!
exchange_order.update!
subject
expect(new_order.additional_tax_total).to be > 0
Expand Down
4 changes: 1 addition & 3 deletions core/spec/models/spree/order/updating_spec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
require 'spec_helper'

describe Spree::Order, type: :model do
let(:order) { stub_model(Spree::Order) }
let(:order) { create(:order) }

context "#update!" do
let(:line_items) { [mock_model(Spree::LineItem, amount: 5)] }

context "when there are update hooks" do
before { Spree::Order.register_update_hook :foo }
after { Spree::Order.update_hooks.clear }
Expand Down
10 changes: 6 additions & 4 deletions core/spec/models/spree/order_cancellations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,19 @@
let(:line_item) { order.line_items.to_a.first }
let(:inventory_unit_1) { line_item.inventory_units[0] }
let(:inventory_unit_2) { line_item.inventory_units[1] }
let(:promotion) { create(:promotion, :with_line_item_adjustment) }
let(:promotion_action) { promotion.actions[0] }

before do
order.contents.add(line_item.variant)

# make the total $1.67 so it divides unevenly
line_item.adjustments.create!(
source_type: 'Spree::TaxRate',
order: order,
amount: 0.01,
label: 'some fake tax',
finalized: true
label: 'some promo',
source: promotion_action,
finalized: true,
)
order.update!
end
Expand All @@ -154,7 +156,7 @@
order.cancellations.short_ship([inventory_unit_2])
expect(line_item.adjustments.map(&:amount)).to match_array(
[
0.01, # tax adjustment
0.01, # promo adjustment
-0.84, # short ship 1
-0.83, # short ship 2
]
Expand Down
5 changes: 0 additions & 5 deletions core/spec/models/spree/order_contents_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,6 @@
}.to change { subject.order.total }
end

it "updates tax adjustments" do
expect(subject.order).to receive(:create_tax_charge!)
subject.update_cart params
end

context "submits item quantity 0" do
let(:params) do
{ line_items_attributes: {
Expand Down
18 changes: 17 additions & 1 deletion core/spec/models/spree/order_updater_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,6 @@ def create_adjustment(label, amount)
let(:order) do
create(
:order_with_line_items,
line_items_count: 1,
line_items_attributes: [{ price: 10, variant: variant }],
ship_address: ship_address,
)
Expand All @@ -299,6 +298,23 @@ def create_adjustment(label, amount)
}.from(1).to(2)
end
end

context 'with a custom tax_adjuster_class' do
let(:custom_adjuster_class) { double }
let(:custom_adjuster_instance) { double }

before do
order # generate this first so we can expect it
Spree::Config.tax_adjuster_class = custom_adjuster_class
end

it 'uses the configured class' do
expect(custom_adjuster_class).to receive(:new).with(order).at_least(:once).and_return(custom_adjuster_instance)
expect(custom_adjuster_instance).to receive(:adjust!).at_least(:once)

order.update!
end
end
end
end

Expand Down
19 changes: 16 additions & 3 deletions core/spec/models/spree/shipment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,23 @@
end

context "#item_cost" do
let(:shipment) { order.shipments[0] }

let(:order) do
create(
:order_ready_to_ship,
line_items_attributes: [{ price: 10, variant: variant }],
ship_address: ship_address,
)
end

let!(:ship_address) { create(:address) }
let!(:tax_zone) { create(:global_zone) } # will include the above address
let!(:tax_rate) { create(:tax_rate, amount: 0.1, zone: tax_zone, tax_category: tax_category) }
let(:tax_category) { create(:tax_category) }
let(:variant) { create(:variant, tax_category: tax_category) }

it 'should equal line items final amount with tax' do
shipment = create(:shipment, order: create(:order_with_totals))
create :tax_adjustment, adjustable: shipment.order.line_items.first, order: shipment.order
shipment.order.update!
expect(shipment.item_cost).to eql(11.0)
end
end
Expand Down