solidus_avatax 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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