1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
module SuperGood
module SolidusTaxJar
class TaxCalculator
def self.default_api
::SuperGood::SolidusTaxJar::API.new
end
def initialize(order, api: self.class.default_api)
@order = order
@api = api
end
def calculate
return no_tax if SuperGood::SolidusTaxJar.test_mode
return no_tax if order.tax_address.empty? || order.line_items.none?
cache do
next no_tax unless taxjar_breakdown
Spree::Tax::OrderTax.new(
order_id: order.id,
line_item_taxes: line_item_taxes,
shipment_taxes: shipment_taxes
)
end
rescue StandardError => e
exception_handler.(e)
no_tax
end
private
attr_reader :order, :api
def line_item_taxes
@line_item_taxes ||=
taxjar_breakdown.line_items.map do |line_item|
Spree::Tax::ItemTax.new(
item_id: line_item.id.to_i,
label: "Sales Tax",
tax_rate: tax_rate,
amount: line_item.tax_collectable,
included_in_price: false
)
end
end
def shipment_taxes
@shipment_taxes ||=
if taxjar_breakdown.shipping? &&
(total_shipping_tax = taxjar_breakdown.shipping.tax_collectable) != 0
# Distribute shipping tax across shipments:
# TaxJar does not provide a breakdown of shipping taxes, so we have
# to proportionally distribute the tax across the shipments,
# accounting for rounding errors.
tax_items = []
remaining_tax = total_shipping_tax
shipments = order.shipments.to_a
total_shipping_cost = shipments.sum(&:total_before_tax)
shipments[0...-1].each do |shipment|
percentage_of_tax = shipment.total_before_tax / total_shipping_cost
shipping_tax = (percentage_of_tax * total_shipping_tax).round(2)
remaining_tax -= shipping_tax
tax_items << ::Spree::Tax::ItemTax.new(
item_id: shipment.id,
label: "Sales Tax",
tax_rate: tax_rate,
amount: shipping_tax,
included_in_price: false
)
end
tax_items << ::Spree::Tax::ItemTax.new(
item_id: shipments.last.id,
label: "Sales Tax",
tax_rate: tax_rate,
amount: remaining_tax,
included_in_price: false
)
tax_items
else
[]
end
end
def taxjar_breakdown
@taxjar_breakdown ||= taxjar_tax.breakdown
end
def taxjar_tax
@taxjar_taxes ||= api.tax_for(order)
end
def no_tax
Spree::Tax::OrderTax.new(
order_id: order.id,
line_item_taxes: [],
shipment_taxes: []
)
end
def tax_rate
Spree::TaxRate.find_by(name: "Sales Tax")
end
def cache
if !Rails.env.test?
Rails.cache.fetch(cache_key, expires_in: 10.minutes) { yield }
else
yield
end
end
def cache_key
APIParams.order_params(order).transform_values do |value|
case value
when Array, Hash then value.hash
else
value
end
end
end
def exception_handler
SuperGood::SolidusTaxJar.exception_handler
end
end
end
end
|