super_good-solidus_taxjar 0.17.1 → 0.18.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -13
  3. data/.travis.yml +11 -5
  4. data/CHANGELOG.md +30 -0
  5. data/Gemfile +21 -5
  6. data/LICENSE +26 -0
  7. data/PULL_REQUEST_TEMPLATE.md +19 -0
  8. data/README.md +20 -11
  9. data/Rakefile +4 -17
  10. data/bin/rails +7 -0
  11. data/bin/rails-engine +13 -0
  12. data/bin/rails-sandbox +16 -0
  13. data/bin/rake +7 -0
  14. data/bin/sandbox +84 -0
  15. data/lib/super_good-solidus_taxjar.rb +4 -0
  16. data/lib/super_good/engine.rb +8 -0
  17. data/lib/super_good/solidus_taxjar.rb +11 -6
  18. data/lib/super_good/solidus_taxjar/addresses.rb +63 -0
  19. data/lib/super_good/solidus_taxjar/api.rb +20 -11
  20. data/lib/super_good/solidus_taxjar/api_params.rb +19 -9
  21. data/lib/super_good/solidus_taxjar/calculator_helper.rb +4 -10
  22. data/lib/super_good/solidus_taxjar/discount_calculator.rb +1 -1
  23. data/lib/super_good/solidus_taxjar/tax_calculator.rb +12 -12
  24. data/lib/super_good/solidus_taxjar/tax_rate_calculator.rb +6 -7
  25. data/lib/super_good/solidus_taxjar/version.rb +2 -2
  26. data/spec/spec_helper.rb +20 -0
  27. data/spec/super_good/solidus_taxjar/addresses_spec.rb +288 -0
  28. data/spec/super_good/solidus_taxjar/api_params_spec.rb +402 -0
  29. data/spec/super_good/solidus_taxjar/api_spec.rb +205 -0
  30. data/spec/super_good/solidus_taxjar/discount_calculator_spec.rb +13 -0
  31. data/spec/super_good/solidus_taxjar/tax_calculator_spec.rb +332 -0
  32. data/spec/super_good/solidus_taxjar/tax_rate_calculator_spec.rb +116 -0
  33. data/spec/super_good/solidus_taxjar_spec.rb +77 -0
  34. data/super_good-solidus_taxjar.gemspec +17 -14
  35. metadata +35 -10
  36. data/LICENSE.txt +0 -21
@@ -0,0 +1,205 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe SuperGood::SolidusTaxjar::Api do
4
+ describe ".new" do
5
+ subject { described_class.new }
6
+
7
+ before do
8
+ ENV["TAXJAR_API_KEY"] = 'taxjar_api_token'
9
+ end
10
+
11
+ it "sets the correct headers" do
12
+ expect_any_instance_of(::Taxjar::Client).to receive(:set_api_config).with('headers', {
13
+ 'x-api-version' => '2020-08-07',
14
+ 'plugin' => 'supergoodsolidustaxjar'
15
+ })
16
+ subject
17
+ end
18
+ end
19
+
20
+ describe ".default_taxjar_client" do
21
+ subject { described_class.default_taxjar_client }
22
+
23
+ before do
24
+ ENV["TAXJAR_API_KEY"] = 'taxjar_api_token'
25
+ end
26
+
27
+ it "returns an instance of the TaxJar client" do
28
+ expect(subject).to be_an_instance_of(::Taxjar::Client)
29
+ end
30
+ end
31
+
32
+
33
+ describe "#tax_for" do
34
+ subject { api.tax_for order }
35
+
36
+ let(:api) { described_class.new(taxjar_client: dummy_client) }
37
+ let(:dummy_client) { instance_double ::Taxjar::Client }
38
+ let(:order) { Spree::Order.new }
39
+
40
+ before do
41
+ allow(SuperGood::SolidusTaxjar::ApiParams)
42
+ .to receive(:order_params)
43
+ .with(order)
44
+ .and_return({order: "params"})
45
+
46
+ allow(dummy_client)
47
+ .to receive(:tax_for_order)
48
+ .with({order: "params"})
49
+ .and_return({some_kind_of: "response"})
50
+ end
51
+
52
+ it { is_expected.to eq({some_kind_of: "response"}) }
53
+ end
54
+
55
+ describe "tax_rate_for" do
56
+ subject { api.tax_rate_for address }
57
+
58
+ let(:api) { described_class.new(taxjar_client: dummy_client) }
59
+ let(:dummy_client) { instance_double ::Taxjar::Client }
60
+ let(:address) { Spree::Address.new }
61
+ let(:tax_rate) { 0.04 }
62
+ let(:response) { double(rate: tax_rate) }
63
+
64
+ before do
65
+ allow(SuperGood::SolidusTaxjar::ApiParams)
66
+ .to receive(:tax_rate_address_params)
67
+ .with(address)
68
+ .and_return({address: "params"})
69
+
70
+ allow(dummy_client)
71
+ .to receive(:tax_for_order)
72
+ .with({address: "params"})
73
+ .and_return(response)
74
+ end
75
+
76
+ it { is_expected.to eq(tax_rate) }
77
+ end
78
+
79
+ describe "#tax_rates_for" do
80
+ subject { api.tax_rates_for address }
81
+
82
+ let(:api) { described_class.new(taxjar_client: dummy_client) }
83
+ let(:dummy_client) { instance_double ::Taxjar::Client }
84
+ let(:address) { Spree::Address.new }
85
+
86
+ before do
87
+ allow(SuperGood::SolidusTaxjar::ApiParams)
88
+ .to receive(:address_params)
89
+ .with(address)
90
+ .and_return(["zipcode", {address: "params"}])
91
+
92
+ allow(dummy_client)
93
+ .to receive(:rates_for_location)
94
+ .with("zipcode", {address: "params"})
95
+ .and_return({some_kind_of: "response"})
96
+ end
97
+
98
+ it { is_expected.to eq({some_kind_of: "response"}) }
99
+ end
100
+
101
+ describe "#create_transaction_for" do
102
+ subject { api.create_transaction_for order }
103
+
104
+ let(:api) { described_class.new(taxjar_client: dummy_client) }
105
+ let(:dummy_client) { instance_double ::Taxjar::Client }
106
+ let(:order) { Spree::Order.new }
107
+
108
+ before do
109
+ allow(SuperGood::SolidusTaxjar::ApiParams)
110
+ .to receive(:transaction_params)
111
+ .with(order)
112
+ .and_return({transaction: "params"})
113
+
114
+ allow(dummy_client)
115
+ .to receive(:create_order)
116
+ .with({transaction: "params"})
117
+ .and_return({some_kind_of: "response"})
118
+ end
119
+
120
+ it { is_expected.to eq({some_kind_of: "response"}) }
121
+ end
122
+
123
+ describe "#update_transaction_for" do
124
+ subject { api.update_transaction_for order }
125
+
126
+ let(:api) { described_class.new(taxjar_client: dummy_client) }
127
+ let(:dummy_client) { instance_double ::Taxjar::Client }
128
+ let(:order) { Spree::Order.new }
129
+
130
+ before do
131
+ allow(SuperGood::SolidusTaxjar::ApiParams)
132
+ .to receive(:transaction_params)
133
+ .with(order)
134
+ .and_return({transaction: "params"})
135
+
136
+ allow(dummy_client)
137
+ .to receive(:update_order)
138
+ .with({transaction: "params"})
139
+ .and_return({some_kind_of: "response"})
140
+ end
141
+
142
+ it { is_expected.to eq({some_kind_of: "response"}) }
143
+ end
144
+
145
+ describe "#update_transaction_for" do
146
+ subject { api.delete_transaction_for order }
147
+
148
+ let(:api) { described_class.new(taxjar_client: dummy_client) }
149
+ let(:dummy_client) { instance_double ::Taxjar::Client }
150
+ let(:order) { Spree::Order.new(number: "R111222333") }
151
+
152
+ before do
153
+ allow(dummy_client)
154
+ .to receive(:delete_order)
155
+ .with("R111222333")
156
+ .and_return({some_kind_of: "response"})
157
+ end
158
+
159
+ it { is_expected.to eq({some_kind_of: "response"}) }
160
+ end
161
+
162
+ describe "#create_refund_for" do
163
+ subject { api.create_refund_for reimbursement }
164
+
165
+ let(:api) { described_class.new(taxjar_client: dummy_client) }
166
+ let(:dummy_client) { instance_double ::Taxjar::Client }
167
+ let(:reimbursement) { Spree::Reimbursement.new }
168
+
169
+ before do
170
+ allow(SuperGood::SolidusTaxjar::ApiParams)
171
+ .to receive(:refund_params)
172
+ .with(reimbursement)
173
+ .and_return({refund: "params"})
174
+
175
+ allow(dummy_client)
176
+ .to receive(:create_refund)
177
+ .with({refund: "params"})
178
+ .and_return({some_kind_of: "response"})
179
+ end
180
+
181
+ it { is_expected.to eq({some_kind_of: "response"}) }
182
+ end
183
+
184
+ describe "#validate_spree_address" do
185
+ subject { api.validate_spree_address spree_address }
186
+
187
+ let(:api) { described_class.new(taxjar_client: dummy_client) }
188
+ let(:dummy_client) { instance_double ::Taxjar::Client }
189
+ let(:spree_address) { build :address }
190
+
191
+ before do
192
+ allow(SuperGood::SolidusTaxjar::ApiParams)
193
+ .to receive(:validate_address_params)
194
+ .with(spree_address)
195
+ .and_return({address: "params"})
196
+
197
+ allow(dummy_client)
198
+ .to receive(:validate_address)
199
+ .with({address: "params"})
200
+ .and_return({some_kind_of: "response"})
201
+ end
202
+
203
+ it { is_expected.to eq({some_kind_of: "response"}) }
204
+ end
205
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe SuperGood::SolidusTaxjar::DiscountCalculator do
4
+ describe "#discount" do
5
+ subject { calculator.discount }
6
+
7
+ let(:calculator) { described_class.new line_item }
8
+
9
+ let(:line_item) { ::Spree::LineItem.new(promo_total: 12.34) }
10
+
11
+ it { is_expected.to eq(-12.34) }
12
+ end
13
+ end
@@ -0,0 +1,332 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe ::SuperGood::SolidusTaxjar::TaxCalculator do
4
+ describe "#calculate" do
5
+ subject { calculator.calculate }
6
+
7
+ let(:calculator) { described_class.new(order, api: dummy_api) }
8
+
9
+ let(:dummy_api) do
10
+ instance_double ::SuperGood::SolidusTaxjar::Api
11
+ end
12
+
13
+ let(:order) do
14
+ ::Spree::Order.new(
15
+ id: 10,
16
+ store: store,
17
+ ship_address: address,
18
+ line_items: line_items,
19
+ shipments: [
20
+ boring_shipment,
21
+ shipment_with_adjustment,
22
+ shipment_with_existing_tax
23
+ ]
24
+ )
25
+ end
26
+
27
+ let(:line_items) { [::Spree::LineItem.new(id: 33)] }
28
+
29
+ let(:boring_shipment) do
30
+ ::Spree::Shipment.new(id: 1, cost: 7)
31
+ end
32
+
33
+ let(:shipment_with_adjustment) do
34
+ ::Spree::Shipment.new(
35
+ id: 2,
36
+ cost: 20,
37
+ adjustments: [
38
+ ::Spree::Adjustment.new(amount: -7)
39
+ ]
40
+ )
41
+ end
42
+
43
+ let(:shipment_with_existing_tax) do
44
+ ::Spree::Shipment.new(
45
+ id: 3,
46
+ cost: 10,
47
+ additional_tax_total: 3,
48
+ adjustments: [
49
+ ::Spree::Adjustment.new(
50
+ amount: 3,
51
+ source_type: "Spree::TaxRate"
52
+ )
53
+ ]
54
+ )
55
+ end
56
+
57
+ let(:store) do
58
+ ::Spree::Store.new(
59
+ name: "Default Store",
60
+ url: "https://store.example.com",
61
+ code: "store",
62
+ mail_from_address: "contact@example.com",
63
+ cart_tax_country_iso: "US"
64
+ )
65
+ end
66
+
67
+ context "when the order has an empty tax address" do
68
+ let(:address) { nil }
69
+
70
+ it "returns no taxes" do
71
+ expect(subject.order_id).to eq order.id
72
+ expect(subject.shipment_taxes).to be_empty
73
+ expect(subject.line_item_taxes).to be_empty
74
+ end
75
+ end
76
+
77
+ context "when the order has an incomplete tax address" do
78
+ let(:address) do
79
+ ::Spree::Address.new(
80
+ first_name: "Ronnie James",
81
+ zipcode: nil,
82
+ address1: nil,
83
+ city: "Beverly Hills",
84
+ state_name: "California",
85
+ country: ::Spree::Country.new(iso: "US")
86
+ )
87
+ end
88
+
89
+ it "returns no taxes" do
90
+ expect(subject.order_id).to eq order.id
91
+ expect(subject.shipment_taxes).to be_empty
92
+ expect(subject.line_item_taxes).to be_empty
93
+ end
94
+ end
95
+
96
+ context "when the order has no tax address" do
97
+ let(:address) { nil }
98
+
99
+ it "returns no taxes" do
100
+ expect(subject.order_id).to eq order.id
101
+ expect(subject.shipment_taxes).to be_empty
102
+ expect(subject.line_item_taxes).to be_empty
103
+ end
104
+ end
105
+
106
+ context "when the order has no line items" do
107
+ let(:address) do
108
+ ::Spree::Address.new(
109
+ first_name: "Ronnie James",
110
+ zipcode: "90210",
111
+ address1: "9900 Wilshire Blvd",
112
+ city: "Beverly Hills",
113
+ state_name: "California",
114
+ country: ::Spree::Country.new(iso: "US")
115
+ )
116
+ end
117
+
118
+ let(:line_items) { [] }
119
+
120
+ it "returns no taxes" do
121
+ expect(subject.order_id).to eq order.id
122
+ expect(subject.shipment_taxes).to be_empty
123
+ expect(subject.line_item_taxes).to be_empty
124
+ end
125
+ end
126
+
127
+ context "when the API encounters an error" do
128
+ let(:address) do
129
+ ::Spree::Address.new(
130
+ first_name: "Ronnie James",
131
+ zipcode: "90210",
132
+ address1: "9900 Wilshire Blvd",
133
+ city: "Beverly Hills",
134
+ state_name: "California",
135
+ country: ::Spree::Country.new(iso: "US")
136
+ )
137
+ end
138
+
139
+ before do
140
+ allow(dummy_api).to receive(:tax_for).with(order).and_raise("A bad thing happened.")
141
+ end
142
+
143
+ it "calls the configured error handler" do
144
+ expect(SuperGood::SolidusTaxjar.exception_handler).to receive(:call) do |e|
145
+ expect(e).to be_a StandardError
146
+ expect(e.message).to eq "A bad thing happened."
147
+ end
148
+
149
+ subject
150
+ end
151
+
152
+ it "returns no taxes" do
153
+ expect(subject.order_id).to eq order.id
154
+ expect(subject.shipment_taxes).to be_empty
155
+ expect(subject.line_item_taxes).to be_empty
156
+ end
157
+ end
158
+
159
+ context "when the order has a non-empty tax address" do
160
+ let(:address) do
161
+ ::Spree::Address.new(
162
+ first_name: "Ronnie James",
163
+ zipcode: "90210",
164
+ address1: "9900 Wilshire Blvd",
165
+ city: "Beverly Hills",
166
+ state_name: "California",
167
+ country: ::Spree::Country.new(iso: "US")
168
+ )
169
+ end
170
+
171
+ before do
172
+ allow(dummy_api).to receive(:tax_for).with(order).and_return(
173
+ instance_double(
174
+ ::Taxjar::Tax,
175
+ breakdown: breakdown
176
+ )
177
+ )
178
+ end
179
+
180
+ context "and there is tax" do
181
+ let!(:tax_rate) do
182
+ ::Spree::TaxRate.create!(
183
+ name: "Sales Tax",
184
+ amount: 0.5,
185
+ calculator: ::Spree::Calculator.new
186
+ )
187
+ end
188
+
189
+ let(:breakdown) do
190
+ instance_double ::Taxjar::Breakdown,
191
+ line_items: [taxjar_line_item],
192
+ shipping?: !!shipping_tax_breakdown,
193
+ shipping: shipping_tax_breakdown
194
+ end
195
+
196
+ let(:taxjar_line_item) do
197
+ instance_double ::Taxjar::BreakdownLineItem, id: "33", tax_collectable: 6.66
198
+ end
199
+
200
+ let(:shipping_tax_breakdown) { nil }
201
+
202
+ it "returns the taxes" do
203
+ expect(subject.order_id).to eq order.id
204
+ expect(subject.shipment_taxes).to be_empty
205
+ expect(subject.line_item_taxes.length).to eq 1
206
+
207
+ item_tax = subject.line_item_taxes.first
208
+
209
+ aggregate_failures do
210
+ expect(item_tax.item_id).to eq 33
211
+ expect(item_tax.label).to eq "Sales Tax"
212
+ expect(item_tax.tax_rate).to eq tax_rate
213
+ expect(item_tax.amount).to eq 6.66
214
+ expect(item_tax.included_in_price).to eq false
215
+ end
216
+ end
217
+
218
+ context "with custom line item tax labels" do
219
+ before do
220
+ allow(SuperGood::SolidusTaxjar.line_item_tax_label_maker)
221
+ .to receive(:call)
222
+ .with(taxjar_line_item, line_items.first)
223
+ .and_return("Space Tax")
224
+ end
225
+
226
+ it "applies those labels" do
227
+ expect(subject.line_item_taxes.length).to eq 1
228
+ expect(subject.line_item_taxes.first.label).to eq "Space Tax"
229
+ end
230
+ end
231
+
232
+ context "but the taxable address check returns false" do
233
+ before do
234
+ allow(SuperGood::SolidusTaxjar.taxable_address_check)
235
+ .to receive(:call).with(address)
236
+ .and_return(false)
237
+ end
238
+
239
+ it "returns no taxes" do
240
+ expect(subject.order_id).to eq order.id
241
+ expect(subject.shipment_taxes).to be_empty
242
+ expect(subject.line_item_taxes).to be_empty
243
+ end
244
+ end
245
+
246
+ context "but the taxable order check returns false" do
247
+ before do
248
+ allow(SuperGood::SolidusTaxjar.taxable_order_check)
249
+ .to receive(:call).with(order)
250
+ .and_return(false)
251
+ end
252
+
253
+ it "returns no taxes" do
254
+ expect(subject.order_id).to eq order.id
255
+ expect(subject.shipment_taxes).to be_empty
256
+ expect(subject.line_item_taxes).to be_empty
257
+ end
258
+ end
259
+
260
+ context "when there are shipping taxes" do
261
+ let(:shipping_tax_breakdown) do
262
+ instance_double ::Taxjar::Shipping, tax_collectable: 10.00
263
+ end
264
+
265
+ it "returns the shipping taxes" do
266
+ shipment_taxes = subject.shipment_taxes
267
+ expect(shipment_taxes.length).to eq 3
268
+
269
+ aggregate_failures do
270
+ expect(shipment_taxes[0].item_id).to eq 1
271
+ expect(shipment_taxes[0].label).to eq "Sales Tax"
272
+ expect(shipment_taxes[0].tax_rate).to eq tax_rate
273
+ expect(shipment_taxes[0].amount).to eq 2.33
274
+ expect(shipment_taxes[0].included_in_price).to eq false
275
+
276
+ expect(shipment_taxes[1].item_id).to eq 2
277
+ expect(shipment_taxes[1].label).to eq "Sales Tax"
278
+ expect(shipment_taxes[1].tax_rate).to eq tax_rate
279
+ expect(shipment_taxes[1].amount).to eq 4.33
280
+ expect(shipment_taxes[1].included_in_price).to eq false
281
+
282
+ expect(shipment_taxes[2].item_id).to eq 3
283
+ expect(shipment_taxes[2].label).to eq "Sales Tax"
284
+ expect(shipment_taxes[2].tax_rate).to eq tax_rate
285
+ expect(shipment_taxes[2].amount).to eq 3.34
286
+ expect(shipment_taxes[2].included_in_price).to eq false
287
+ end
288
+ end
289
+
290
+ context "with custom shipping tax labels" do
291
+ before do
292
+ allow(SuperGood::SolidusTaxjar.shipping_tax_label_maker).to receive(:call)
293
+ .and_return("Magic Tax", "Spicy Tax", "Vegetable Tax")
294
+ end
295
+
296
+ it "applies those labels" do
297
+ shipment_taxes = subject.shipment_taxes
298
+ expect(shipment_taxes.length).to eq 3
299
+
300
+ aggregate_failures do
301
+ expect(shipment_taxes[0].label).to eq "Magic Tax"
302
+ expect(shipment_taxes[1].label).to eq "Spicy Tax"
303
+ expect(shipment_taxes[2].label).to eq "Vegetable Tax"
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ context "when test_mode is set" do
310
+ before { SuperGood::SolidusTaxjar.test_mode = true }
311
+ after { SuperGood::SolidusTaxjar.test_mode = false }
312
+
313
+ it "returns no taxes" do
314
+ expect(subject.order_id).to eq order.id
315
+ expect(subject.shipment_taxes).to be_empty
316
+ expect(subject.line_item_taxes).to be_empty
317
+ end
318
+ end
319
+ end
320
+
321
+ context "and there is not a breakdown" do
322
+ let(:breakdown) { nil }
323
+
324
+ it "returns no taxes" do
325
+ expect(subject.order_id).to eq order.id
326
+ expect(subject.shipment_taxes).to be_empty
327
+ expect(subject.line_item_taxes).to be_empty
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end