Remove tax rate guessing from TaxRateFinder

🎉
This commit is contained in:
Matt-Yorkley
2021-06-16 19:37:33 +01:00
parent f2e63fff2e
commit 0876f2ae0d
2 changed files with 12 additions and 76 deletions

View File

@@ -8,16 +8,13 @@ class TaxRateFinder
def self.tax_rates_of(adjustment)
new.tax_rates(
adjustment.originator,
adjustment.adjustable,
adjustment.amount,
adjustment.included_tax
adjustment.adjustable
)
end
# @return [Array<Spree::TaxRate>]
def tax_rates(originator, adjustable, amount, included_tax)
find_associated_tax_rate(originator, adjustable) ||
find_closest_tax_rates_from_included_tax(amount, included_tax)
def tax_rates(originator, adjustable)
find_associated_tax_rate(originator, adjustable)
end
private
@@ -48,37 +45,4 @@ class TaxRateFinder
enterprise_fee.tax_category
end
end
# There are two cases in which a line item is not associated to a tax rate.
#
# 1. Shipping fees and adjustments created from the admin panel have taxes set
# at creation in the included_tax field without relation to the
# corresponding TaxRate.
# 2. Removing line items from an order doesn't always remove the associated
# enterprise fees. These orphaned fees don't have a line item any more to
# find the item's tax rate.
#
# In these cases we try to find the used tax rate based on the included tax.
# For example, if the included tax is 10% of the adjustment, we look for a tax
# rate of 10%. Due to rounding errors, the included tax may be 9.9% of the
# adjustment. That's why we call it an approximation of the tax rate and look
# for the closest and hopefully find the 10% tax rate.
#
# This attempt can fail.
#
# - If an admin created an adjustment with a miscalculated included tax then
# we don't know which tax rate the admin intended to use.
# - An admin may also enter included tax that doesn't correspond to any tax
# rate in the system. They may enter a fee of $1.2 with tax of $0.2, but
# that doesn't mean that there is a 20% tax rate in the database.
# - The used tax rate may also have been deleted. Maybe the tax law changed.
#
# In either of these cases, we will find a tax rate that doesn't correspond
# to the included tax.
def find_closest_tax_rates_from_included_tax(amount, included_tax)
approximation = (included_tax / (amount - included_tax))
return [] if approximation.infinite? || approximation.zero? || approximation.nan?
[Spree::TaxRate.order(Arel.sql("ABS(amount - #{approximation})")).first]
end
end

View File

@@ -5,61 +5,33 @@ require 'spec_helper'
describe TaxRateFinder do
describe "getting the corresponding tax rate" do
let(:amount) { BigDecimal(120) }
let(:included_tax) { BigDecimal(20) }
let(:tax_rate) { create_rate(0.2) }
let(:tax_category) { create(:tax_category, tax_rates: [tax_rate]) }
let(:zone) { create(:zone_with_member) }
let(:shipment) { create(:shipment) }
let(:line_item) { create(:line_item) }
let(:enterprise_fee) { create(:enterprise_fee, tax_category: tax_category) }
let(:order) { create(:order_with_taxes, zone: zone) }
it "finds the tax rate of a shipping fee" do
rates = TaxRateFinder.new.tax_rates(
tax_rate,
shipment,
amount,
included_tax
)
rates = TaxRateFinder.new.tax_rates(tax_rate, shipment)
expect(rates).to eq [tax_rate]
end
it "finds a close match" do
it "deals with soft-deleted tax rates" do
tax_rate.destroy
close_tax_rate = create_rate(tax_rate.amount + 0.05)
# other tax rates, not as close to the real one
create_rate(tax_rate.amount + 0.06)
create_rate(tax_rate.amount - 0.06)
rates = TaxRateFinder.new.tax_rates(
nil,
shipment,
amount,
included_tax
)
expect(rates).to eq [close_tax_rate]
rates = TaxRateFinder.new.tax_rates(tax_rate, shipment)
expect(rates).to eq [tax_rate]
end
it "finds the tax rate of an enterprise fee" do
rates = TaxRateFinder.new.tax_rates(
enterprise_fee,
order,
amount,
included_tax
)
rates = TaxRateFinder.new.tax_rates(enterprise_fee, order)
expect(rates).to eq [tax_rate]
end
# There is a bug that leaves orphan adjustments on an order after
# associated line items have been removed.
# https://github.com/openfoodfoundation/openfoodnetwork/issues/3127
it "deals with a missing line item" do
rates = TaxRateFinder.new.tax_rates(
enterprise_fee,
nil,
amount,
included_tax
)
it "deals with a soft-deleted line item" do
line_item.destroy
rates = TaxRateFinder.new.tax_rates(enterprise_fee, line_item)
expect(rates).to eq [tax_rate]
end