solidus_avatax 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +15 -0
  6. data/LICENSE +26 -0
  7. data/README.md +71 -0
  8. data/Rakefile +21 -0
  9. data/app/assets/javascripts/spree/frontend/solidus_avatax.js +1 -0
  10. data/app/assets/stylesheets/spree/frontend/solidus_avatax.css +1 -0
  11. data/app/models/spree/adjustment_decorator.rb +21 -0
  12. data/app/models/spree/order_contents_decorator.rb +26 -0
  13. data/app/models/spree/order_decorator.rb +43 -0
  14. data/app/models/spree/promotion_handler/coupon_decorator.rb +11 -0
  15. data/app/models/spree/reimbursement_decorator.rb +1 -0
  16. data/app/models/spree/tax_rate_decorator.rb +45 -0
  17. data/app/models/spree_avatax/calculator.rb +26 -0
  18. data/app/models/spree_avatax/return_invoice.rb +197 -0
  19. data/app/models/spree_avatax/sales_invoice.rb +157 -0
  20. data/app/models/spree_avatax/sales_shared.rb +211 -0
  21. data/app/models/spree_avatax/shared.rb +53 -0
  22. data/app/models/spree_avatax/short_ship_return_invoice.rb +133 -0
  23. data/app/models/spree_avatax/short_ship_return_invoice_inventory_unit.rb +11 -0
  24. data/bin/rails +7 -0
  25. data/circle.yml +6 -0
  26. data/config/locales/en.yml +5 -0
  27. data/db/migrate/20140122165618_add_avatax_response_at_to_orders.rb +5 -0
  28. data/db/migrate/20140214153139_add_avatax_invoice_at_to_orders.rb +5 -0
  29. data/db/migrate/20140617222244_close_all_tax_adjustments.rb +5 -0
  30. data/db/migrate/20140701144237_update_avatax_calculator_type.rb +17 -0
  31. data/db/migrate/20140801132302_create_spree_avatax_return_invoices.rb +19 -0
  32. data/db/migrate/20140903135132_create_spree_avatax_sales_orders.rb +15 -0
  33. data/db/migrate/20140903135357_create_spree_avatax_sales_invoices.rb +19 -0
  34. data/db/migrate/20140904171341_generate_uncommitted_sales_invoices.rb +31 -0
  35. data/db/migrate/20140911214414_add_transaction_id_to_sales_orders_and_sales_invoices.rb +6 -0
  36. data/db/migrate/20140911215422_add_canceled_at_and_cancel_transaction_id_to_sales_invoices.rb +6 -0
  37. data/db/migrate/20150427154942_create_spree_avatax_short_ship_return_invoices.rb +44 -0
  38. data/db/migrate/20150518172627_fix_avatax_short_ship_index.rb +30 -0
  39. data/lib/generators/solidus_avatax/install/install_generator.rb +31 -0
  40. data/lib/generators/solidus_avatax/install/templates/config/initializers/avatax.rb +18 -0
  41. data/lib/solidus_avatax.rb +4 -0
  42. data/lib/spree_avatax/config.rb +16 -0
  43. data/lib/spree_avatax/engine.rb +29 -0
  44. data/lib/spree_avatax/factories.rb +25 -0
  45. data/lib/tasks/commit_backfill.rake +119 -0
  46. data/lib/tasks/sales_invoice_backfill.rake +43 -0
  47. data/solidus_avatax.gemspec +34 -0
  48. data/spec/features/store_credits_spec.rb +92 -0
  49. data/spec/features/tax_calculation_spec.rb +75 -0
  50. data/spec/fixtures/vcr_cassettes/sales_invoice_gettax_with_discounts.yml +136 -0
  51. data/spec/fixtures/vcr_cassettes/sales_invoice_gettax_without_discounts.yml +136 -0
  52. data/spec/fixtures/vcr_cassettes/taxes_with_store_credits.yml +113 -0
  53. data/spec/models/spree/adjustment_spec.rb +23 -0
  54. data/spec/models/spree/order_contents_spec.rb +35 -0
  55. data/spec/models/spree/order_spec.rb +111 -0
  56. data/spec/models/spree/shipping_rate_spec.rb +23 -0
  57. data/spec/models/spree/tax_rate_spec.rb +40 -0
  58. data/spec/models/spree_avatax/calculator.rb +20 -0
  59. data/spec/models/spree_avatax/return_invoice_spec.rb +193 -0
  60. data/spec/models/spree_avatax/sales_invoice_spec.rb +491 -0
  61. data/spec/models/spree_avatax/sales_shared_spec.rb +47 -0
  62. data/spec/models/spree_avatax/shared_spec.rb +89 -0
  63. data/spec/models/spree_avatax/short_ship_return_invoice_spec.rb +181 -0
  64. data/spec/spec_helper.rb +85 -0
  65. data/spec/support/return_invoice_soap_responses.rb +117 -0
  66. data/spec/support/sales_invoice_soap_responses.rb +259 -0
  67. data/spec/support/short_ship_return_invoice_soap_responses.rb +178 -0
  68. data/spec/support/zone_support.rb +6 -0
  69. metadata +320 -0
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpreeAvatax::SalesShared do
4
+
5
+ describe '.reset_tax_attributes' do
6
+ subject do
7
+ SpreeAvatax::SalesShared.reset_tax_attributes(order)
8
+ end
9
+
10
+ let(:order) { create(:order_with_line_items, additional_tax_total: 1, adjustment_total: 1, included_tax_total: 1, line_items_count: 1) }
11
+ let(:line_item) { order.line_items.first }
12
+
13
+ before do
14
+ line_item.adjustments.eligible.tax.additional.create!({
15
+ adjustable: line_item,
16
+ amount: 1.23,
17
+ order: order,
18
+ label: 'Previous Tax',
19
+ included: false,
20
+ finalized: true,
21
+ })
22
+
23
+ line_item.update_attributes!({
24
+ additional_tax_total: 1,
25
+ adjustment_total: 1,
26
+ pre_tax_amount: 1,
27
+ included_tax_total: 1,
28
+ })
29
+ end
30
+
31
+ context 'when order is completed' do
32
+ before do
33
+ order.update_attributes!(completed_at: Time.now)
34
+ end
35
+
36
+ it 'should leave adjustments in place' do
37
+ subject
38
+ expect(line_item.adjustments.tax.count).to eq 1
39
+ end
40
+ end
41
+
42
+ it 'should remove all eligible tax adjustments' do
43
+ subject
44
+ expect(line_item.adjustments.tax.count).to eq 0
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpreeAvatax::Shared do
4
+ describe '.logger' do
5
+ subject { SpreeAvatax::Shared.logger }
6
+ it { is_expected.to be_a Logger }
7
+ end
8
+
9
+ describe '.taxable_order?' do
10
+ subject { SpreeAvatax::Shared.taxable_order?(order) }
11
+
12
+ context 'when the order is taxable' do
13
+ let(:order) { create(:shipped_order, line_items_count: 1) }
14
+ it { is_expected.to be true }
15
+ end
16
+
17
+ context 'when the order has no line items' do
18
+ let(:order) { create(:order) }
19
+ it { is_expected.to be false }
20
+ end
21
+
22
+ context 'when the order has no ship address' do
23
+ let(:order) { create(:order_with_totals, ship_address: nil) }
24
+ it { is_expected.to be false }
25
+ end
26
+ end
27
+
28
+ describe '.tax_svc' do
29
+ subject { SpreeAvatax::Shared.tax_svc }
30
+ it { is_expected.to be_a AvaTax::TaxService }
31
+ end
32
+
33
+ describe '.require_success!' do
34
+ subject do
35
+ SpreeAvatax::Shared.require_success!(response)
36
+ end
37
+
38
+ context 'when the response is a success' do
39
+ let(:response) { {transaction_id: "4315047353885220", result_code: 'Success'} }
40
+
41
+ it 'does not raise' do
42
+ expect { subject }.not_to raise_error
43
+ end
44
+ end
45
+
46
+ context 'when the response if a failure' do
47
+ # See notes in shared.rb about the two formats
48
+ context 'format 1' do
49
+ let(:response) do
50
+ {
51
+ transaction_id: "4315046676197187",
52
+ result_code: 'Error',
53
+ messages: {
54
+ message: {summary: 'Something awful happened.'},
55
+ },
56
+ }
57
+ end
58
+
59
+ it 'raises a FailedApiResponse' do
60
+ expect { subject }.to(raise_error { |error|
61
+ expect(error).to be_a(SpreeAvatax::Shared::FailedApiResponse)
62
+ expect(error.message).to eq('["Something awful happened."]')
63
+ expect(error.messages).to eq([{summary: 'Something awful happened.'}])
64
+ })
65
+ end
66
+ end
67
+
68
+ context 'format 2' do
69
+ let(:response) do
70
+ {
71
+ transaction_id: "4315046676197187",
72
+ result_code: 'Error',
73
+ messages: [
74
+ {summary: 'Something awful happened.'},
75
+ ],
76
+ }
77
+ end
78
+
79
+ it 'raises a FailedApiResponse' do
80
+ expect { subject }.to(raise_error { |error|
81
+ expect(error).to be_a(SpreeAvatax::Shared::FailedApiResponse)
82
+ expect(error.message).to eq('["Something awful happened."]')
83
+ expect(error.messages).to eq([{summary: 'Something awful happened.'}])
84
+ })
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpreeAvatax::ShortShipReturnInvoice do
4
+ include ShortShipReturnInvoiceSoapResponses
5
+
6
+ describe '.generate' do
7
+ before do
8
+ # disable avatax tax calculation. we're not testing calculating tax.
9
+ allow(SpreeAvatax::Shared).to receive(:taxable_order?).and_return(false)
10
+ allow(SpreeAvatax::SalesShared).to receive(:reset_tax_attributes)
11
+ end
12
+
13
+ let(:order) do
14
+ create(
15
+ :order_with_line_items,
16
+ line_items_price: 10,
17
+ line_items_count: 2,
18
+ )
19
+ end
20
+
21
+ def line_item_1
22
+ order.line_items.first
23
+ end
24
+
25
+ def line_item_2
26
+ order.line_items.last
27
+ end
28
+
29
+ def inventory_unit_1
30
+ line_item_1.inventory_units.first
31
+ end
32
+
33
+ def inventory_unit_2
34
+ line_item_2.inventory_units.first
35
+ end
36
+
37
+ # add some tax and complete the order
38
+ before do
39
+ line_item_1.adjustments.create!(
40
+ amount: 1,
41
+ label: 'fake tax',
42
+ order: order,
43
+ source: Spree::TaxRate.avatax_the_one_rate,
44
+ finalized: true,
45
+ )
46
+ order.contents.advance
47
+ create(:payment, amount: order.total, order: order)
48
+ order.contents.advance
49
+ order.complete!
50
+ order.reload
51
+ end
52
+
53
+ describe 'gettax params' do
54
+ let(:now) { Time.now }
55
+
56
+ around do |example|
57
+ Timecop.freeze(now) do
58
+ example.run
59
+ end
60
+ end
61
+
62
+ def expected_gettax_params
63
+ {
64
+ doccode: "#{order.number}-short-#{now.to_f}",
65
+ referencecode: order.number,
66
+ customercode: order.user_id,
67
+ companycode: SpreeAvatax::Config.company_code,
68
+
69
+ doctype: SpreeAvatax::ShortShipReturnInvoice::DOC_TYPE,
70
+ docdate: now.to_date,
71
+
72
+ commit: true,
73
+
74
+ addresses: [
75
+ {
76
+ addresscode: SpreeAvatax::ShortShipReturnInvoice::DESTINATION_CODE,
77
+ line1: REXML::Text.normalize(order.ship_address.address1),
78
+ line2: REXML::Text.normalize(order.ship_address.address2),
79
+ city: REXML::Text.normalize(order.ship_address.city),
80
+ postalcode: REXML::Text.normalize(order.ship_address.zipcode),
81
+ },
82
+ ],
83
+
84
+ lines: [
85
+ {
86
+ no: inventory_unit_1.id,
87
+ itemcode: inventory_unit_1.line_item.variant.sku,
88
+ taxcode: inventory_unit_1.line_item.tax_category.tax_code,
89
+ qty: 1,
90
+ amount: -10.to_d,
91
+ origincodeline: SpreeAvatax::ShortShipReturnInvoice::DESTINATION_CODE,
92
+ destinationcodeline: SpreeAvatax::ShortShipReturnInvoice::DESTINATION_CODE,
93
+
94
+ taxoverridetypeline: SpreeAvatax::ShortShipReturnInvoice::TAX_OVERRIDE_TYPE,
95
+ reasonline: SpreeAvatax::ShortShipReturnInvoice::TAX_OVERRIDE_REASON,
96
+ taxamountline: -1.to_d,
97
+ taxdateline: now.to_date,
98
+
99
+ description: REXML::Text.normalize(inventory_unit_1.line_item.variant.product.description[0...100]),
100
+ },
101
+ {
102
+ no: inventory_unit_2.id,
103
+ itemcode: inventory_unit_2.line_item.variant.sku,
104
+ taxcode: inventory_unit_2.line_item.tax_category.tax_code,
105
+ qty: 1,
106
+ amount: -10.to_d,
107
+ origincodeline: SpreeAvatax::ShortShipReturnInvoice::DESTINATION_CODE,
108
+ destinationcodeline: SpreeAvatax::ShortShipReturnInvoice::DESTINATION_CODE,
109
+
110
+ taxoverridetypeline: SpreeAvatax::ShortShipReturnInvoice::TAX_OVERRIDE_TYPE,
111
+ reasonline: SpreeAvatax::ShortShipReturnInvoice::TAX_OVERRIDE_REASON,
112
+ taxamountline: 0.to_d,
113
+ taxdateline: now.to_date,
114
+
115
+ description: REXML::Text.normalize(inventory_unit_2.line_item.variant.product.description[0...100]),
116
+ },
117
+ ],
118
+ }
119
+ end
120
+
121
+ let(:unit_cancels) do
122
+ Spree::OrderCancellations.new(order).short_ship(order.inventory_units)
123
+ end
124
+
125
+ it 'generates the expected params for avatax' do
126
+ expect(SpreeAvatax::Shared.tax_svc).to(
127
+ receive(:gettax).
128
+ with(expected_gettax_params).
129
+ and_return(short_ship_return_invoice_gettax_response)
130
+ )
131
+
132
+ SpreeAvatax::ShortShipReturnInvoice.generate(unit_cancels: unit_cancels)
133
+ end
134
+ end
135
+
136
+ context 'with a successful response' do
137
+ let(:unit_cancels) do
138
+ Spree::OrderCancellations.new(order).short_ship(order.inventory_units)
139
+ end
140
+
141
+ before do
142
+ expect(SpreeAvatax::Shared.tax_svc).to(
143
+ receive(:gettax).
144
+ and_return(short_ship_return_invoice_gettax_response)
145
+ )
146
+ end
147
+
148
+ it 'creates a return invoice with the correct inventory units' do
149
+ expect {
150
+ SpreeAvatax::ShortShipReturnInvoice.generate(unit_cancels: unit_cancels)
151
+ }.to change { SpreeAvatax::ShortShipReturnInvoice.count }.by(1)
152
+
153
+ short_ship_return_invoice = SpreeAvatax::ShortShipReturnInvoice.last
154
+
155
+ expect(short_ship_return_invoice.inventory_units).to eq(
156
+ [inventory_unit_1, inventory_unit_2]
157
+ )
158
+ end
159
+ end
160
+
161
+ context 'with inventory units from multiple orders' do
162
+ let(:order2) { create(:order_ready_to_ship) }
163
+
164
+ let(:unit_cancels1) do
165
+ Spree::OrderCancellations.new(order).short_ship([inventory_unit_1])
166
+ end
167
+ let(:unit_cancels2) do
168
+ Spree::OrderCancellations.new(order2).short_ship(order2.inventory_units)
169
+ end
170
+
171
+ it 'fails' do
172
+ expect {
173
+ SpreeAvatax::ShortShipReturnInvoice.generate(
174
+ unit_cancels: unit_cancels1 + unit_cancels2
175
+ )
176
+ }.to raise_error(/more than one order/)
177
+ end
178
+ end
179
+
180
+ end
181
+ end
@@ -0,0 +1,85 @@
1
+ # Run Coverage report
2
+ require "simplecov"
3
+ SimpleCov.start do
4
+ add_filter "spec/dummy"
5
+ add_group "Controllers", "app/controllers"
6
+ add_group "Helpers", "app/helpers"
7
+ add_group "Mailers", "app/mailers"
8
+ add_group "Models", "app/models"
9
+ add_group "Views", "app/views"
10
+ add_group "Libraries", "lib"
11
+ end
12
+
13
+ # Configure Rails Environment
14
+ ENV["RAILS_ENV"] = "test"
15
+
16
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
17
+
18
+ require "rspec/rails"
19
+
20
+ require "database_cleaner"
21
+ require "ffaker"
22
+
23
+ require "spree/testing_support/factories"
24
+ require "spree/testing_support/controller_requests"
25
+ require "spree/testing_support/authorization_helpers"
26
+ require "spree/testing_support/url_helpers"
27
+
28
+ require 'spree_avatax/factories'
29
+
30
+ require 'avatax_taxservice'
31
+ require 'hashie'
32
+ require 'vcr'
33
+
34
+ Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each { |f| require f }
35
+
36
+ VCR.configure do |c|
37
+ c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
38
+ c.hook_into :webmock
39
+ c.filter_sensitive_data("[Username]") { ENV["AVATAX_USERNAME"] }
40
+ c.filter_sensitive_data("[Password]") { ENV["AVATAX_PASSWORD"] }
41
+ c.filter_sensitive_data("[Company]") { ENV["AVATAX_COMPANY_CODE"] }
42
+ end
43
+
44
+ RSpec.configure do |config|
45
+ config.infer_spec_type_from_file_location!
46
+ config.mock_with :rspec
47
+
48
+ config.color = true
49
+ config.order = "random"
50
+
51
+ config.expose_current_running_example_as :example
52
+ config.fail_fast = ENV["FAIL_FAST"] || false
53
+
54
+ config.filter_run focus: true
55
+ config.run_all_when_everything_filtered = true
56
+ config.use_transactional_fixtures = false
57
+
58
+ config.include FactoryGirl::Syntax::Methods
59
+ config.include Spree::TestingSupport::ControllerRequests, type: :controller
60
+ config.include Spree::TestingSupport::UrlHelpers, type: :controller
61
+
62
+ config.include ActionView::Helpers::TagHelper
63
+ config.include ActionView::Context
64
+ config.include SalesInvoiceSoapResponses
65
+ config.include ReturnInvoiceSoapResponses
66
+
67
+ # Ensure Suite is set to use transactions for speed.
68
+ config.before :suite do
69
+ DatabaseCleaner.strategy = :transaction
70
+ DatabaseCleaner.clean_with :truncation
71
+ end
72
+
73
+ # Before each spec check if it is a Javascript test and switch between using database transactions or not where necessary.
74
+ config.before :each do
75
+ DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
76
+ DatabaseCleaner.start
77
+
78
+ FactoryGirl.create(:tax_rate, calculator: create(:avatax_tax_calculator))
79
+ end
80
+
81
+ # After each spec clean the database.
82
+ config.after :each do
83
+ DatabaseCleaner.clean
84
+ end
85
+ end
@@ -0,0 +1,117 @@
1
+ module ReturnInvoiceSoapResponses
2
+ def return_invoice_gettax_response(doc_code, return_item_id, time = Time.now)
3
+ {
4
+ transaction_id: "4289094359310439",
5
+ result_code: "Success",
6
+ doc_id: '55527402',
7
+ doc_type: "ReturnInvoice",
8
+ doc_code: doc_code,
9
+ doc_date: time.to_date,
10
+ doc_status: "Saved",
11
+ reconciled: false,
12
+ timestamp: time,
13
+ total_amount: "-10",
14
+ total_discount: "0",
15
+ total_exemption: "-10",
16
+ total_taxable: "0",
17
+ total_tax: "0",
18
+ total_tax_calculated: "0",
19
+ hash_code: "0",
20
+ tax_lines: {
21
+ tax_line: {
22
+ no: return_item_id.to_s,
23
+ tax_code: "P0000000",
24
+ taxability: true,
25
+ boundary_level: "Zip5",
26
+ exemption: "-10",
27
+ discount: "0",
28
+ taxable: "0",
29
+ rate: "0",
30
+ tax: "-0.01",
31
+ tax_calculated: "-0.01",
32
+ tax_included: false,
33
+ tax_details: {
34
+ tax_detail: {
35
+ country: "US",
36
+ region: "AL",
37
+ juris_type: "State",
38
+ juris_code: "01",
39
+ tax_type: "Sales",
40
+ base: "0",
41
+ taxable: "0",
42
+ rate: "0",
43
+ tax: "-0.01",
44
+ tax_calculated: "-0.01",
45
+ non_taxable: "-10",
46
+ exemption: "0",
47
+ juris_name: "ALABAMA",
48
+ tax_name: "AL STATE TAX",
49
+ tax_authority_type: "45",
50
+ tax_group: nil,
51
+ rate_type: "G",
52
+ state_assigned_no: nil,
53
+ },
54
+ },
55
+ exempt_cert_id: "0",
56
+ tax_date: time.to_date,
57
+ reporting_date: time.to_date,
58
+ accounting_method: "Accrual",
59
+ },
60
+ },
61
+ tax_addresses: {
62
+ tax_address: {
63
+ address: "Northwest",
64
+ address_code: "1",
65
+ boundary_level: "2",
66
+ city: "Herndon",
67
+ country: "US",
68
+ postal_code: "35005",
69
+ region: "AL",
70
+ tax_region_id: "2052474",
71
+ juris_code: "0107300460",
72
+ latitude: nil,
73
+ longitude: nil,
74
+ geocode_type: "ZIP5Centroid",
75
+ validate_status: "HouseNotOnStreet",
76
+ distance_to_boundary: "0",
77
+ },
78
+ },
79
+ locked: false,
80
+ adjustment_reason: "0",
81
+ adjustment_description: nil,
82
+ version: "1",
83
+ tax_date: time.to_date,
84
+ tax_summary: nil,
85
+ volatile_tax_rates: false,
86
+ messages: [
87
+ {
88
+ summary: nil,
89
+ details: nil,
90
+ helplink: nil,
91
+ refersto: nil,
92
+ severity: nil,
93
+ source: nil,
94
+ },
95
+ ],
96
+ }
97
+ end
98
+
99
+ def return_invoice_posttax_response
100
+ {
101
+ transaction_id: "4289138364000017",
102
+ result_code: "Success",
103
+ doc_id: "55529724",
104
+
105
+ messages: [
106
+ {
107
+ summary: nil,
108
+ details: nil,
109
+ helplink: nil,
110
+ refersto: nil,
111
+ severity: nil,
112
+ source: nil,
113
+ },
114
+ ],
115
+ }
116
+ end
117
+ end