solidus_avatax_certified 1.0.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 (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