mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-17 00:07:24 +00:00
419 lines
15 KiB
Ruby
419 lines
15 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
module Spree
|
|
describe TaxRate do
|
|
describe "#match" do
|
|
let!(:zone) { create(:zone_with_member) }
|
|
let!(:order) { create(:order, distributor: hub, bill_address: create(:address)) }
|
|
let!(:tax_rate) {
|
|
create(:tax_rate, included_in_price: true,
|
|
calculator: ::Calculator::FlatRate.new(preferred_amount: 0.1), zone: zone)
|
|
}
|
|
|
|
describe "when the order's hub charges sales tax" do
|
|
let(:hub) { create(:distributor_enterprise, charges_sales_tax: true) }
|
|
|
|
it "selects all tax rates" do
|
|
expect(TaxRate.match(order)).to eq([tax_rate])
|
|
end
|
|
end
|
|
|
|
describe "when the order's hub does not charge sales tax" do
|
|
let(:hub) { create(:distributor_enterprise, charges_sales_tax: false) }
|
|
|
|
it "selects no tax rates" do
|
|
expect(TaxRate.match(order)).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "when the order does not have a hub" do
|
|
let!(:order) { create(:order, distributor: nil, bill_address: create(:address)) }
|
|
|
|
it "selects all tax rates" do
|
|
expect(TaxRate.match(order)).to eq([tax_rate])
|
|
end
|
|
end
|
|
end
|
|
|
|
context "original Spree::TaxRate specs" do
|
|
context "match" do
|
|
let(:order) { create(:order) }
|
|
let(:country) { create(:country) }
|
|
let(:tax_category) { create(:tax_category) }
|
|
let(:calculator) { ::Calculator::FlatRate.new }
|
|
|
|
it "should return an empty array when tax_zone is nil" do
|
|
allow(order).to receive(:tax_zone) { nil }
|
|
expect(Spree::TaxRate.match(order)).to eq []
|
|
end
|
|
|
|
context "when no rate zones match the tax zone" do
|
|
before do
|
|
Spree::TaxRate.create(amount: 1, zone: create(:zone))
|
|
end
|
|
|
|
context "when there is no default tax zone" do
|
|
before do
|
|
@zone = create(:zone, name: "Country Zone", default_tax: false, zone_members: [])
|
|
@zone.zone_members.create(zoneable: country)
|
|
end
|
|
|
|
it "should return an empty array" do
|
|
order.stub tax_zone: @zone
|
|
expect(Spree::TaxRate.match(order)).to eq []
|
|
end
|
|
|
|
it "should return the rate that matches the rate zone" do
|
|
rate = Spree::TaxRate.create(
|
|
amount: 1,
|
|
zone: @zone,
|
|
tax_category: tax_category,
|
|
calculator: calculator
|
|
)
|
|
|
|
order.stub tax_zone: @zone
|
|
expect(Spree::TaxRate.match(order)).to eq [rate]
|
|
end
|
|
|
|
it "should return all rates that match the rate zone" do
|
|
rate1 = Spree::TaxRate.create(
|
|
amount: 1,
|
|
zone: @zone,
|
|
tax_category: tax_category,
|
|
calculator: calculator
|
|
)
|
|
|
|
rate2 = Spree::TaxRate.create(
|
|
amount: 2,
|
|
zone: @zone,
|
|
tax_category: tax_category,
|
|
calculator: ::Calculator::FlatRate.new
|
|
)
|
|
|
|
order.stub tax_zone: @zone
|
|
expect(Spree::TaxRate.match(order)).to eq [rate1, rate2]
|
|
end
|
|
|
|
context "when the tax_zone is contained within a rate zone" do
|
|
before do
|
|
sub_zone = create(:zone, name: "State Zone", zone_members: [])
|
|
sub_zone.zone_members.create(zoneable: create(:state, country: country))
|
|
order.stub tax_zone: sub_zone
|
|
@rate = Spree::TaxRate.create(
|
|
amount: 1,
|
|
zone: @zone,
|
|
tax_category: tax_category,
|
|
calculator: calculator
|
|
)
|
|
end
|
|
|
|
it "should return the rate zone" do
|
|
expect(Spree::TaxRate.match(order)).to eq [@rate]
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when there is a default tax zone" do
|
|
before do
|
|
@zone = create(:zone, name: "Country Zone", default_tax: true, zone_members: [])
|
|
@zone.zone_members.create(zoneable: country)
|
|
end
|
|
|
|
let(:included_in_price) { false }
|
|
let!(:rate) do
|
|
Spree::TaxRate.create(amount: 1,
|
|
zone: @zone,
|
|
tax_category: tax_category,
|
|
calculator: calculator,
|
|
included_in_price: included_in_price)
|
|
end
|
|
|
|
subject { Spree::TaxRate.match(order) }
|
|
|
|
context "when the order has the same tax zone" do
|
|
before do
|
|
order.stub tax_zone: @zone
|
|
order.stub billing_address: tax_address
|
|
end
|
|
|
|
let(:tax_address) { build_stubbed(:address) }
|
|
|
|
context "when the tax is not a VAT" do
|
|
it { is_expected.to eq [rate] }
|
|
end
|
|
|
|
context "when the tax is a VAT" do
|
|
let(:included_in_price) { true }
|
|
it { is_expected.to eq [rate] }
|
|
end
|
|
end
|
|
|
|
context "when the order has a different tax zone" do
|
|
let(:other_zone) { create(:zone, name: "Other Zone") }
|
|
|
|
before do
|
|
allow(order).to receive(:tax_zone) { other_zone }
|
|
allow(order).to receive(:billing_address) { tax_address }
|
|
end
|
|
|
|
context "when the order has a tax_address" do
|
|
let(:tax_address) { build_stubbed(:address) }
|
|
|
|
context "when the tax is a VAT" do
|
|
let(:included_in_price) { true }
|
|
# The rate should match in this instance because:
|
|
# 1) It's the default rate (and as such, a negative adjustment should apply)
|
|
it { is_expected.to eq [rate] }
|
|
end
|
|
|
|
context "when the tax is not VAT" do
|
|
it "returns no tax rate" do
|
|
expect(subject).to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when the order does not have a tax_address" do
|
|
let(:tax_address) { nil }
|
|
|
|
context "when the tax is a VAT" do
|
|
let(:included_in_price) { true }
|
|
# The rate should match in this instance because:
|
|
# 1) The order has no tax address by this stage
|
|
# 2) With no tax address, it has no tax zone
|
|
# 3) Therefore, we assume the default tax zone
|
|
# 4) This default zone has a default tax rate.
|
|
it { is_expected.to eq [rate] }
|
|
end
|
|
|
|
context "when the tax is not a VAT" do
|
|
it { is_expected.to be_empty }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "adjust" do
|
|
let(:order) { create(:order) }
|
|
let(:tax_category_1) { build_stubbed(:tax_category) }
|
|
let(:tax_category_2) { build_stubbed(:tax_category) }
|
|
let(:rate_1) { build_stubbed(:tax_rate, tax_category: tax_category_1) }
|
|
let(:rate_2) { build_stubbed(:tax_rate, tax_category: tax_category_2) }
|
|
let(:line_items) { [build_stubbed(:line_item)] }
|
|
|
|
context "with line items" do
|
|
let(:line_item) { build_stubbed(:line_item, tax_category: tax_category_1) }
|
|
let(:line_items) { [line_item] }
|
|
|
|
before do
|
|
allow(Spree::TaxRate).to receive(:match) { [rate_1, rate_2] }
|
|
end
|
|
|
|
it "should apply adjustments for two tax rates to the order" do
|
|
expect(rate_1).to receive(:adjust)
|
|
expect(rate_2).to_not receive(:adjust)
|
|
Spree::TaxRate.adjust(order, line_items)
|
|
end
|
|
end
|
|
|
|
context "with shipments" do
|
|
let(:shipment) { build_stubbed(:shipment, order: order) }
|
|
let(:shipments) { [shipment] }
|
|
|
|
before do
|
|
allow(shipment).to receive(:tax_category) { tax_category_1 }
|
|
allow(Spree::TaxRate).to receive(:match) { [rate_1, rate_2] }
|
|
end
|
|
|
|
it "should apply adjustments for two tax rates to the order" do
|
|
expect(rate_1).to receive(:adjust)
|
|
expect(rate_2).to_not receive(:adjust)
|
|
Spree::TaxRate.adjust(order, shipments)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "default" do
|
|
let(:tax_category) { create(:tax_category) }
|
|
let(:country) { create(:country) }
|
|
let(:calculator) { ::Calculator::FlatRate.new }
|
|
|
|
context "when there is no default tax_category" do
|
|
before { tax_category.is_default = false }
|
|
|
|
it "should return 0" do
|
|
expect(Spree::TaxRate.default).to eq 0
|
|
end
|
|
end
|
|
|
|
context "when there is a default tax_category" do
|
|
before { tax_category.update_column :is_default, true }
|
|
|
|
context "when the default category has tax rates in the default tax zone" do
|
|
before(:each) do
|
|
allow(DefaultCountry).to receive(:id) { country.id }
|
|
@zone = create(:zone, name: "Country Zone", default_tax: true)
|
|
@zone.zone_members.create(zoneable: country)
|
|
rate = Spree::TaxRate.create(
|
|
amount: 1,
|
|
zone: @zone,
|
|
tax_category: tax_category,
|
|
calculator: calculator
|
|
)
|
|
end
|
|
|
|
it "should return the correct tax_rate" do
|
|
expect(Spree::TaxRate.default.to_f).to eq 1.0
|
|
end
|
|
end
|
|
|
|
context "when the default category has no tax rates in the default tax zone" do
|
|
it "should return 0" do
|
|
expect(Spree::TaxRate.default).to eq 0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#adjust" do
|
|
before do
|
|
@country = create(:country)
|
|
@zone = create(:zone, name: "Country Zone", default_tax: true, zone_members: [])
|
|
@zone.zone_members.create(zoneable: @country)
|
|
@category = Spree::TaxCategory.create(name: "Taxable Foo")
|
|
@category2 = Spree::TaxCategory.create(name: "Non Taxable")
|
|
@rate1 = Spree::TaxRate.create(
|
|
amount: 0.10,
|
|
calculator: ::Calculator::DefaultTax.new,
|
|
tax_category: @category,
|
|
zone: @zone
|
|
)
|
|
@rate2 = Spree::TaxRate.create(
|
|
amount: 0.05,
|
|
calculator: ::Calculator::DefaultTax.new,
|
|
tax_category: @category,
|
|
zone: @zone
|
|
)
|
|
@order = Spree::Order.create!
|
|
@taxable = create(:product, tax_category: @category)
|
|
@nontaxable = create(:product, tax_category: @category2)
|
|
end
|
|
|
|
context "not taxable line item " do
|
|
let!(:line_item) { @order.contents.add(@nontaxable.variants.first, 1) }
|
|
|
|
it "should not create a tax adjustment" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.tax.charge.count).to eq 0
|
|
end
|
|
|
|
it "should not create a refund" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.credit.count).to eq 0
|
|
end
|
|
end
|
|
|
|
context "taxable line item" do
|
|
let!(:line_item) { @order.contents.add(@taxable.variants.first, 1) }
|
|
|
|
before do
|
|
@rate1.update_column(:included_in_price, true)
|
|
@rate2.update_column(:included_in_price, true)
|
|
end
|
|
|
|
context "when price includes tax" do
|
|
context "when zone is contained by default tax zone" do
|
|
it "should create two adjustments, one for each tax rate" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.count).to eq 2
|
|
end
|
|
|
|
it "should not create a tax refund" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.credit.count).to eq 0
|
|
end
|
|
end
|
|
|
|
context "when order's zone is neither the default zone, or included in the default zone, but matches the rate's zone" do
|
|
before do
|
|
# With no zone members, this zone will not contain anything
|
|
@zone.zone_members.delete_all
|
|
end
|
|
|
|
it "should create an adjustment" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.charge.count).to eq 2
|
|
end
|
|
|
|
it "should not create a tax refund for each tax rate" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.credit.count).to eq 0
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when order's zone does not match default zone, is not included in the default zone, AND does not match the rate's zone" do
|
|
before do
|
|
@new_zone = create(:zone, name: "New Zone", default_tax: false)
|
|
@new_country = create(:country, name: "New Country")
|
|
@new_zone.zone_members.create(zoneable: @new_country)
|
|
@new_state = create(:state, country: @new_country)
|
|
@order.ship_address = create(:address, country: @new_country, state: @new_state)
|
|
@order.save
|
|
end
|
|
|
|
it "should not create positive adjustments" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.charge.count).to eq 0
|
|
end
|
|
|
|
it "should create a tax refund for each tax rate" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.credit.count).to eq 2
|
|
end
|
|
end
|
|
|
|
context "when price does not include tax" do
|
|
before do
|
|
allow(@order).to receive(:tax_zone) { @zone }
|
|
|
|
[@rate1, @rate2].each do |rate|
|
|
rate.included_in_price = false
|
|
rate.zone = @zone
|
|
rate.save
|
|
end
|
|
end
|
|
|
|
it "should not delete adjustments for complete order when taxrate is deleted" do
|
|
@order.update_column :completed_at, Time.now
|
|
@rate1.destroy!
|
|
@rate2.destroy!
|
|
expect(line_item.adjustments.count).to eq 2
|
|
end
|
|
|
|
it "should create adjustments" do
|
|
expect(line_item.adjustments.count).to eq 2
|
|
end
|
|
|
|
it "should not create a tax refund" do
|
|
expect(line_item.adjustments.credit.count).to eq 0
|
|
end
|
|
|
|
it "should remove adjustments when tax_zone is removed" do
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.count).to eq 2
|
|
allow(@order).to receive(:tax_zone) { nil }
|
|
Spree::TaxRate.adjust(@order, @order.line_items)
|
|
expect(line_item.adjustments.count).to eq 0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|