solidus_avatax_certified 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/.env_example +4 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/GEM_VERSION +1 -0
  7. data/Gemfile +18 -0
  8. data/LICENSE +26 -0
  9. data/README.md +27 -0
  10. data/Rakefile +21 -0
  11. data/app/assets/javascripts/spree/backend/address_validator.js +84 -0
  12. data/app/assets/javascripts/spree/backend/solidus_avatax_certified.js +5 -0
  13. data/app/assets/javascripts/spree/backend/use_code_picker.js +51 -0
  14. data/app/assets/javascripts/spree/frontend/address_validator.js +87 -0
  15. data/app/assets/javascripts/spree/frontend/flash.js.coffee +26 -0
  16. data/app/assets/javascripts/spree/frontend/solidus_avatax_certified.js +4 -0
  17. data/app/assets/stylesheets/spree/backend/solidus_avatax_certified.css +4 -0
  18. data/app/assets/stylesheets/spree/frontend/solidus_avatax_certified.css +4 -0
  19. data/app/controllers/spree/admin/avalara_entity_use_codes_controller.rb +15 -0
  20. data/app/controllers/spree/admin/avatax_settings_controller.rb +82 -0
  21. data/app/controllers/spree/admin/users_controller_decorator.rb +11 -0
  22. data/app/controllers/spree/checkout_controller_decorator.rb +22 -0
  23. data/app/models/solidus_avatax_certified/address.rb +95 -0
  24. data/app/models/solidus_avatax_certified/avatax_log.rb +69 -0
  25. data/app/models/solidus_avatax_certified/line.rb +146 -0
  26. data/app/models/solidus_avatax_certified/preference_updater.rb +74 -0
  27. data/app/models/spree/address_decorator.rb +13 -0
  28. data/app/models/spree/adjustment_decorator.rb +10 -0
  29. data/app/models/spree/avalara_entity_use_code.rb +5 -0
  30. data/app/models/spree/avalara_preference.rb +72 -0
  31. data/app/models/spree/avalara_transaction.rb +181 -0
  32. data/app/models/spree/calculator/avalara_transaction.rb +86 -0
  33. data/app/models/spree/line_item_decorator.rb +25 -0
  34. data/app/models/spree/order_decorator.rb +85 -0
  35. data/app/models/spree/payment_decorator.rb +23 -0
  36. data/app/models/spree/refund_decorator.rb +30 -0
  37. data/app/models/spree/shipment_decorator.rb +28 -0
  38. data/app/models/spree/shipping_rate_decorator.rb +25 -0
  39. data/app/models/spree/user_decorator.rb +3 -0
  40. data/app/models/tax_svc.rb +143 -0
  41. data/app/overrides/spree/admin/shared/_configuration_menu.rb +10 -0
  42. data/app/overrides/spree/admin/users/sidebar.rb +8 -0
  43. data/app/overrides/spree/admin/users/validation_button.rb +6 -0
  44. data/app/views/spree/admin/avalara_entity_use_codes/_form.html.erb +27 -0
  45. data/app/views/spree/admin/avalara_entity_use_codes/edit.html.erb +16 -0
  46. data/app/views/spree/admin/avalara_entity_use_codes/index.html.erb +33 -0
  47. data/app/views/spree/admin/avalara_entity_use_codes/new.html.erb +17 -0
  48. data/app/views/spree/admin/avalara_entity_use_codes/show.html.erb +24 -0
  49. data/app/views/spree/admin/avatax_settings/edit.html.erb +183 -0
  50. data/app/views/spree/admin/avatax_settings/ping_my_service.js.erb +8 -0
  51. data/app/views/spree/admin/avatax_settings/show.html.erb +124 -0
  52. data/app/views/spree/admin/avatax_settings/validate_address.js.erb +8 -0
  53. data/app/views/spree/admin/users/avalara_information.html.erb +53 -0
  54. data/app/views/spree/checkout/_address.html.erb +35 -0
  55. data/app/views/spree/checkout/_address_validator_button.html.erb +3 -0
  56. data/bin/rails +7 -0
  57. data/circle.yml +17 -0
  58. data/config/initializers/avatax.rb +5 -0
  59. data/config/initializers/spree.rb +3 -0
  60. data/config/locales/en.yml +35 -0
  61. data/config/routes.rb +18 -0
  62. data/db/migrate/20160624154205_create_spree_avalara_transactions.rb +9 -0
  63. data/db/migrate/20160624154423_create_spree_avalara_entity_use_codes.rb +9 -0
  64. data/db/migrate/20160624155217_add_columns_to_spree_users.rb +6 -0
  65. data/db/migrate/20160628200504_create_spree_avalara_preferences.rb +10 -0
  66. data/db/migrate/20160805193512_add_vat_id_to_spree_users.rb +5 -0
  67. data/db/seeds.rb +1 -0
  68. data/lib/generators/solidus_avatax_certified/install/install_generator.rb +45 -0
  69. data/lib/solidus_avatax_certified/engine.rb +22 -0
  70. data/lib/solidus_avatax_certified/preference_seeder.rb +69 -0
  71. data/lib/solidus_avatax_certified/seeder.rb +117 -0
  72. data/lib/solidus_avatax_certified.rb +2 -0
  73. data/lib/tasks/load_seeds.rake +15 -0
  74. data/setup.md +12 -0
  75. data/solidus_avatax_certified.gemspec +42 -0
  76. data/spec/controllers/spree/admin/avalara_entity_use_codes_controller_spec.rb +53 -0
  77. data/spec/controllers/spree/admin/avatax_settings_controller_spec.rb +67 -0
  78. data/spec/factories/avalara_entity_use_code_factory.rb +6 -0
  79. data/spec/factories/avalara_factories.rb +76 -0
  80. data/spec/factories/avalara_order_factory.rb +62 -0
  81. data/spec/factories/avalara_preference_factory.rb +7 -0
  82. data/spec/factories/avalara_shipment_factory.rb +27 -0
  83. data/spec/factories/avalara_shipping_method_factory.rb +23 -0
  84. data/spec/factories/tax_rate_factory.rb +19 -0
  85. data/spec/features/certification_spec.rb +77 -0
  86. data/spec/features/checkout_spec.rb +156 -0
  87. data/spec/features/vat_spec.rb +153 -0
  88. data/spec/models/solidus_avatax_certified/address_spec.rb +80 -0
  89. data/spec/models/solidus_avatax_certified/avatax_log_spec.rb +97 -0
  90. data/spec/models/solidus_avatax_certified/line_spec.rb +100 -0
  91. data/spec/models/spree/address_decorator_spec.rb +44 -0
  92. data/spec/models/spree/adjustment_decorator_spec.rb +25 -0
  93. data/spec/models/spree/avalara_entity_use_code_spec.rb +5 -0
  94. data/spec/models/spree/avalara_preference_spec.rb +173 -0
  95. data/spec/models/spree/avalara_transaction_spec.rb +203 -0
  96. data/spec/models/spree/calculator/avalara_transaction_spec.rb +129 -0
  97. data/spec/models/spree/line_item_decorator_spec.rb +33 -0
  98. data/spec/models/spree/order_decorator_spec.rb +164 -0
  99. data/spec/models/spree/payment_decorator_spec.rb +117 -0
  100. data/spec/models/spree/refund_decorator_spec.rb +81 -0
  101. data/spec/models/spree/shipment_decorator_spec.rb +36 -0
  102. data/spec/models/spree/shipping_rate_decorator_spec.rb +17 -0
  103. data/spec/models/spree/user_decorator_spec.rb +5 -0
  104. data/spec/spec_helper.rb +81 -0
  105. data/spec/support/avatax_config_preferences.rb +8 -0
  106. data/spec/support/vcr.rb +9 -0
  107. data/spec/vcr/Certification/Exempt_sales_should_be_reflected_in_the_test_data_through_use_of_ExemptionNo_or_CustomerUsageType_/does_not_add_additional_tax.yml +276 -0
  108. data/spec/vcr/Certification/Transactions_have_been_committed_/commits_an_order.yml +336 -0
  109. data/spec/vcr/Certification/Transactions_have_been_voided/cancelled_/should_be_successful/returns_ResultCode_with_value_Success.yml +522 -0
  110. data/spec/vcr/Certification/return_orders/_commit_avatax_final/should_commit_avatax_final.yml +404 -0
  111. data/spec/vcr/Checkout/complete_order/has_tax_and_shipping_tax_adjustments_on_page.yml +72 -0
  112. data/spec/vcr/Checkout/complete_order/order_line_items_and_shipments_have_an_additional_tax_total_sum_of_0_40.yml +72 -0
  113. data/spec/vcr/Checkout/payment/tax_included/has_tax_and_shipping_tax_adjustments_on_page.yml +72 -0
  114. data/spec/vcr/Checkout/payment/tax_included/order_line_items_and_shipments_have_an_included_tax_total_sum_of_0_38.yml +72 -0
  115. data/spec/vcr/Checkout/payment/tax_not_included/on_payment_page/has_tax_and_shipping_tax_adjustments_on_page.yml +72 -0
  116. data/spec/vcr/Checkout/payment/tax_not_included/on_payment_page/order_line_items_and_shipments_and_shipments_have_an_additional_tax_total_sum_of_0_40.yml +72 -0
  117. data/spec/vcr/Checkout/payment/with_promotion/tax_not_included/has_adjusted_tax_amount_after_promotion_applied.yml +73 -0
  118. data/spec/vcr/SolidusAvataxCertified_Address/_validate/fails_when_information_is_incorrect.yml +54 -0
  119. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_included_in_price/should_be_equal_to_the_item_pre-tax_total_rate.yml +137 -0
  120. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_included_in_price/should_be_equal_to_the_previous_included_tax_total_is_order_is_at_address.yml +138 -0
  121. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_included_in_price/should_be_equal_to_the_previous_included_tax_total_is_order_is_at_cart.yml +137 -0
  122. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_not_included_in_price/should_be_equal_to_the_item_pre-tax_total_rate.yml +136 -0
  123. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_not_included_in_price/should_be_equal_to_the_previous_additional_tax_total_if_order_is_at_address.yml +137 -0
  124. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_not_included_in_price/should_be_equal_to_the_previous_additional_tax_total_if_order_is_at_cart.yml +136 -0
  125. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_not_included_in_price/should_be_equal_to_the_previous_tax_total_if_preference_tax_calculation_is_false.yml +136 -0
  126. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_not_included_in_price/when_the_line_item_is_discounted/should_be_equal_to_the_items_pre-tax_total_rate.yml +276 -0
  127. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_computing_a_line_item/when_tax_is_not_included_in_price/when_the_order_is_discounted/should_be_equal_to_the_items_pre-tax_total_rate.yml +206 -0
  128. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_given_a_shipment/computing_normal_shipment/should_be_equal_4_0.yml +141 -0
  129. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_given_a_shipment/when_tax_is_included_in_price/should_be_equal_to_3_85.yml +143 -0
  130. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_given_a_shipment/with_promotion/should_equal_3_96.yml +210 -0
  131. data/spec/vcr/Spree_Calculator_AvalaraTransaction/_compute/when_given_an_order/when_computing_an_order/should_raise_error.yml +136 -0
  132. data/spec/vcr/Spree_Order/_cancel_avalara/should_receive_cancel_order_when_cancel_avalara_is_called.yml +136 -0
  133. data/spec/vcr/Spree_Order/_cancel_avalara/state_machine_event_cancel/should_recieve_cancel_avalara_when_event_cancel_is_called.yml +259 -0
  134. data/spec/vcr/Spree_Order/_validate_ship_address/should_return_false_if_validation_failed.yml +54 -0
  135. data/spec/vcr/Spree_Order/_validate_ship_address/should_return_the_response_if_refuse_checkout_on_address_validation_is_disabled.yml +59 -0
  136. data/spec/vcr/Spree_Order/_validate_ship_address/should_return_the_response_if_validation_is_success.yml +59 -0
  137. data/spec/vcr/Spree_Payment/_avalara_finalize/should_update_the_amount_to_be_the_order_total.yml +136 -0
  138. data/spec/vcr/Spree_Payment/_cancel_avalara/committed_order/should_be_successful/ResultCode_returns_Success.yml +205 -0
  139. data/spec/vcr/Spree_Payment/_cancel_avalara/uncommitted_order/should_fail/ResultCode_returns_Error.yml +191 -0
  140. data/spec/vcr/Spree_Payment/_purchase_/receive_avalara_finalize.yml +136 -0
  141. data/spec/vcr/Spree_Payment/_void_transaction_/receive_cancel_avalara.yml +136 -0
  142. data/spec/vcr/Spree_Refund/full_refund/returns_correct_tax_calculations.yml +270 -0
  143. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_in_another_EU_country/Seller_does_not_have_Nexus_Jurisdition_Registered/TotalTax_is_equal_to_order_included_tax_total.yml +137 -0
  144. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_in_another_EU_country/Seller_does_not_have_Nexus_Jurisdition_Registered/origin_country_tax_is_returned.yml +138 -0
  145. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_in_another_EU_country/Seller_does_not_have_Nexus_Jurisdition_Registered/with_BusinessIdentificationNo/origin_country_zero_rate_is_returned.yml +143 -0
  146. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_in_another_EU_country/Seller_has_Nexus_Jurisdition_Registered/TotalTax_is_equal_to_order_included_tax_total.yml +139 -0
  147. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_in_another_EU_country/Seller_has_Nexus_Jurisdition_Registered/destination_country_tax_is_returned.yml +139 -0
  148. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_in_another_EU_country/Seller_has_Nexus_Jurisdition_Registered/with_BusinessIdentificationNo/origin_country_zero_rate_is_returned.yml +144 -0
  149. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_in_same_EU_country/TotalTax_is_equal_to_order_included_tax_total.yml +137 -0
  150. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_in_same_EU_country/tax_detail_country_equals_to_IT.yml +138 -0
  151. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_is_outside_EU/Seller_does_not_have_Nexus_Jurisdition_registered/TotalTax_is_equal_to_order_included_tax_total.yml +137 -0
  152. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_is_outside_EU/Seller_does_not_have_Nexus_Jurisdition_registered/tax_detail_country_equals_to_IT.yml +137 -0
  153. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_is_outside_EU/Seller_does_not_have_Nexus_Jurisdition_registered/total_tax_is_0_since_utah_is_not_in_jurisdiction.yml +137 -0
  154. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_is_outside_EU/Seller_does_not_have_Nexus_Jurisdition_registered/with_BusinessIdentificationNo/origin_country_zero_rate_is_returned.yml +138 -0
  155. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_is_outside_EU/Seller_has_Nexus_Jurisdiction_Registered/TotalTax_is_equal_to_order_included_tax_total.yml +195 -0
  156. data/spec/vcr/VAT/Seller_in_EU_country_Buyer_is_outside_EU/Seller_has_Nexus_Jurisdiction_Registered/tax_detail_region_equals_to_AL.yml +195 -0
  157. data/spec/vcr/address_validation_success.yml +59 -0
  158. data/spec/vcr/capture_with_customer_usage_type.yml +72 -0
  159. data/spec/vcr/order_cancel.yml +258 -0
  160. data/spec/vcr/order_capture.yml +72 -0
  161. data/spec/vcr/order_capture_finalize.yml +141 -0
  162. data/spec/vcr/order_capture_with_promo.yml +73 -0
  163. data/spec/vcr/order_return_capture.yml +68 -0
  164. data/spec/vcr/tax_included_order.yml +73 -0
  165. metadata +581 -0
@@ -0,0 +1,69 @@
1
+ module SolidusAvataxCertified
2
+ class PreferenceSeeder
3
+ BOOLEAN_PREFERENCES = ['log', 'address_validation', 'tax_calculation', 'document_commit', 'log_to_stdout', 'refuse_checkout_address_validation_error', 'customer_can_validate'].freeze
4
+ STORABLE_ENV_PREFERENCES = ['company_code', 'endpoint', 'account', 'license_key'].freeze
5
+
6
+ class << self
7
+
8
+ def seed!(print_messages=true)
9
+ @print_messages = print_messages
10
+ stored_env_prefs
11
+ boolean_prefs
12
+ enabled_countries_pref
13
+ origin_address
14
+ end
15
+
16
+ protected
17
+
18
+ def stored_env_prefs
19
+ STORABLE_ENV_PREFERENCES.each do |env|
20
+ if !ENV["AVATAX_#{env.upcase}"].blank?
21
+ value = ENV["AVATAX_#{env.upcase}"]
22
+ else
23
+ value = nil
24
+ end
25
+
26
+ pref = Spree::AvalaraPreference.new(name: env, value: value, object_type: 'string')
27
+
28
+ save_preference(pref)
29
+ end
30
+ end
31
+
32
+ def boolean_prefs
33
+ BOOLEAN_PREFERENCES.each do |preference|
34
+ if ['refuse_checkout_address_validation_error', 'log_to_stdout'].include?(preference)
35
+ pref = Spree::AvalaraPreference.new(name: preference, value: 'false', object_type: 'boolean')
36
+ else
37
+ pref = Spree::AvalaraPreference.new(name: preference, value: 'true', object_type: 'boolean')
38
+ end
39
+
40
+ save_preference(pref)
41
+ end
42
+ end
43
+
44
+ def enabled_countries_pref
45
+ pref = Spree::AvalaraPreference.new(name: 'address_validation_enabled_countries', value: 'United States,Canada', object_type: 'array')
46
+ save_preference(pref)
47
+ end
48
+
49
+ def origin_address
50
+ pref = Spree::AvalaraPreference.find_or_create_by(name: 'origin_address', object_type: 'json')
51
+ pref.value = "{}" if pref.value.nil?
52
+
53
+ save_preference(pref)
54
+ end
55
+
56
+ def save_preference(preference)
57
+ if preference.save
58
+ success_message(preference) if @print_messages
59
+ else
60
+ puts "#{preference.errors.full_messages.to_sentence}"
61
+ end
62
+ end
63
+
64
+ def success_message(preference)
65
+ puts "Created: #{preference.name} - #{preference.value ? preference.value : 'Please input value in avalara settings!'}"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,117 @@
1
+ module SolidusAvataxCertified
2
+ class Seeder
3
+ class << self
4
+
5
+ def seed!
6
+ create_use_codes
7
+ create_tax
8
+ add_tax_category_to_shipping_methods
9
+ add_tax_category_to_products
10
+ populate_default_stock_location
11
+
12
+ puts "***** SOLIDUS AVATAX CERTIFIED *****"
13
+ puts ""
14
+ puts "Please remember to:"
15
+ puts "- Add tax category to all shipping methods that need to be taxed."
16
+ puts "- Don't assign anything default tax."
17
+ puts "- Assign proper Tax Category to each product"
18
+ puts "- Fill in Stock Location Address."
19
+ puts "- Fill Origin Address in Avatax Settings."
20
+ puts ""
21
+ puts "***** SOLIDUS AVATAX CERTIFIED *****"
22
+ end
23
+
24
+ def seed_use_codes!
25
+ create_use_codes
26
+ puts "***** SOLIDUS AVATAX CERTIFIED: Use Codes Seeded"
27
+ end
28
+
29
+ def create_tax
30
+ default_tax_category = Spree::TaxCategory.find_by(name: 'Default')
31
+ default_tax_rate = Spree::TaxRate.find_by(name: 'North America')
32
+
33
+ default_tax_category.destroy if default_tax_category
34
+ default_tax_rate.destroy if default_tax_rate
35
+
36
+ clothing = Spree::TaxCategory.find_or_create_by(name: 'Clothing')
37
+ clothing.update_attributes(tax_code: 'P0000000')
38
+ tax_zone = Spree::Zone.find_or_create_by(name: 'North America')
39
+ tax_calculator = Spree::Calculator::AvalaraTransaction.create!
40
+ sales_tax = Spree::TaxRate.find_or_create_by(name: 'Tax') do |tax_rate|
41
+ # default values for the create
42
+ tax_rate.amount = BigDecimal.new('0')
43
+ tax_rate.calculator = tax_calculator
44
+ tax_rate.tax_category = clothing
45
+ end
46
+ sales_tax.update!(tax_category: clothing, name: 'Tax', amount: BigDecimal.new('0'), zone: tax_zone, show_rate_in_label: false, calculator: tax_calculator)
47
+
48
+ shipping = Spree::TaxCategory.find_or_create_by(name: 'Shipping', tax_code: 'FR000000')
49
+ shipping_tax = Spree::TaxRate.find_or_create_by(name: 'Shipping Tax') do |shipping_tax|
50
+ shipping_tax.tax_category = shipping
51
+ shipping_tax.amount = BigDecimal.new('0')
52
+ shipping_tax.zone = tax_zone
53
+ shipping_tax.show_rate_in_label = false
54
+ end
55
+ shipping_tax.update!(tax_category: shipping, amount: BigDecimal.new('0'), zone: Spree::Zone.find_by_name('North America'), show_rate_in_label: false, calculator: Spree::Calculator::AvalaraTransaction.create!)
56
+ end
57
+
58
+ def add_tax_category_to_shipping_methods
59
+ Spree::ShippingMethod.update_all(tax_category_id: Spree::TaxCategory.find_by(name: 'Shipping').id)
60
+ end
61
+
62
+ def add_tax_category_to_products
63
+ Spree::Product.update_all(tax_category_id: Spree::TaxCategory.find_by(name: 'Clothing').id)
64
+ end
65
+
66
+ def populate_default_stock_location
67
+ default = Spree::StockLocation.find_or_create_by(name: 'default')
68
+
69
+ return unless default.zipcode.nil? || default.address1.nil?
70
+
71
+ state = Spree::State.find_by(name: 'Alabama')
72
+
73
+ address = {
74
+ address1: '915 S Jackson St',
75
+ city: 'Montgomery',
76
+ state: state,
77
+ country: state.country,
78
+ zipcode: '36104',
79
+ default: true,
80
+ name: 'default',
81
+ backorderable_default: true
82
+ }
83
+
84
+ default.update_attributes(address)
85
+ end
86
+
87
+ def create_use_codes
88
+ unless Spree::AvalaraEntityUseCode.count >= 16
89
+ use_codes.each do |key, value|
90
+ Spree::AvalaraEntityUseCode.find_or_create_by(use_code: key, use_code_description: value)
91
+ end
92
+ end
93
+ end
94
+
95
+ def use_codes
96
+ {
97
+ "A" => "Federal government",
98
+ "B" => "State government",
99
+ "C" => "Tribe/Status Indian/Indian Band",
100
+ "D" => "Foreign diplomat",
101
+ "E" => "Charitable or benevolent organization",
102
+ "F" => "Religious or educational organization",
103
+ "G" => "Resale",
104
+ "H" => "Commercial agricultural production",
105
+ "I" => "Industrial production/manufacturer",
106
+ "J" => "Direct pay permit",
107
+ "K" => "Direct mail",
108
+ "L" => "Other",
109
+ "N" => "Local government",
110
+ "P" => "Commercial aquaculture (Canada only)",
111
+ "Q" => "Commercial fishery (Canada only)",
112
+ "R" => "Non-resident (Canada only)"
113
+ }
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,2 @@
1
+ require 'spree_core'
2
+ require 'solidus_avatax_certified/engine'
@@ -0,0 +1,15 @@
1
+ namespace :solidus_avatax_certified do
2
+ desc "Loads seed data."
3
+ task load_seeds: :environment do
4
+ SolidusAvataxCertified::Seeder.seed!
5
+ SolidusAvataxCertified::PreferenceSeeder.seed!
6
+ end
7
+
8
+ task load_preferences: :environment do
9
+ SolidusAvataxCertified::PreferenceSeeder.seed!
10
+ end
11
+
12
+ task load_use_codes: :environment do
13
+ SolidusAvataxCertified::Seeder.seed_use_codes!
14
+ end
15
+ end
data/setup.md ADDED
@@ -0,0 +1,12 @@
1
+ Setup Steps
2
+ ===========
3
+
4
+ * Setup Avalara gem
5
+ * Create config/initializer/avalara.rb
6
+ * Set up avalara username, password, endpoint, and avatax origin
7
+
8
+ Avatax Gem Setup
9
+ ================
10
+ * bundle exec rails g solidus_avatax_certified:install
11
+ * Stock location needs to be set up in order for gem to work
12
+ * Fill in Avalara Settings information
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+ gem_version = File.read(File.expand_path('../GEM_VERSION',__FILE__)).strip
3
+
4
+ Gem::Specification.new do |s|
5
+ s.platform = Gem::Platform::RUBY
6
+ s.name = 'solidus_avatax_certified'
7
+ s.version = gem_version
8
+ s.summary = 'Solidus extension for Avalara tax calculation.'
9
+ s.description = 'Solidus extension for Avalara tax calculation.'
10
+ s.required_ruby_version = '>= 2.1'
11
+
12
+ s.author = 'Allison Reilly'
13
+ s.email = 'acreilly3@gmail.com'
14
+ s.homepage = 'http://www.boomer.digital'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.require_path = "lib"
19
+ s.requirements << "none"
20
+
21
+ s.add_dependency "solidus_core", [">= 1.0.0", "< 2.1.0"]
22
+ s.add_dependency "json", "~> 1.8"
23
+ s.add_dependency "addressable", "~> 2.4"
24
+ s.add_dependency "rest-client", "~> 1.7"
25
+ s.add_dependency "logging", "~> 2.0"
26
+
27
+ s.add_development_dependency "dotenv"
28
+ s.add_development_dependency "rspec-rails", "~> 3.2"
29
+ s.add_development_dependency "simplecov"
30
+ s.add_development_dependency "sqlite3"
31
+ s.add_development_dependency "sass-rails"
32
+ s.add_development_dependency "coffee-rails"
33
+ s.add_development_dependency "factory_girl"
34
+ s.add_development_dependency "selenium-webdriver", "~> 2.53.4"
35
+ s.add_development_dependency "capybara"
36
+ s.add_development_dependency "capybara-screenshot"
37
+ s.add_development_dependency 'capybara-accessible'
38
+ s.add_development_dependency "database_cleaner"
39
+ s.add_development_dependency 'shoulda-matchers', '~> 2.7'
40
+ s.add_development_dependency 'vcr'
41
+ s.add_development_dependency 'webmock'
42
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Admin::AvalaraEntityUseCodesController do
4
+ let(:avalara_entity_use_code) { FactoryGirl.create(:avalara_entity_use_code) }
5
+
6
+ stub_authorization!
7
+
8
+ before :each do
9
+ DatabaseCleaner.clean
10
+ end
11
+
12
+ describe '#index' do
13
+ subject { get :index }
14
+
15
+ it { is_expected.to be_success }
16
+ end
17
+
18
+ describe '#show' do
19
+ subject { get :show, params: { id: avalara_entity_use_code.id } }
20
+
21
+ it { is_expected.to be_success }
22
+ end
23
+
24
+ describe '#new' do
25
+ subject { get :new }
26
+
27
+ it { is_expected.to be_success }
28
+ end
29
+
30
+ describe '#edit' do
31
+ subject { get :edit, params: { id: avalara_entity_use_code.id} }
32
+
33
+ it { is_expected.to be_success }
34
+ end
35
+
36
+ describe '#update' do
37
+ let(:params) do
38
+ {
39
+ id: avalara_entity_use_code.to_param,
40
+ avalara_entity_use_code: {
41
+ use_code: '55',
42
+ }
43
+ }
44
+ end
45
+ subject { put :update, params: params }
46
+
47
+ it { is_expected.to redirect_to(spree.admin_avalara_entity_use_codes_path) }
48
+
49
+ it 'should update use_code' do
50
+ expect{subject}.to change { avalara_entity_use_code.reload.use_code }.from('A').to('55')
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Admin::AvataxSettingsController, :type => :controller do
4
+
5
+ stub_authorization!
6
+
7
+ describe '/avatax_settings' do
8
+ subject { get :show }
9
+ it { should be_success }
10
+ end
11
+
12
+ describe '/avatax_settings/edit' do
13
+ subject { get :edit }
14
+ it { should be_success }
15
+ end
16
+
17
+ describe '/avatax_settings/get_file_post_order_to_avalara' do
18
+ before { File.new("#{Rails.root}/log/post_order_to_avalara.log", 'w') }
19
+ after { File.delete("#{Rails.root}/log/post_order_to_avalara.log") }
20
+
21
+ subject { get :get_file_post_order_to_avalara }
22
+ it { should be_success }
23
+ end
24
+
25
+ describe '/avatax_settings/erase_data' do
26
+ it 'erases the log' do
27
+ Dir.mkdir('log') unless Dir.exist?('log')
28
+ file = File.open("log/test.log", 'w') { |f| f.write('Hyah!') }
29
+
30
+ expect(File.read('log/test.log')).to eq('Hyah!')
31
+
32
+ get :erase_data, params: { log_name: 'test' }
33
+
34
+ expect(File.read('log/test.log')).to eq('')
35
+ end
36
+ end
37
+
38
+ describe '/avatax_settings/ping_my_service' do
39
+ it 'flashes message' do
40
+ subject { get :ping_my_service }
41
+ response.should be_success
42
+ flash.should_not be_nil
43
+ end
44
+ end
45
+
46
+ describe '#update' do
47
+ let(:params) do
48
+ {
49
+ address: {
50
+ Line1: "",
51
+ Line2: "",
52
+ City: "",
53
+ Region: "",
54
+ PostalCode: "",
55
+ Country: ""
56
+ },
57
+ settings: {
58
+ account: '123456789',
59
+ address_validation_enabled_countries: []
60
+ }
61
+ }
62
+ end
63
+ subject { put :update, params: params }
64
+
65
+ it { is_expected.to redirect_to(spree.admin_avatax_settings_path) }
66
+ end
67
+ end
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :avalara_entity_use_code, class: Spree::AvalaraEntityUseCode do
3
+ use_code 'A'
4
+ use_code_description 'Federal government'
5
+ end
6
+ end
@@ -0,0 +1,76 @@
1
+ FactoryGirl.define do
2
+ factory :request_hash, class: Hash do
3
+ Commit false
4
+ CustomerCode '1'
5
+ DocDate '2014-11-03'
6
+ CompanyCode '54321'
7
+ CustomerUsageType ''
8
+ ExemptionNo nil
9
+ Client AVATAX_CLIENT_VERSION
10
+ DocCode 'R731071205'
11
+ ReferenceCode 'R731071205'
12
+ DetailLevel 'Tax'
13
+ DocType 'SalesOrder'
14
+ Discount '0.00'
15
+ Addresses [{:AddressCode=>'9', :Line1=>'31 South St', :City=>'Morristown', :PostalCode=>'07960', :Country=>'US'},{:AddressCode=>'Dest', :Line1=>'73 Glenmere Drive', :Line2=>'', :City=>'Chatham', :Region=>'NJ', :Country=>'US', :PostalCode=>'07928'},{:AddressCode=>'Orig', :Line1=>'73 Glenmere Drive', :City=>'Chatham', :PostalCode=>'07928', :Country=>'United States'}]
16
+ Lines [{:LineNo=>'1-LI', :ItemCode=>'ROR-00013', :Qty=>3, :Amount=>62.97, :OriginCode=>'9', :DestinationCode=>'Dest', :Description=>'Ruby on Rails Jr. Spaghetti', :TaxCode=>'P0000000', :Discounted=>false}]
17
+ end
18
+
19
+ factory :avalara_transaction_calculator, class: Spree::Calculator::AvalaraTransaction do
20
+ end
21
+ end
22
+
23
+ FactoryGirl.modify do
24
+ factory :tax_category, class: Spree::TaxCategory do
25
+ name { "TaxCategory - #{rand(999999)}" }
26
+ tax_code { 'PC030000' }
27
+ end
28
+
29
+ factory :address, class: Spree::Address do
30
+ transient do
31
+ country_iso_code 'US'
32
+ state_code 'AL'
33
+ end
34
+
35
+ firstname 'John'
36
+ lastname 'Doe'
37
+ company 'Company'
38
+ address1 '915 S Jackson St'
39
+ address2 ''
40
+ city 'Montgomery'
41
+ state_name 'Alabama'
42
+ zipcode '36104'
43
+ phone '555-555-0199'
44
+ alternative_phone '555-555-0199'
45
+
46
+ state do |address|
47
+ if !Spree::State.find_by(name: address.state_name).nil?
48
+ Spree::State.find_by(name: address.state_name)
49
+ else
50
+ address.association(:state)
51
+ end
52
+ end
53
+
54
+ country do |address|
55
+ if address.state
56
+ address.state.country
57
+ else
58
+ address.association(:country, iso: country_iso_code)
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ factory :ship_address, parent: :address do
65
+ address1 '915 S Jackson St'
66
+ end
67
+
68
+ # Adding this modifier since US country name changes between solidus versions
69
+ factory :country, class: Spree::Country do
70
+ iso_name 'UNITED STATES'
71
+ name 'United States'
72
+ iso 'US'
73
+ iso3 'USA'
74
+ numcode 840
75
+ end
76
+ end
@@ -0,0 +1,62 @@
1
+ FactoryGirl.define do
2
+ factory :avalara_order, class: Spree::Order do
3
+ user
4
+ bill_address
5
+ ship_address
6
+ completed_at nil
7
+ email { user.email }
8
+ store
9
+ state 'delivery'
10
+
11
+ transient do
12
+ line_items_price BigDecimal.new(10)
13
+ line_items_count 1
14
+ line_items_quantity 1
15
+ shipment_cost 5
16
+ tax_category Spree::TaxCategory.first
17
+ tax_included false
18
+ end
19
+
20
+ before(:create) do |order, evaluator|
21
+ if Spree::Country.count == 0
22
+ create(:country)
23
+ end
24
+ if Spree::Zone.find_by(name: 'GlobalZone').nil?
25
+ create(:global_zone, default_tax: true)
26
+ end
27
+ if Spree::TaxCategory.first.nil?
28
+ create(:clothing_tax_rate, tax_category: create(:tax_category), included_in_price: evaluator.tax_included)
29
+ else
30
+ create(:clothing_tax_rate, tax_category: Spree::TaxCategory.first, included_in_price: evaluator.tax_included)
31
+ end
32
+ end
33
+
34
+ after(:create) do |order, evaluator|
35
+ create_list(:line_item, evaluator.line_items_count, order: order, price: evaluator.line_items_price, tax_category: evaluator.tax_category, quantity: evaluator.line_items_quantity)
36
+ order.line_items.reload
37
+
38
+ create(:avalara_shipment, order: order, cost: evaluator.shipment_cost, tax_included: evaluator.tax_included)
39
+ order.shipments.reload
40
+
41
+ order.update!
42
+ order.next
43
+ end
44
+
45
+ factory :completed_avalara_order do
46
+ shipment_state 'shipped'
47
+ payment_state 'paid'
48
+
49
+ after(:create) do |order|
50
+ # order.refresh_shipment_rates
51
+ order.update_column(:completed_at, Time.now)
52
+ order.update_column(:state, 'complete')
53
+ payment = create(:credit_card_payment, amount: order.total, order: order, state: 'completed')
54
+
55
+ order.update!
56
+ order.next
57
+
58
+ payment.avalara_finalize
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,7 @@
1
+ FactoryGirl.define do
2
+ factory :avalara_preference, class: Spree::AvalaraPreference do
3
+ name { "AvalaraPreference - #{rand(999999)}" }
4
+ value '54321'
5
+ object_type 'string'
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ FactoryGirl.define do
2
+ factory :avalara_shipment, class: Spree::Shipment do
3
+ tracking 'U10000'
4
+ cost BigDecimal.new(10)
5
+ state 'pending'
6
+ order
7
+ stock_location
8
+
9
+ transient do
10
+ tax_included false
11
+ end
12
+
13
+ after(:create) do |shipment, evalulator|
14
+ shipment.add_shipping_method(create(:avalara_shipping_method, tax_included: evalulator.tax_included), true)
15
+
16
+ shipment.order.line_items.each do |line_item|
17
+ line_item.quantity.times do
18
+ shipment.inventory_units.create(
19
+ order_id: shipment.order_id,
20
+ variant_id: line_item.variant_id,
21
+ line_item_id: line_item.id
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ FactoryGirl.define do
2
+ factory :avalara_shipping_method, class: Spree::ShippingMethod do
3
+ zones { |a| [Spree::Zone.find_by(name: 'GlobalZone') || create(:zone, :with_country, default_tax: true)] }
4
+ name 'Avalara Ground'
5
+ code 'Avalara_Ground'
6
+ association(:calculator, factory: :shipping_calculator, strategy: :create)
7
+
8
+ transient do
9
+ tax_included false
10
+ end
11
+
12
+ before(:create) do |shipping_method, evaluator|
13
+ if Spree::Country.count == 0
14
+ create(:country)
15
+ end
16
+ shipping_tax_rate = create(:shipping_tax_rate, included_in_price: evaluator.tax_included)
17
+ shipping_method.tax_category = shipping_tax_rate.tax_category
18
+ if shipping_method.shipping_categories.empty?
19
+ shipping_method.shipping_categories << (Spree::ShippingCategory.first || create(:shipping_category))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ FactoryGirl.define do
2
+ factory :clothing_tax_rate, class: Spree::TaxRate do
3
+ name 'Tax'
4
+ amount 0.0
5
+ tax_category { Spree::TaxCategory.find_by(tax_code: 'PC030000') || create(:tax_category) }
6
+ association(:calculator, factory: :avalara_transaction_calculator)
7
+ zone { Spree::Zone.find_or_create_by(name: 'GlobalZone') }
8
+ show_rate_in_label false
9
+ end
10
+
11
+ factory :shipping_tax_rate, class: Spree::TaxRate do
12
+ name 'Shipping Tax'
13
+ amount 0.0
14
+ tax_category { Spree::TaxCategory.find_by(tax_code: 'FR000000') || create(:tax_category, tax_code: 'FR000000') }
15
+ association(:calculator, factory: :avalara_transaction_calculator)
16
+ zone { Spree::Zone.find_or_create_by(name: 'GlobalZone') }
17
+ show_rate_in_label false
18
+ end
19
+ end