spree_avatax_official 1.9.0 → 2.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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/settings.local.json +8 -0
  3. data/.env-sample +3 -3
  4. data/.gem_release.yml +2 -0
  5. data/.github/workflows/ci.yml +80 -0
  6. data/.gitignore +1 -0
  7. data/Gemfile +12 -6
  8. data/README.md +1 -1
  9. data/app/assets/images/integration_icons/avalara-logo.png +0 -0
  10. data/app/controllers/spree/admin/avalara_entity_use_codes_controller.rb +11 -47
  11. data/app/controllers/spree/admin/avatax_customer_settings_controller.rb +43 -0
  12. data/app/controllers/spree_avatax_official/admin/base_controller_decorator.rb +11 -0
  13. data/app/helpers/spree_avatax_official/avalara_integration_helper.rb +10 -0
  14. data/app/helpers/spree_avatax_official/tax_adjustment_label_helper.rb +5 -3
  15. data/app/models/spree/integrations/avalara.rb +61 -0
  16. data/app/models/spree_avatax_official/calculator/avatax_transaction_calculator.rb +0 -3
  17. data/app/models/spree_avatax_official/configuration.rb +0 -1
  18. data/app/models/spree_avatax_official/entity_use_code.rb +5 -4
  19. data/app/models/spree_avatax_official/spree/address_decorator.rb +2 -8
  20. data/app/models/spree_avatax_official/spree/line_item_decorator.rb +1 -7
  21. data/app/models/spree_avatax_official/spree/order_decorator.rb +35 -13
  22. data/app/models/spree_avatax_official/spree/refund_decorator.rb +2 -2
  23. data/app/models/spree_avatax_official/spree/shipment_decorator.rb +8 -1
  24. data/app/models/spree_avatax_official/spree/user_decorator.rb +1 -1
  25. data/app/models/spree_avatax_official/transaction.rb +3 -2
  26. data/app/presenters/spree_avatax_official/item_presenter.rb +16 -10
  27. data/app/presenters/spree_avatax_official/transactions/amount_refund_presenter.rb +35 -0
  28. data/app/presenters/spree_avatax_official/transactions/create_presenter.rb +31 -27
  29. data/app/services/spree_avatax_official/address/validate.rb +13 -4
  30. data/app/services/spree_avatax_official/base.rb +12 -12
  31. data/app/services/spree_avatax_official/create_tax_adjustments_service.rb +5 -5
  32. data/app/services/spree_avatax_official/generate_transaction_cache_key_service.rb +16 -6
  33. data/app/services/spree_avatax_official/get_tax_service.rb +1 -1
  34. data/app/services/spree_avatax_official/transactions/adjust_service.rb +1 -1
  35. data/app/services/spree_avatax_official/transactions/amount_refund_service.rb +31 -0
  36. data/app/services/spree_avatax_official/transactions/create_service.rb +2 -2
  37. data/app/services/spree_avatax_official/transactions/full_refund_service.rb +1 -1
  38. data/app/services/spree_avatax_official/transactions/get_by_code_service.rb +1 -1
  39. data/app/services/spree_avatax_official/transactions/partial_refund_service.rb +1 -1
  40. data/app/services/spree_avatax_official/transactions/refund_service.rb +15 -5
  41. data/app/services/spree_avatax_official/transactions/void_service.rb +1 -1
  42. data/app/views/spree/admin/avalara_entity_use_codes/_form.html.erb +5 -18
  43. data/app/views/spree/admin/avalara_entity_use_codes/edit.html.erb +1 -12
  44. data/app/views/spree/admin/avalara_entity_use_codes/index.html.erb +6 -28
  45. data/app/views/spree/admin/avalara_entity_use_codes/new.html.erb +1 -12
  46. data/app/views/spree/admin/avatax_customer_settings/_form.html.erb +18 -0
  47. data/app/views/spree/admin/avatax_customer_settings/edit.html.erb +3 -0
  48. data/app/views/spree/admin/integrations/forms/_avalara.html.erb +33 -0
  49. data/app/views/spree/admin/shared/_avatax_customer_settings_link.html.erb +12 -0
  50. data/config/initializers/spree_avatax_official_navigation.rb +42 -0
  51. data/config/initializers/spree_permitted_attributes.rb +1 -1
  52. data/config/locales/en.yml +27 -22
  53. data/config/routes.rb +3 -9
  54. data/db/migrate/20190118155953_add_exemption_number_to_spree_users.rb +1 -1
  55. data/db/migrate/20190118160653_create_spree_avalara_entity_use_codes.rb +1 -1
  56. data/db/migrate/20190118161318_add_avalara_entity_use_code_reference_to_users.rb +1 -1
  57. data/db/migrate/20190118162345_add_vat_id_to_spree_users.rb +1 -1
  58. data/db/migrate/20190122154119_create_spree_avatax_official_transactions.rb +1 -1
  59. data/db/migrate/20190201123706_add_tax_code_to_spree_tax_categories.rb +1 -1
  60. data/db/migrate/20190208130136_add_avatax_company_code_to_spree_stores.rb +1 -1
  61. data/db/migrate/20190225145808_add_uuid_to_spree_line_items_and_spree_shipments.rb +1 -1
  62. data/db/migrate/20191001085458_remove_spree_avalara_entity_use_codes_table.rb +1 -1
  63. data/db/migrate/20191001092353_create_spree_avatax_official_entity_use_codes_table.rb +5 -6
  64. data/lib/generators/spree_avatax_official/install/templates/config/initializers/spree_avatax_official.rb +16 -9
  65. data/lib/spree_avatax_official/configuration.rb +7 -16
  66. data/lib/spree_avatax_official/engine.rb +1 -2
  67. data/lib/spree_avatax_official/factories.rb +2 -8
  68. data/lib/spree_avatax_official/seeder.rb +1 -1
  69. data/lib/spree_avatax_official/testing_support/factories/avalara_integration.rb +13 -0
  70. data/lib/spree_avatax_official/version.rb +1 -1
  71. data/lib/spree_avatax_official.rb +2 -3
  72. data/spec/controllers/spree/admin/avalara_entity_use_codes_controller_spec.rb +64 -0
  73. data/spec/controllers/spree/admin/avatax_customer_settings_controller_spec.rb +57 -0
  74. data/spec/factories/spree_avatax_official/avalara_entity_use_code_factory.rb +3 -1
  75. data/spec/helpers/spree_avatax_official/tax_adjustment_label_helper_spec.rb +13 -14
  76. data/spec/models/spree/integrations/avalara_spec.rb +75 -0
  77. data/spec/models/spree/line_item_spec.rb +0 -65
  78. data/spec/models/spree/order_spec.rb +36 -28
  79. data/spec/models/spree/shipment_spec.rb +33 -0
  80. data/spec/models/spree_avatax_official/spree/refund_decorator_spec.rb +4 -7
  81. data/spec/presenters/spree_avatax_official/address_presenter_spec.rb +11 -2
  82. data/spec/presenters/spree_avatax_official/item_presenter_spec.rb +32 -8
  83. data/spec/presenters/spree_avatax_official/transactions/amount_refund_presenter_spec.rb +44 -0
  84. data/spec/presenters/spree_avatax_official/transactions/create_presenter_spec.rb +39 -19
  85. data/spec/presenters/spree_avatax_official/transactions/partial_refund_presenter_spec.rb +17 -18
  86. data/spec/services/spree_avatax_official/address/validate_spec.rb +44 -2
  87. data/spec/services/spree_avatax_official/create_tax_adjustments_service_spec.rb +105 -179
  88. data/spec/services/spree_avatax_official/generate_transaction_cache_key_service_spec.rb +45 -1
  89. data/spec/services/spree_avatax_official/get_tax_service_spec.rb +11 -3
  90. data/spec/services/spree_avatax_official/transactions/adjust_service_spec.rb +13 -3
  91. data/spec/services/spree_avatax_official/transactions/amount_refund_service_spec.rb +55 -0
  92. data/spec/services/spree_avatax_official/transactions/create_service_spec.rb +77 -71
  93. data/spec/services/spree_avatax_official/transactions/find_order_transaction_service_spec.rb +1 -1
  94. data/spec/services/spree_avatax_official/transactions/full_refund_service_spec.rb +7 -3
  95. data/spec/services/spree_avatax_official/transactions/get_by_code_service_spec.rb +1 -1
  96. data/spec/services/spree_avatax_official/transactions/partial_refund_service_spec.rb +6 -4
  97. data/spec/services/spree_avatax_official/transactions/refund_service_spec.rb +88 -10
  98. data/spec/services/spree_avatax_official/transactions/void_service_spec.rb +13 -2
  99. data/spec/spec_helper.rb +14 -26
  100. data/spec/support/avatax.rb +0 -10
  101. data/spec/support/factory_bot.rb +4 -0
  102. data/spec/support/integration_helper.rb +16 -0
  103. data/spec/support/tax_inclusive_helper.rb +29 -0
  104. data/spec/support/vcr.rb +3 -13
  105. data/spec/vcr/SpreeAvataxOfficial_Transactions_FindOrderTransactionService/_call/with_Spree_Order/when_order_does_NOT_have_a_SalesInvoice_transaction/when_transaction_exists_in_avatax/returns_success_and_creates_a_new_SalesInvoice_transaction.yml +2 -47
  106. data/spec/vcr/spree/integrations/avalara/can_connect/authenticated.yml +65 -0
  107. data/spec/vcr/spree/integrations/avalara/can_connect/unauthenticated.yml +65 -0
  108. data/spec/vcr/spree_avatax_official/address/validate_failure.yml +48 -12
  109. data/spec/vcr/spree_avatax_official/address/validate_success.yml +123 -9
  110. data/spec/vcr/spree_avatax_official/address/zipcode_failure.yml +45 -9
  111. data/spec/vcr/spree_avatax_official/create_tax_adjustments/californian_stock_location.yml +242 -20
  112. data/spec/vcr/spree_avatax_official/create_tax_adjustments/completed_order_line_item_update.yml +156 -25
  113. data/spec/vcr/spree_avatax_official/create_tax_adjustments/order_adjustment_promotion.yml +360 -22
  114. data/spec/vcr/spree_avatax_official/create_tax_adjustments/shipment_adjustment_promotion.yml +335 -20
  115. data/spec/vcr/spree_avatax_official/create_tax_adjustments/simple_completed_order.yml +341 -22
  116. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_address_not_persisted.yml +110 -0
  117. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_excluded/line_item_adjustment_promotion.yml +362 -22
  118. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_excluded/line_item_and_shipment.yml +242 -20
  119. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_excluded/multiple_line_items_multiple_quantity.yml +165 -23
  120. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_included/line_item_adjustment_promotion.yml +362 -22
  121. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_included/line_item_and_shipment.yml +245 -23
  122. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_included/multiple_line_items_multiple_quantity.yml +165 -23
  123. data/spec/vcr/spree_avatax_official/create_tax_adjustments/with_incorrect_address.yml +119 -31
  124. data/spec/vcr/spree_avatax_official/get_tax/error.yml +48 -14
  125. data/spec/vcr/spree_avatax_official/get_tax/sucsess.yml +62 -21
  126. data/spec/vcr/spree_avatax_official/spree/shipment/selected_shipping_rate_change.yml +189 -0
  127. data/spec/vcr/spree_avatax_official/spree/shipment/selected_shipping_rate_change_completed.yml +96 -0
  128. data/spec/vcr/spree_avatax_official/transactions/adjust/failure.yml +107 -36
  129. data/spec/vcr/spree_avatax_official/transactions/adjust/invoice_order_success.yml +117 -54
  130. data/spec/vcr/spree_avatax_official/transactions/create/complete_order_success.yml +152 -23
  131. data/spec/vcr/spree_avatax_official/transactions/create/gst/canada_to_canada.yml +241 -18
  132. data/spec/vcr/spree_avatax_official/transactions/create/gst/canada_to_us.yml +154 -21
  133. data/spec/vcr/spree_avatax_official/transactions/create/gst/us_to_canada.yml +255 -19
  134. data/spec/vcr/spree_avatax_official/transactions/create/invoice_order_success.yml +150 -21
  135. data/spec/vcr/spree_avatax_official/transactions/create/sales_order_success.yml +150 -21
  136. data/spec/vcr/spree_avatax_official/transactions/create/timeout.yml +144 -15
  137. data/spec/vcr/spree_avatax_official/transactions/create/vat/europe_to_europe.yml +245 -18
  138. data/spec/vcr/spree_avatax_official/transactions/create/vat/europe_to_us.yml +153 -20
  139. data/spec/vcr/spree_avatax_official/transactions/create/vat/us_to_europe.yml +247 -19
  140. data/spec/vcr/spree_avatax_official/transactions/refund/amount_refund_success.yml +196 -0
  141. data/spec/vcr/spree_avatax_official/transactions/refund/full_refund_success.yml +121 -49
  142. data/spec/vcr/spree_avatax_official/transactions/refund/partial_refund_success.yml +57 -23
  143. data/spec/vcr/spree_avatax_official/transactions/refund/partial_refund_with_refund_success.yml +57 -23
  144. data/spec/vcr/spree_avatax_official/transactions/void/success.yml +117 -45
  145. data/spec/vcr/spree_order/california_order.yml +66 -28
  146. data/spec/vcr/spree_order/complete_order.yml +236 -23
  147. data/spec/vcr/spree_order/complete_order_no_transaction.yml +96 -0
  148. data/spec/vcr/spree_order/order_transition_to_completed.yml +58 -74
  149. data/spec/vcr/spree_order/order_with_line_item_adjustment.yml +151 -19
  150. data/spec/vcr/spree_order/order_with_order_adjustment.yml +151 -19
  151. data/spec/vcr/spree_order/simple_order_with_single_line_item_and_shipment.yml +60 -21
  152. data/spec/zeitwerk_spec.rb +9 -0
  153. data/spree_avatax_official.gemspec +4 -5
  154. metadata +58 -73
  155. data/.circleci/config.yml +0 -115
  156. data/app/controllers/spree/admin/avatax_pings_controller.rb +0 -19
  157. data/app/controllers/spree/admin/avatax_settings_controller.rb +0 -26
  158. data/app/controllers/spree/admin/users_controller_decorator.rb +0 -27
  159. data/app/overrides/spree/admin/shared/sub_menu/_configuration.rb +0 -10
  160. data/app/overrides/spree/admin/sidebar.rb +0 -14
  161. data/app/services/spree_avatax_official/settings/update_service.rb +0 -36
  162. data/app/services/spree_avatax_official/utilities/ping_service.rb +0 -9
  163. data/app/views/spree/admin/avatax_settings/edit.html.erb +0 -99
  164. data/app/views/spree/admin/users/avalara_information.html.erb +0 -39
  165. data/app/views/spree/admin/zones/_form.html.erb +0 -58
  166. data/db/migrate/20190329071439_add_included_in_price_to_spree_zones.rb +0 -5
  167. data/spec/features/admin/edit_avatax_settings_spec.rb +0 -11
  168. data/spec/features/admin/update_avatax_settings_spec.rb +0 -200
  169. data/spec/features/spree/admin/avalara_entity_use_codes_controller_spec.rb +0 -71
  170. data/spec/features/spree/admin/users_controller_spec.rb +0 -34
  171. data/spec/services/spree_avatax_official/settings/update_service_spec.rb +0 -131
  172. data/spec/services/spree_avatax_official/utilities/ping_service_spec.rb +0 -35
  173. data/spec/support/capybara.rb +0 -21
  174. data/spec/support/database_cleaner.rb +0 -25
  175. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_excluded/single_line_item.yml +0 -55
  176. data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_included/single_line_item.yml +0 -55
  177. data/spec/vcr/spree_avatax_official/utilities/ping.yml +0 -41
  178. data/spec/vcr/spree_avatax_official/utilities/ping_unauthorized.yml +0 -42
  179. data/spec/vcr/spree_line_item/update_tax_charge/decrease_quantity.yml +0 -55
  180. data/spec/vcr/spree_line_item/update_tax_charge/increase_quantity.yml +0 -55
  181. data/spec/vcr/spree_order/simple_order_with_single_line_item.yml +0 -55
  182. data/spec/vcr/spree_order/update_tax_charge/create_line_item.yml +0 -55
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d13bf7b694d8c9b6efe525686532f59b7e31549b53f0bd117f26b5064b95f70c
4
- data.tar.gz: c54628d389689c43ef49bb7e408bc4dad98a2a19a6b9478f53b4ee1b71df2fe4
3
+ metadata.gz: dbc764902a265aa63189cd4bd388a9d527c6dde67864a039519978c868adf4de
4
+ data.tar.gz: 9c3fca8394ddbd9b8191a97ca939a2ac0c3ac76945e15ed47b096001b5d1ae1b
5
5
  SHA512:
6
- metadata.gz: 8cc63f004da79486591e5f6ae4a55b9a412e9eb3bd0be3210d37eb24eca10c19654896fd7400b698a893c390c112869b256496d17122965e02519086d0cb5d61
7
- data.tar.gz: ae4c87f21dcc95427abeedd6c9000ce29717c47604518c93626bc60e0d9acd9684777ab131a8c1820cf7c6eb098007de37608f089e05d2817f4584c2e51c9a13
6
+ metadata.gz: 3fef2a3753c92f98eee99edf803d4796487427b41a58d842e1c81d6925cbc60d4c072d6f71f845c6ac2ca7a3553831432f58aade0a83b7d5cd4b45ae11933b2b
7
+ data.tar.gz: 75e78335343ca45ff5c1b178e8db0baf5a3dc52d57bc5652b4cad96425299425a374b3602845682fae6af02bd17b109b554c2600cdd71ec65032484e84c5643a
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "mcp__claude_ai_Spree_Commerce_Docs_MCP__search_spree_commerce_documentation",
5
+ "mcp__claude_ai_Spree_Commerce_Docs_MCP__get_page_spree_commerce_documentation"
6
+ ]
7
+ }
8
+ }
data/.env-sample CHANGED
@@ -1,4 +1,4 @@
1
1
  AVATAX_ENDPOINT=https://sandbox-rest.avatax.com
2
- AVATAX_USERNAME=
3
- AVATAX_PASSWORD=
4
- AVATAX_COMPANY_CODE=
2
+ AVATAX_ACCOUNT_NUMBER=
3
+ AVATAX_LICENSE_KEY=
4
+ AVATAX_COMPANY_CODE=test1
data/.gem_release.yml ADDED
@@ -0,0 +1,2 @@
1
+ bump:
2
+ file: "lib/spree_avatax_official/version.rb"
@@ -0,0 +1,80 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ tests_postgres:
11
+ name: Tests (PostgreSQL)
12
+ runs-on: ubuntu-latest
13
+ services:
14
+ postgres:
15
+ image: postgres:16
16
+ env:
17
+ POSTGRES_USER: postgres
18
+ POSTGRES_PASSWORD: postgres
19
+ ports:
20
+ - 5432:5432
21
+ options: >-
22
+ --health-cmd pg_isready
23
+ --health-interval 10s
24
+ --health-timeout 5s
25
+ --health-retries 5
26
+ env:
27
+ DB: postgres
28
+ DB_HOST: localhost
29
+ DB_USERNAME: postgres
30
+ DB_PASSWORD: postgres
31
+ BUNDLE_JOBS: 4
32
+ BUNDLE_RETRY: 3
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - name: Install libvips
36
+ run: sudo apt-get update && sudo apt-get install -y libvips
37
+ - name: Set up Ruby
38
+ uses: ruby/setup-ruby@v1
39
+ with:
40
+ ruby-version: '4.0'
41
+ bundler-cache: true
42
+ - name: Create test app
43
+ run: bundle exec rake test_app
44
+ - name: Run tests
45
+ run: bundle exec rspec --format documentation
46
+
47
+ tests_mysql:
48
+ name: Tests (MySQL)
49
+ runs-on: ubuntu-latest
50
+ services:
51
+ mysql:
52
+ image: mysql:8.0
53
+ env:
54
+ MYSQL_ALLOW_EMPTY_PASSWORD: yes
55
+ ports:
56
+ - 3306:3306
57
+ options: >-
58
+ --health-cmd "mysqladmin ping -h 127.0.0.1"
59
+ --health-interval 10s
60
+ --health-timeout 5s
61
+ --health-retries 5
62
+ env:
63
+ DB: mysql
64
+ DB_HOST: 127.0.0.1
65
+ DB_USERNAME: root
66
+ BUNDLE_JOBS: 4
67
+ BUNDLE_RETRY: 3
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+ - name: Install libvips
71
+ run: sudo apt-get update && sudo apt-get install -y libvips
72
+ - name: Set up Ruby
73
+ uses: ruby/setup-ruby@v1
74
+ with:
75
+ ruby-version: '4.0'
76
+ bundler-cache: true
77
+ - name: Create test app
78
+ run: bundle exec rake test_app
79
+ - name: Run tests
80
+ run: bundle exec rspec --format documentation
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ public/spree
20
20
  .ruby-version
21
21
  .ruby-gemset
22
22
  gemfiles/*.gemfile.lock
23
+ .env
data/Gemfile CHANGED
@@ -6,20 +6,26 @@ git_source(:github) do |repo_name|
6
6
  end
7
7
 
8
8
  gem 'rails-controller-testing'
9
- gem 'spree', github: 'spree/spree', branch: 'main'
10
- gem 'spree_backend', github: 'spree/spree_backend', branch: 'main'
11
- gem 'spree_emails', github: 'spree/spree', branch: 'main'
12
- gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: 'main'
9
+
10
+ spree_opts = if ENV['SPREE_PATH']
11
+ { 'path': ENV['SPREE_PATH'] }
12
+ else
13
+ { 'github': 'spree/spree', 'branch': 'main', 'glob': 'spree/**/*.gemspec' }
14
+ end
15
+ gem 'spree', spree_opts
16
+ gem 'spree_core', spree_opts
17
+ gem 'spree_admin', spree_opts
18
+
13
19
 
14
20
  if ENV['DB'] == 'mysql'
15
21
  gem 'mysql2'
16
22
  elsif ENV['DB'] == 'postgres'
17
23
  gem 'pg'
18
24
  else
19
- gem 'sqlite3', '~> 1.4'
25
+ gem 'sqlite3'
20
26
  end
21
27
 
22
- gem 'webdrivers'
28
+ gem 'propshaft'
23
29
  gem 'pry'
24
30
 
25
31
  gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Spree Avalara AvaTax official extension
2
2
 
3
- [![Build Status](https://travis-ci.org/spree-contrib/spree_avatax_official.svg?branch=master)](https://travis-ci.org/spree-contrib/spree_avatax_official)
3
+ [![CI](https://github.com/spree/spree_avatax_official/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/spree/spree_avatax_official/actions/workflows/ci.yml)
4
4
 
5
5
  The new officially supported Avalara AvaTax extension for [Spree Commerce](https://spreecommerce.org/) using [Avalara REST API v2](https://developer.avalara.com/api-reference/avatax/rest/v2/).
6
6
  * [Introduction](#introduction)
@@ -1,60 +1,24 @@
1
1
  module Spree
2
2
  module Admin
3
- class AvalaraEntityUseCodesController < Spree::Admin::BaseController
4
- before_action :load_use_code, only: %i[edit update destroy]
3
+ class AvalaraEntityUseCodesController < ResourceController
4
+ include Spree::Admin::SettingsConcern
5
5
 
6
- def index
7
- @use_codes = SpreeAvataxOfficial::EntityUseCode.all.order(code: :asc)
8
- respond_with(@use_codes)
9
- end
10
-
11
- def new
12
- @use_code = SpreeAvataxOfficial::EntityUseCode.new
13
- end
14
-
15
- def edit; end
16
-
17
- def update
18
- if @use_code.update(entity_use_codes_params)
19
- redirect_to admin_avalara_entity_use_codes_path, success: Spree.t('spree_avatax_official.entity_use_code_updated')
20
- else
21
- redirect_to edit_admin_avalara_entity_use_code_path(@use_code), error: @use_code.errors.full_messages.to_sentence
22
- end
23
- end
6
+ private
24
7
 
25
- def create
26
- @use_code = SpreeAvataxOfficial::EntityUseCode.new(entity_use_codes_params)
27
- if @use_code.save
28
- redirect_to admin_avalara_entity_use_codes_path, success: Spree.t('spree_avatax_official.entity_use_code_created')
29
- else
30
- redirect_to new_admin_avalara_entity_use_code_path, error: @use_code.errors.full_messages.to_sentence
31
- end
8
+ def model_class
9
+ SpreeAvataxOfficial::EntityUseCode
32
10
  end
33
11
 
34
- def destroy
35
- if @use_code.destroy
36
- flash[:success] = Spree.t('spree_avatax_official.entity_use_code_removed')
37
- else
38
- flash[:error] = @use_code.errors.full_messages.to_sentence
39
- end
40
-
41
- respond_with(@use_code) do |format|
42
- format.html { redirect_to admin_avalara_entity_use_codes_path }
43
- format.js { render_js_for_destroy }
44
- end
12
+ def permitted_resource_params
13
+ params.require(:avalara_entity_use_code).permit(:code, :name, :description)
45
14
  end
46
15
 
47
- private
48
-
49
- def load_use_code
50
- @use_code ||= SpreeAvataxOfficial::EntityUseCode.find(params[:id])
51
- rescue ActiveRecord::RecordNotFound => e
52
- flash[:error] = e.message
53
- redirect_to admin_avalara_entity_use_codes_path
16
+ def collection_url(options = {})
17
+ spree.admin_avalara_entity_use_codes_url(options)
54
18
  end
55
19
 
56
- def entity_use_codes_params
57
- params.require(:entity_use_code).permit(:code, :name, :description)
20
+ def new_object_url(options = {})
21
+ spree.new_admin_avalara_entity_use_code_url(options)
58
22
  end
59
23
  end
60
24
  end
@@ -0,0 +1,43 @@
1
+ module Spree
2
+ module Admin
3
+ class AvataxCustomerSettingsController < Spree::Admin::BaseController
4
+ helper_method :object_url
5
+
6
+ before_action :load_user
7
+ before_action :load_entity_use_codes, only: %i[edit update]
8
+
9
+ def edit; end
10
+
11
+ def update
12
+ if @user.update(permitted_resource_params)
13
+ flash[:success] = flash_message_for(@user, :successfully_updated)
14
+ redirect_to spree.admin_user_path(@user), status: :see_other
15
+ else
16
+ render :edit, status: :unprocessable_entity
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def load_user
23
+ @user = Spree.user_class.find_by_prefix_id!(params[:user_id])
24
+ authorize! :update, @user
25
+
26
+ @object = @user
27
+ @resource = Spree::Admin::Resource.new(controller_path, controller_name, nil)
28
+ end
29
+
30
+ def object_url
31
+ spree.admin_user_avatax_customer_settings_path(@user)
32
+ end
33
+
34
+ def load_entity_use_codes
35
+ @entity_use_codes = SpreeAvataxOfficial::EntityUseCode.order(:code)
36
+ end
37
+
38
+ def permitted_resource_params
39
+ params.require(:avatax_customer_setting).permit(:avatax_entity_use_code_id, :exemption_number, :vat_id)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,11 @@
1
+ module SpreeAvataxOfficial
2
+ module Admin
3
+ module BaseControllerDecorator
4
+ def self.prepended(base)
5
+ base.helper SpreeAvataxOfficial::AvalaraIntegrationHelper
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ ::Spree::Admin::BaseController.prepend(::SpreeAvataxOfficial::Admin::BaseControllerDecorator) if defined?(::Spree::Admin::BaseController)
@@ -0,0 +1,10 @@
1
+ module SpreeAvataxOfficial
2
+ module AvalaraIntegrationHelper
3
+ def avalara_endpoint_options
4
+ [
5
+ [::Spree.t('spree_avatax_official.endpoint_sandbox'), ::Spree::Integrations::Avalara::SANDBOX_ENDPOINT],
6
+ [::Spree.t('spree_avatax_official.endpoint_production'), ::Spree::Integrations::Avalara::PRODUCTION_ENDPOINT]
7
+ ]
8
+ end
9
+ end
10
+ end
@@ -10,7 +10,8 @@ module SpreeAvataxOfficial
10
10
 
11
11
  format_adjustment_label(
12
12
  ::Spree.t("spree_avatax_official.create_tax_adjustments.#{item_class}_tax_adjustment_default_label", included_label: included_label(item)),
13
- rate
13
+ rate,
14
+ item.order
14
15
  )
15
16
  end
16
17
 
@@ -18,13 +19,14 @@ module SpreeAvataxOfficial
18
19
  item.included_in_price ? 'Included ' : ''
19
20
  end
20
21
 
21
- def format_adjustment_label(adjustment_default_label, rate)
22
+ def format_adjustment_label(adjustment_default_label, rate, order)
22
23
  rate_in_percents = number_to_percentage(
23
24
  rate * 100.0,
24
25
  precision: PRECISION_OF_PERCENT_VALUE,
25
26
  strip_insignificant_zeros: STRIP_INSIGNIFICANT_ZEROS
26
27
  )
27
- SpreeAvataxOfficial::Config.show_rate_in_label ? "#{adjustment_default_label} (#{rate_in_percents})" : adjustment_default_label
28
+ show_rate = order.avalara_integration&.preferred_show_rate_in_label || false
29
+ show_rate ? "#{adjustment_default_label} (#{rate_in_percents})" : adjustment_default_label
28
30
  end
29
31
  end
30
32
  end
@@ -0,0 +1,61 @@
1
+ module Spree
2
+ module Integrations
3
+ class Avalara < Spree::Integration
4
+ SANDBOX_ENDPOINT = 'https://sandbox-rest.avatax.com'.freeze
5
+ PRODUCTION_ENDPOINT = 'https://rest.avatax.com'.freeze
6
+
7
+ preference :account_number, :string
8
+ preference :license_key, :password
9
+ preference :endpoint, :string, default: SANDBOX_ENDPOINT
10
+ preference :company_code, :string
11
+
12
+ preference :address_validation_enabled, :boolean, default: false
13
+ preference :commit_transaction_enabled, :boolean, default: true
14
+ preference :show_rate_in_label, :boolean, default: false
15
+
16
+ validates :preferred_account_number, :preferred_license_key, :preferred_company_code, presence: true
17
+
18
+ def self.integration_group
19
+ 'tax'
20
+ end
21
+
22
+ def self.icon_path
23
+ 'integration_icons/avalara-logo.png'
24
+ end
25
+
26
+ def can_connect?
27
+ response = avatax_client.ping
28
+ body = response.respond_to?(:body) ? response.body : response
29
+
30
+ return true if body.is_a?(Hash) && body['authenticated']
31
+
32
+ @connection_error_message =
33
+ if body.is_a?(Hash) && body.key?('authenticated')
34
+ 'Invalid credentials'
35
+ elsif body.is_a?(Hash) && body['error'].is_a?(Hash)
36
+ body['error']['message'].presence || 'Could not connect to AvaTax'
37
+ else
38
+ 'Could not connect to AvaTax'
39
+ end
40
+
41
+ false
42
+ rescue StandardError => e
43
+ @connection_error_message = e.message
44
+ false
45
+ end
46
+
47
+ def avatax_client
48
+ AvaTax::Client.new(
49
+ app_name: SpreeAvataxOfficial::Base::APP_NAME,
50
+ app_version: SpreeAvataxOfficial::Base::APP_VERSION,
51
+ connection_options: SpreeAvataxOfficial::Base::CONNECTION_OPTIONS,
52
+ logger: SpreeAvataxOfficial::Config.log,
53
+ faraday_response: true,
54
+ endpoint: preferred_endpoint,
55
+ username: preferred_account_number,
56
+ password: preferred_license_key
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
@@ -9,9 +9,6 @@ module SpreeAvataxOfficial
9
9
  raise 'Tax adjustments should be calculated on line item or shipment level'
10
10
  end
11
11
 
12
- # Following lines require this recalculation:
13
- # https://github.com/spree/spree/blob/master/core/app/models/spree/adjustment.rb#L104
14
- # https://github.com/spree/spree/blob/master/core/app/models/spree/line_item.rb#L159
15
12
  def compute_line_item(line_item)
16
13
  line_item.adjustments.tax.sum(:amount)
17
14
  end
@@ -3,7 +3,6 @@ module SpreeAvataxOfficial
3
3
  preference :address_validation_enabled, :boolean, default: false
4
4
  preference :company_code, :string, default: ''
5
5
  preference :enabled, :boolean, default: false
6
- preference :ship_from_address, :hash, default: {}
7
6
  preference :log, :boolean, default: true
8
7
  preference :log_to_stdout, :boolean, default: false
9
8
  preference :log_file_name, :string, default: 'avatax.log'
@@ -1,7 +1,8 @@
1
1
  module SpreeAvataxOfficial
2
- class EntityUseCode < ::Spree::Base
3
- with_options presence: true do
4
- validates :code, :name, uniqueness: true
5
- end
2
+ class EntityUseCode < ::Spree.base_class
3
+ self.whitelisted_ransackable_attributes = %w[code name description]
4
+
5
+ validates :code, presence: true, uniqueness: { scope: spree_base_uniqueness_scope }
6
+ validates :name, presence: true, uniqueness: { scope: spree_base_uniqueness_scope }
6
7
  end
7
8
  end
@@ -2,20 +2,14 @@ module SpreeAvataxOfficial
2
2
  module Spree
3
3
  module AddressDecorator
4
4
  def self.prepended(base)
5
- base.around_save :recalculate_avatax_taxes
5
+ base.after_commit :recalculate_avatax_taxes, on: %i[update]
6
6
  base.const_set 'OBSERVABLE_FIELDS', %w[address1 address2 city zipcode state_id country_id].freeze
7
7
  end
8
8
 
9
9
  private
10
10
 
11
11
  def recalculate_avatax_taxes
12
- return yield unless SpreeAvataxOfficial::Config.enabled
13
-
14
- observed_fields_changed = self.class::OBSERVABLE_FIELDS & changed
15
-
16
- yield # around_save requires yield to perform save operation
17
-
18
- return unless observed_fields_changed.any? && persisted?
12
+ return unless (self.class::OBSERVABLE_FIELDS & saved_changes.keys).any?
19
13
 
20
14
  address_sym = ::Spree::Config.tax_using_ship_address ? :ship_address : :bill_address
21
15
  order = ::Spree::Order.incomplete.find_by(address_sym => self)
@@ -8,13 +8,7 @@ module SpreeAvataxOfficial
8
8
  end
9
9
 
10
10
  def included_in_price
11
- tax_zone.try(:included_in_price) || false
12
- end
13
-
14
- def update_tax_charge
15
- return super unless SpreeAvataxOfficial::Config.enabled
16
-
17
- SpreeAvataxOfficial::CreateTaxAdjustmentsService.call(order: order)
11
+ order.market&.tax_inclusive || false
18
12
  end
19
13
 
20
14
  def avatax_tax_code
@@ -13,6 +13,17 @@ module SpreeAvataxOfficial
13
13
  base.state_machine.before_transition to: :delivery, do: :validate_tax_address, if: :address_validation_enabled?
14
14
  base.state_machine.after_transition to: :canceled, do: :void_in_avatax
15
15
  base.state_machine.after_transition to: :complete, do: :commit_in_avatax
16
+
17
+ # Recalculate when the order's address changes (selecting an existing address from the address book).
18
+ base.after_update :recalculate_avatax_taxes_on_address_change
19
+ end
20
+
21
+ def avalara_integration
22
+ store&.integrations&.active&.find_by(type: 'Spree::Integrations::Avalara')
23
+ end
24
+
25
+ def avatax_enabled?
26
+ avalara_integration.present?
16
27
  end
17
28
 
18
29
  def taxable_items
@@ -20,17 +31,17 @@ module SpreeAvataxOfficial
20
31
  end
21
32
 
22
33
  def avatax_tax_calculation_required?
23
- tax_address&.persisted? && line_items.any?
34
+ return false unless tax_address&.persisted?
35
+ return false unless line_items.any?
36
+ return false if delivery_required? && shipments.empty?
37
+
38
+ true
24
39
  end
25
40
 
26
41
  def avatax_discount_amount
27
42
  adjustments.promotion.eligible.sum(:amount).abs
28
43
  end
29
44
 
30
- def avatax_ship_from_address
31
- SpreeAvataxOfficial::Config.ship_from_address
32
- end
33
-
34
45
  def line_items_discounted_in_avatax?
35
46
  adjustments.promotion.eligible.any?
36
47
  end
@@ -39,14 +50,16 @@ module SpreeAvataxOfficial
39
50
  ::Spree::Config.tax_using_ship_address ? :ship_address : :bill_address
40
51
  end
41
52
 
53
+ # We need to override this so the default Spree tax calculation is not triggered.
54
+ # The actual tax calculation by Avalara is done in #recalculate_avatax_taxes
42
55
  def create_tax_charge!
43
- return super unless SpreeAvataxOfficial::Config.enabled
56
+ return if avatax_enabled?
44
57
 
45
- SpreeAvataxOfficial::CreateTaxAdjustmentsService.call(order: self)
58
+ super
46
59
  end
47
60
 
48
61
  def recalculate_avatax_taxes
49
- return unless SpreeAvataxOfficial::Config.enabled
62
+ return unless avatax_enabled?
50
63
 
51
64
  SpreeAvataxOfficial::CreateTaxAdjustmentsService.call(order: self)
52
65
  update_totals
@@ -55,12 +68,14 @@ module SpreeAvataxOfficial
55
68
 
56
69
  def validate_tax_address
57
70
  response = SpreeAvataxOfficial::Address::Validate.call(
58
- address: tax_address
71
+ address: tax_address,
72
+ order: self
59
73
  )
60
74
 
61
75
  return if response.success?
62
76
 
63
- error_message = response.value.body['messages'].map { |message| message['summary'] }.join('. ')
77
+ messages = response.value&.body&.dig('messages')
78
+ error_message = messages.present? ? messages.map { |message| message['summary'] }.join('. ') : 'Address validation failed'
64
79
 
65
80
  errors.add(:base, error_message)
66
81
 
@@ -68,19 +83,26 @@ module SpreeAvataxOfficial
68
83
  end
69
84
 
70
85
  def address_validation_enabled?
71
- SpreeAvataxOfficial::Config.address_validation_enabled
86
+ avalara_integration&.preferred_address_validation_enabled || false
72
87
  end
73
88
 
74
89
  private
75
90
 
91
+ def recalculate_avatax_taxes_on_address_change
92
+ return unless avatax_enabled?
93
+ return unless (%w[ship_address_id bill_address_id] & saved_changes.keys).any?
94
+
95
+ recalculate_avatax_taxes
96
+ end
97
+
76
98
  def commit_in_avatax
77
- return unless SpreeAvataxOfficial::Config.enabled && SpreeAvataxOfficial::Config.commit_transaction_enabled
99
+ return unless avatax_enabled? && avalara_integration.preferred_commit_transaction_enabled
78
100
 
79
101
  SpreeAvataxOfficial::Transactions::CreateService.call(order: self)
80
102
  end
81
103
 
82
104
  def void_in_avatax
83
- return unless SpreeAvataxOfficial::Config.enabled
105
+ return unless avatax_enabled?
84
106
 
85
107
  SpreeAvataxOfficial::Transactions::VoidService.call(order: self)
86
108
  end
@@ -5,13 +5,13 @@ module SpreeAvataxOfficial
5
5
  base.after_create :refund_in_avatax
6
6
 
7
7
  base.delegate :order, to: :payment
8
- base.delegate :inventory_units, :number, to: :order, prefix: true
8
+ base.delegate :number, to: :order, prefix: true
9
9
  end
10
10
 
11
11
  private
12
12
 
13
13
  def refund_in_avatax
14
- return unless SpreeAvataxOfficial::Config.enabled && SpreeAvataxOfficial::Config.commit_transaction_enabled
14
+ return unless order.avatax_enabled? && order.avalara_integration.preferred_commit_transaction_enabled
15
15
 
16
16
  SpreeAvataxOfficial::Transactions::RefundService.call(refundable: self)
17
17
  end
@@ -8,7 +8,7 @@ module SpreeAvataxOfficial
8
8
  end
9
9
 
10
10
  def included_in_price
11
- tax_zone.try(:included_in_price) || false
11
+ order.market&.tax_inclusive || false
12
12
  end
13
13
 
14
14
  def tax_category
@@ -18,6 +18,13 @@ module SpreeAvataxOfficial
18
18
  def avatax_tax_code
19
19
  tax_category.try(:tax_code).presence || ::Spree::TaxCategory::DEFAULT_TAX_CODES['Shipment']
20
20
  end
21
+
22
+ def selected_shipping_rate_id=(id)
23
+ super
24
+ return if order.nil? || order.completed?
25
+
26
+ order.recalculate_avatax_taxes
27
+ end
21
28
  end
22
29
  end
23
30
  end
@@ -8,4 +8,4 @@ module SpreeAvataxOfficial
8
8
  end
9
9
  end
10
10
 
11
- Spree.user_class.prepend ::SpreeAvataxOfficial::Spree::UserDecorator
11
+ Spree.user_class.prepend(::SpreeAvataxOfficial::Spree::UserDecorator) if Spree.user_class
@@ -1,5 +1,5 @@
1
1
  module SpreeAvataxOfficial
2
- class Transaction < ActiveRecord::Base
2
+ class Transaction < ::Spree.base_class
3
3
  SALES_ORDER = 'SalesOrder'.freeze
4
4
  SALES_INVOICE = 'SalesInvoice'.freeze
5
5
  RETURN_INVOICE = 'ReturnInvoice'.freeze
@@ -12,8 +12,9 @@ module SpreeAvataxOfficial
12
12
 
13
13
  belongs_to :order, class_name: 'Spree::Order'
14
14
 
15
+ validates :code, presence: true, uniqueness: { scope: spree_base_uniqueness_scope }
16
+
15
17
  with_options presence: true do
16
- validates :code, uniqueness: true
17
18
  validates :order
18
19
  validates :transaction_type
19
20
  end