diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d270a06a81..c21442c8b20 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,10 @@
- Ignore `adjustment.finalized` on tax adjustments. [\#1936](https://github.com/solidusio/solidus/pull/1936) ([jordan-brough](https://github.com/jordan-brough))
- Deprecate `#simple_current_order`
[\#1915](https://github.com/solidusio/solidus/pull/1915) ([ericsaupe](https://github.com/ericsaupe))
+- Transform the relation between TaxRate and TaxCategory to a Many to Many [\#1851](https://github.com/solidusio/solidus/pull/1851) ([vladstoick](https://github.com/vladstoick))
+
+ This fixes issue [\#1836](https://github.com/solidusio/solidus/issues/1836). By allowing a TaxRate to tax multiple categories, stores don't have to create multiple TaxRates with the same value if a zone doesn't have different tax rates for some tax categories.
+
## Solidus 2.2.1 (2017-05-09)
diff --git a/backend/app/views/spree/admin/tax_rates/_form.html.erb b/backend/app/views/spree/admin/tax_rates/_form.html.erb
index 6177cc46a3f..b4afb5d125a 100644
--- a/backend/app/views/spree/admin/tax_rates/_form.html.erb
+++ b/backend/app/views/spree/admin/tax_rates/_form.html.erb
@@ -25,8 +25,8 @@
<%= f.collection_select(:zone_id, @available_zones, :id, :name, {}, {class: 'select2 fullwidth'}) %>
- <%= f.label :tax_category_id, Spree::TaxCategory.model_name.human %>
- <%= f.collection_select(:tax_category_id, @available_categories,:id, :name, {}, {class: 'select2 fullwidth'}) %>
+ <%= f.label :tax_category_ids, Spree::TaxCategory.model_name.human %>
+ <%= f.collection_select(:tax_category_ids, @available_categories, :id, :name, {}, {class: 'select2 fullwidth', multiple: "multiple"}) %>
<%= f.check_box :show_rate_in_label %>
diff --git a/backend/app/views/spree/admin/tax_rates/index.html.erb b/backend/app/views/spree/admin/tax_rates/index.html.erb
index 55519d65ced..17a455c0b31 100644
--- a/backend/app/views/spree/admin/tax_rates/index.html.erb
+++ b/backend/app/views/spree/admin/tax_rates/index.html.erb
@@ -29,7 +29,7 @@
<%= Spree::TaxRate.human_attribute_name(:zone) %> |
<%= Spree::TaxRate.human_attribute_name(:name) %> |
- <%= Spree::TaxRate.human_attribute_name(:tax_category_id) %> |
+ <%= Spree::TaxRate.human_attribute_name(:tax_categories) %> |
<%= Spree::TaxRate.human_attribute_name(:amount) %> |
<%= Spree::TaxRate.human_attribute_name(:included_in_price) %> |
<%= Spree::TaxRate.human_attribute_name(:show_rate_in_label) %> |
@@ -42,7 +42,13 @@
<%=tax_rate.zone.try(:name) || Spree.t(:not_available) %> |
<%=tax_rate.name %> |
- <%=tax_rate.tax_category.try(:name) || Spree.t(:not_available) %> |
+
+ <% if tax_rate.tax_categories.any? %>
+ <%= tax_rate.tax_categories.map(&:name).join(", ") %>
+ <% else %>
+ <%= Spree.t(:not_available) %>
+ <% end %>
+ |
<%=tax_rate.amount %> |
<%=tax_rate.included_in_price? ? Spree.t(:say_yes) : Spree.t(:say_no) %> |
<%=tax_rate.show_rate_in_label? ? Spree.t(:say_yes) : Spree.t(:say_no) %> |
diff --git a/backend/spec/features/admin/configuration/tax_rates_spec.rb b/backend/spec/features/admin/configuration/tax_rates_spec.rb
index ba71e6a9a74..c59ebb927c6 100644
--- a/backend/spec/features/admin/configuration/tax_rates_spec.rb
+++ b/backend/spec/features/admin/configuration/tax_rates_spec.rb
@@ -12,7 +12,7 @@
# Regression test for https://github.com/spree/spree/issues/535
it "can see a tax rate in the list if the tax category has been deleted" do
- tax_rate.tax_category.update_column(:deleted_at, Time.current)
+ tax_rate.tax_categories.first.update_column(:deleted_at, Time.current)
click_link "Tax Rates"
expect(find("table tbody td:nth-child(3)")).to have_content('N/A')
diff --git a/backend/spec/features/admin/orders/adjustments_spec.rb b/backend/spec/features/admin/orders/adjustments_spec.rb
index 3cae2732ac9..312394f734c 100644
--- a/backend/spec/features/admin/orders/adjustments_spec.rb
+++ b/backend/spec/features/admin/orders/adjustments_spec.rb
@@ -5,7 +5,7 @@
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!(:tax_rate) { create(:tax_rate, amount: 0.20, zone: tax_zone, tax_categories: [tax_category]) }
let!(:order) do
create(
diff --git a/core/app/models/spree/calculator/default_tax.rb b/core/app/models/spree/calculator/default_tax.rb
index 74f0c02714e..d232ef18f4b 100644
--- a/core/app/models/spree/calculator/default_tax.rb
+++ b/core/app/models/spree/calculator/default_tax.rb
@@ -9,7 +9,7 @@ class Calculator::DefaultTax < Calculator
# Orders created with Spree 2.2 and after, have them applied to the line items individually.
def compute_order(order)
matched_line_items = order.line_items.select do |line_item|
- line_item.tax_category == rate.tax_category
+ rate.tax_categories.include?(line_item.tax_category)
end
line_items_total = matched_line_items.sum(&:discounted_amount)
diff --git a/core/app/models/spree/tax/shipping_rate_taxer.rb b/core/app/models/spree/tax/shipping_rate_taxer.rb
index b5ac68382cd..45568ca16be 100644
--- a/core/app/models/spree/tax/shipping_rate_taxer.rb
+++ b/core/app/models/spree/tax/shipping_rate_taxer.rb
@@ -23,7 +23,7 @@ def tax(shipping_rate)
def tax_rates_for_shipping_rate(shipping_rate)
applicable_rates(shipping_rate.order).select do |tax_rate|
- tax_rate.tax_category == shipping_rate.tax_category
+ tax_rate.tax_categories.include?(shipping_rate.tax_category)
end
end
end
diff --git a/core/app/models/spree/tax/tax_helpers.rb b/core/app/models/spree/tax/tax_helpers.rb
index 10a7d13fa76..c40b8d19f52 100644
--- a/core/app/models/spree/tax/tax_helpers.rb
+++ b/core/app/models/spree/tax/tax_helpers.rb
@@ -17,9 +17,9 @@ module TaxHelpers
#
# For further discussion, see https://github.com/spree/spree/issues/4397 and https://github.com/spree/spree/issues/4327.
def applicable_rates(order)
- order_zone_tax_category_ids = rates_for_order(order).map(&:tax_category_id)
+ order_zone_tax_category_ids = rates_for_order(order).flat_map(&:tax_categories).map(&:id)
default_rates_with_unmatched_tax_category = rates_for_default_zone.to_a.delete_if do |default_rate|
- order_zone_tax_category_ids.include?(default_rate.tax_category_id)
+ (order_zone_tax_category_ids & default_rate.tax_categories.map(&:id)).any?
end
(rates_for_order(order) + default_rates_with_unmatched_tax_category).uniq
@@ -38,7 +38,9 @@ def sum_of_included_tax_rates(item)
end
def rates_for_item(item)
- applicable_rates(item.order).select { |rate| rate.tax_category_id == item.tax_category_id }
+ applicable_rates(item.order).select do |rate|
+ rate.tax_categories.map(&:id).include?(item.tax_category_id)
+ end
end
end
end
diff --git a/core/app/models/spree/tax_category.rb b/core/app/models/spree/tax_category.rb
index 3360c7997d6..4097b0d778b 100644
--- a/core/app/models/spree/tax_category.rb
+++ b/core/app/models/spree/tax_category.rb
@@ -4,7 +4,15 @@ class TaxCategory < Spree::Base
validates :name, presence: true
validates_uniqueness_of :name, unless: :deleted_at
- has_many :tax_rates, dependent: :destroy, inverse_of: :tax_category
+ has_many :tax_rate_tax_categories,
+ class_name: Spree::TaxRateTaxCategory,
+ dependent: :destroy,
+ inverse_of: :tax_category
+ has_many :tax_rates,
+ through: :tax_rate_tax_categories,
+ class_name: Spree::TaxRate,
+ inverse_of: :tax_categories
+
after_save :ensure_one_default
def self.default
diff --git a/core/app/models/spree/tax_rate.rb b/core/app/models/spree/tax_rate.rb
index ddc2485920d..e6a88221452 100644
--- a/core/app/models/spree/tax_rate.rb
+++ b/core/app/models/spree/tax_rate.rb
@@ -9,13 +9,20 @@ class TaxRate < Spree::Base
include Spree::AdjustmentSource
belongs_to :zone, class_name: "Spree::Zone", inverse_of: :tax_rates
- belongs_to :tax_category, class_name: "Spree::TaxCategory", inverse_of: :tax_rates
+
+ has_many :tax_rate_tax_categories,
+ class_name: Spree::TaxRateTaxCategory,
+ dependent: :destroy,
+ inverse_of: :tax_rate
+ has_many :tax_categories,
+ through: :tax_rate_tax_categories,
+ class_name: Spree::TaxCategory,
+ inverse_of: :tax_rates
has_many :adjustments, as: :source
has_many :shipping_rate_taxes, class_name: "Spree::ShippingRateTax"
validates :amount, presence: true, numericality: true
- validates :tax_category_id, presence: true
# Finds all tax rates whose zones match a given address
scope :for_address, ->(address) { joins(:zone).merge(Spree::Zone.for_address(address)) }
@@ -94,10 +101,12 @@ def compute_amount(item)
private
def adjustment_label(amount)
- Spree.t translation_key(amount),
- scope: "adjustment_labels.tax_rates",
- name: name.presence || tax_category.name,
- amount: amount_for_adjustment_label
+ Spree.t(
+ translation_key(amount),
+ scope: "adjustment_labels.tax_rates",
+ name: name.presence || tax_categories.map(&:name).join(", "),
+ amount: amount_for_adjustment_label
+ )
end
def amount_for_adjustment_label
diff --git a/core/app/models/spree/tax_rate_tax_category.rb b/core/app/models/spree/tax_rate_tax_category.rb
new file mode 100644
index 00000000000..fefa7364acc
--- /dev/null
+++ b/core/app/models/spree/tax_rate_tax_category.rb
@@ -0,0 +1,6 @@
+module Spree
+ class TaxRateTaxCategory < Spree::Base
+ belongs_to :tax_rate, class_name: Spree::TaxRate, inverse_of: :tax_rate_tax_categories
+ belongs_to :tax_category, class_name: Spree::TaxCategory, inverse_of: :tax_rate_tax_categories
+ end
+end
diff --git a/core/db/migrate/20170412103617_transform_tax_rate_category_relation.rb b/core/db/migrate/20170412103617_transform_tax_rate_category_relation.rb
new file mode 100644
index 00000000000..58389a002e9
--- /dev/null
+++ b/core/db/migrate/20170412103617_transform_tax_rate_category_relation.rb
@@ -0,0 +1,48 @@
+class TransformTaxRateCategoryRelation < ActiveRecord::Migration[5.0]
+ class TaxRate < ActiveRecord::Base
+ self.table_name = "spree_tax_rates"
+ end
+
+ class TaxRateTaxCategory < ActiveRecord::Base
+ self.table_name = "spree_tax_rate_tax_categories"
+ end
+
+ def up
+ create_table :spree_tax_rate_tax_categories do |t|
+ t.integer :tax_category_id, index: true, null: false
+ t.integer :tax_rate_id, index: true, null: false
+ end
+
+ add_foreign_key :spree_tax_rate_tax_categories, :spree_tax_categories, column: :tax_category_id
+ add_foreign_key :spree_tax_rate_tax_categories, :spree_tax_rates, column: :tax_rate_id
+
+ TaxRate.where.not(tax_category_id: nil).find_each do |tax_rate|
+ TaxRateTaxCategory.create!(
+ tax_rate_id: tax_rate.id,
+ tax_category_id: tax_rate.tax_category_id
+ )
+ end
+
+ remove_column :spree_tax_rates, :tax_category_id
+ end
+
+ def down
+ add_column :spree_tax_rates, :tax_category_id, :integer, index: true
+ add_foreign_key :spree_tax_rates, :spree_tax_categories, column: :tax_category_id
+
+ TaxRate.find_each do |tax_rate|
+ tax_category_ids = TaxRateTaxCategory.where(tax_rate_id: tax_rate.id).pluck(:tax_category_id)
+
+ tax_category_ids.each_with_index do |category_id, i|
+ if i.zero?
+ tax_rate.update!(tax_category_id: category_id)
+ else
+ new_tax_rate = tax_rate.dup
+ new_tax_rate.update!(tax_category_id: category_id)
+ end
+ end
+ end
+
+ drop_table :spree_tax_rate_tax_categories
+ end
+end
diff --git a/core/lib/spree/testing_support/factories/adjustment_factory.rb b/core/lib/spree/testing_support/factories/adjustment_factory.rb
index 581f1d77f7d..e558138f913 100644
--- a/core/lib/spree/testing_support/factories/adjustment_factory.rb
+++ b/core/lib/spree/testing_support/factories/adjustment_factory.rb
@@ -29,7 +29,11 @@
after(:create) do |adjustment|
# Set correct tax category, so that adjustment amount is not 0
if adjustment.adjustable.is_a?(Spree::LineItem)
- adjustment.source.tax_category = adjustment.adjustable.tax_category
+ if adjustment.adjustable.tax_category.present?
+ adjustment.source.tax_categories = [adjustment.adjustable.tax_category]
+ else
+ adjustment.source.tax_categories = []
+ end
adjustment.source.save
adjustment.update!
end
diff --git a/core/lib/spree/testing_support/factories/tax_rate_factory.rb b/core/lib/spree/testing_support/factories/tax_rate_factory.rb
index 89bd53c8f78..12f9ec4bb18 100644
--- a/core/lib/spree/testing_support/factories/tax_rate_factory.rb
+++ b/core/lib/spree/testing_support/factories/tax_rate_factory.rb
@@ -6,7 +6,7 @@
factory :tax_rate, class: Spree::TaxRate do
zone
amount 0.1
- tax_category
association(:calculator, factory: :default_tax_calculator)
+ tax_categories { [build(:tax_category)] }
end
end
diff --git a/core/spec/lib/spree/core/price_migrator_spec.rb b/core/spec/lib/spree/core/price_migrator_spec.rb
index ca45efb8b56..aadc4525466 100644
--- a/core/spec/lib/spree/core/price_migrator_spec.rb
+++ b/core/spec/lib/spree/core/price_migrator_spec.rb
@@ -55,7 +55,7 @@
name: "German reduced VAT",
included_in_price: true,
amount: 0.07,
- tax_category: books_category,
+ tax_categories: [books_category],
zone: eu_zone
)
end
@@ -65,7 +65,7 @@
name: "German VAT",
included_in_price: true,
amount: 0.19,
- tax_category: normal_category,
+ tax_categories: [normal_category],
zone: eu_zone
)
end
@@ -75,7 +75,7 @@
name: "German VAT",
included_in_price: true,
amount: 0.19,
- tax_category: digital_category,
+ tax_categories: [digital_category],
zone: germany_zone
)
end
@@ -85,7 +85,7 @@
name: "Romanian VAT",
included_in_price: true,
amount: 0.24,
- tax_category: digital_category,
+ tax_categories: [digital_category],
zone: romania_zone
)
end
diff --git a/core/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb b/core/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb
index 268005e9c97..9f36eb1bb5e 100644
--- a/core/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb
+++ b/core/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb
@@ -79,7 +79,7 @@
context 'when shipments should be taxed' do
let!(:ship_address) { create(:address) }
let!(:tax_zone) { create(:global_zone) } # will include the above address
- let!(:tax_rate) { create(:tax_rate, amount: 0.10, zone: tax_zone, tax_category: tax_category) }
+ let!(:tax_rate) { create(:tax_rate, amount: 0.10, zone: tax_zone, tax_categories: [tax_category]) }
let(:tax_category) { create(:tax_category) }
let(:shipping_method) { create(:shipping_method, tax_category: tax_category, zones: [tax_zone]) }
diff --git a/core/spec/models/spree/calculator/default_tax_spec.rb b/core/spec/models/spree/calculator/default_tax_spec.rb
index b06eee500b2..2aa8f46f2a1 100644
--- a/core/spec/models/spree/calculator/default_tax_spec.rb
+++ b/core/spec/models/spree/calculator/default_tax_spec.rb
@@ -6,7 +6,7 @@
let!(:zone) { create(:zone, name: "Country Zone", default_tax: default_tax, countries: [tax_rate_country]) }
let(:tax_rate_country) { address.country }
let(:tax_category) { create(:tax_category) }
- let!(:rate) { create(:tax_rate, tax_category: tax_category, amount: 0.05, included_in_price: included_in_price, zone: zone) }
+ let!(:rate) { create(:tax_rate, tax_categories: [tax_category], amount: 0.05, included_in_price: included_in_price, zone: zone) }
let(:included_in_price) { false }
let(:default_tax) { false }
subject(:calculator) { Spree::Calculator::DefaultTax.new(calculable: rate ) }
diff --git a/core/spec/models/spree/order/checkout_spec.rb b/core/spec/models/spree/order/checkout_spec.rb
index 3966c566d62..7567d9d244e 100644
--- a/core/spec/models/spree/order/checkout_spec.rb
+++ b/core/spec/models/spree/order/checkout_spec.rb
@@ -184,7 +184,7 @@ def assert_state_changed(order, from, to)
it "recalculates tax and updates totals" do
zone = create(:zone, countries: [order.tax_address.country])
- create(:tax_rate, tax_category: line_item.tax_category, amount: 0.05, zone: zone)
+ create(:tax_rate, tax_categories: [line_item.tax_category], amount: 0.05, zone: zone)
order.next!
expect(order).to have_attributes(
adjustment_total: 0.5,
diff --git a/core/spec/models/spree/order_capturing_spec.rb b/core/spec/models/spree/order_capturing_spec.rb
index 16d31ff3e63..efae69eda34 100644
--- a/core/spec/models/spree/order_capturing_spec.rb
+++ b/core/spec/models/spree/order_capturing_spec.rb
@@ -38,7 +38,7 @@
let!(:product) { create(:product, price: 10.00) }
let!(:variant) do
- create(:variant, price: 10, product: product, track_inventory: false, tax_category: tax_rate.tax_category)
+ create(:variant, price: 10, product: product, track_inventory: false, tax_category: tax_rate.tax_categories.first)
end
let!(:shipping_method) { create(:free_shipping_method) }
let(:tax_rate) { create(:tax_rate, amount: 0.1, zone: create(:global_zone, name: "Some Tax Zone")) }
diff --git a/core/spec/models/spree/order_contents_spec.rb b/core/spec/models/spree/order_contents_spec.rb
index afde24603c6..0b26ea22c63 100644
--- a/core/spec/models/spree/order_contents_spec.rb
+++ b/core/spec/models/spree/order_contents_spec.rb
@@ -123,7 +123,7 @@
describe 'tax calculations' do
let!(:zone) { create(:global_zone) }
let!(:tax_rate) do
- create(:tax_rate, zone: zone, tax_category: variant.tax_category)
+ create(:tax_rate, zone: zone, tax_categories: [variant.tax_category])
end
context 'when the order has a taxable address' do
diff --git a/core/spec/models/spree/order_updater_spec.rb b/core/spec/models/spree/order_updater_spec.rb
index eabb0f452ac..350955ff555 100644
--- a/core/spec/models/spree/order_updater_spec.rb
+++ b/core/spec/models/spree/order_updater_spec.rb
@@ -273,7 +273,7 @@ def create_adjustment(label, amount)
describe 'tax recalculation' do
let!(:ship_address) { create(:address) }
let!(:tax_zone) { create(:global_zone) } # will include the above address
- let!(:tax_rate) { create(:tax_rate, zone: tax_zone, tax_category: tax_category) }
+ let!(:tax_rate) { create(:tax_rate, zone: tax_zone, tax_categories: [tax_category]) }
let(:order) do
create(
diff --git a/core/spec/models/spree/price_spec.rb b/core/spec/models/spree/price_spec.rb
index 4043ac9b152..766658742ba 100644
--- a/core/spec/models/spree/price_spec.rb
+++ b/core/spec/models/spree/price_spec.rb
@@ -133,7 +133,7 @@
describe 'net_amount' do
let(:country) { create(:country, iso: "DE") }
let(:zone) { create(:zone, countries: [country]) }
- let!(:tax_rate) { create(:tax_rate, included_in_price: true, zone: zone, tax_category: variant.tax_category) }
+ let!(:tax_rate) { create(:tax_rate, included_in_price: true, zone: zone, tax_categories: [variant.tax_category]) }
let(:variant) { create(:product).master }
diff --git a/core/spec/models/spree/promotion_handler/coupon_spec.rb b/core/spec/models/spree/promotion_handler/coupon_spec.rb
index b861bec4313..72084de70e4 100644
--- a/core/spec/models/spree/promotion_handler/coupon_spec.rb
+++ b/core/spec/models/spree/promotion_handler/coupon_spec.rb
@@ -255,7 +255,7 @@ def expect_adjustment_creation(adjustable:, promotion:, promotion_code:nil)
let(:order) { create(:order, store: store) }
let(:tax_category) { create(:tax_category, name: "Taxable Foo") }
let(:zone) { create(:zone, :with_country) }
- let!(:tax_rate) { create(:tax_rate, amount: 0.1, tax_category: tax_category, zone: zone )}
+ let!(:tax_rate) { create(:tax_rate, amount: 0.1, tax_categories: [tax_category], zone: zone ) }
before(:each) do
expect(order).to receive(:tax_address).at_least(:once).and_return(Spree::Tax::TaxLocation.new(country: zone.countries.first))
diff --git a/core/spec/models/spree/shipment_spec.rb b/core/spec/models/spree/shipment_spec.rb
index 2e9f2129a6d..151027489b4 100644
--- a/core/spec/models/spree/shipment_spec.rb
+++ b/core/spec/models/spree/shipment_spec.rb
@@ -130,7 +130,7 @@
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_rate) { create(:tax_rate, amount: 0.1, zone: tax_zone, tax_categories: [tax_category]) }
let(:tax_category) { create(:tax_category) }
let(:variant) { create(:variant, tax_category: tax_category) }
@@ -548,7 +548,7 @@
context "changes shipping rate via general update" do
let!(:ship_address) { create(:address) }
let!(:tax_zone) { create(:global_zone) } # will include the above address
- let!(:tax_rate) { create(:tax_rate, amount: 0.10, zone: tax_zone, tax_category: tax_category) }
+ let!(:tax_rate) { create(:tax_rate, amount: 0.10, zone: tax_zone, tax_categories: [tax_category]) }
let(:tax_category) { create(:tax_category) }
let(:order) do
diff --git a/core/spec/models/spree/shipping_rate_spec.rb b/core/spec/models/spree/shipping_rate_spec.rb
index 16c18d26420..d7822eb660f 100644
--- a/core/spec/models/spree/shipping_rate_spec.rb
+++ b/core/spec/models/spree/shipping_rate_spec.rb
@@ -33,7 +33,7 @@
included_in_price: true,
name: "VAT",
zone: default_zone,
- tax_category: tax_category
+ tax_categories: [tax_category]
end
let(:order_address) { address }
@@ -64,7 +64,7 @@
included_in_price: true,
name: "VAT",
zone: default_zone,
- tax_category: tax_category
+ tax_categories: [tax_category]
end
let(:order_address) { foreign_address }
@@ -96,7 +96,7 @@
included_in_price: false,
name: "Sales Tax",
zone: default_zone,
- tax_category: tax_category
+ tax_categories: [tax_category]
end
let(:order_address) { address }
@@ -126,7 +126,7 @@
included_in_price: false,
name: "Sales Tax",
zone: default_zone,
- tax_category: tax_category
+ tax_categories: [tax_category]
end
let!(:other_tax_rate) do
@@ -134,7 +134,7 @@
included_in_price: false,
name: "Other Sales Tax",
zone: default_zone,
- tax_category: tax_category,
+ tax_categories: [tax_category],
amount: 0.05
end
diff --git a/core/spec/models/spree/stock/estimator_spec.rb b/core/spec/models/spree/stock/estimator_spec.rb
index 675b12c5941..5c5d8743f59 100644
--- a/core/spec/models/spree/stock/estimator_spec.rb
+++ b/core/spec/models/spree/stock/estimator_spec.rb
@@ -145,7 +145,7 @@ module Stock
let!(:tax_rate) { create(:tax_rate, zone: zone) }
before do
- shipping_method.update!(tax_category: tax_rate.tax_category)
+ shipping_method.update!(tax_category: tax_rate.tax_categories.first)
end
it "links the shipping rate and the tax rate" do
diff --git a/core/spec/models/spree/tax/item_adjuster_spec.rb b/core/spec/models/spree/tax/item_adjuster_spec.rb
index 6429d5c5086..6b3d7dc875b 100644
--- a/core/spec/models/spree/tax/item_adjuster_spec.rb
+++ b/core/spec/models/spree/tax/item_adjuster_spec.rb
@@ -67,13 +67,19 @@ def tax_adjustments
context 'and all rates have the same tax category as the item' do
let(:item) { create :line_item, order: order, tax_category: item_tax_category }
let(:item_tax_category) { create(:tax_category) }
- let(:rate_1) { create :tax_rate, tax_category: item_tax_category, amount: 0.1 }
+ let(:rate_1) { create :tax_rate, tax_categories: [item_tax_category], amount: 0.1 }
let(:rate_2) { create :tax_rate }
- let(:rates_for_order_zone) { [rate_1, rate_2] }
+ let(:rate_3) { create :tax_rate, tax_categories: [item_tax_category, build(:tax_category)] }
+ let(:rates_for_order_zone) { [rate_1, rate_2, rate_3] }
it 'creates an adjustment for every matching rate' do
adjuster.adjust!
- expect(tax_adjustments.length).to eq(1)
+ expect(tax_adjustments.length).to eq(2)
+ end
+
+ it 'creates adjustments only for matching rates' do
+ adjuster.adjust!
+ expect(tax_adjustments.map(&:source)).to match_array([rate_1, rate_3])
end
context 'when the adjustment exists' do
@@ -89,8 +95,10 @@ def tax_adjustments
it 'updates the adjustment' do
item.update_columns(price: item.price * 2)
adjuster.adjust!
- expect(tax_adjustments.length).to eq(1)
- expect(tax_adjustments.first.amount).to eq(0.1 * item.price)
+ tax_rate1_adjustment = tax_adjustments.detect do |adjustment|
+ adjustment.source == rate_1
+ end
+ expect(tax_rate1_adjustment.amount).to eq(0.1 * item.price)
end
end
end
diff --git a/core/spec/models/spree/tax/shipping_rate_taxer_spec.rb b/core/spec/models/spree/tax/shipping_rate_taxer_spec.rb
index 9ebdd437c64..3f10879b6f4 100644
--- a/core/spec/models/spree/tax/shipping_rate_taxer_spec.rb
+++ b/core/spec/models/spree/tax/shipping_rate_taxer_spec.rb
@@ -22,12 +22,19 @@
let(:shipment) { create :shipment, order: order }
let!(:shipping_method) { create :shipping_method, tax_category: tax_category, zones: [zone] }
let(:zone) { create :zone, countries: [ship_address.country] }
- let!(:tax_rate_one) { create :tax_rate, tax_category: tax_category, zone: zone, amount: 0.1 }
- let!(:tax_rate_two) { create :tax_rate, tax_category: tax_category, zone: zone, amount: 0.2 }
+ let!(:tax_rate_one) { create :tax_rate, tax_categories: [tax_category], zone: zone, amount: 0.1 }
+ let!(:tax_rate_two) do
+ create(
+ :tax_rate,
+ tax_categories: [create(:tax_category), tax_category],
+ zone: zone,
+ amount: 0.2
+ )
+ end
let!(:non_applicable_rate) { create :tax_rate, zone: zone }
let(:shipping_rate) { create :shipping_rate, cost: 10, shipping_method: shipping_method }
- it 'builds a shipping rate tax for every tax rate' do
+ it 'builds a shipping rate tax for every matching tax rate' do
expect(subject.taxes.length).to eq(2)
expect(subject.taxes.map(&:tax_rate)).to include(tax_rate_one)
expect(subject.taxes.map(&:tax_rate)).to include(tax_rate_two)
diff --git a/core/spec/models/spree/tax/taxation_integration_spec.rb b/core/spec/models/spree/tax/taxation_integration_spec.rb
index 48f802ec7a8..1b2788fdf91 100644
--- a/core/spec/models/spree/tax/taxation_integration_spec.rb
+++ b/core/spec/models/spree/tax/taxation_integration_spec.rb
@@ -23,14 +23,24 @@
tax_category: normal_category,
shipping_category: normal_shipping_category
end
+ let(:fruit_product) do
+ create :product,
+ price: 5,
+ name: "Food",
+ tax_category: fruit_category,
+ shipping_category: normal_shipping_category
+ end
let(:book) { book_product.master }
let(:download) { download_product.master }
let(:sweater) { sweater_product.master }
+ let(:fruit) { fruit_product.master }
let(:books_category) { create :tax_category, name: "Books" }
let(:normal_category) { create :tax_category, name: "Normal" }
let(:digital_category) { create :tax_category, name: "Digital Goods" }
+ let(:fruit_category) { create :tax_category, name: "Fruit Product" }
+ let(:milk_category) { create :tax_category, name: "Milk Product" }
let(:books_shipping_category) { create :shipping_category, name: "Book Shipping" }
let(:normal_shipping_category) { create :shipping_category, name: "Normal Shipping" }
@@ -55,7 +65,7 @@
name: "German reduced VAT",
included_in_price: true,
amount: 0.07,
- tax_category: books_category,
+ tax_categories: [books_category],
zone: eu_zone
)
end
@@ -65,7 +75,7 @@
name: "German VAT",
included_in_price: true,
amount: 0.19,
- tax_category: normal_category,
+ tax_categories: [normal_category],
zone: eu_zone
)
end
@@ -75,7 +85,17 @@
name: "German VAT",
included_in_price: true,
amount: 0.19,
- tax_category: digital_category,
+ tax_categories: [digital_category],
+ zone: germany_zone
+ )
+ end
+ let!(:german_food_vat) do
+ create(
+ :tax_rate,
+ name: "German Food VAT",
+ included_in_price: true,
+ amount: 0.09,
+ tax_categories: [fruit_category, milk_category],
zone: germany_zone
)
end
@@ -85,7 +105,7 @@
name: "Romanian VAT",
included_in_price: true,
amount: 0.24,
- tax_category: digital_category,
+ tax_categories: [digital_category],
zone: romania_zone
)
end
@@ -222,6 +242,22 @@
expect(shipping_rate.display_price).to eq("$2.00 (incl. $0.32 German VAT)")
end
end
+
+ context 'an order containg a fruit' do
+ let(:variant) { fruit }
+
+ it 'still has the original price' do
+ expect(line_item.price).to eq(5)
+ end
+
+ it 'has one tax adjustment' do
+ expect(line_item.adjustments.tax.count).to eq(1)
+ end
+
+ it 'has 0.45 of included tax' do
+ expect(line_item.included_tax_total).to eq(0.41)
+ end
+ end
end
context 'to romania' do
@@ -345,7 +381,6 @@
end
end
end
-
# Technically, this can't be the case yet as the order won't pass the shipment stage,
# but the taxation code shouldn't implicitly depend on the shipping code.
context 'to an address that does not have a zone associated' do
@@ -528,7 +563,7 @@
create(
:tax_rate,
name: "New York Sales Tax",
- tax_category: books_category,
+ tax_categories: [books_category],
zone: new_york_zone,
included_in_price: false,
amount: 0.05
@@ -539,7 +574,7 @@
create(
:tax_rate,
name: "Federal Sales Tax",
- tax_category: books_category,
+ tax_categories: [books_category],
zone: united_states_zone,
included_in_price: false,
amount: 0.10
@@ -550,7 +585,7 @@
create(
:tax_rate,
name: "Federal Sales Tax",
- tax_category: digital_category,
+ tax_categories: [digital_category],
zone: united_states_zone,
included_in_price: false,
amount: 0.20
diff --git a/core/spec/models/spree/variant/vat_price_generator_spec.rb b/core/spec/models/spree/variant/vat_price_generator_spec.rb
index 7279a72fbc9..5c9a54cbbe8 100644
--- a/core/spec/models/spree/variant/vat_price_generator_spec.rb
+++ b/core/spec/models/spree/variant/vat_price_generator_spec.rb
@@ -10,10 +10,10 @@
context "with Germany as default admin country" do
let(:germany) { create(:country, iso: "DE") }
let(:germany_zone) { create(:zone, countries: [germany]) }
- let!(:german_vat) { create(:tax_rate, included_in_price: true, amount: 0.19, zone: germany_zone, tax_category: tax_category) }
+ let!(:german_vat) { create(:tax_rate, included_in_price: true, amount: 0.19, zone: germany_zone, tax_categories: [tax_category]) }
let(:france) { create(:country, iso: "FR") }
let(:france_zone) { create(:zone, countries: [france]) }
- let!(:french_vat) { create(:tax_rate, included_in_price: true, amount: 0.20, zone: france_zone, tax_category: tax_category) }
+ let!(:french_vat) { create(:tax_rate, included_in_price: true, amount: 0.20, zone: france_zone, tax_categories: [tax_category]) }
before do
Spree::Config.admin_vat_country_iso = "DE"
@@ -45,10 +45,10 @@
context "with no default admin country" do
let(:germany) { create(:country, iso: "DE") }
let(:germany_zone) { create(:zone, countries: [germany]) }
- let!(:german_vat) { create(:tax_rate, included_in_price: true, amount: 0.19, zone: germany_zone, tax_category: tax_category) }
+ let!(:german_vat) { create(:tax_rate, included_in_price: true, amount: 0.19, zone: germany_zone, tax_categories: [tax_category]) }
let(:france) { create(:country, iso: "FR") }
let(:france_zone) { create(:zone, countries: [france]) }
- let!(:french_vat) { create(:tax_rate, included_in_price: true, amount: 0.20, zone: france_zone, tax_category: tax_category) }
+ let!(:french_vat) { create(:tax_rate, included_in_price: true, amount: 0.20, zone: france_zone, tax_categories: [tax_category]) }
it "builds a correct price including VAT for all VAT countries" do
subject
diff --git a/core/spec/models/spree/variant_spec.rb b/core/spec/models/spree/variant_spec.rb
index aa7e39a9cdf..28654b0c02c 100644
--- a/core/spec/models/spree/variant_spec.rb
+++ b/core/spec/models/spree/variant_spec.rb
@@ -63,8 +63,8 @@
let(:tax_category) { create(:tax_category) }
- let!(:high_vat) { create(:tax_rate, included_in_price: true, amount: 0.25, zone: high_vat_zone, tax_category: tax_category) }
- let!(:low_vat) { create(:tax_rate, included_in_price: true, amount: 0.15, zone: low_vat_zone, tax_category: tax_category) }
+ let!(:high_vat) { create(:tax_rate, included_in_price: true, amount: 0.25, zone: high_vat_zone, tax_categories: [tax_category]) }
+ let!(:low_vat) { create(:tax_rate, included_in_price: true, amount: 0.15, zone: low_vat_zone, tax_categories: [tax_category]) }
let(:product) { build(:product, tax_category: tax_category) }
diff --git a/sample/db/samples/tax_rates.rb b/sample/db/samples/tax_rates.rb
index 0f320c87814..fd9c2df96fe 100644
--- a/sample/db/samples/tax_rates.rb
+++ b/sample/db/samples/tax_rates.rb
@@ -3,7 +3,11 @@
tax_rate = Spree::TaxRate.create(
name: "North America",
zone: north_america,
- amount: 0.05,
- tax_category: clothing)
+ amount: 0.05
+)
tax_rate.calculator = Spree::Calculator::DefaultTax.create!
tax_rate.save!
+Spree::TaxRateTaxCategory.create!(
+ tax_rate: tax_rate,
+ tax_category: clothing
+)