diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..31620a50 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,44 @@ +version: 2.1 +orbs: + # Browser tools are required for feature specs. + browser-tools: circleci/browser-tools@1.1 + + # Always run tests agains the versions of Solidus defined in the + # most recent version of the Orb. + solidusio_extensions: solidusio/extensions@volatile + +jobs: + run-specs-with-postgres: + executor: solidusio_extensions/postgres + steps: + - browser-tools/install-browser-tools + - checkout + - solidusio_extensions/run-tests-solidus-older + - solidusio_extensions/run-tests-solidus-current + - solidusio_extensions/store-test-results + run-specs-with-master: + executor: solidusio_extensions/postgres + steps: + - browser-tools/install-browser-tools + - checkout + - solidusio_extensions/run-tests-solidus-master + - solidusio_extensions/store-test-results + + +workflows: + "Run specs on supported Solidus versions": + jobs: + - run-specs-with-postgres + "Run specs on Solidus master": + jobs: + - run-specs-with-master + "Weekly run specs against master": + triggers: + - schedule: + cron: "0 0 * * 4" # every Thursday + filters: + branches: + only: + - master + jobs: + - run-specs-with-master diff --git a/.travis.yml b/.travis.yml index bf76f965..584b2c80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,15 @@ dist: trusty sudo: false language: ruby cache: bundler +addons: + postgresql: "9.3" jobs: include: - rvm: 2.6.6 env: SOLIDUS_BRANCH=v2.10 DB=postgresql RAILS_VERSION="~> 5.2.0" + - rvm: 2.6.6 + env: SOLIDUS_BRANCH=v2.10 DB=postgresql RAILS_VERSION="~> 6.0.0" + before_install: - gem update --system - gem install bundler diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d85f11..6ffca239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,90 @@ # Changelog -## v1.0.0 +## master + +- [#190](https://github.com/SuperGoodSoft/solidus_taxjar/pull/190) Add transaction sync batch show page +- [#188](https://github.com/SuperGoodSoft/solidus_taxjar/pull/188) Show transaction sync batches in user interface +- [#185](https://github.com/SuperGoodSoft/solidus_taxjar/pull/185) Backfill transactions in batches +- [#139](https://github.com/SuperGoodSoft/solidus_taxjar/pull/139) Refund and create a new order transaction when an order is recalculated +- [#175](https://github.com/SuperGoodSoft/solidus_taxjar/pull/175) Add request logging to TaxJar API requests +- [#138](https://github.com/SuperGoodSoft/solidus_taxjar/pull/138) Add admin UI for configuring reporting +- [#158](https://github.com/SuperGoodSoft/solidus_taxjar/pull/158) Update sandbox bin stub for `solidus@3` +- [#88](https://github.com/SuperGoodSoft/solidus_taxjar/pull/88) Fire `shipment_shipped` event when any shipment on an order ships. +- [#81](https://github.com/SuperGoodSoft/solidus_taxjar/pull/81) Add install generator +- [#95](https://github.com/SuperGoodSoft/solidus_taxjar/pull/95) Only require "state" for Canadian and US addresses +- [#98](https://github.com/SuperGoodSoft/solidus_taxjar/pull/98) Add generator for TaxJar transaction IDs +- [#103](https://github.com/SuperGoodSoft/solidus_taxjar/pull/103) Add `reporting_enabled` configuration option +- [#97](https://github.com/SuperGoodSoft/solidus_taxjar/pull/97) Add public API method to request the latest transaction associated with a solidus order. +- [#100](https://github.com/SuperGoodSoft/solidus_taxjar/pull/100) Add public API method to post a taxjar refund transaction for a solidus order. +- [#102](https://github.com/SuperGoodSoft/solidus_taxjar/pull/102) Add description to transaction line item params +- [#107](https://github.com/SuperGoodSoft/solidus_taxjar/pull/107) Fix rails-engine binstub to point to correct engine entry point +- [#108](https://github.com/SuperGoodSoft/solidus_taxjar/pull/108) Add new model associated with a `Spree::Order` to represent taxjar order creation transactions +- [#117](https://github.com/SuperGoodSoft/solidus_taxjar/pull/117) Fix migration install +- [#114](https://github.com/SuperGoodSoft/solidus_taxjar/pull/114) Add new model associated with a `SuperGood::SolidusTaxjar::OrderTransaction` to represent taxjar refund creation transactions +- [#116](https://github.com/SuperGoodSoft/solidus_taxjar/pull/116) Update the `OrderTransaction` model to record the transaction date. +- [#120](https://github.com/SuperGoodSoft/solidus_taxjar/pull/120) Change default `SOLIDUS_BRANCH` and `RAILS_VERSION` +- [#109](https://github.com/SuperGoodSoft/solidus_taxjar/pull/109) Save `OrderTransaction` after API call to TaxJar +- [#119](https://github.com/SuperGoodSoft/solidus_taxjar/pull/119) Move the install generator into the correct path so that it will be installed in the dummy app. +- [#111](https://github.com/SuperGoodSoft/solidus_taxjar/pull/111) Create a new taxjar transaction when a shipment is shipped. +- [#137](https://github.com/SuperGoodSoft/solidus_taxjar/pull/137) Only run tests against solidus 2.11. This also represents the drop of official support for solidus 2.9 and 2.10. +- [#137](https://github.com/SuperGoodSoft/solidus_taxjar/pull/137) Run tests against the most up to date versions of solidus. +- [#141](https://github.com/SuperGoodSoft/solidus_taxjar/pull/141) Handle unimplemented reporting features +- [#129](https://github.com/SuperGoodSoft/solidus_taxjar/pull/129) Report transaction asynchronously when a shipment is shipped. +- [#127](https://github.com/SuperGoodSoft/solidus_taxjar/pull/127) Add acceptance test for calculating taxes with the extension. +- [#160](https://github.com/SuperGoodSoft/solidus_taxjar/pull/160) Add tax categories API endpoint wrapper +- [#171](https://github.com/SuperGoodSoft/solidus_taxjar/pull/171) Display existing Nexus regions +- [#170](https://github.com/SuperGoodSoft/solidus_taxjar/pull/170) Hide all reporting features behind API key +- [#174](https://github.com/SuperGoodSoft/solidus_taxjar/pull/174) Provide a link to Taxjar state settings +- [#177](https://github.com/SuperGoodSoft/solidus_taxjar/pull/177) Make nexus caching configurable +- [#169](https://github.com/SuperGoodSoft/solidus_taxjar/pull/169) Add basic backfill transaction functionality +- [#181](https://github.com/SuperGoodSoft/solidus_taxjar/pull/181) Take all non-tax adjustment types into account when calculating a line item's discount +- [#182](https://github.com/SuperGoodSoft/solidus_taxjar/pull/182) Automatically create default Tax Rate + +## Upgrading Instructions + +* If you had previously configured a `Spree::TaxRate` with the name "Sales Tax", it can be deleted after upgrading, as a new `Spree::TaxRate` with the name "Solidus TaxJar Rate" will automatically be created. Alternatively, you can rename your existing `Spree::TaxRate` from "Sales Tax" to "Solidus TaxJar Rate". [#182](https://github.com/SuperGoodSoft/solidus_taxjar/pull/182) + +## v0.18.2 + +- [#71](https://github.com/SuperGoodSoft/solidus_taxjar/pull/69) Unlock ExecJS version. This reverts the temporary fix introduced in #69 +- [#79](https://github.com/SuperGoodSoft/solidus_taxjar/pull/79) Relax Ruby required version to support Ruby 3.0+ +- [#51](https://github.com/SuperGoodSoft/solidus_taxjar/pull/51) Add nexus regions method to API +- [#58](https://github.com/SuperGoodSoft/solidus_taxjar/pull/58) Take shipping promotions into account in default calculator +- [#59](https://github.com/SuperGoodSoft/solidus_taxjar/pull/59) Add pry debugging tools +- [#69](https://github.com/SuperGoodSoft/solidus_taxjar/pull/69) Lock ExecJS version +- [#37](https://github.com/SuperGoodSoft/solidus_taxjar/pull/37) Added a basic Taxjar settings admin interface which displays placeholder text. +- [#64](https://github.com/SuperGoodSoft/solidus_taxjar/pull/64) Provide Spree::Address.address2 to TaxJar address validation if it is present. +- [#80](https://github.com/SuperGoodSoft/solidus_taxjar/pull/80) Support order_recalculated event in < 2.11 + +## v0.18.1 + +[#52](https://github.com/supergoodsoft/solidus_taxjar/pull/52) fixes a critical bug in the API class that was released in `v0.18.0`. Please upgrade. + +- [#47](https://github.com/SuperGoodSoft/solidus_taxjar/pull/47) Fixed bug in `validate_address_params` for addresses without a state +- [#52](https://github.com/supergoodsoft/solidus_taxjar/pull/52) Fixed critical bug in API class + +## ~~v0.18.0~~ +`v0.18.0` was removed due to a regression in the API class that was fixed in [#52](https://github.com/SuperGoodSoft/solidus_taxjar/pull/52) and `v0.18.1` -- [#32](https://github.com/SuperGoodSoft/solidus_taxjar/pull/32) Add Taxjar customers and exempt regions integration - [#21](https://github.com/SuperGoodSoft/solidus_taxjar/pull/21) Migrated project to `solidus_dev_support` - [#22](https://github.com/SuperGoodSoft/solidus_taxjar/pull/22) Added support for TaxJar address validation API through `SuperGood::SolidusTaxJar::Addresses` class +- [#34](https://github.com/SuperGoodSoft/solidus_taxjar/pull/34) Include API version in request headers +- [#38](https://github.com/SuperGoodSoft/solidus_taxjar/pull/38) Added a rails engine to support future solidus backend UI +- [#43](https://github.com/SuperGoodSoft/solidus_taxjar/pull/43) Support zeitwerk loading **Breaking Changes**: -- Gem name `super_good-solidus_taxjar` renamed to `super_good_solidus_taxjar` - Module name `SolidusTaxJar` renamed to `SolidusTaxjar` - Class name `API` renamed to `Api` - Class name `APIParams` renamed to `ApiParams` -- Removed support for Rails 5.1. This gem now requires Rails >= 5.2.0 for `active_storage` + +### Upgrading from 0.17.X to 0.18.X + +If you're currently using version 0.17.X and want to upgrade to 0.18.X, follow these steps: + +- Rename any instances of the module `SolidusTaxJar` to `SolidusTaxjar` +- Rename any instances of the class `API` to `Api` +- Rename any instances of the class `APIParams` to `ApiParams` ## v0.17.1 diff --git a/Gemfile b/Gemfile index 6be78e42..be60bb57 100644 --- a/Gemfile +++ b/Gemfile @@ -2,15 +2,10 @@ source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } -branch = ENV.fetch('SOLIDUS_BRANCH', 'master') +branch = ENV.fetch("SOLIDUS_BRANCH", "v3.1") gem "solidus", github: "solidusio/solidus", branch: branch -# Needed to help Bundler figure out how to resolve dependencies, -# otherwise it takes forever to resolve them. -# See https://github.com/bundler/bundler/issues/6677 -gem "rails", ENV.fetch("RAILS_VERSION") { ">0.a" } - # Provides basic authentication functionality for testing parts of your engine gem "solidus_auth_devise" @@ -25,6 +20,8 @@ end group :development, :test do gem "pry" + gem "pry-stack_explorer" + gem "pry-byebug" end gemspec diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..8c254a7c --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +What is the goal of this PR? +--- + + + +How do you manually test these changes? (if applicable) +--- + +1. Do a thing + * [ ] Assert a result + +Merge Checklist +--- + +- [ ] Run the manual tests +- [ ] Update the changelog + +Screenshots +--- diff --git a/README.md b/README.md index 46db4af5..1d24f884 100644 --- a/README.md +++ b/README.md @@ -1,111 +1,191 @@ -# `SuperGood::SolidusTaxjar` [![Build Status](https://travis-ci.com/SuperGoodSoft/solidus_taxjar.svg?token=rc5QTgHvLLF7cpqkmyfd&branch=master)](https://travis-ci.com/SuperGoodSoft/solidus_taxjar) +# `SuperGood::SolidusTaxjar` -`SuperGood::SolidusTaxjar` is a [Solidus](https://github.com/solidusio/solidus) extension that allows Solidus stores to use [TaxJar](https://www.taxjar.com/) for tax calculations. +[![CircleCI build status](https://circleci.com/gh/SuperGoodSoft/solidus_taxjar/tree/master.svg?style=shield)](https://circleci.com/gh/SuperGoodSoft/solidus_taxjar/tree/master) -This is not a fork of [spree_taxjar](https://github.com/vinsol-spree-contrib/spree_taxjar), like [solidus_taxjar](https://github.com/boomerdigital/solidus_taxjar). Instead of using a custom calculator, `SuperGood::SolidusTaxjar` uses the new configurable tax system [by @adammathys](https://github.com/solidusio/solidus/pull/1892) introduced in Solidus v2.4. This maps better to how the TaxJar API itself works. +`SuperGood::SolidusTaxjar` is a [Solidus](https://github.com/solidusio/solidus) +extension that allows Solidus stores to use [TaxJar](https://www.taxjar.com/) +for tax calculations. + +This is not a fork of [spree_taxjar](https://github.com/vinsol-spree-contrib/spree_taxjar), +like [solidus_taxjar](https://github.com/boomerdigital/solidus_taxjar). Instead +of using a custom calculator, `SuperGood::SolidusTaxjar` uses the new +configurable tax system [by @adammathys](https://github.com/solidusio/solidus/pull/1892) +introduced in Solidus v2.4. This maps better to how the TaxJar API itself works. ## Installation 1. Add this line to your application's Gemfile: - ```ruby - gem 'super_good_solidus_taxjar' - ``` + ```ruby + gem 'super_good-solidus_taxjar' + ``` - And then execute: + And then execute: - $ bundle + ```sh + bundle + ``` -2. Install and run the necessary migrations: +1. Next, configure Solidus to use this gem by running the install generator: - ```shell - bundle exec rails g super_good:solidus_taxjar:install - bundle exec rake db:migrate - ``` + ```sh + bundle exec rails generate super_good:solidus_taxjar:install + ``` -3. Next, configure Solidus to use this gem: +1. Also, configure your error handling: - ```ruby - # Put this in config/initializers/solidus.rb + ```ruby + # Put this in config/initializers/taxjar.rb - Spree.config do |config| - config.tax_calculator_class = SuperGood::SolidusTaxjar::TaxCalculator - end - ``` + SuperGood::SolidusTaxjar.exception_handler = ->(e) { + # Report exceptions in here. For example, if you were using the Sentry's + # raven-ruby gem to report errors, you might do this: + Raven.capture_exception(e) + } + ``` -4. Also, configure your error handling: + For more information about configuring the extension, see + [Configuration](#configuration). - ```ruby - # Put this in config/initializers/taxjar.rb +1. Finally, make sure that the `TAXJAR_API_KEY` environment variable is set to + your TaxJar API key. - SuperGood::SolidusTaxjar.exception_handler = ->(e) { - # Report exceptions in here. For example, if you were using the Sentry's - # raven-ruby gem to report errors, you might do this: - Raven.capture_exception(e) - } - ``` +## Project Status -5. Finally, make sure that the `TAXJAR_API_KEY` environment variable is set to a your TaxJar API key and make sure that you have a `Spree::TaxRate` with the name "Sales Tax". This will be used as the source for the tax adjustments that Solidus creates. +This extension is under active development and not yet at a v1.0 release, but +it's currently being used in production by multiple Solidus stores. -## Upgrading from 0.X to 1.0.X +Requirements for TaxJar integrations vary as some stores also need reporting, +which isn't provided out of the box by this extension. This is because +individual stores will be using different background job frameworks or runners +(Sidekiq, delayed_job, ActiveJob, etc.) and a reliable integration will rely on +one of these. Because this part of the integration is small, we've chosen to +provide the transaction reporting functionality, but have skipped directly +integrating it. -If you're currently using version 0.X and want to upgrade to 1.0.X, follow these steps: +If you're having trouble integrating this extension with your store and would +like some assistance, please reach out to Jared via e-mail at [jared@super.gd](mailto:jared@super.gd) +or on the official Solidus Slack as `@Jared Norman`. -- Upgrade to at least Rails version 5.2.0. This is due to 1.0.X requiring the `active_storage` gem -- Rename the gem `super_good-solidus_taxjar` to `super_good_solidus_taxjar` in your Gemfile -- Rename any instances of the module `SolidusTaxJar` to `SolidusTaxjar` -- Rename any instances of the class `API` to `Api` -- Rename any instances of the class `APIParams` to `ApiParams` +## Features -## Project Status +The extension provides currently two high level `calculator` classes that wrap +the low-level Ruby taxjar gem API calls: -Requirements for TaxJar integrations vary as some stores also need reporting, which isn't provided out of the box by this extension. This is because individual stores will be using different background job frameworks or runners (Sidekiq, delayed_job, ActiveJob, etc.) and a reliable integration will rely on one of these. Because this part of the integration is small, we've chosen to provide the transaction reporting functionality, but have skipped directly integrating it. +* tax calculator +* tax rate calculator -If you're having trouble integrating this extension with your store and would like some assistance, please reach out to Jared via e-mail at [jared@super.gd](mailto:jared@super.gd) or on the official Solidus as `@jared`. +The extension requires the `order_recalculated` event which is not supported on +Solidus < 2.11, so this extension provides a [compatibility layer](app/overrides/super_good/solidus_taxjar/spree/order_updater/fire_recalculated_event.rb). -## Features +### TaxCalculator -The extension provides currently two high level `calculator` classes that wrap the low-level Ruby taxjar gem API calls: +`SuperGood::SolidusTaxjar::TaxCalculator` allows calculating the full tax +breakdown for a given `Spree::Order`. The breakdown includes separate line items +taxes and shipment taxes. -* tax calculator -* tax rate calculator +This tax calculator will create a `Spree::TaxRate` that is required for tax adjustments. All other `Spree::TaxRate`s will be ignored in calculations. See [this wiki page](https://github.com/SuperGoodSoft/solidus_taxjar/wiki/How-Solidus-TaxJar-calculates-tax) for more details. + +### TaxRateCalculator -The extension also provides two ActiveRecord classes that represent Taxjar Exempt Regions Taxjar Customers: +`SuperGood::SolidusTaxjar::TaxRateCalculator` allows calculating the tax rate +for a given `Spree::Address`. It relies on the same low-level Ruby TaxJar API +endpoint of the tax calculator in order to provide the most coherent and +reliable results. TaxJar support recommends using this endpoint for live +calculations. -* exempt region -* customer +## Configuration +See [`lib/super_good/solidus_taxjar.rb`](lib/super_good/solidus_taxjar.rb) for a +list of configuration options and their default values. You can override the +default values in one of your Rails application's initializers: -### TaxCalculator +```ruby +# config/initializers/taxjar.rb -`SuperGood::SolidusTaxjar::TaxCalculator` allows calculating the full tax breakdown for a given `Spree::Order`. The breakdown includes separate line items taxes and shipment taxes. +SuperGood::SolidusTaxjar.tap do |config| + config.cache_duration = 2.hours + config.line_item_tax_label_maker = ->(taxjar_line_item, spree_line_item) { + "My Tax Label" + } + config.test_mode = true +end +``` -### TaxRateCalculator +### Logging +By default, all requests to TaxJar are logged to the Rails logger. URIs, HTTP method, and response codes will be logged at the `info` level, any further details (such as response body) are logged at the `debug` level. -`SuperGood::SolidusTaxjar::TaxRateCalculator` allows calculating the tax rate for a given `Spree::Address`. It relies on the same low-level Ruby TaxJar API endpoint of the tax calculator in order to provide the most coherent and reliable results. TaxJar support recommends using this endpoint for live calculations. +If you are interested in logs for full request responses, be sure to set your logger to the `debug` level: -### ExemptRegion +```ruby +Rails.logger.level = 0 # debug +``` -`SuperGood::SolidusTaxjar::ExemptRegion` is an ActiveRecord model used for storing TaxJar Exempt Region information. It contains exempt region information such as the State the customer is tax exempt from as well as the tax exemption document as an active storage attachment. +Or, if you want an alternate logger implementation, you can provide your own: -### Customer +```ruby +SuperGood::SolidusTaxjar.tap do |config| + logger = Logger.new(STDOUT) + logger.log_level = :warn + config.logger = logger +end +``` -`SuperGood::SolidusTaxjar::Customer` is an ActiveRecord model used for storing TaxJar tax exempt customer information. It has many TaxJar Exempt Regions. +More details on the Rails Logger are available [here](https://guides.rubyonrails.org/debugging_rails_applications.html#the-logger). + +Logging can also be disabled all together: + +```ruby +SuperGood::SolidusTaxjar.tap do |config| + config.logging_enabled = false +end +``` ## Development -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +Run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run the +tests. You can also run `bin/console` for an interactive prompt that will allow +you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. + +### Changing the Rails/Solidus Versions + +The Rails and the Solidus version can be changed by setting the `RAILS_VERSION` +and `SOLIDUS_BRANCH` environment variables, respectively. See the +[Gemfile](./Gemfile) for examples. + +### Changing the Database Vendor + +The database vendor can also be changed from the default (`sqlite3`) by setting +the `DB` environment variable. + +## Releasing a New Version -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). +1. Update the `master` header in changelog to the version that you're releasing. +2. Commit your changes and open a PR for the release +3. Once the PR has been merged into master, run `gem bump -v [||] -p -t -r` to create a tag and release the gem to ✨ RubyGems ✨ +4. Push your local master branch with `--tags` to the repository to add the tag to github. +5. Create a new release on github with the tag + * [ ] Ensure the changelog since the previous release is included + * [ ] Ensure you have noted version migration instructions if applicable + * [ ] Ensure breaking/significant changes are clearly highlighted + * [ ] Ensure changes that are **not production ready** are clearly highlighted ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/SuperGoodSoft/solidus_taxjar. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. +Bug reports and pull requests are welcome on GitHub at [https://github.com/SuperGoodSoft/solidus_taxjar](https://github.com/SuperGoodSoft/solidus_taxjar). +This project is intended to be a safe, welcoming space for collaboration, and +contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) +code of conduct. ## License -The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). +The gem is available as open source under the terms of the +[MIT License](https://opensource.org/licenses/MIT). ## Code of Conduct -Everyone interacting in the `SuperGood::SolidusTaxjar` project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/SuperGoodSoft/solidus_taxjar/blob/master/CODE_OF_CONDUCT.md). +Everyone interacting in the `SuperGood::SolidusTaxjar` project’s codebases, +issue trackers, chat rooms and mailing lists is expected to follow the +[code of conduct](https://github.com/SuperGoodSoft/solidus_taxjar/blob/master/CODE_OF_CONDUCT.md). diff --git a/Rakefile b/Rakefile index c08aa468..29cbb9de 100644 --- a/Rakefile +++ b/Rakefile @@ -3,4 +3,6 @@ require 'solidus_dev_support/rake_tasks' SolidusDevSupport::RakeTasks.install +ENV['LIB_NAME'] = "super_good/solidus_taxjar" + task default: 'extension:specs' diff --git a/app/controllers/spree/admin/taxjar_customers_controller.rb b/app/controllers/spree/admin/taxjar_customers_controller.rb new file mode 100644 index 00000000..bc35493d --- /dev/null +++ b/app/controllers/spree/admin/taxjar_customers_controller.rb @@ -0,0 +1,62 @@ +module Spree + module Admin + class TaxjarCustomersController < Spree::Admin::BaseController + before_action :load_user + before_action :load_taxjar_customer, except: %i[create] + + def new + @taxjar_customer = @user.build_taxjar_customer + @taxjar_customer.taxjar_exempt_regions.build + end + + def show; end + + def edit; end + + def create + @taxjar_customer = @user.build_taxjar_customer(object_params) + + if @taxjar_customer.save + flash[:success] = "your user tax exemption has been saved" + ::Spree::Event.fire "tax_exemption_created", user: @user + redirect_to admin_user_tax_exemptions_path + else + flash[:error] = "your user tax exemption failed to save" + redirect_back(fallback_location: new_admin_user_tax_exemptions_path) + end + end + + def destroy + if @taxjar_customer.destroy + flash[:success] = "Tax exemption has been deleted" + ::Spree::Event.fire "tax_exemption_destroyed", user: @user + redirect_to admin_user_tax_exemptions_path + else + flash[:error] = "Tax exemption could not be deleted" + redirect_back(fallback_location: admin_user_tax_exemptions_path) + end + flash[:success] = "tax exemption has been deleted" + end + + private + + def object_params + params.require(:super_good_solidus_taxjar_customer). + permit(:address_id, :tax_exemption_type, + taxjar_exempt_regions_attributes: [:state_id, :tax_exemption_document]) + end + + def load_taxjar_customer + @taxjar_customer = @user.taxjar_customer + end + + def load_user + @user = ::Spree::User.find_by(id: params[:user_id]) + end + + def model_class + SuperGood::SolidusTaxjar::Customer + end + end + end +end diff --git a/app/controllers/spree/admin/taxjar_exempt_regions_controller.rb b/app/controllers/spree/admin/taxjar_exempt_regions_controller.rb new file mode 100644 index 00000000..cdaec548 --- /dev/null +++ b/app/controllers/spree/admin/taxjar_exempt_regions_controller.rb @@ -0,0 +1,85 @@ +module Spree + module Admin + class TaxjarExemptRegionsController < Spree::Admin::BaseController + before_action :load_user + before_action :load_taxjar_customer + + def new + @taxjar_exempt_region = @taxjar_customer.taxjar_exempt_regions.build + end + + def create + @taxjar_exempt_region = @taxjar_customer.taxjar_exempt_regions.new(object_params) + + if @taxjar_exempt_region.save + flash[:success] = "State exemption has been saved" + ::Spree::Event.fire "tax_exemption_updated", user: @user + redirect_to admin_user_tax_exemptions_path + else + flash[:error] = "State exemption failed to save" + redirect_back(fallback_location: new_admin_user_tax_exemptions_exemption_region_path) + end + end + + def destroy + exempt_region = @taxjar_customer.taxjar_exempt_regions.find(params[:id]) + + if exempt_region.destroy + flash[:success] = "State exemption has been deleted" + if @taxjar_customer.taxjar_exempt_regions.blank? + @taxjar_customer.destroy + ::Spree::Event.fire "tax_exemption_destroyed", user: @user + else + ::Spree::Event.fire "tax_exemption_updated", user: @user + end + else + flash[:error] = "State exemption could not be deleted" + end + redirect_to admin_user_tax_exemptions_path + end + + def approve + exempt_region = @taxjar_customer.taxjar_exempt_regions.find(params[:id]) + if exempt_region.update(approved: true) + flash[:success] = "State exemption approved" + ::Spree::Event.fire "tax_exemption_updated", user: @user + ::Spree::Event.fire "tax_exemption_approved", user: @user, state: exempt_region.state + else + flash[:error] = "State exemption could not be approved" + end + redirect_to admin_user_tax_exemptions_path + end + + def disapprove + exempt_region = @taxjar_customer.taxjar_exempt_regions.find(params[:id]) + if exempt_region.update(approved: false) + flash[:success] = "State exemption disapproved" + ::Spree::Event.fire "tax_exemption_updated", user: @user + ::Spree::Event.fire "tax_exemption_disapproved", user: @user, state: exempt_region.state + else + flash[:error] = "State exemption could not be disapproved" + end + redirect_to admin_user_tax_exemptions_path + end + + private + + def object_params + params.require(:exempt_region). + permit(:state_id, :tax_exemption_document) + end + + def load_taxjar_customer + @taxjar_customer = @user.taxjar_customer + end + + def load_user + @user = Spree::User.find_by(id: params[:user_id]) + end + + def model_class + SuperGood::SolidusTaxjar::ExemptRegion + end + end + end +end diff --git a/app/controllers/spree/admin/taxjar_settings_controller.rb b/app/controllers/spree/admin/taxjar_settings_controller.rb new file mode 100644 index 00000000..5e03a796 --- /dev/null +++ b/app/controllers/spree/admin/taxjar_settings_controller.rb @@ -0,0 +1,46 @@ +module Spree + module Admin + class TaxjarSettingsController < Spree::Admin::BaseController + def edit + @configuration = SuperGood::SolidusTaxjar.configuration + @nexus_regions = Rails.cache.fetch(:nexus_regions) || [] + end + + def update + if SuperGood::SolidusTaxjar.configuration.update(configuration_params) + flash[:success] = "TaxJar settings updated!" + else + flash[:alert] = "Failed to update settings!" + end + redirect_back(fallback_location: spree.admin_taxjar_settings_path) + end + + def sync_nexus_regions + if ENV["TAXJAR_API_KEY"] + begin + Rails.cache.write( + :nexus_regions, + SuperGood::SolidusTaxjar.api.nexus_regions, + expires_in: SuperGood::SolidusTaxjar.cache_duration + ) + flash[:success] = "Updated with new Nexus Regions" + rescue Taxjar::Error => exception + flash[:error] = exception.message + end + end + redirect_back(fallback_location: spree.admin_taxjar_settings_path) + end + + def backfill_transactions + @transaction_sync_batch = ::SuperGood::SolidusTaxjar::BackfillTransactions.new.call + redirect_to spree.admin_transaction_sync_batch_path(@transaction_sync_batch) + end + + private + + def configuration_params + params.require(:super_good_solidus_taxjar_configuration).permit(:preferred_reporting_enabled) + end + end + end +end diff --git a/app/controllers/spree/admin/transaction_sync_batches_controller.rb b/app/controllers/spree/admin/transaction_sync_batches_controller.rb new file mode 100644 index 00000000..a57ee7b1 --- /dev/null +++ b/app/controllers/spree/admin/transaction_sync_batches_controller.rb @@ -0,0 +1,13 @@ +module Spree + module Admin + class TransactionSyncBatchesController < Spree::Admin::BaseController + def index + @batches = SuperGood::SolidusTaxjar::TransactionSyncBatch.all.page(params[:page]).per(params[:per_page]) + end + + def show + @batch = SuperGood::SolidusTaxjar::TransactionSyncBatch.find(params[:id]) + end + end + end +end diff --git a/app/controllers/spree/taxjar_customers_controller.rb b/app/controllers/spree/taxjar_customers_controller.rb new file mode 100644 index 00000000..77c36e6b --- /dev/null +++ b/app/controllers/spree/taxjar_customers_controller.rb @@ -0,0 +1,78 @@ +module Spree + class TaxjarCustomersController < APIController + before_action :load_taxjar_customer, except: %i[create] + + def show + if @taxjar_customer + render_ok_with_csrf("api/v1/taxjar_customers/show", taxjar_customer: @taxjar_customer) + else + render_ok_with_alerts_and_csrf + end + end + + def create + @taxjar_customer = spree_current_user.build_taxjar_customer(object_params) + + if @taxjar_customer.save + flash[:success] = "Tax exemption has been saved" + ::Spree::Event.fire "tax_exemption_created", user: spree_current_user + ::Spree::Event.fire "tax_exemption_customer_request", user: spree_current_user + render_ok_with_csrf("api/v1/taxjar_customers/show", taxjar_customer: @taxjar_customer) + else + flash[:error] = "Tax exemption failed to save" + render_bad_request_with_custom_message_and_errors("Tax exemption failed to save", @taxjar_customer.errors) + end + end + + def update + @taxjar_customer = spree_current_user.taxjar_customer + + if @taxjar_customer.update(object_params) + flash[:success] = "tax exemption has been updated" + ::Spree::Event.fire "tax_exemption_updated", user: spree_current_user + ::Spree::Event.fire "tax_exemption_customer_request", user: spree_current_user + render_ok_with_csrf("api/v1/taxjar_customers/show", taxjar_customer: @taxjar_customer) + else + flash[:error] = "Tax exemption failed to update" + render_bad_request_with_custom_message_and_errors("Tax exemption failed to update", @taxjar_customer.errors) + end + end + + def destroy + if @taxjar_customer.destroy + flash[:success] = "tax exemption has been deleted" + ::Spree::Event.fire "tax_exemption_destroyed", user: spree_current_user + render_ok_with_csrf("api/v1/taxjar_customers/show", taxjar_customer: @taxjar_customer) + else + flash[:error] = "tax exemption could not be deleted" + render_bad_request_with_custom_message_and_errors("Tax exemption failed to update", @ta.errors) + end + end + + def nexus_regions + nexus_regions = SuperGood::SolidusTaxjar.api.nexus_regions + + render_ok_with_csrf("api/v1/taxjar_customers/nexus_regions", nexus_regions: nexus_regions) + end + + private + + def object_params + tax_params = params.require(:tax_exemption). + permit(:address_id, :tax_exemption_type, address_attributes: permitted_address_attributes, + taxjar_exempt_regions_attributes: %i[id _destroy state_id tax_exemption_document]) + + if tax_params[:address_id].to_i.positive? + tax_params.delete(:address_attributes) + else + tax_params.delete(:ship_address_id) + end + + tax_params + end + + def load_taxjar_customer + @taxjar_customer = spree_current_user.taxjar_customer + end + end +end diff --git a/app/jobs/super_good/solidus_taxjar/replace_transaction_job.rb b/app/jobs/super_good/solidus_taxjar/replace_transaction_job.rb new file mode 100644 index 00000000..a77cbc0c --- /dev/null +++ b/app/jobs/super_good/solidus_taxjar/replace_transaction_job.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module SuperGood + module SolidusTaxjar + class ReplaceTransactionJob < ApplicationJob + queue_as { SuperGood::SolidusTaxjar.job_queue } + + def perform(order) + SuperGood::SolidusTaxjar.reporting.refund_and_create_new_transaction(order) + end + end + end +end diff --git a/app/jobs/super_good/solidus_taxjar/report_refund_job.rb b/app/jobs/super_good/solidus_taxjar/report_refund_job.rb new file mode 100644 index 00000000..9021515e --- /dev/null +++ b/app/jobs/super_good/solidus_taxjar/report_refund_job.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module SuperGood + module SolidusTaxjar + class ReportRefundJob < ApplicationJob + queue_as { SuperGood::SolidusTaxjar.job_queue } + + def perform(reimbursement) + SuperGood::SolidusTaxjar.reporting.create_refund(reimbursement) + end + end + end +end diff --git a/app/jobs/super_good/solidus_taxjar/report_transaction_job.rb b/app/jobs/super_good/solidus_taxjar/report_transaction_job.rb new file mode 100644 index 00000000..9a14b055 --- /dev/null +++ b/app/jobs/super_good/solidus_taxjar/report_transaction_job.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module SuperGood + module SolidusTaxjar + class ReportTransactionJob < ApplicationJob + queue_as { SuperGood::SolidusTaxjar.job_queue } + + def perform(order) + SuperGood::SolidusTaxjar.reporting.show_or_create_transaction(order) + end + end + end +end diff --git a/app/mailers/super_good/solidus_taxjar/tax_exemption_mailer.rb b/app/mailers/super_good/solidus_taxjar/tax_exemption_mailer.rb new file mode 100644 index 00000000..38de02ce --- /dev/null +++ b/app/mailers/super_good/solidus_taxjar/tax_exemption_mailer.rb @@ -0,0 +1,41 @@ +module SuperGood + module SolidusTaxjar + class TaxExemptionMailer < ApplicationMailer + + def tax_exemption_request(user) + @user = user + @store = ::Spree::Store.default + + mail(to: SuperGood::SolidusTaxjar.tax_exemption_mailer_to_address, + subject: format('[TAX EXEMPTION REQUEST] Tax exemption request for ("%s")', + @user.email), + from: SuperGood::SolidusTaxjar.tax_exemption_mailer_from_address, + reply_to: @user.email) + end + + def approved_email(user, state) + @user = user + @state = state + @store = ::Spree::Store.default + + mail(to: @user.email, + subject: "Your tax exemption request for #{@state.abbr} has been approved", + from: SuperGood::SolidusTaxjar.tax_exemption_mailer_from_address) + end + + def disapproved_email(user, state) + @user = user + @state = state + @store = ::Spree::Store.default + + mail(to: @user.email, + subject: "Your tax exemption request for #{@state.abbr} has not been approved", + from: SuperGood::SolidusTaxjar.tax_exemption_mailer_from_address) + end + + def from_address(store) + store.mail_from_address + end + end + end +end diff --git a/app/models/super_good/solidus_taxjar/configuration.rb b/app/models/super_good/solidus_taxjar/configuration.rb new file mode 100644 index 00000000..2f658ad5 --- /dev/null +++ b/app/models/super_good/solidus_taxjar/configuration.rb @@ -0,0 +1,18 @@ +require 'spree/preferences/persistable' + +module SuperGood + module SolidusTaxjar + class Configuration < ::Spree::Base + include ::Spree::Preferences::Persistable + + self.table_name = 'solidus_taxjar_configuration' + preference :reporting_enabled, :boolean, default: false + + class << self + def default + first || create + end + end + end + end +end diff --git a/app/models/super_good/solidus_taxjar/order_transaction.rb b/app/models/super_good/solidus_taxjar/order_transaction.rb new file mode 100644 index 00000000..f900d6d8 --- /dev/null +++ b/app/models/super_good/solidus_taxjar/order_transaction.rb @@ -0,0 +1,14 @@ +module SuperGood + module SolidusTaxjar + class OrderTransaction < ActiveRecord::Base + belongs_to :order, class_name: "Spree::Order" + + validates_presence_of :transaction_id + validates_presence_of :transaction_date + + def self.latest_for(order) + where(order: order).order(transaction_date: :desc).limit(1).first + end + end + end +end diff --git a/app/models/super_good/solidus_taxjar/refund_transaction.rb b/app/models/super_good/solidus_taxjar/refund_transaction.rb new file mode 100644 index 00000000..bf93de88 --- /dev/null +++ b/app/models/super_good/solidus_taxjar/refund_transaction.rb @@ -0,0 +1,13 @@ +module SuperGood + module SolidusTaxjar + class RefundTransaction < ActiveRecord::Base + belongs_to :order_transaction + + delegate :order, to: :order_transaction + + validates_presence_of :order_transaction + validates_presence_of :transaction_id + validates_presence_of :transaction_date + end + end +end diff --git a/app/models/super_good/solidus_taxjar/transaction_sync_batch.rb b/app/models/super_good/solidus_taxjar/transaction_sync_batch.rb new file mode 100644 index 00000000..ec5082ff --- /dev/null +++ b/app/models/super_good/solidus_taxjar/transaction_sync_batch.rb @@ -0,0 +1,10 @@ +class SuperGood::SolidusTaxjar::TransactionSyncBatch < ApplicationRecord + has_many :transaction_sync_logs + + def status + return 'processing' if transaction_sync_logs.processing.any? + return 'error' if transaction_sync_logs.error.any? + + 'success' + end +end diff --git a/app/models/super_good/solidus_taxjar/transaction_sync_log.rb b/app/models/super_good/solidus_taxjar/transaction_sync_log.rb new file mode 100644 index 00000000..789d0317 --- /dev/null +++ b/app/models/super_good/solidus_taxjar/transaction_sync_log.rb @@ -0,0 +1,7 @@ +class SuperGood::SolidusTaxjar::TransactionSyncLog < ApplicationRecord + belongs_to :transaction_sync_batch + belongs_to :order, class_name: "Spree::Order" + belongs_to :order_transaction, optional: true + + enum status: [:processing, :success, :error] +end diff --git a/app/overrides/spree/admin/shared/_configuration_menu.rb b/app/overrides/spree/admin/shared/_configuration_menu.rb new file mode 100644 index 00000000..9045f8cb --- /dev/null +++ b/app/overrides/spree/admin/shared/_configuration_menu.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +Deface::Override.new( + virtual_path: 'spree/admin/shared/_taxes_tabs', + name: 'add_taxjar_admin_menu_links', + insert_bottom: "[data-hook='admin_settings_taxes_tabs']" +) do + <<-HTML + <%= configurations_sidebar_menu_item "TaxJar Settings", edit_admin_taxjar_settings_path %> + HTML +end diff --git a/app/overrides/spree/admin/users/_tabs/add_tax_exemptions.html.erb.deface b/app/overrides/spree/admin/users/_tabs/add_tax_exemptions.html.erb.deface new file mode 100644 index 00000000..ce882ec4 --- /dev/null +++ b/app/overrides/spree/admin/users/_tabs/add_tax_exemptions.html.erb.deface @@ -0,0 +1,7 @@ + + +<% if can?(:manage, SuperGood::SolidusTaxjar::Customer) %> + > + <%= link_to 'tax exemptions', "/admin/users/#{@user.id}/tax_exemptions" %> + +<% end %> diff --git a/app/overrides/super_good/solidus_taxjar/spree/order_override.rb b/app/overrides/super_good/solidus_taxjar/spree/order_override.rb new file mode 100644 index 00000000..98b8f3cd --- /dev/null +++ b/app/overrides/super_good/solidus_taxjar/spree/order_override.rb @@ -0,0 +1,16 @@ +module SuperGood + module SolidusTaxjar + module Spree + module OrderOverride + def self.prepended(base) + base.has_many :taxjar_order_transactions, + class_name: "SuperGood::SolidusTaxjar::OrderTransaction", + dependent: :destroy, + inverse_of: :order + end + + ::Spree::Order.prepend self + end + end + end +end diff --git a/app/overrides/super_good/solidus_taxjar/spree/order_updater/fire_recalculated_event.rb b/app/overrides/super_good/solidus_taxjar/spree/order_updater/fire_recalculated_event.rb new file mode 100644 index 00000000..1e169bc3 --- /dev/null +++ b/app/overrides/super_good/solidus_taxjar/spree/order_updater/fire_recalculated_event.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module SuperGood + module SolidusTaxjar + module Spree + module OrderUpdater + module FireRecalculatedEvent + def persist_totals + ::Spree::Event.fire 'order_recalculated', order: order + super + end + + ::Spree::OrderUpdater.prepend(self) if ::Spree.solidus_gem_version < Gem::Version.new('2.11.0') + end + end + end + end +end diff --git a/app/overrides/super_good/solidus_taxjar/spree/shipment/fire_shipment_shipped_event.rb b/app/overrides/super_good/solidus_taxjar/spree/shipment/fire_shipment_shipped_event.rb new file mode 100644 index 00000000..a109745b --- /dev/null +++ b/app/overrides/super_good/solidus_taxjar/spree/shipment/fire_shipment_shipped_event.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module SuperGood + module SolidusTaxjar + module Spree + module Shipment + module FireShipmentShippedEvent + def after_ship + ::Spree::Event.fire 'shipment_shipped', shipment: self + super + end + + ::Spree::Shipment.prepend(self) + end + end + end + end +end diff --git a/app/overrides/super_good/solidus_taxjar/spree/user_override.rb b/app/overrides/super_good/solidus_taxjar/spree/user_override.rb new file mode 100644 index 00000000..156914e6 --- /dev/null +++ b/app/overrides/super_good/solidus_taxjar/spree/user_override.rb @@ -0,0 +1,16 @@ +module SuperGood + module SolidusTaxjar + module Spree + module UserOverride + def self.prepended(base) + base.has_one :taxjar_customer, + class_name: "SuperGood::SolidusTaxjar::Customer", + dependent: :destroy, + inverse_of: :user + end + + ::Spree::User.prepend self + end + end + end +end \ No newline at end of file diff --git a/app/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber.rb b/app/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber.rb new file mode 100644 index 00000000..9eb29c2f --- /dev/null +++ b/app/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber.rb @@ -0,0 +1,68 @@ +module SuperGood + module SolidusTaxjar + module Spree + module ReportingSubscriber + include ::Spree::Event::Subscriber + + if ::Spree::Event.method_defined?(:register) + ::Spree::Event.register("shipment_shipped") + end + + event_action :report_transaction, event_name: :shipment_shipped + event_action :replace_transaction, event_name: :order_recalculated + event_action :create_refund, event_name: :reimbursement_reimbursed + + def report_transaction(event) + shipment = event.payload[:shipment] + order = shipment.order + + return unless SuperGood::SolidusTaxjar.configuration.preferred_reporting_enabled + + if reportable_order?(order) + SuperGood::SolidusTaxjar::ReportTransactionJob.perform_later(order) + end + end + + def replace_transaction(event) + order = event.payload[:order] + + return unless SuperGood::SolidusTaxjar.configuration.preferred_reporting_enabled + + if reportable_order?(order) && transaction_replaceable?(order) && amount_changed?(order) + SuperGood::SolidusTaxjar::ReplaceTransactionJob.perform_later(order) + end + end + + def create_refund(event) + reimbursement = event.payload[:reimbursement] + order = reimbursement.order + + return unless SuperGood::SolidusTaxjar.configuration.preferred_reporting_enabled + + if reportable_order?(order) && transaction_refundable?(order) + SuperGood::SolidusTaxjar::ReportRefundJob.perform_later(reimbursement) + end + end + + private + + def amount_changed?(order) + SuperGood::SolidusTaxjar.api.show_latest_transaction_for(order).amount != + (order.total - order.additional_tax_total) + end + + def reportable_order?(order) + SuperGood::SolidusTaxjar.reportable_order_check.call(order) + end + + def transaction_replaceable?(order) + order.taxjar_order_transactions.present? && + order.complete? && + order.payment_state == "paid" + end + alias transaction_refundable? transaction_replaceable? + + end + end + end +end diff --git a/app/subscribers/super_good/solidus_taxjar/spree/tax_exemptions_subscriber.rb b/app/subscribers/super_good/solidus_taxjar/spree/tax_exemptions_subscriber.rb new file mode 100644 index 00000000..77718808 --- /dev/null +++ b/app/subscribers/super_good/solidus_taxjar/spree/tax_exemptions_subscriber.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require "spree/event/subscriber" +module SuperGood + module SolidusTaxjar + module Spree + module TaxExemptionsSubscriber + QUEUE_NAME = "taxjar" + + include ::Spree::Event::Subscriber + + event_action :create_customer, event_name: :tax_exemption_created + event_action :update_customer, event_name: :tax_exemption_updated + event_action :delete_customer, event_name: :tax_exemption_destroyed + event_action :send_notification_email, event_name: :tax_exemption_customer_request + event_action :send_approved_email, event_name: :tax_exemption_approved + event_action :send_disapproved_email, event_name: :tax_exemption_disapproved + + def create_customer(event) + user = event.payload[:user] + SuperGood::SolidusTaxjar.api.create_customer_for(user) if eligible_tax_exemption(user) + end + + def update_customer(event) + user = event.payload[:user] + customer = SuperGood::SolidusTaxjar.api.show_customer_for(user) + if customer + SuperGood::SolidusTaxjar.api.update_customer_for(user) if eligible_tax_exemption(user) + SuperGood::SolidusTaxjar.api.delete_customer_for(user) if no_exemptions(user) + else + SuperGood::SolidusTaxjar.api.create_customer_for(user) if eligible_tax_exemption(user) + end + end + + def delete_customer(event) + user = event.payload[:user] + SuperGood::SolidusTaxjar.api.delete_customer_for(user) + end + + def send_notification_email(event) + user = event.payload[:user] + SuperGood::SolidusTaxjar::TaxExemptionMailer.tax_exemption_request(user).deliver_now + end + + def send_approved_email(event) + user = event.payload[:user] + state = event.payload[:state] + SuperGood::SolidusTaxjar::TaxExemptionMailer.approved_email(user, state).deliver_now + end + + def send_disapproved_email(event) + user = event.payload[:user] + state = event.payload[:state] + SuperGood::SolidusTaxjar::TaxExemptionMailer.disapproved_email(user, state).deliver_now + end + + private + + def eligible_tax_exemption(user) + user.taxjar_customer&.taxjar_exempt_regions&.map(&:approved)&.any? + end + + def no_exemptions(user) + user.taxjar_customer&.taxjar_exempt_regions&.blank? + end + end + end + end +end diff --git a/app/views/spree/admin/taxjar_customers/_form.html.erb b/app/views/spree/admin/taxjar_customers/_form.html.erb new file mode 100644 index 00000000..0fe530cd --- /dev/null +++ b/app/views/spree/admin/taxjar_customers/_form.html.erb @@ -0,0 +1,29 @@ + diff --git a/app/views/spree/admin/taxjar_customers/edit.html.erb b/app/views/spree/admin/taxjar_customers/edit.html.erb new file mode 100644 index 00000000..2fc64ba1 --- /dev/null +++ b/app/views/spree/admin/taxjar_customers/edit.html.erb @@ -0,0 +1,24 @@ +<% admin_breadcrumb(link_to plural_resource_name(Spree::LegacyUser), spree.admin_users_path) %> +<% admin_breadcrumb(link_to @user.email, spree.edit_admin_user_url(@user)) %> +<% admin_breadcrumb("Tax Exemptions") %> + + +<%= render 'spree/admin/users/sidebar' %> +<%= render 'spree/admin/users/tabs', current: :tax_exemptions %> +<%= render :partial => 'spree/admin/users/user_page_actions' %> + +<% content_for :title do %> + Edit User Tax Exemption - Accounts - yVantage +<% end %> + +
+ +
\ No newline at end of file diff --git a/app/views/spree/admin/taxjar_customers/new.html.erb b/app/views/spree/admin/taxjar_customers/new.html.erb new file mode 100644 index 00000000..f376d034 --- /dev/null +++ b/app/views/spree/admin/taxjar_customers/new.html.erb @@ -0,0 +1,24 @@ +<% admin_breadcrumb(link_to plural_resource_name(Spree::LegacyUser), spree.admin_users_path) %> +<% admin_breadcrumb(link_to @user.email, spree.edit_admin_user_url(@user)) %> +<% admin_breadcrumb("Tax Exemptions") %> + + +<%= render 'spree/admin/users/sidebar' %> +<%= render 'spree/admin/users/tabs', current: :tax_exemptions %> +<%= render :partial => 'spree/admin/users/user_page_actions' %> + +<% content_for :title do %> + New User Tax Exemption - Accounts - yVantage +<% end %> + +
+ +
\ No newline at end of file diff --git a/app/views/spree/admin/taxjar_customers/show.html.erb b/app/views/spree/admin/taxjar_customers/show.html.erb new file mode 100644 index 00000000..325948f2 --- /dev/null +++ b/app/views/spree/admin/taxjar_customers/show.html.erb @@ -0,0 +1,90 @@ +<% admin_breadcrumb(link_to plural_resource_name(Spree::LegacyUser), spree.admin_users_path) %> +<% admin_breadcrumb(link_to @user.email, spree.edit_admin_user_url(@user)) %> +<% admin_breadcrumb("Tax Exemptions") %> + + +<%= render 'spree/admin/users/sidebar' %> +<%= render 'spree/admin/users/tabs', current: :tax_exemptions %> +<%= render :partial => 'spree/admin/users/user_page_actions' %> + +<% if @taxjar_customer %> +
+

Address : <%= @taxjar_customer.address ? @taxjar_customer.address.to_s_one_line : "No address found." %>

+

Tax exemption type : <%= @taxjar_customer.tax_exemption_type.humanize %>

+ <% if can? :delete, SuperGood::SolidusTaxjar::Customer %> + <%= button_to "Delete Tax Exemption", admin_user_tax_exemptions_path, :method=>:delete %> + <% end %> +
State Exemptions:
+ <% if @taxjar_customer.taxjar_exempt_regions.any? %> + + + + + + + + + + + + + + + + + + + <% @taxjar_customer.taxjar_exempt_regions.each do |exempt_region| %> + + + + + + + + <% end %> + +
Created AtState exemptionTax Exemption DocumentApproved
<%= exempt_region.created_at.strftime("%b %d %Y %l:%M%p") %><%= exempt_region.state.name %><%= exempt_region.tax_exemption_document.filename %> + + <%= exempt_region.approved.nil? ? "Pending Approval" : exempt_region.approved ? "Approved" : "Rejected" %> + + + <% if exempt_region.approved.nil? %> + <%= link_to '', disapprove_admin_user_tax_exemptions_exempt_region_path(@user, exempt_region), + class: 'delete-state-exemption fa fa-thumbs-down no-text with-tip', + data: { action: 'destroy'}, title: "Disapprove", method: :get %> + <%= link_to '', approve_admin_user_tax_exemptions_exempt_region_path(@user, exempt_region), + class: 'delete-state-exemption fa fa-thumbs-up no-text with-tip', + data: { action: 'destroy'}, title: "Approve", method: :get %> + <% elsif exempt_region.approved%> + <%= link_to '', admin_user_tax_exemptions_exempt_region_path(@user, exempt_region), + class: 'delete-state-exemption fa fa-trash no-text with-tip', + data: { action: 'destroy'}, title: "Delete State Exemption", method: :delete %> + <%= link_to '', disapprove_admin_user_tax_exemptions_exempt_region_path(@user, exempt_region), + class: 'delete-state-exemption fa fa-thumbs-down no-text with-tip', + data: { action: 'destroy'}, title: "Disapprove", method: :get %> + <% else %> + <%= link_to '', admin_user_tax_exemptions_exempt_region_path(@user, exempt_region), + class: 'delete-state-exemption fa fa-trash no-text with-tip', + data: { action: 'destroy'}, title: "Delete State Exemption", method: :delete %> + <%= link_to '', approve_admin_user_tax_exemptions_exempt_region_path(@user, exempt_region), + class: 'delete-state-exemption fa fa-thumbs-up no-text with-tip', + data: { action: 'destroy'}, title: "Approve", method: :get %> + <% end %> +
+ <% else %> +
+ <%= I18n.t("No State Regions Found. ") %>, + <%= link_to I18n.t(:add_one), new_admin_user_tax_exemptions_exempt_region_path(@user) %>! +
+ <% end %> + <% if can? :create, SuperGood::SolidusTaxjar::ExemptRegion %> + <%= button_to "Create State Exemption", new_admin_user_tax_exemptions_exempt_region_path(@user), method: :get %> + <% end %> +
+<% else %> + No Tax Exemptions Found. + <% if can? :create, SuperGood::SolidusTaxjar::Customer %> + <%= link_to "Create One", new_admin_user_tax_exemptions_path %> + <% end %> +<% end %> diff --git a/app/views/spree/admin/taxjar_exempt_regions/_form.html.erb b/app/views/spree/admin/taxjar_exempt_regions/_form.html.erb new file mode 100644 index 00000000..164a0b73 --- /dev/null +++ b/app/views/spree/admin/taxjar_exempt_regions/_form.html.erb @@ -0,0 +1,12 @@ +

+ <%= f.label :state, t(:state) %> + + <%= f.collection_select(:state_id, Spree::Country.find(214).states, + :id, :name, + {:include_blank => true}, + {:class => 'required select2'}) + %> +

+

+ <%= f.file_field :tax_exemption_document, direct_upload: true %> +

diff --git a/app/views/spree/admin/taxjar_exempt_regions/new.html.erb b/app/views/spree/admin/taxjar_exempt_regions/new.html.erb new file mode 100644 index 00000000..ed75335b --- /dev/null +++ b/app/views/spree/admin/taxjar_exempt_regions/new.html.erb @@ -0,0 +1,14 @@ +<% admin_breadcrumb(link_to plural_resource_name(Spree::LegacyUser), spree.admin_users_path) %> +<% admin_breadcrumb(link_to @user.email, spree.edit_admin_user_url(@user)) %> +<% admin_breadcrumb("Tax Exemptions") %> + +<%= render 'spree/admin/users/sidebar' %> +<%= render 'spree/admin/users/tabs', current: :tax_exemptions %> +<%= render :partial => 'spree/admin/users/user_page_actions' %> + +<%= form_for @taxjar_exempt_region, as: :exempt_region, url: "/admin/users/#{@user.id}/tax_exemptions/exempt_regions", method: :post do |f| %> + <%= render partial: 'form', locals: { f: f } %> +
+ +
+<% end %> diff --git a/app/views/spree/admin/taxjar_settings/edit.html.erb b/app/views/spree/admin/taxjar_settings/edit.html.erb new file mode 100644 index 00000000..5e020dee --- /dev/null +++ b/app/views/spree/admin/taxjar_settings/edit.html.erb @@ -0,0 +1,46 @@ +<%= render 'spree/admin/shared/taxes_tabs' %> + +<% content_for :page_title do %> + <%= "Taxjar Settings" %> +<% end %> + +<% if ENV["TAXJAR_API_KEY"] %> +

Nexus Regions

+ + + + + + + + + + <% @nexus_regions.each do |region| %> + + + + + + + <% end %> +
Country CodeCountryRegion CodeRegion
<%= region.country_code %><%= region.country %><%= region.region_code %><%= region.region %>
+ Sync Nexus Regions + Go to TaxJar to configure states + <% if SuperGood::SolidusTaxjar.reporting_ui_enabled %> + <%= form_with model: @configuration, url: admin_taxjar_settings_path, method: :put, local: true do |form| %> + <%= render "spree/admin/shared/preference_fields/boolean", + attribute: :preferred_reporting_enabled, + label: "Transaction Sync", + form: form + %> +

Sync orders and refund with TaxJar for automated sales tax reporting and filing. Complete and closed transactions sync automatically on update.

+ <%= form.submit %> + <% end %> + <%= form_with url: admin_taxjar_settings_backfill_transactions_path, method: :post, local: true do %> + <%= submit_tag "Backfill Transactions" %> + <% end %> + <% end %> +<% else %> +

You must provide a TaxJar API token to use this extension. You can sign up for TaxJar <%= link_to "here", "https://app.taxjar.com/api_sign_up", target: "_blank", rel: "noreferrer" %>. Please see the extension documentation for details on providing this token to the extension.

+

For more help in aquiring a TaxJar API token, see <%= link_to "How do I get a TaxJar sales tax API token?", "https://support.taxjar.com/article/160-how-do-i-get-a-sales-tax-api-token", target: "_blank", rel: "noreferrer" %>

+<% end %> diff --git a/app/views/spree/admin/transaction_sync_batches/index.html.erb b/app/views/spree/admin/transaction_sync_batches/index.html.erb new file mode 100644 index 00000000..78e0377e --- /dev/null +++ b/app/views/spree/admin/transaction_sync_batches/index.html.erb @@ -0,0 +1,34 @@ +<%= render 'spree/admin/shared/taxes_tabs' %> + +<% content_for :page_title do %> + <%= "Transaction Sync Batches" %> +<% end %> + + + + + + + + + + + + + + <% @batches.each do |batch| %> + + + + + + + + + <% end %> + +
Batch IDCreated atUpdated atProcessed OrdersStatus
<%= batch.id %><%= batch.created_at %><%= batch.updated_at %><%= batch.transaction_sync_logs.not_processing.count %>/<%= batch.transaction_sync_logs.count %><%= batch.status.capitalize %> + <%= link_to_edit_url admin_transaction_sync_batch_path(batch), title: "admin_transaction_sync_batch_show_#{dom_id(batch)}", no_text: true %> +
+ +<%= paginate @batches, theme: "solidus_admin" %> diff --git a/app/views/spree/admin/transaction_sync_batches/show.html.erb b/app/views/spree/admin/transaction_sync_batches/show.html.erb new file mode 100644 index 00000000..f3f1ebb5 --- /dev/null +++ b/app/views/spree/admin/transaction_sync_batches/show.html.erb @@ -0,0 +1,31 @@ +<%= render 'spree/admin/shared/taxes_tabs' %> + +<% content_for :page_title do %> + <%= "Transaction Sync Batch #{@batch.id}" %> +<% end %> + + + + + + + + + + + + + + <% @batch.transaction_sync_logs.each do |log| %> + + + + + + + + + <% end %> + +
Log IDOrderTaxJar Transaction IDStatusCreated atUpdated at
<%= log.id %><%= link_to log.order.number, spree.edit_admin_order_path(log.order) %><%= log.order_transaction&.transaction_id || "-" %><%= log.status.capitalize %><%= log.created_at %><%= log.updated_at %>
+ diff --git a/app/views/super_good/solidus_taxjar/tax_exemption_mailer/approved_email.html.erb b/app/views/super_good/solidus_taxjar/tax_exemption_mailer/approved_email.html.erb new file mode 100644 index 00000000..e35d26e4 --- /dev/null +++ b/app/views/super_good/solidus_taxjar/tax_exemption_mailer/approved_email.html.erb @@ -0,0 +1,27 @@ + + + + Tax exemption for <%= @state.abbr %> approved
+
+
+
+ + + + + + + + + If you have any questions, please contact Customer Service at 1-877-649-5463. + + + + + + + + Please do not reply to this automated email. This email was sent from an outgoing-only address and is not a managed email address. + + + diff --git a/app/views/super_good/solidus_taxjar/tax_exemption_mailer/disapproved_email.html.erb b/app/views/super_good/solidus_taxjar/tax_exemption_mailer/disapproved_email.html.erb new file mode 100644 index 00000000..f1ebcea1 --- /dev/null +++ b/app/views/super_good/solidus_taxjar/tax_exemption_mailer/disapproved_email.html.erb @@ -0,0 +1,27 @@ + + + + Tax exemption for <%= @state.abbr %> not approved
+
+
+
+ + + + + + + + + If you have any questions, please contact Customer Service at 1-877-649-5463. + + + + + + + + Please do not reply to this automated email. This email was sent from an outgoing-only address and is not a managed email address. + + + diff --git a/app/views/super_good/solidus_taxjar/tax_exemption_mailer/tax_exemption_request.html.erb b/app/views/super_good/solidus_taxjar/tax_exemption_mailer/tax_exemption_request.html.erb new file mode 100644 index 00000000..3b63bdcc --- /dev/null +++ b/app/views/super_good/solidus_taxjar/tax_exemption_mailer/tax_exemption_request.html.erb @@ -0,0 +1,30 @@ +<%= render 'mgx/contact_us_mailer/shared_css' %> + +
+ To a Printivity service representative: + +

+

Tax Exemption Request

+ Request Sent: <%= Time.now.strftime "%m/%d/%y %I:%M %p" %> + +
+
+

Customer Information

+
+

Customer Name: <%= "#{@user.first_name} #{@user.last_name}" %>

+

Customer Email: <%= @user.email %>

+
+ +  
+

Comments

+ +
+ +

+ +--
+This message was automatically generated by printivity.com on behalf of an Internet visitor.
+Target of request: Printivity customer service department
+Type of information request: General Request
diff --git a/bin/rails-engine b/bin/rails-engine index 5853e4d5..883b240a 100755 --- a/bin/rails-engine +++ b/bin/rails-engine @@ -3,7 +3,7 @@ # installed from the root of your application. ENGINE_ROOT = File.expand_path("..", __dir__) -ENGINE_PATH = File.expand_path("../lib/solidus_taxjar/engine", __dir__) +ENGINE_PATH = File.expand_path("../lib/super_good-solidus_taxjar", __dir__) # Set up gems listed in the Gemfile. ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) diff --git a/bin/sandbox b/bin/sandbox index ece914be..9f6d2bd2 100755 --- a/bin/sandbox +++ b/bin/sandbox @@ -25,7 +25,7 @@ else BRANCH="master" fi -extension_name="super_good_solidus_taxjar" +extension_name="super_good-solidus_taxjar" # Stay away from the bundler env of the containing extension. function unbundled { @@ -67,14 +67,16 @@ unbundled bundle install --gemfile Gemfile unbundled bundle exec rake db:drop db:create -unbundled bundle exec rails generate spree:install \ +unbundled bundle exec rails generate solidus:install \ --auto-accept \ --user_class=Spree::User \ --enforce_available_locales=true \ --with-authentication=false \ + --payment-method=none \ $@ unbundled bundle exec rails generate solidus:auth:install +unbundled bundle exec rails generate super_good:solidus_taxjar:install echo echo "🚀 Sandbox app successfully created for $extension_name!" diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 00000000..1f67500f --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +Spree::Core::Engine.routes.draw do + resource :tax_exemptions, controller: "taxjar_customers" do + get :nexus_regions + end + namespace :admin do + resource :taxjar_settings, only: [:edit, :update] + resources :transaction_sync_batches, only: [:index, :show] + resources :users, only: [] do + resource :tax_exemptions, controller: "taxjar_customers" do + resources :exempt_regions, controller: "taxjar_exempt_regions" do + get :approve, :disapprove, on: :member + end + end + end + get 'taxjar_settings/sync_nexus_regions', to: 'taxjar_settings#sync_nexus_regions' + post 'taxjar_settings/backfill_transactions', to: 'taxjar_settings#backfill_transactions' + end +end diff --git a/db/migrate/20210114001022_add_tax_exempt_customers.rb b/db/migrate/20210114001022_add_tax_exempt_customers.rb index e2a3d4bb..d1e4e20d 100644 --- a/db/migrate/20210114001022_add_tax_exempt_customers.rb +++ b/db/migrate/20210114001022_add_tax_exempt_customers.rb @@ -10,7 +10,7 @@ def change create_table :super_good_solidus_taxjar_exempt_regions do |t| t.references :taxjar_customer, index: { name: 'index_taxjar_exempt_regions_customer_id' }, null: false t.references :state, null: false - t.boolean :approved, null: false, default: false + t.boolean :approved t.timestamps end diff --git a/db/migrate/20210908205201_create_taxjar_order_transactions.rb b/db/migrate/20210908205201_create_taxjar_order_transactions.rb new file mode 100644 index 00000000..31e89cf5 --- /dev/null +++ b/db/migrate/20210908205201_create_taxjar_order_transactions.rb @@ -0,0 +1,16 @@ +class CreateTaxjarOrderTransactions < ActiveRecord::Migration[5.0] + def change + create_table :solidus_taxjar_order_transactions do |t| + t.references :order, null: false + t.string :transaction_id, null: false + + t.timestamps + end + + add_foreign_key :solidus_taxjar_order_transactions, + :spree_orders, + column: :order_id + + add_index :solidus_taxjar_order_transactions, :transaction_id, unique: true + end +end diff --git a/db/migrate/20211008175113_create_taxjar_refund_transaction.rb b/db/migrate/20211008175113_create_taxjar_refund_transaction.rb new file mode 100644 index 00000000..c9342ab3 --- /dev/null +++ b/db/migrate/20211008175113_create_taxjar_refund_transaction.rb @@ -0,0 +1,15 @@ +class CreateTaxjarRefundTransaction < ActiveRecord::Migration[5.0] + def change + create_table :solidus_taxjar_refund_transactions do |t| + t.references :order_transaction, index: { unique: true, name: :refund_transactions_orders_idx } + t.string :transaction_id, null: false, index: { unique: true } + t.datetime :transaction_date, null: false + + t.timestamps + end + + add_foreign_key :solidus_taxjar_refund_transactions, + :solidus_taxjar_order_transactions, + column: :order_transaction_id + end +end diff --git a/db/migrate/20211008183858_add_transaction_date_to_order_transaction.rb b/db/migrate/20211008183858_add_transaction_date_to_order_transaction.rb new file mode 100644 index 00000000..5f53d4ab --- /dev/null +++ b/db/migrate/20211008183858_add_transaction_date_to_order_transaction.rb @@ -0,0 +1,5 @@ +class AddTransactionDateToOrderTransaction < ActiveRecord::Migration[5.0] + def change + add_column :solidus_taxjar_order_transactions, :transaction_date, :datetime, null: false + end +end diff --git a/db/migrate/20211119143354_create_configuration.rb b/db/migrate/20211119143354_create_configuration.rb new file mode 100644 index 00000000..a40ea9f9 --- /dev/null +++ b/db/migrate/20211119143354_create_configuration.rb @@ -0,0 +1,8 @@ +class CreateConfiguration < ActiveRecord::Migration[5.0] + def change + create_table :solidus_taxjar_configuration do |t| + t.text :preferences + t.timestamps + end + end +end diff --git a/db/migrate/20220405213958_create_transaction_sync_batches.rb b/db/migrate/20220405213958_create_transaction_sync_batches.rb new file mode 100644 index 00000000..450f1037 --- /dev/null +++ b/db/migrate/20220405213958_create_transaction_sync_batches.rb @@ -0,0 +1,7 @@ +class CreateTransactionSyncBatches < ActiveRecord::Migration[5.0] + def change + create_table :solidus_taxjar_transaction_sync_batches do |t| + t.timestamps + end + end +end diff --git a/db/migrate/20220405215225_create_transaction_sync_logs.rb b/db/migrate/20220405215225_create_transaction_sync_logs.rb new file mode 100644 index 00000000..576c18fc --- /dev/null +++ b/db/migrate/20220405215225_create_transaction_sync_logs.rb @@ -0,0 +1,14 @@ +class CreateTransactionSyncLogs < ActiveRecord::Migration[5.0] + def change + create_table :solidus_taxjar_transaction_sync_logs do |t| + t.references :transaction_sync_batch, foreign_key: {to_table: :solidus_taxjar_transaction_sync_batches}, index: {name: "index_transaction_sync_logs_on_transaction_sync_batch_id"}, null: false + t.references :order, foreign_key: {to_table: :spree_orders}, null: false + + t.references :order_transaction, foreign_key: {to_table: :solidus_taxjar_order_transactions}, index: {name: "index_transaction_sync_logs_on_order_transaction_id"} + t.integer :status, null: false, default: 0 + t.string :error_message + + t.timestamps + end + end +end diff --git a/lib/generators/super_good/solidus_taxjar/install/install_generator.rb b/lib/generators/super_good/solidus_taxjar/install/install_generator.rb new file mode 100644 index 00000000..a61facf4 --- /dev/null +++ b/lib/generators/super_good/solidus_taxjar/install/install_generator.rb @@ -0,0 +1,33 @@ +module SuperGood + module SolidusTaxjar + module Generators + class InstallGenerator < Rails::Generators::Base + class_option :auto_run_migrations, type: :boolean, default: false + + def create_initializer_file + solidus_initializer_path = "config/initializers/solidus.rb" + + create_file(solidus_initializer_path) unless File.exists?(solidus_initializer_path) + append_to_file(solidus_initializer_path, <<~INIT) + Spree.config do |config| + config.tax_calculator_class = SuperGood::SolidusTaxjar::TaxCalculator + end + INIT + end + + def add_migrations + run 'bin/rails railties:install:migrations FROM=super_good_solidus_taxjar' + end + + def run_migrations + run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) + if run_migrations + run 'bin/rails db:migrate' + else + puts 'Skipping bin/rails db:migrate, don\'t forget to run it!' + end + end + end + end + end +end diff --git a/lib/super_good-solidus_taxjar.rb b/lib/super_good-solidus_taxjar.rb new file mode 100644 index 00000000..768d9f8e --- /dev/null +++ b/lib/super_good-solidus_taxjar.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require "super_good/solidus_taxjar" +require "super_good/engine" diff --git a/lib/super_good/engine.rb b/lib/super_good/engine.rb new file mode 100644 index 00000000..d222d492 --- /dev/null +++ b/lib/super_good/engine.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module SuperGoodSolidusTaxjar + class Engine < Rails::Engine + isolate_namespace Spree + engine_name 'super_good_solidus_taxjar' + + if Spree.solidus_gem_version < Gem::Version.new('2.11.0') + require root.join('app/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber') + SuperGood::SolidusTaxjar::Spree::ReportingSubscriber.subscribe! + SuperGood::SolidusTaxjar::Spree::TaxExemptionsSubscriber.subscribe! + end + + include SolidusSupport::EngineExtensions + end +end diff --git a/lib/super_good/solidus_taxjar.rb b/lib/super_good/solidus_taxjar.rb index a9874366..dd9e5be1 100644 --- a/lib/super_good/solidus_taxjar.rb +++ b/lib/super_good/solidus_taxjar.rb @@ -1,9 +1,10 @@ require "solidus_core" require "solidus_support" require "taxjar" +require "super_good/solidus_taxjar/overrides/request_override" -require "super_good/solidus_taxjar/engine" require "super_good/solidus_taxjar/version" +require "super_good/solidus_taxjar/transaction_id_generator" require "super_good/solidus_taxjar/api_params" require "super_good/solidus_taxjar/api" require "super_good/solidus_taxjar/calculator_helper" @@ -11,6 +12,8 @@ require "super_good/solidus_taxjar/tax_rate_calculator" require "super_good/solidus_taxjar/discount_calculator" require "super_good/solidus_taxjar/addresses" +require "super_good/solidus_taxjar/reporting" +require "super_good/solidus_taxjar/backfill_transactions" module SuperGood module SolidusTaxjar @@ -20,18 +23,40 @@ class << self attr_accessor :custom_order_params attr_accessor :discount_calculator attr_accessor :exception_handler + attr_accessor :job_queue attr_accessor :line_item_tax_label_maker attr_accessor :line_item_unit_price_calculator attr_accessor :logging_enabled + attr_accessor :reportable_order_check + attr_accessor :reporting_ui_enabled attr_accessor :shipping_calculator attr_accessor :shipping_tax_label_maker + attr_accessor :tax_exemption_mailer_from_address + attr_accessor :tax_exemption_mailer_to_address attr_accessor :taxable_address_check attr_accessor :taxable_order_check attr_accessor :test_mode + attr_writer :logger + + def configuration + ::SuperGood::SolidusTaxjar::Configuration.default + end def api ::SuperGood::SolidusTaxjar::Api.new end + + def table_name_prefix + "solidus_taxjar_" + end + + def reporting + ::SuperGood::SolidusTaxjar::Reporting.new + end + + def logger + @logger || Rails.logger + end end self.cache_duration = 3.hours @@ -42,13 +67,22 @@ def api self.custom_order_params = ->(order) { {} } self.discount_calculator = ::SuperGood::SolidusTaxjar::DiscountCalculator self.exception_handler = ->(e) { - Rails.logger.error "An error occurred while fetching TaxJar tax rates - #{e}: #{e.message}" + self.logger.error "An error occurred while fetching TaxJar tax rates - #{e}: #{e.message}" } + self.job_queue = :default self.line_item_tax_label_maker = ->(taxjar_line_item, spree_line_item) { "Sales Tax" } self.line_item_unit_price_calculator = ->(spree_line_item) { spree_line_item.price } self.logging_enabled = false - self.shipping_calculator = ->(order) { order.shipment_total } + + self.reportable_order_check = ->(order) { true } + # The reporting setting in the admin UI is disabled for now till the reporting + # feature is fully implemented. + self.reporting_ui_enabled = false + + self.shipping_calculator = ->(order) { order.shipments.sum(&:total_before_tax) } self.shipping_tax_label_maker = ->(shipment, shipping_tax) { "Sales Tax" } + self.tax_exemption_mailer_from_address = "admin@example.com" + self.tax_exemption_mailer_to_address = "admin@example.com" self.taxable_address_check = ->(address) { true } self.taxable_order_check = ->(order) { true } self.test_mode = false diff --git a/lib/super_good/solidus_taxjar/api.rb b/lib/super_good/solidus_taxjar/api.rb index 7955615d..6fc305c6 100644 --- a/lib/super_good/solidus_taxjar/api.rb +++ b/lib/super_good/solidus_taxjar/api.rb @@ -2,24 +2,27 @@ module SuperGood module SolidusTaxjar class Api def self.default_taxjar_client - ::Taxjar::Client.new( - api_key: ENV.fetch("TAXJAR_API_KEY"), + client = ::Taxjar::Client.new( + api_key: ENV["TAXJAR_API_KEY"], api_url: ENV.fetch("TAXJAR_API_URL") { "https://api.taxjar.com" } # Sandbox URL: https://api.sandbox.taxjar.com ) + client.set_api_config('headers', { + 'x-api-version' => '2020-08-07', + 'plugin' => 'supergoodsolidustaxjar' + }) + client end def initialize(taxjar_client: self.class.default_taxjar_client) @taxjar_client = taxjar_client end - def tax_for(order) - taxjar_client.tax_for_order(ApiParams.order_params(order)).tap do |taxes| - next unless SuperGood::SolidusTaxjar.logging_enabled + def tax_categories + taxjar_client.categories + end - Rails.logger.info( - "TaxJar response for #{order.number}: #{taxes.to_h.inspect}" - ) - end + def tax_for(order) + taxjar_client.tax_for_order(ApiParams.order_params(order)) end def tax_rate_for(address) @@ -31,7 +34,17 @@ def tax_rates_for(address) end def create_transaction_for(order) - taxjar_client.create_order ApiParams.transaction_params(order) + latest_transaction_id = + OrderTransaction.latest_for(order)&.transaction_id + + transaction_id = TransactionIdGenerator.next_transaction_id( + order: order, + current_transaction_id: latest_transaction_id + ) + + taxjar_client.create_order( + ApiParams.transaction_params(order, transaction_id) + ) end def update_transaction_for(order) @@ -42,6 +55,30 @@ def delete_transaction_for(order) taxjar_client.delete_order order.number end + def show_latest_transaction_for(order) + latest_transaction_id = + OrderTransaction.latest_for(order)&.transaction_id + + return unless latest_transaction_id + + taxjar_client.show_order(latest_transaction_id) + rescue Taxjar::Error::NotFound + nil + end + + def create_refund_transaction_for(order) + unless OrderTransaction.latest_for(order) + raise NotImplementedError, + "No latest TaxJar order transaction for #{order.number}. " \ + "Backfilling TaxJar transaction orders from Solidus is not yet " \ + "implemented." + end + + taxjar_order = show_latest_transaction_for(order) + + taxjar_client.create_refund ApiParams.refund_transaction_params(order, taxjar_order) + end + def create_refund_for(reimbursement) taxjar_client.create_refund ApiParams.refund_params(reimbursement) end @@ -52,6 +89,8 @@ def validate_spree_address(spree_address) def show_customer_for(user) taxjar_client.show_customer user.id + rescue Taxjar::Error::NotFound + nil end def create_customer_for(user) diff --git a/lib/super_good/solidus_taxjar/api_params.rb b/lib/super_good/solidus_taxjar/api_params.rb index 5a089446..5aa9f972 100644 --- a/lib/super_good/solidus_taxjar/api_params.rb +++ b/lib/super_good/solidus_taxjar/api_params.rb @@ -9,13 +9,6 @@ def order_params(order) .merge(line_items_params(order.line_items)) .merge(shipping: shipping(order)) .merge(SuperGood::SolidusTaxjar.custom_order_params.call(order)) - .tap do |params| - next unless SuperGood::SolidusTaxjar.logging_enabled - - Rails.logger.info( - "TaxJar params for #{order.number}: #{params.inspect}" - ) - end end #ADNAN: temp fix until we have a solution @@ -42,13 +35,13 @@ def tax_rate_address_params(address) }.merge(order_address_params(address)) end - def transaction_params(order) + def transaction_params(order, transaction_id = order.number) {} .merge(customer_id(order)) .merge(order_address_params(order.tax_address)) .merge(transaction_line_items_params(order.line_items)) .merge( - transaction_id: order.number, + transaction_id: transaction_id, transaction_date: order.completed_at.to_formatted_s(:iso8601), amount: [order.total - order.additional_tax_total, 0].max, shipping: shipping(order), @@ -56,6 +49,28 @@ def transaction_params(order) ) end + def refund_transaction_params(spree_order, taxjar_order) + {} + .merge(order_address_params(spree_order.tax_address)) + .merge( + { + transaction_id: TransactionIdGenerator.refund_transaction_id(taxjar_order.transaction_id), + transaction_reference_id: taxjar_order.transaction_id, + transaction_date: spree_order.completed_at.to_formatted_s(:iso8601), + amount: -1 * taxjar_order.amount, + sales_tax: -1 * taxjar_order.sales_tax, + shipping: -1 * taxjar_order.shipping, + line_items: taxjar_order.line_items.map { |line_item| + line_item.to_h.merge({ + unit_price: line_item.unit_price * -1, + discount: line_item.discount * -1, + sales_tax: line_item.sales_tax * -1 + }) + } + } + ) + end + def refund_params(reimbursement) additional_taxes = reimbursement.return_items.sum(&:additional_tax_total) @@ -74,10 +89,10 @@ def refund_params(reimbursement) def validate_address_params(spree_address) { country: spree_address.country&.iso, - state: spree_address.state&.abbr || adddress.state_name, + state: spree_address.state&.abbr || spree_address.state_name, zip: spree_address.zipcode, city: spree_address.city, - street: spree_address.address1 + street: [spree_address.address1, spree_address.address2].compact.join(' ') } end @@ -143,6 +158,7 @@ def transaction_line_items_params(line_items) id: line_item.id, quantity: line_item.quantity, product_identifier: line_item.sku, + description: line_item.variant.descriptive_name, product_tax_code: line_item.tax_category&.tax_code, unit_price: SuperGood::SolidusTaxjar.line_item_unit_price_calculator.call(line_item), discount: discount(line_item), diff --git a/lib/super_good/solidus_taxjar/backfill_transactions.rb b/lib/super_good/solidus_taxjar/backfill_transactions.rb new file mode 100644 index 00000000..917f99c2 --- /dev/null +++ b/lib/super_good/solidus_taxjar/backfill_transactions.rb @@ -0,0 +1,33 @@ +module SuperGood + module SolidusTaxjar + class BackfillTransactions + def initialize(api: SuperGood::SolidusTaxjar.api) + @api = api + end + + def call + transaction_sync_batch = SuperGood::SolidusTaxjar::TransactionSyncBatch.create! + + ::Spree::Order.complete.where(shipment_state: 'shipped').find_each do |order| + next if order.taxjar_order_transactions.any? + transaction_sync_log = SuperGood::SolidusTaxjar::TransactionSyncLog.create!( + transaction_sync_batch: transaction_sync_batch, + order: order + ) + begin + order_transaction = SuperGood::SolidusTaxjar.reporting.show_or_create_transaction(order) + transaction_sync_log.update!(order_transaction: order_transaction, status: :success) + rescue Taxjar::Error => exception + transaction_sync_log.update!(status: :error, error_message: exception.message) + end + end + + transaction_sync_batch + end + + private + + attr_reader :api + end + end +end diff --git a/lib/super_good/solidus_taxjar/calculator_helper.rb b/lib/super_good/solidus_taxjar/calculator_helper.rb index 91456584..50bc40e7 100644 --- a/lib/super_good/solidus_taxjar/calculator_helper.rb +++ b/lib/super_good/solidus_taxjar/calculator_helper.rb @@ -6,13 +6,18 @@ module CalculatorHelper def incomplete_address?(address) return true if address.is_a?(::Spree::Tax::TaxLocation) - [ + fields = [ address.address1, address.city, - address.state&.abbr || address.state_name, address.zipcode, address.country&.iso - ].any?(&:blank?) + ] + + if state_required?(address.country) + fields << (address.state&.abbr || address.state_name) + end + + fields.any?(&:blank?) end def taxable_address?(address) @@ -33,6 +38,15 @@ def cache def exception_handler SuperGood::SolidusTaxjar.exception_handler end + + # Only require a "state" value if this is an address for Canada or the + # USA. This aligns with TaxJar's API requirement for `to_state`. + # + # @param country [Spree::Country] The country to check. + # @return [Boolean] True if the "state" field is required for the country + def state_required?(country) + ["CA", "US"].include?(country&.iso) + end end end end diff --git a/lib/super_good/solidus_taxjar/discount_calculator.rb b/lib/super_good/solidus_taxjar/discount_calculator.rb index e5ae91c7..a4fe9aea 100644 --- a/lib/super_good/solidus_taxjar/discount_calculator.rb +++ b/lib/super_good/solidus_taxjar/discount_calculator.rb @@ -6,7 +6,7 @@ def initialize(line_item) end def discount - -line_item.promo_total + -1 * line_item.adjustments.select { |value| !value.tax? && value.eligible? }.sum(&:amount) end private diff --git a/lib/super_good/solidus_taxjar/overrides/request_override.rb b/lib/super_good/solidus_taxjar/overrides/request_override.rb new file mode 100644 index 00000000..41ab6d8e --- /dev/null +++ b/lib/super_good/solidus_taxjar/overrides/request_override.rb @@ -0,0 +1,15 @@ +module SuperGood + module SolidusTaxjar + module RequestOverride + def build_http_client + if SuperGood::SolidusTaxjar.logging_enabled + super.use(logging: {logger: SuperGood::SolidusTaxjar.logger}) + else + super + end + end + + Taxjar::API::Request.prepend(self) + end + end +end diff --git a/lib/super_good/solidus_taxjar/reporting.rb b/lib/super_good/solidus_taxjar/reporting.rb new file mode 100644 index 00000000..8c5dbdc5 --- /dev/null +++ b/lib/super_good/solidus_taxjar/reporting.rb @@ -0,0 +1,37 @@ +module SuperGood + module SolidusTaxjar + class Reporting + def initialize(api: SuperGood::SolidusTaxjar.api) + @api = api + end + + def create_refund(reimbursement) + @api.create_refund_for(reimbursement) + end + + def refund_and_create_new_transaction(order) + @api.create_refund_transaction_for(order) + if transaction_response = @api.create_transaction_for(order) + order.taxjar_order_transactions.create!( + transaction_id: transaction_response.transaction_id, + transaction_date: transaction_response.transaction_date + ) + end + end + + def show_or_create_transaction(order) + if transaction_response = @api.show_latest_transaction_for(order) + SuperGood::SolidusTaxjar::OrderTransaction.find_by!( + transaction_id: transaction_response.transaction_id + ) + else + transaction_response = @api.create_transaction_for(order) + order.taxjar_order_transactions.create!( + transaction_id: transaction_response.transaction_id, + transaction_date: transaction_response.transaction_date + ) + end + end + end + end +end diff --git a/lib/super_good/solidus_taxjar/tax_calculator.rb b/lib/super_good/solidus_taxjar/tax_calculator.rb index 1c9a4dab..79f76de5 100644 --- a/lib/super_good/solidus_taxjar/tax_calculator.rb +++ b/lib/super_good/solidus_taxjar/tax_calculator.rb @@ -109,8 +109,14 @@ def no_tax ) end + # Tax adjustments require an associated Tax Rate. This tax rate is not + # used in the extension's calculations as the rates from the TaxJar API + # are used, so a placeholder rate and calculator is created. def tax_rate - ::Spree::TaxRate.find_by(name: "Sales Tax") + ::Spree::TaxRate.find_or_create_by!(name: "Solidus TaxJar Rate") do |tax_rate| + tax_rate.calculator = ::Spree::Calculator.new + tax_rate.amount = 0 + end end def cache_key diff --git a/lib/super_good/solidus_taxjar/testing_support/factories/address_factory.rb b/lib/super_good/solidus_taxjar/testing_support/factories/address_factory.rb new file mode 100644 index 00000000..610993d3 --- /dev/null +++ b/lib/super_good/solidus_taxjar/testing_support/factories/address_factory.rb @@ -0,0 +1,11 @@ +FactoryBot.modify do + # Solidus's default address factories provide an invalid state and zipcode + # combination. + factory :address do + zipcode { "11430" } + + transient do + state_code { "NY" } + end + end +end diff --git a/lib/super_good/solidus_taxjar/testing_support/factories/configuration_factory.rb b/lib/super_good/solidus_taxjar/testing_support/factories/configuration_factory.rb new file mode 100644 index 00000000..835f45a7 --- /dev/null +++ b/lib/super_good/solidus_taxjar/testing_support/factories/configuration_factory.rb @@ -0,0 +1,3 @@ +FactoryBot.define do + factory :taxjar_configuration, class: "SuperGood::SolidusTaxjar::Configuration" +end diff --git a/lib/super_good/solidus_taxjar/testing_support/factories/order_transaction_factory.rb b/lib/super_good/solidus_taxjar/testing_support/factories/order_transaction_factory.rb new file mode 100644 index 00000000..4a7958b2 --- /dev/null +++ b/lib/super_good/solidus_taxjar/testing_support/factories/order_transaction_factory.rb @@ -0,0 +1,22 @@ +FactoryBot.define do + factory :taxjar_order_transaction, class: "SuperGood::SolidusTaxjar::OrderTransaction" do + order + transaction_date { Date.current } + + transient do + last_transaction_id { + SuperGood::SolidusTaxjar::OrderTransaction + .latest_for(order) + &.transaction_id + } + end + + transaction_id { + SuperGood::SolidusTaxjar::TransactionIdGenerator + .next_transaction_id( + order: order, + current_transaction_id: last_transaction_id + ) + } + end +end diff --git a/lib/super_good/solidus_taxjar/testing_support/factories/transaction_sync_batch_factory.rb b/lib/super_good/solidus_taxjar/testing_support/factories/transaction_sync_batch_factory.rb new file mode 100644 index 00000000..da3c4b28 --- /dev/null +++ b/lib/super_good/solidus_taxjar/testing_support/factories/transaction_sync_batch_factory.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :transaction_sync_batch, class: "SuperGood::SolidusTaxjar::TransactionSyncBatch" do + trait :with_logs do + after :build do |batch| + batch.transaction_sync_logs << build(:transaction_sync_log) + end + end + end +end diff --git a/lib/super_good/solidus_taxjar/testing_support/factories/transaction_sync_log_factory.rb b/lib/super_good/solidus_taxjar/testing_support/factories/transaction_sync_log_factory.rb new file mode 100644 index 00000000..51770ed5 --- /dev/null +++ b/lib/super_good/solidus_taxjar/testing_support/factories/transaction_sync_log_factory.rb @@ -0,0 +1,13 @@ +FactoryBot.define do + factory :transaction_sync_log, class: "SuperGood::SolidusTaxjar::TransactionSyncLog" do + order + + trait :success do + status { "success" } + + after :build do |log| + log.order_transaction = build(:taxjar_order_transaction, order: log.order) + end + end + end +end diff --git a/lib/super_good/solidus_taxjar/transaction_id_generator.rb b/lib/super_good/solidus_taxjar/transaction_id_generator.rb new file mode 100644 index 00000000..bd566eb5 --- /dev/null +++ b/lib/super_good/solidus_taxjar/transaction_id_generator.rb @@ -0,0 +1,45 @@ +module SuperGood + module SolidusTaxjar + # Responsible for generating `transaction_id` references for transactions + # we create on TaxJar for Solidus orders. This class handles creating + # associated ID's for transactions which need to be cancelled and recreated + # when the order is updated in Solidus after it has been sent to the TaxJar + # reporting API. + class TransactionIdGenerator + class << self + # Generates the next sequential `transaction_id` given an order and + # optionally the current transaction ID on TaxJar. This handles the + # case where a transaction already has been created on TaxJar and later + # needs to be cancelled and we need to create an updated transaction + # with an associated identifier. + # + # @param order [Spree::Order] the order for which we want to generate a + # transaction ID. + # @param current_transaction_id [String] the current transaction ID for + # the order if it exists on TaxJar. + # @return [String] the next sequential `transaction_id` + def next_transaction_id(order:, current_transaction_id: nil) + if current_transaction_id.nil? + "#{order.number}" + elsif order.number == current_transaction_id + "#{current_transaction_id}-1" + else + parts = current_transaction_id.rpartition("-") + parts.last.next! + parts.join + end + end + + # Generates a `transaction_id` for a refund transaction based on the + # ID of the transaction we're refunding. + # + # @param transaction_id [String] the ID of the transaction we are + # refunding. + # @return [String] the expected refund transaction ID. + def refund_transaction_id(transaction_id) + "#{transaction_id}-REFUND" + end + end + end + end +end diff --git a/lib/super_good/solidus_taxjar/version.rb b/lib/super_good/solidus_taxjar/version.rb index bf4fe139..2cfa6997 100644 --- a/lib/super_good/solidus_taxjar/version.rb +++ b/lib/super_good/solidus_taxjar/version.rb @@ -1,5 +1,5 @@ module SuperGood module SolidusTaxjar - VERSION = "1.0.0" + VERSION = "0.18.2" end end diff --git a/lib/super_good_solidus_taxjar.rb b/lib/super_good_solidus_taxjar.rb deleted file mode 100644 index f7f97c72..00000000 --- a/lib/super_good_solidus_taxjar.rb +++ /dev/null @@ -1 +0,0 @@ -require "super_good/solidus_taxjar" diff --git a/spec/features/spree/admin/backfill_transactions_spec.rb b/spec/features/spree/admin/backfill_transactions_spec.rb new file mode 100644 index 00000000..b491acdc --- /dev/null +++ b/spec/features/spree/admin/backfill_transactions_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +RSpec.feature 'Admin Transaction Sync Batches', js: true do + stub_authorization! + + background do + create :store, default: true + end + + let!(:transaction_sync_batch) { create :transaction_sync_batch } + let!(:second_transaction_sync_batch) { create :transaction_sync_batch, :with_logs } + let!(:processing_transaction_sync_log) { create :transaction_sync_log, transaction_sync_batch: transaction_sync_batch } + let!(:success_transaction_sync_log) { create :transaction_sync_log, :success, transaction_sync_batch: transaction_sync_batch } + + scenario "renders transaction backfill index" do + visit spree.admin_transaction_sync_batches_path(per_page: 1) + + within "#transaction_sync_batches" do + expect(page).to have_content(transaction_sync_batch.id) + expect(page).to have_content(transaction_sync_batch.created_at) + expect(page).to have_content(transaction_sync_batch.updated_at) + + within "tbody td:nth-child(4)" do + expect(page).to have_content("1/2") + end + + within "tbody td:nth-child(5)" do + expect(page).to have_content("Processing") + end + end + + within ".pagination" do + click_on "Next" + end + + within "#transaction_sync_batches" do + expect(page).to have_content(second_transaction_sync_batch.id) + expect(page).to have_content(second_transaction_sync_batch.created_at) + expect(page).to have_content(second_transaction_sync_batch.updated_at) + + within "tbody td:nth-child(4)" do + expect(page).to have_content("0/1") + end + + within "tbody td:nth-child(5)" do + expect(page).to have_content("Processing") + end + end + + within ".pagination" do + click_on "Prev" + end + + within ".actions" do + find(".fa-edit").click + end + + within "#transaction_sync_batch_logs" do + within "tbody tr:first-child" do + within "td:first-child" do + expect(page).to have_content(processing_transaction_sync_log.id) + end + + within "td:nth-child(2)" do + expect(page).to have_content(processing_transaction_sync_log.order.number) + end + + within "td:nth-child(3)" do + expect(page).to have_content("-") + end + + within "td:nth-child(4)" do + expect(page).to have_content("Processing") + end + + expect(page).to have_content(processing_transaction_sync_log.created_at) + expect(page).to have_content(processing_transaction_sync_log.updated_at) + end + + within "tbody tr:nth-child(2)" do + within "td:first-child" do + expect(page).to have_content(success_transaction_sync_log.id) + end + + within "td:nth-child(2)" do + expect(page).to have_content(success_transaction_sync_log.order.number) + end + + within "td:nth-child(3)" do + expect(page).to have_content(success_transaction_sync_log.order_transaction.transaction_id) + end + + within "td:nth-child(4)" do + expect(page).to have_content("Success") + end + + expect(page).to have_content(success_transaction_sync_log.created_at) + expect(page).to have_content(success_transaction_sync_log.updated_at) + end + end + end +end diff --git a/spec/features/spree/admin/checkout_spec.rb b/spec/features/spree/admin/checkout_spec.rb new file mode 100644 index 00000000..208cefb9 --- /dev/null +++ b/spec/features/spree/admin/checkout_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +RSpec.feature 'Checkout', js: true do + let!(:country) { create(:country, states_required: true) } + let!(:mug) { create(:product, name: "RoR Mug") } + + before do + create(:store, default: true) + create(:state, country: country, state_code: "CA") + create(:shipping_method) + create(:stock_location) + create(:check_payment_method) + create(:zone) + end + + def fill_in_address + # This order needs to be a taxable address in your TaxJar + # account in order to actually be charged tax. This means + # your account should have "nexus" in the state. + address = "order_bill_address_attributes" + + if Spree.solidus_gem_version >= Gem::Version.new("2.11") + fill_in "#{address}_name", with: "Ryan Bigg" + else + fill_in "#{address}_firstname", with: "Ryan" + fill_in "#{address}_lastname", with: "Bigg" + end + + fill_in "#{address}_address1", with: "450 Helen Ave" + fill_in "#{address}_city", with: "Ontario" + select "United States of America", from: "#{address}_country_id" + select "California", from: "#{address}_state_id" + fill_in "#{address}_zipcode", with: "91761" + fill_in "#{address}_phone", with: "(555) 555-5555" + end + + it "adds tax calculated by TaxJar to the order total", js: true, vcr: {cassette_name: "features/spree/admin/checkout", allow_playback_repeats: true, allow_unused_http_interactions: false} do + visit spree.root_path + + click_link mug.name + click_button "add-to-cart-button" + + click_button "Checkout" + + # Taxes are calculated by matching the line items returned in a tax response with the line items in the order by ID. + # When this spec is run in conjuction with other specs that create line items, the generated line item ID for the + # order becomes dynamic, while the line item ID in the cassette is static. We need to ensure these IDs always match, + # so we fix the line item ID to match the ID in the cassette. + Spree::Order.last.line_items.first.update!(id: 9999) + + fill_in "order_email", with: "test@example.com" + click_on "Continue" + + expect(page).to have_content("BILLING ADDRESS") + fill_in_address + click_button "Save and Continue" + + expect(page).to have_content("DELIVERY") + click_button "Save and Continue" + + # Check that the total on the page includes tax. Without tax, the total is $29.99. + expect(page).to have_content("Order Total: $31.54") + end +end diff --git a/spec/features/spree/admin/taxjar_settings_spec.rb b/spec/features/spree/admin/taxjar_settings_spec.rb new file mode 100644 index 00000000..51af3470 --- /dev/null +++ b/spec/features/spree/admin/taxjar_settings_spec.rb @@ -0,0 +1,96 @@ +require 'spec_helper' + +RSpec.feature 'Admin TaxJar Settings', js: true, vcr: true do + stub_authorization! + + background do + create :store, default: true + end + + describe "Taxjar settings tab" do + let(:api_token) { ENV.fetch("TAXJAR_API_KEY", "fake_token") } + + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("TAXJAR_API_KEY").and_return(api_token) + allow(SuperGood::SolidusTaxjar).to receive(:reporting_ui_enabled).and_return(true) + end + + scenario "the user navigates to the TaxJar Settings" do + visit "/admin" + click_on "Settings" + expect(page).to have_content("Taxes") + click_on "Taxes" + expect(page).to have_content("TaxJar Settings") + click_on "TaxJar Settings" + + within('[data-hook="admin_settings_taxes_tabs"] > .active') do + expect(page).to have_content("TaxJar Settings") + end + expect(page).to have_content("Transaction Sync") + end + + context "order is shipped" do + let(:order) { create :shipped_order } + + before do + create(:tax_rate, name: "Sales Tax") + # The order must be created **after** the TaxRate to have the correct totals + order + end + + scenario "the user backfills their transactions" do + visit "/admin/taxjar_settings/edit" + click_on "Backfill Transactions" + expect(page).to have_content(order.number) + end + end + + context "Taxjar reporting is enabled" do + it "shows that reporting is enabled" do + visit "/admin/taxjar_settings/edit" + + expect(page).to have_content("Transaction Sync") + expect(page).to have_field("Transaction Sync", checked: false) + expect(page).to have_content("Sync orders and refund with TaxJar for automated sales tax reporting and filing. Complete and closed transactions sync automatically on update.") + + check "Transaction Sync" + click_on "Update Configuration" + expect(page).to have_content("TaxJar settings updated!") + expect(page).to have_field("Transaction Sync", checked: true) + end + end + + context "Taxjar API token is set" do + it "shows the settings page" do + visit "/admin/taxjar_settings/edit" + expect(page).not_to have_content "You must provide a TaxJar API token" + expect(page).to have_content "Transaction Sync" + expect(page).to have_content("Nexus Regions") + expect(page).to have_link("Go to TaxJar to configure states", href: "https://app.taxjar.com/account#states") + expect(page).not_to have_content("British Columbia") + click_on "Sync Nexus Regions" + expect(page).to have_content("Updated with new Nexus Regions") + expect(page).to have_content("British Columbia") + end + end + + context "Taxjar API token isn't set" do + let(:api_token) { nil } + + it "shows a descriptive error message" do + visit "/admin/taxjar_settings/edit" + + expect(page).to have_content "You must provide a TaxJar API token" + + expect(page).to have_link(href: "https://app.taxjar.com/api_sign_up") + expect(page).to have_link(href: "https://support.taxjar.com/article/160-how-do-i-get-a-sales-tax-api-token") + end + + it "doesn't show any other TaxJar features" do + expect(page).not_to have_content("Transaction Sync") + expect(page).not_to have_content("Nexus Regions") + end + end + end +end diff --git a/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_API_token_is_set/shows_the_settings_page.yml b/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_API_token_is_set/shows_the_settings_page.yml new file mode 100644 index 00000000..60f2b251 --- /dev/null +++ b/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_API_token_is_set/shows_the_settings_page.yml @@ -0,0 +1,223 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.taxjar.com/v2/nexus/regions + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin MacBook-Pro.hitronhub.home 21.2.0 Darwin Kernel Version + 21.2.0: Sun Nov 28 20:28:54 PST 2021; root:xnu-8019.61.5~1/RELEASE_X86_64 + x86_64; ruby 2.6.6-p146; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '277' + Connection: + - close + Date: + - Wed, 16 Feb 2022 19:05:43 GMT + X-Amzn-Requestid: + - c115e204-2da1-4c4f-8a6f-812c62062eed + X-Amz-Apigw-Id: + - NpipOHfOoAMFmCg= + X-Amzn-Trace-Id: + - Root=1-620d4b07-225913c229f5a88b65fd6f36 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 ae06b19943a6bad1c1b12b79f7339498.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA19-C3 + X-Amz-Cf-Id: + - _87jwDiFvnDXNSDNMCaNT67Cq4AUP5XsO8dgzFGbX5LM9qK0N3xNzw== + body: + encoding: UTF-8 + string: '{"regions":[{"region_code":"BC","region":"British Columbia","country_code":"CA","country":"Canada"},{"region_code":"CA","region":"California","country_code":"US","country":"United + States"},{"region_code":"NY","region":"New York","country_code":"US","country":"United + States"}]}' + http_version: + recorded_at: Wed, 16 Feb 2022 19:05:43 GMT +- request: + method: get + uri: https://api.taxjar.com/v2/nexus/regions + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin MacBook-Pro.hitronhub.home 21.2.0 Darwin Kernel Version + 21.2.0: Sun Nov 28 20:28:54 PST 2021; root:xnu-8019.61.5~1/RELEASE_X86_64 + x86_64; ruby 2.6.6-p146; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '277' + Connection: + - close + Date: + - Wed, 16 Feb 2022 19:05:45 GMT + X-Amzn-Requestid: + - aca24973-b4eb-4a51-ad50-dbdb827afae8 + X-Amz-Apigw-Id: + - NpiphG5JoAMFg6w= + X-Amzn-Trace-Id: + - Root=1-620d4b09-724d75cb4d6c2cf74fbfdb9b + X-Cache: + - Miss from cloudfront + Via: + - 1.1 1f41b5f27f3ec2e93db2155dbc56900c.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA19-C3 + X-Amz-Cf-Id: + - 1RI42feI6GRc-JvGRgfeqW7UtWEN27GGE9PLsNxdEoANyZ1da3s5kg== + body: + encoding: UTF-8 + string: '{"regions":[{"region_code":"BC","region":"British Columbia","country_code":"CA","country":"Canada"},{"region_code":"CA","region":"California","country_code":"US","country":"United + States"},{"region_code":"NY","region":"New York","country_code":"US","country":"United + States"}]}' + http_version: + recorded_at: Wed, 16 Feb 2022 19:05:45 GMT +- request: + method: get + uri: https://api.taxjar.com/v2/nexus/regions + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin MacBook-Pro.hitronhub.home 21.2.0 Darwin Kernel Version + 21.2.0: Sun Nov 28 20:28:54 PST 2021; root:xnu-8019.61.5~1/RELEASE_X86_64 + x86_64; ruby 2.6.6-p146; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '277' + Connection: + - close + Date: + - Wed, 16 Feb 2022 19:05:48 GMT + X-Amzn-Requestid: + - 7ce419e9-f8f2-46d4-9caa-08af15ca95d1 + X-Amz-Apigw-Id: + - Npip7EHNIAMFRbA= + X-Amzn-Trace-Id: + - Root=1-620d4b0c-606c6896015b79d873567533 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 a9e73292d0b92053c3e38dcec15fd0e2.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA19-C3 + X-Amz-Cf-Id: + - 8QPS2lmr4ssZdm_yziyqCHJsXErDRJH3v7Mk-QjLhlzbo3OD0RHHNg== + body: + encoding: UTF-8 + string: '{"regions":[{"region_code":"BC","region":"British Columbia","country_code":"CA","country":"Canada"},{"region_code":"CA","region":"California","country_code":"US","country":"United + States"},{"region_code":"NY","region":"New York","country_code":"US","country":"United + States"}]}' + http_version: + recorded_at: Wed, 16 Feb 2022 19:05:48 GMT +- request: + method: get + uri: https://api.taxjar.com/v2/nexus/regions + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin MacBook-Pro.hitronhub.home 21.2.0 Darwin Kernel Version + 21.2.0: Sun Nov 28 20:28:54 PST 2021; root:xnu-8019.61.5~1/RELEASE_X86_64 + x86_64; ruby 2.6.6-p146; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '277' + Connection: + - close + Date: + - Wed, 16 Feb 2022 19:05:50 GMT + X-Amzn-Requestid: + - 647305bf-f303-4ab8-bd7f-10769927dba8 + X-Amz-Apigw-Id: + - NpiqPGM-IAMFliA= + X-Amzn-Trace-Id: + - Root=1-620d4b0e-2dab60b437f1a21222b45d49 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 6bdc2963c9ed59b475ec36c35e5932a4.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA19-C3 + X-Amz-Cf-Id: + - cAVmcW2057d2pagrCyIeQk40aTPmCc5TbrCWL-8uhcoFZjLRMAJy0Q== + body: + encoding: UTF-8 + string: '{"regions":[{"region_code":"BC","region":"British Columbia","country_code":"CA","country":"Canada"},{"region_code":"CA","region":"California","country_code":"US","country":"United + States"},{"region_code":"NY","region":"New York","country_code":"US","country":"United + States"}]}' + http_version: + recorded_at: Wed, 16 Feb 2022 19:05:50 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_reporting_is_enabled/shows_that_reporting_is_enabled.yml b/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_reporting_is_enabled/shows_that_reporting_is_enabled.yml new file mode 100644 index 00000000..3dcd8cca --- /dev/null +++ b/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_reporting_is_enabled/shows_that_reporting_is_enabled.yml @@ -0,0 +1,58 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.taxjar.com/v2/nexus/regions + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin MacBook-Pro.hitronhub.home 21.2.0 Darwin Kernel Version + 21.2.0: Sun Nov 28 20:28:54 PST 2021; root:xnu-8019.61.5~1/RELEASE_X86_64 + x86_64; ruby 2.6.6-p146; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '277' + Connection: + - close + Date: + - Wed, 16 Feb 2022 19:05:40 GMT + X-Amzn-Requestid: + - c395ceb2-62ec-4f9b-839d-8a9b482026a3 + X-Amz-Apigw-Id: + - NpiovG_VIAMF0yQ= + X-Amzn-Trace-Id: + - Root=1-620d4b04-6801a29a7e37dd427bd6a7e7 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 ae06b19943a6bad1c1b12b79f7339498.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA19-C3 + X-Amz-Cf-Id: + - zQxugXItdDwa7eNj4OTUQB2c7wdabn2nj0MW-9abeEokPBb1AOzKOw== + body: + encoding: UTF-8 + string: '{"regions":[{"region_code":"BC","region":"British Columbia","country_code":"CA","country":"Canada"},{"region_code":"CA","region":"California","country_code":"US","country":"United + States"},{"region_code":"NY","region":"New York","country_code":"US","country":"United + States"}]}' + http_version: + recorded_at: Wed, 16 Feb 2022 19:05:40 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/order_is_shipped/the_user_backfills_their_transactions.yml b/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/order_is_shipped/the_user_backfills_their_transactions.yml new file mode 100644 index 00000000..553b0fe7 --- /dev/null +++ b/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/order_is_shipped/the_user_backfills_their_transactions.yml @@ -0,0 +1,195 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"unit_price":"10.0","discount":"-0.0","product_tax_code":"TaxCode + - 16961"}],"shipping":"100.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1487' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:26:17 GMT + X-Amzn-Requestid: + - 0e6e41fe-9d8b-4cfe-bb6c-f4e148dbb95a + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - PWoLBHlfoAMFe7g= + X-Amzn-Trace-Id: + - Root=1-6238ed79-1b3d098b28f33fde253b05c0 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 ead78c395f4bede3ec6cd7ea180e3d3a.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - WSDtPQxVpt8E4oXUax0OhSb3nONYVbmTZI9DF5r3MKUxIdECtRi9Tw== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":9.76,"breakdown":{"city_tax_collectable":5.36,"city_tax_rate":0.04875,"city_taxable_amount":110.0,"combined_tax_rate":0.08875,"county_tax_collectable":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"line_items":[{"city_amount":0.49,"city_tax_rate":0.04875,"city_taxable_amount":10.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"id":"1","special_district_amount":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_amount":0.4,"state_sales_tax_rate":0.04,"state_taxable_amount":10.0,"tax_collectable":0.89,"taxable_amount":10.0}],"shipping":{"city_amount":4.88,"city_tax_rate":0.04875,"city_taxable_amount":100.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"special_district_amount":0.0,"special_tax_rate":0.0,"special_taxable_amount":0.0,"state_amount":4.0,"state_sales_tax_rate":0.04,"state_taxable_amount":100.0,"tax_collectable":8.88,"taxable_amount":100.0},"special_district_tax_collectable":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_tax_collectable":4.4,"state_tax_rate":0.04,"state_taxable_amount":110.0,"tax_collectable":9.76,"taxable_amount":110.0},"freight_taxable":true,"has_nexus":true,"jurisdictions":{"city":"NEW + YORK CITY","country":"US","county":"QUEENS","state":"NY"},"order_total_amount":110.0,"rate":0.08875,"shipping":100.0,"tax_source":"destination","taxable_amount":110.0}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:26:17 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"unit_price":"10.0","discount":"-0.0","product_tax_code":"TaxCode + - 16961"}],"shipping":"100.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1487' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:26:18 GMT + X-Amzn-Requestid: + - a3b1b949-1158-4194-856c-7fc46b06314d + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - PWoLFHm2IAMFe7g= + X-Amzn-Trace-Id: + - Root=1-6238ed79-142a467d715eb0ef75b32c96 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 91356d2137f5a7345e93da4516c49ec4.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - QKfY3g5fuy3wJYPDMbkqk74xh_X5YCdIuqGDllzEbPagyn7uSiUlIw== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":9.76,"breakdown":{"city_tax_collectable":5.36,"city_tax_rate":0.04875,"city_taxable_amount":110.0,"combined_tax_rate":0.08875,"county_tax_collectable":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"line_items":[{"city_amount":0.49,"city_tax_rate":0.04875,"city_taxable_amount":10.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"id":"1","special_district_amount":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_amount":0.4,"state_sales_tax_rate":0.04,"state_taxable_amount":10.0,"tax_collectable":0.89,"taxable_amount":10.0}],"shipping":{"city_amount":4.88,"city_tax_rate":0.04875,"city_taxable_amount":100.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"special_district_amount":0.0,"special_tax_rate":0.0,"special_taxable_amount":0.0,"state_amount":4.0,"state_sales_tax_rate":0.04,"state_taxable_amount":100.0,"tax_collectable":8.88,"taxable_amount":100.0},"special_district_tax_collectable":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_tax_collectable":4.4,"state_tax_rate":0.04,"state_taxable_amount":110.0,"tax_collectable":9.76,"taxable_amount":110.0},"freight_taxable":true,"has_nexus":true,"jurisdictions":{"city":"NEW + YORK CITY","country":"US","county":"QUEENS","state":"NY"},"order_total_amount":110.0,"rate":0.08875,"shipping":100.0,"tax_source":"destination","taxable_amount":110.0}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:26:17 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/orders + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"product_identifier":"SKU-1","description":"Product + #1 - 2658 - Master","product_tax_code":"TaxCode - 16961","unit_price":"10.0","discount":"-0.0","sales_tax":"0.89"}],"transaction_id":"R707003799","transaction_date":"2022-03-21T21:26:17Z","amount":"110.0","shipping":"100.0","sales_tax":"9.77"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 201 + message: Created + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '642' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:26:18 GMT + X-Amzn-Requestid: + - e305dae5-01ee-497d-a668-c0479cbd6b39 + X-Amzn-Remapped-Content-Length: + - '642' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - Ft6Cewuma5JfYtUY7hdC + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - PWoLMF7goAMFYOA= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Mon, 21 Mar 2022 21:26:18 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 73cb83fe6699afc2791b5c690c1ff8c6.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - HyKlDQcXrkWtBaKVDGib4ctiL3kj9gDGuECsegnDYStFFXWK8aiPGw== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R707003799","transaction_date":"2022-03-21T21:26:17.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 16961","product_identifier":"SKU-1","id":0,"discount":"0.0","description":"Product + #1 - 2658 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"1","amount":"110.0"}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:26:18 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/Admin_TaxJar_Settings/_backfill_transactions/shows_a_flash_message.yml b/spec/fixtures/cassettes/Admin_TaxJar_Settings/_backfill_transactions/shows_a_flash_message.yml new file mode 100644 index 00000000..7df12481 --- /dev/null +++ b/spec/fixtures/cassettes/Admin_TaxJar_Settings/_backfill_transactions/shows_a_flash_message.yml @@ -0,0 +1,195 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"unit_price":"10.0","discount":"-0.0","product_tax_code":"TaxCode + - 628555"}],"shipping":"100.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1487' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:19:24 GMT + X-Amzn-Requestid: + - 55402023-6ea7-4225-8848-075436c2084c + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - PWnKhEnFoAMFRgw= + X-Amzn-Trace-Id: + - Root=1-6238ebdc-31bca8f21ece910819296c15 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 b36bf2c460ac693ce304817aed073112.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - sA913tfpvSDNA45keoACZM-WQmziZsjE990sV5Y-lIbByJCuBZfN_A== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":9.76,"breakdown":{"city_tax_collectable":5.36,"city_tax_rate":0.04875,"city_taxable_amount":110.0,"combined_tax_rate":0.08875,"county_tax_collectable":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"line_items":[{"city_amount":0.49,"city_tax_rate":0.04875,"city_taxable_amount":10.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"id":"1","special_district_amount":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_amount":0.4,"state_sales_tax_rate":0.04,"state_taxable_amount":10.0,"tax_collectable":0.89,"taxable_amount":10.0}],"shipping":{"city_amount":4.88,"city_tax_rate":0.04875,"city_taxable_amount":100.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"special_district_amount":0.0,"special_tax_rate":0.0,"special_taxable_amount":0.0,"state_amount":4.0,"state_sales_tax_rate":0.04,"state_taxable_amount":100.0,"tax_collectable":8.88,"taxable_amount":100.0},"special_district_tax_collectable":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_tax_collectable":4.4,"state_tax_rate":0.04,"state_taxable_amount":110.0,"tax_collectable":9.76,"taxable_amount":110.0},"freight_taxable":true,"has_nexus":true,"jurisdictions":{"city":"NEW + YORK CITY","country":"US","county":"QUEENS","state":"NY"},"order_total_amount":110.0,"rate":0.08875,"shipping":100.0,"tax_source":"destination","taxable_amount":110.0}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:19:24 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"unit_price":"10.0","discount":"-0.0","product_tax_code":"TaxCode + - 628555"}],"shipping":"100.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1487' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:19:25 GMT + X-Amzn-Requestid: + - 30a8185f-0afd-42fd-9854-406b54c97605 + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - PWnKlGjVoAMFQjA= + X-Amzn-Trace-Id: + - Root=1-6238ebdd-46f9cca029c612e54a4e06cc + X-Cache: + - Miss from cloudfront + Via: + - 1.1 91356d2137f5a7345e93da4516c49ec4.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - ChhWyg3jTVLjpLmKfgv4sU_AVu9m5Ozmvky4Cphm8KaEoxfIFERbbw== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":9.76,"breakdown":{"city_tax_collectable":5.36,"city_tax_rate":0.04875,"city_taxable_amount":110.0,"combined_tax_rate":0.08875,"county_tax_collectable":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"line_items":[{"city_amount":0.49,"city_tax_rate":0.04875,"city_taxable_amount":10.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"id":"1","special_district_amount":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_amount":0.4,"state_sales_tax_rate":0.04,"state_taxable_amount":10.0,"tax_collectable":0.89,"taxable_amount":10.0}],"shipping":{"city_amount":4.88,"city_tax_rate":0.04875,"city_taxable_amount":100.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"special_district_amount":0.0,"special_tax_rate":0.0,"special_taxable_amount":0.0,"state_amount":4.0,"state_sales_tax_rate":0.04,"state_taxable_amount":100.0,"tax_collectable":8.88,"taxable_amount":100.0},"special_district_tax_collectable":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_tax_collectable":4.4,"state_tax_rate":0.04,"state_taxable_amount":110.0,"tax_collectable":9.76,"taxable_amount":110.0},"freight_taxable":true,"has_nexus":true,"jurisdictions":{"city":"NEW + YORK CITY","country":"US","county":"QUEENS","state":"NY"},"order_total_amount":110.0,"rate":0.08875,"shipping":100.0,"tax_source":"destination","taxable_amount":110.0}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:19:25 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/orders + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"product_identifier":"SKU-1","description":"Product + #1 - 6147 - Master","product_tax_code":"TaxCode - 628555","unit_price":"10.0","discount":"-0.0","sales_tax":"0.89"}],"transaction_id":"R544482330","transaction_date":"2022-03-21T21:19:24Z","amount":"110.0","shipping":"100.0","sales_tax":"9.77"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 201 + message: Created + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '643' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:19:25 GMT + X-Amzn-Requestid: + - 8a1b5211-d797-4ec3-9291-0ece37952546 + X-Amzn-Remapped-Content-Length: + - '643' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - Ft6CGt0zAif1v4oPhdtD + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - PWnKpHP5IAMFWAA= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Mon, 21 Mar 2022 21:19:25 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 5397b304713f6301c7c94ac084b6ed08.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - 34sibkRUBSduxgDNnHQ3hmrPFgZXxC2BZVZM-tOzi6ILsvkIi3gRiQ== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R544482330","transaction_date":"2022-03-21T21:19:24.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 628555","product_identifier":"SKU-1","id":0,"discount":"0.0","description":"Product + #1 - 6147 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"1","amount":"110.0"}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:19:25 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/Admin_TaxJar_Settings/_backfill_transactions/shows_the_backfilled_orders.yml b/spec/fixtures/cassettes/Admin_TaxJar_Settings/_backfill_transactions/shows_the_backfilled_orders.yml new file mode 100644 index 00000000..e47692de --- /dev/null +++ b/spec/fixtures/cassettes/Admin_TaxJar_Settings/_backfill_transactions/shows_the_backfilled_orders.yml @@ -0,0 +1,195 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"customer_id":"2","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":2,"quantity":1,"unit_price":"10.0","discount":"-0.0","product_tax_code":"TaxCode + - 584814"}],"shipping":"100.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1487' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:19:31 GMT + X-Amzn-Requestid: + - 2e02c1b5-69bf-4ea8-9f82-4150026a849c + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - PWnLgEuHIAMF9iw= + X-Amzn-Trace-Id: + - Root=1-6238ebe3-54cfd5734d4370fe5940033c + X-Cache: + - Miss from cloudfront + Via: + - 1.1 989017835f43d594873bde5a7ee7fe5e.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - aMDnhMn6DBh8NXIwvdKtIiTG5XY7-9Lr4G60MG-HZzPBa0gsNYo7hg== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":9.76,"breakdown":{"city_tax_collectable":5.36,"city_tax_rate":0.04875,"city_taxable_amount":110.0,"combined_tax_rate":0.08875,"county_tax_collectable":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"line_items":[{"city_amount":0.49,"city_tax_rate":0.04875,"city_taxable_amount":10.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"id":"2","special_district_amount":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_amount":0.4,"state_sales_tax_rate":0.04,"state_taxable_amount":10.0,"tax_collectable":0.89,"taxable_amount":10.0}],"shipping":{"city_amount":4.88,"city_tax_rate":0.04875,"city_taxable_amount":100.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"special_district_amount":0.0,"special_tax_rate":0.0,"special_taxable_amount":0.0,"state_amount":4.0,"state_sales_tax_rate":0.04,"state_taxable_amount":100.0,"tax_collectable":8.88,"taxable_amount":100.0},"special_district_tax_collectable":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_tax_collectable":4.4,"state_tax_rate":0.04,"state_taxable_amount":110.0,"tax_collectable":9.76,"taxable_amount":110.0},"freight_taxable":true,"has_nexus":true,"jurisdictions":{"city":"NEW + YORK CITY","country":"US","county":"QUEENS","state":"NY"},"order_total_amount":110.0,"rate":0.08875,"shipping":100.0,"tax_source":"destination","taxable_amount":110.0}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:19:31 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"customer_id":"2","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":2,"quantity":1,"unit_price":"10.0","discount":"-0.0","product_tax_code":"TaxCode + - 584814"}],"shipping":"100.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1487' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:19:31 GMT + X-Amzn-Requestid: + - 5805eaf2-40eb-4c12-be92-746b3c1ca357 + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - PWnLkFk9IAMF_pQ= + X-Amzn-Trace-Id: + - Root=1-6238ebe3-7dd5738065775a6a19c5aa01 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 707e733794d52100fde0ab21bf0b1462.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - OwDpFidE_pv2lD9aSrWITwIkz_1D8kra_VF02i45KvtHmoH2XUXmMQ== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":9.76,"breakdown":{"city_tax_collectable":5.36,"city_tax_rate":0.04875,"city_taxable_amount":110.0,"combined_tax_rate":0.08875,"county_tax_collectable":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"line_items":[{"city_amount":0.49,"city_tax_rate":0.04875,"city_taxable_amount":10.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"id":"2","special_district_amount":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_amount":0.4,"state_sales_tax_rate":0.04,"state_taxable_amount":10.0,"tax_collectable":0.89,"taxable_amount":10.0}],"shipping":{"city_amount":4.88,"city_tax_rate":0.04875,"city_taxable_amount":100.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"special_district_amount":0.0,"special_tax_rate":0.0,"special_taxable_amount":0.0,"state_amount":4.0,"state_sales_tax_rate":0.04,"state_taxable_amount":100.0,"tax_collectable":8.88,"taxable_amount":100.0},"special_district_tax_collectable":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_tax_collectable":4.4,"state_tax_rate":0.04,"state_taxable_amount":110.0,"tax_collectable":9.76,"taxable_amount":110.0},"freight_taxable":true,"has_nexus":true,"jurisdictions":{"city":"NEW + YORK CITY","country":"US","county":"QUEENS","state":"NY"},"order_total_amount":110.0,"rate":0.08875,"shipping":100.0,"tax_source":"destination","taxable_amount":110.0}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:19:31 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/orders + body: + encoding: UTF-8 + string: '{"customer_id":"2","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":2,"quantity":1,"product_identifier":"SKU-2","description":"Product + #2 - 5017 - Master","product_tax_code":"TaxCode - 584814","unit_price":"10.0","discount":"-0.0","sales_tax":"0.89"}],"transaction_id":"R731441257","transaction_date":"2022-03-21T21:19:31Z","amount":"110.0","shipping":"100.0","sales_tax":"9.77"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 201 + message: Created + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '643' + Connection: + - close + Date: + - Mon, 21 Mar 2022 21:19:32 GMT + X-Amzn-Requestid: + - fdef4954-15c6-4eb9-9671-dba5f7eb6e68 + X-Amzn-Remapped-Content-Length: + - '643' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - Ft6CHFmgx5VazQIgpy0B + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - PWnLpHGLIAMFUrQ= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Mon, 21 Mar 2022 21:19:32 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 32d624dbeb2a8b7f24dbe49007e37c90.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - LaCiEiMKoNoBWfYSh9Wj6pDSILWJKWEn1BNIftZhKHph2nrfyqRtKQ== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R731441257","transaction_date":"2022-03-21T21:19:31.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 584814","product_identifier":"SKU-2","id":0,"discount":"0.0","description":"Product + #2 - 5017 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"2","amount":"110.0"}}' + http_version: + recorded_at: Mon, 21 Mar 2022 21:19:31 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_Reporting/_refund_and_create_transaction/when_Taxjar_cannot_create_a_refund_transaction/doesn_t_create_a_new_transaction.yml b/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_Reporting/_refund_and_create_transaction/when_Taxjar_cannot_create_a_refund_transaction/doesn_t_create_a_new_transaction.yml new file mode 100644 index 00000000..75086575 --- /dev/null +++ b/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_Reporting/_refund_and_create_transaction/when_Taxjar_cannot_create_a_refund_transaction/doesn_t_create_a_new_transaction.yml @@ -0,0 +1,415 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"unit_price":"10.0","discount":"-0.0","product_tax_code":"TaxCode + - 514214"}],"shipping":"100.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1487' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:49 GMT + X-Amzn-Requestid: + - 460fb8ca-2570-4a44-8892-4c548f8b1e86 + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - NtWaFEQlIAMF2bA= + X-Amzn-Trace-Id: + - Root=1-620ed10d-3d86387a11c36f2e11a0ef76 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 733ae4e17f2a4786e797d3450daabd46.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - sn4iI6fHlTydVvuP_OYQJis8Q64y6k85mgar2BXXwHFooU7cA-WLBw== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":9.76,"breakdown":{"city_tax_collectable":5.36,"city_tax_rate":0.04875,"city_taxable_amount":110.0,"combined_tax_rate":0.08875,"county_tax_collectable":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"line_items":[{"city_amount":0.49,"city_tax_rate":0.04875,"city_taxable_amount":10.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"id":"1","special_district_amount":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_amount":0.4,"state_sales_tax_rate":0.04,"state_taxable_amount":10.0,"tax_collectable":0.89,"taxable_amount":10.0}],"shipping":{"city_amount":4.88,"city_tax_rate":0.04875,"city_taxable_amount":100.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"special_district_amount":0.0,"special_tax_rate":0.0,"special_taxable_amount":0.0,"state_amount":4.0,"state_sales_tax_rate":0.04,"state_taxable_amount":100.0,"tax_collectable":8.88,"taxable_amount":100.0},"special_district_tax_collectable":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_tax_collectable":4.4,"state_tax_rate":0.04,"state_taxable_amount":110.0,"tax_collectable":9.76,"taxable_amount":110.0},"freight_taxable":true,"has_nexus":true,"jurisdictions":{"city":"NEW + YORK CITY","country":"US","county":"QUEENS","state":"NY"},"order_total_amount":110.0,"rate":0.08875,"shipping":100.0,"tax_source":"destination","taxable_amount":110.0}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:49 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/orders + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"product_identifier":"SKU-2","description":"Product + #2 - 9838 - Master","product_tax_code":"TaxCode - 514214","unit_price":"10.0","discount":"-0.0","sales_tax":"0.89"}],"transaction_id":"R010654408","transaction_date":"2022-02-17T22:49:49Z","amount":"110.0","shipping":"100.0","sales_tax":"9.77"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 201 + message: Created + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '643' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:49 GMT + X-Amzn-Requestid: + - 71ac36e8-f871-4096-9da3-fbb9bef47684 + X-Amzn-Remapped-Content-Length: + - '643' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0d4wnyM7rud1JPMBC + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWaIFB0IAMFcFg= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:49 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 f83d0d4febf7c22c3236bd42fa6dcd96.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - 5CitprWWphF0SwanJ7jzkorJg262ja9_1JL_mdjlGu3rs56Hljr6Qw== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R010654408","transaction_date":"2022-02-17T22:49:49.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 514214","product_identifier":"SKU-2","id":0,"discount":"0.0","description":"Product + #2 - 9838 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"1","amount":"110.0"}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:49 GMT +- request: + method: get + uri: https://api.taxjar.com/v2/transactions/orders/R010654408 + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '643' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:49 GMT + X-Amzn-Requestid: + - 17158d67-2bf9-439f-be42-5c54e40a0958 + X-Amzn-Remapped-Content-Length: + - '643' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0d5jh5AiICu9gdq-B + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWaKF43oAMF-NQ= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:49 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 782cf460fc93d8eefdb183b4750900f2.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - R7gMLpx3taeFU87m0gXJcoyhhLRjt6gH0BJZihcr-wV_8Bjrxn5-AA== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R010654408","transaction_date":"2022-02-17T22:49:49.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 514214","product_identifier":"SKU-2","id":0,"discount":"0.0","description":"Product + #2 - 9838 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"1","amount":"110.0"}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:49 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/refunds + body: + encoding: UTF-8 + string: '{"to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","transaction_id":"R010654408-REFUND","transaction_reference_id":"R010654408","transaction_date":"2022-02-17T22:49:49Z","amount":-110.0,"sales_tax":-9.77,"shipping":-100.0,"line_items":[{"unit_price":-10.0,"sales_tax":-0.89,"quantity":1,"product_tax_code":"TaxCode + - 514214","product_identifier":"SKU-2","id":0,"discount":-0.0,"description":"Product + #2 - 9838 - Master"}]}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 201 + message: Created + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '665' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:50 GMT + X-Amzn-Requestid: + - 00ab07ef-a018-4fc8-a920-cb9a02160226 + X-Amzn-Remapped-Content-Length: + - '665' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0d6r47bO_QK1gdPvB + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWaNEfCIAMFXug= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:50 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 87136170926d082ce5ff23d5ad5be32c.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - LVunxnHMc_gzzuPZqC2v-h7GPY-IeFZgO34SRZ6lWB8fhNMhGdLMUQ== + body: + encoding: UTF-8 + string: '{"refund":{"user_id":225397,"transaction_reference_id":"R010654408","transaction_id":"R010654408-REFUND","transaction_date":"2022-02-17T22:49:49.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"-100.0","sales_tax":"-9.77","provider":"api","line_items":[{"unit_price":"-10.0","sales_tax":"-0.89","quantity":1,"product_tax_code":"TaxCode + - 514214","product_identifier":"SKU-2","id":0,"discount":"0.0","description":"Product + #2 - 9838 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":null,"amount":"-110.0"}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:49 GMT +- request: + method: get + uri: https://api.taxjar.com/v2/transactions/orders/R010654408 + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '643' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:50 GMT + X-Amzn-Requestid: + - 5cdd8279-a608-480a-9873-fd8783e8605b + X-Amzn-Remapped-Content-Length: + - '643' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0d7hZm4xMliMxI2bD + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWaPGFDoAMFSdQ= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:50 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 0d5efb0576b3c35a58ca71a83003f34a.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - _ZbTZQCLWEevJRnUvkLlfznLCH1C9imXAyEs6Ru9joATc71sLWXoMA== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R010654408","transaction_date":"2022-02-17T22:49:49.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 514214","product_identifier":"SKU-2","id":0,"discount":"0.0","description":"Product + #2 - 9838 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"1","amount":"110.0"}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:50 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/refunds + body: + encoding: UTF-8 + string: '{"to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","transaction_id":"R010654408-REFUND","transaction_reference_id":"R010654408","transaction_date":"2022-02-17T22:49:49Z","amount":-110.0,"sales_tax":-9.77,"shipping":-100.0,"line_items":[{"unit_price":-10.0,"sales_tax":-0.89,"quantity":1,"product_tax_code":"TaxCode + - 514214","product_identifier":"SKU-2","id":0,"discount":-0.0,"description":"Product + #2 - 9838 - Master"}]}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 422 + message: Unprocessable Entity + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '110' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:50 GMT + X-Amzn-Requestid: + - 99d6aa52-b5d4-4173-8870-aa768984a485 + X-Amzn-Remapped-Content-Length: + - '110' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0d8kMF5NMzr9Ht70C + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWaSElMIAMFYeQ= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:50 GMT + X-Cache: + - Error from cloudfront + Via: + - 1.1 d83887583c419ccbd4f595c44721b292.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - U5YAj6LnkiRUSnG9OdvW_tPi5fopwCGEANbmrv4vypJb910vKWy7fQ== + body: + encoding: UTF-8 + string: '{"status":422,"error":"Unprocessable Entity","detail":"Provider tranx + already imported for your user account"}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:50 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_Reporting/_refund_and_create_transaction/when_Taxjar_cannot_create_a_refund_transaction/raises_an_error.yml b/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_Reporting/_refund_and_create_transaction/when_Taxjar_cannot_create_a_refund_transaction/raises_an_error.yml new file mode 100644 index 00000000..eab282ba --- /dev/null +++ b/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_Reporting/_refund_and_create_transaction/when_Taxjar_cannot_create_a_refund_transaction/raises_an_error.yml @@ -0,0 +1,415 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"unit_price":"10.0","discount":"-0.0","product_tax_code":"TaxCode + - 99028"}],"shipping":"100.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1487' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:47 GMT + X-Amzn-Requestid: + - dea967a5-ccb2-49b7-b988-fad968141e64 + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - NtWZzF5foAMFSdQ= + X-Amzn-Trace-Id: + - Root=1-620ed10b-7e804f7050d46fbe096c7051 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 040bad3c7f7db09654c66da40c719fb0.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - OD2yia0GsS5BqZnX8e3dVd9-MXT4t1ITwZRqGJr5Rj_Np4Wk-fcR6A== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":9.76,"breakdown":{"city_tax_collectable":5.36,"city_tax_rate":0.04875,"city_taxable_amount":110.0,"combined_tax_rate":0.08875,"county_tax_collectable":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"line_items":[{"city_amount":0.49,"city_tax_rate":0.04875,"city_taxable_amount":10.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"id":"1","special_district_amount":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_amount":0.4,"state_sales_tax_rate":0.04,"state_taxable_amount":10.0,"tax_collectable":0.89,"taxable_amount":10.0}],"shipping":{"city_amount":4.88,"city_tax_rate":0.04875,"city_taxable_amount":100.0,"combined_tax_rate":0.08875,"county_amount":0.0,"county_tax_rate":0.0,"county_taxable_amount":0.0,"special_district_amount":0.0,"special_tax_rate":0.0,"special_taxable_amount":0.0,"state_amount":4.0,"state_sales_tax_rate":0.04,"state_taxable_amount":100.0,"tax_collectable":8.88,"taxable_amount":100.0},"special_district_tax_collectable":0.0,"special_district_taxable_amount":0.0,"special_tax_rate":0.0,"state_tax_collectable":4.4,"state_tax_rate":0.04,"state_taxable_amount":110.0,"tax_collectable":9.76,"taxable_amount":110.0},"freight_taxable":true,"has_nexus":true,"jurisdictions":{"city":"NEW + YORK CITY","country":"US","county":"QUEENS","state":"NY"},"order_total_amount":110.0,"rate":0.08875,"shipping":100.0,"tax_source":"destination","taxable_amount":110.0}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:47 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/orders + body: + encoding: UTF-8 + string: '{"customer_id":"1","to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","line_items":[{"id":1,"quantity":1,"product_identifier":"SKU-1","description":"Product + #1 - 2950 - Master","product_tax_code":"TaxCode - 99028","unit_price":"10.0","discount":"-0.0","sales_tax":"0.89"}],"transaction_id":"R851261413","transaction_date":"2022-02-17T22:49:47Z","amount":"110.0","shipping":"100.0","sales_tax":"9.77"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 201 + message: Created + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '642' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:47 GMT + X-Amzn-Requestid: + - 61ccc9e9-8858-4d23-bb87-825dda134175 + X-Amzn-Remapped-Content-Length: + - '642' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0dyMr9tYgMhVd0CZB + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWZ2EIXIAMF_rQ= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:47 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 ffe7114eb67ff864ff5a46aa2b63ce6e.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - s_RvjzqdZtOx4W7h03xEr5DRX5j2jkyuU8NvFFaKyL7GiC5ct-igcA== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R851261413","transaction_date":"2022-02-17T22:49:47.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 99028","product_identifier":"SKU-1","id":0,"discount":"0.0","description":"Product + #1 - 2950 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"1","amount":"110.0"}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:47 GMT +- request: + method: get + uri: https://api.taxjar.com/v2/transactions/orders/R851261413 + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '642' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:48 GMT + X-Amzn-Requestid: + - f19e47c8-3722-4c2e-9fcc-e4fae8ab532a + X-Amzn-Remapped-Content-Length: + - '642' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0dzeqhggb7yxgdp1B + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWZ6FcRoAMF8zw= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:48 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 bf58b72d041cddee9bf926a00eeeb60a.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - 8ccpUBJHwQlikB-_y9ayffv3bVY9jIsbAGqDbr0yHDlk5z9nc9pmPg== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R851261413","transaction_date":"2022-02-17T22:49:47.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 99028","product_identifier":"SKU-1","id":0,"discount":"0.0","description":"Product + #1 - 2950 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"1","amount":"110.0"}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:48 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/refunds + body: + encoding: UTF-8 + string: '{"to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","transaction_id":"R851261413-REFUND","transaction_reference_id":"R851261413","transaction_date":"2022-02-17T22:49:47Z","amount":-110.0,"sales_tax":-9.77,"shipping":-100.0,"line_items":[{"unit_price":-10.0,"sales_tax":-0.89,"quantity":1,"product_tax_code":"TaxCode + - 99028","product_identifier":"SKU-1","id":0,"discount":-0.0,"description":"Product + #1 - 2950 - Master"}]}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 201 + message: Created + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '664' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:48 GMT + X-Amzn-Requestid: + - 3bd35471-dcb5-4d78-a035-87f1b89bb1bb + X-Amzn-Remapped-Content-Length: + - '664' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0d0xYhzf7b85eMUBB + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWZ9GK_oAMFvCg= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:48 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 4201bd1d1fc37ea7749b3bd1b64fce02.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - CGK9xykS2I-tWJFluSGOfSUfOI3oEFDnS_nwZ1bR1kXKDcgw9t9Y4Q== + body: + encoding: UTF-8 + string: '{"refund":{"user_id":225397,"transaction_reference_id":"R851261413","transaction_id":"R851261413-REFUND","transaction_date":"2022-02-17T22:49:47.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"-100.0","sales_tax":"-9.77","provider":"api","line_items":[{"unit_price":"-10.0","sales_tax":"-0.89","quantity":1,"product_tax_code":"TaxCode + - 99028","product_identifier":"SKU-1","id":0,"discount":"0.0","description":"Product + #1 - 2950 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":null,"amount":"-110.0"}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:48 GMT +- request: + method: get + uri: https://api.taxjar.com/v2/transactions/orders/R851261413 + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '642' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:48 GMT + X-Amzn-Requestid: + - ec76ce84-ced5-42c7-b737-dd4452a67a24 + X-Amzn-Remapped-Content-Length: + - '642' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0d1sS544wX_dgbbYB + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWaAFN0IAMFhrA= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:48 GMT + X-Cache: + - Miss from cloudfront + Via: + - 1.1 87136170926d082ce5ff23d5ad5be32c.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - IhIanFsYj_8dvnRbPrW9uiXwjYCr3mIUIZS03Wv8riBO3KCVd9VVqA== + body: + encoding: UTF-8 + string: '{"order":{"user_id":225397,"transaction_reference_id":null,"transaction_id":"R851261413","transaction_date":"2022-02-17T22:49:47.000Z","to_zip":"11430","to_street":"A + Different Road","to_state":"NY","to_country":"US","to_city":"HERNDON","shipping":"100.0","sales_tax":"9.77","provider":"api","line_items":[{"unit_price":"10.0","sales_tax":"0.89","quantity":1,"product_tax_code":"TaxCode + - 99028","product_identifier":"SKU-1","id":0,"discount":"0.0","description":"Product + #1 - 2950 - Master"}],"from_zip":null,"from_street":null,"from_state":null,"from_country":"US","from_city":null,"exemption_type":null,"customer_id":"1","amount":"110.0"}}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:48 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/transactions/refunds + body: + encoding: UTF-8 + string: '{"to_country":"US","to_zip":"11430","to_city":"Herndon","to_state":"NY","to_street":"A + Different Road","transaction_id":"R851261413-REFUND","transaction_reference_id":"R851261413","transaction_date":"2022-02-17T22:49:47Z","amount":-110.0,"sales_tax":-9.77,"shipping":-100.0,"line_items":[{"unit_price":-10.0,"sales_tax":-0.89,"quantity":1,"product_tax_code":"TaxCode + - 99028","product_identifier":"SKU-1","id":0,"discount":-0.0,"description":"Product + #1 - 2950 - Master"}]}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin agis.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 + 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_X86_64 x86_64; ruby 2.7.4-p191; + OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 422 + message: Unprocessable Entity + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '110' + Connection: + - close + Date: + - Thu, 17 Feb 2022 22:49:48 GMT + X-Amzn-Requestid: + - c2b69bea-021c-4421-a18e-d82a5e9c7e7f + X-Amzn-Remapped-Content-Length: + - '110' + X-Amzn-Remapped-Connection: + - keep-alive + X-Request-Id: + - FtS0d2fCgvPxWpAtuzqD + X-Api-Version: + - '2020-08-07' + X-Amz-Apigw-Id: + - NtWaCGZZIAMFWtw= + Cache-Control: + - max-age=0, private, must-revalidate + X-Amzn-Remapped-Server: + - Cowboy + X-Amzn-Remapped-Date: + - Thu, 17 Feb 2022 22:49:48 GMT + X-Cache: + - Error from cloudfront + Via: + - 1.1 bf58b72d041cddee9bf926a00eeeb60a.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - YVR50-C1 + X-Amz-Cf-Id: + - A_83-SzrqF8_8sKeohWF4tAKqC-ahGXTa7LLZn9eR3Kykuljf0VV8w== + body: + encoding: UTF-8 + string: '{"status":422,"error":"Unprocessable Entity","detail":"Provider tranx + already imported for your user account"}' + http_version: + recorded_at: Thu, 17 Feb 2022 22:49:48 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/Taxjar_API_Request/logging_is_disabled/doesn_t_call_the_logger.yml b/spec/fixtures/cassettes/Taxjar_API_Request/logging_is_disabled/doesn_t_call_the_logger.yml new file mode 100644 index 00000000..301623a6 --- /dev/null +++ b/spec/fixtures/cassettes/Taxjar_API_Request/logging_is_disabled/doesn_t_call_the_logger.yml @@ -0,0 +1,158 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.sandbox.taxjar.com/v2/summary_rates + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + Connection: + - close + Host: + - api.sandbox.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Cache-Control: + - max-age=0, private, must-revalidate + Content-Type: + - application/json + Etag: + - W/"e37b629cd4bb4ff95bc96bb6f5fae72b" + Vary: + - Origin + X-Request-Id: + - 7e9f8b53-4d81-4328-a352-9ebf5eb36a30 + X-Runtime: + - '0.026216' + Content-Length: + - '16208' + Connection: + - Close + body: + encoding: ASCII-8BIT + string: '{"summary_rates":[{"country_code":"AT","country":"Austria","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"BE","country":"Belgium","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"BG","country":"Bulgaria","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"CA","country":"Canada","region_code":"AB","region":"Alberta","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST","rate":0.05}},{"country_code":"CA","country":"Canada","region_code":"BC","region":"British + Columbia","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST/PST","rate":0.12}},{"country_code":"CA","country":"Canada","region_code":"MB","region":"Manitoba","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST/PST","rate":0.13}},{"country_code":"US","country":"United + States","region_code":"SC","region":"South Carolina","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0736}},{"country_code":"CA","country":"Canada","region_code":"NS","region":"Nova + Scotia","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.15}},{"country_code":"CA","country":"Canada","region_code":"NT","region":"Northwest + Territories","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST","rate":0.05}},{"country_code":"CA","country":"Canada","region_code":"NU","region":"Nunavut","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST","rate":0.05}},{"country_code":"CA","country":"Canada","region_code":"ON","region":"Ontario","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.13}},{"country_code":"CA","country":"Canada","region_code":"QC","region":"Quebec","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST/QST","rate":0.1498}},{"country_code":"CA","country":"Canada","region_code":"YT","region":"Yukon","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST","rate":0.05}},{"country_code":"CY","country":"Cyprus","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.19},"average_rate":{"label":"VAT","rate":0.19}},{"country_code":"CZ","country":"Czech + Republic","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"DE","country":"Germany","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.19},"average_rate":{"label":"VAT","rate":0.19}},{"country_code":"DK","country":"Denmark","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.25},"average_rate":{"label":"VAT","rate":0.25}},{"country_code":"EE","country":"Estonia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"EL","country":"Greece","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.24},"average_rate":{"label":"VAT","rate":0.24}},{"country_code":"ES","country":"Spain","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"FI","country":"Finland","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.24},"average_rate":{"label":"VAT","rate":0.24}},{"country_code":"FR","country":"France","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"HR","country":"Croatia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.25},"average_rate":{"label":"VAT","rate":0.25}},{"country_code":"HU","country":"Hungary","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.27},"average_rate":{"label":"VAT","rate":0.27}},{"country_code":"IE","country":"Ireland","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.23},"average_rate":{"label":"VAT","rate":0.23}},{"country_code":"IT","country":"Italy","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.22},"average_rate":{"label":"VAT","rate":0.22}},{"country_code":"LT","country":"Lithuania","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"LU","country":"Luxembourg","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.17},"average_rate":{"label":"VAT","rate":0.17}},{"country_code":"LV","country":"Latvia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"MT","country":"Malta","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.18},"average_rate":{"label":"VAT","rate":0.18}},{"country_code":"NL","country":"Netherlands","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"PL","country":"Poland","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.23},"average_rate":{"label":"VAT","rate":0.23}},{"country_code":"PT","country":"Portugal","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.23},"average_rate":{"label":"VAT","rate":0.23}},{"country_code":"US","country":"United + States","region_code":"NC","region":"North Carolina","minimum_rate":{"label":"State + Tax","rate":0.0475},"average_rate":{"label":"Tax","rate":0.069}},{"country_code":"SE","country":"Sweden","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.25},"average_rate":{"label":"VAT","rate":0.25}},{"country_code":"SI","country":"Slovenia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.22},"average_rate":{"label":"VAT","rate":0.22}},{"country_code":"SK","country":"Slovakia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"UK","country":"United + Kingdom","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"US","country":"United + States","region_code":"CT","region":"Connecticut","minimum_rate":{"label":"State + Tax","rate":0.0635},"average_rate":{"label":"Tax","rate":0.0635}},{"country_code":"US","country":"United + States","region_code":"DC","region":"District of Columbia","minimum_rate":{"label":"State + Tax","rate":0.0575},"average_rate":{"label":"Tax","rate":0.0575}},{"country_code":"US","country":"United + States","region_code":"DE","region":"Delaware","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0}},{"country_code":"US","country":"United + States","region_code":"HI","region":"Hawaii","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0424}},{"country_code":"US","country":"United + States","region_code":"IN","region":"Indiana","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.07}},{"country_code":"US","country":"United + States","region_code":"KY","region":"Kentucky","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.06}},{"country_code":"US","country":"United + States","region_code":"MA","region":"Massachusetts","minimum_rate":{"label":"State + Tax","rate":0.0625},"average_rate":{"label":"Tax","rate":0.0625}},{"country_code":"US","country":"United + States","region_code":"MD","region":"Maryland","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.06}},{"country_code":"US","country":"United + States","region_code":"ME","region":"Maine","minimum_rate":{"label":"State + Tax","rate":0.055},"average_rate":{"label":"Tax","rate":0.055}},{"country_code":"US","country":"United + States","region_code":"MI","region":"Michigan","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.06}},{"country_code":"US","country":"United + States","region_code":"ID","region":"Idaho","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0604}},{"country_code":"US","country":"United + States","region_code":"MT","region":"Montana","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0}},{"country_code":"CA","country":"Canada","region_code":"SK","region":"Saskatchewan","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST/PST","rate":0.11}},{"country_code":"US","country":"United + States","region_code":"NV","region":"Nevada","minimum_rate":{"label":"State + Tax","rate":0.046},"average_rate":{"label":"Tax","rate":0.0778}},{"country_code":"US","country":"United + States","region_code":"NH","region":"New Hampshire","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0}},{"country_code":"US","country":"United + States","region_code":"NJ","region":"New Jersey","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.0663}},{"country_code":"US","country":"United + States","region_code":"OR","region":"Oregon","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0}},{"country_code":"US","country":"United + States","region_code":"PR","region":"Puerto Rico","minimum_rate":{"label":"State + Tax","rate":0.115},"average_rate":{"label":"Tax","rate":0.115}},{"country_code":"US","country":"United + States","region_code":"RI","region":"Rhode Island","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.07}},{"country_code":"US","country":"United + States","region_code":"TN","region":"Tennessee","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.095}},{"country_code":"US","country":"United + States","region_code":"VA","region":"Virginia","minimum_rate":{"label":"State + Tax","rate":0.043},"average_rate":{"label":"Tax","rate":0.0546}},{"country_code":"US","country":"United + States","region_code":"NM","region":"New Mexico","minimum_rate":{"label":"State + Tax","rate":0.0513},"average_rate":{"label":"Tax","rate":0.0703}},{"country_code":"US","country":"United + States","region_code":"NY","region":"New York","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0819}},{"country_code":"US","country":"United + States","region_code":"LA","region":"Louisiana","minimum_rate":{"label":"State + Tax","rate":0.05},"average_rate":{"label":"Tax","rate":0.0958}},{"country_code":"US","country":"United + States","region_code":"IL","region":"Illinois","minimum_rate":{"label":"State + Tax","rate":0.0625},"average_rate":{"label":"Tax","rate":0.075}},{"country_code":"US","country":"United + States","region_code":"MS","region":"Mississippi","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.0702}},{"country_code":"US","country":"United + States","region_code":"IA","region":"Iowa","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0689}},{"country_code":"US","country":"United + States","region_code":"AL","region":"Alabama","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0836}},{"country_code":"CA","country":"Canada","region_code":"NL","region":"Newfoundland + and Labrador","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.15}},{"country_code":"US","country":"United + States","region_code":"UT","region":"Utah","minimum_rate":{"label":"State + Tax","rate":0.047},"average_rate":{"label":"Tax","rate":0.0661}},{"country_code":"CA","country":"Canada","region_code":"PE","region":"Prince + Edward Island","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.15}},{"country_code":"US","country":"United + States","region_code":"CA","region":"California","minimum_rate":{"label":"State + Tax","rate":0.0625},"average_rate":{"label":"Tax","rate":0.0826}},{"country_code":"US","country":"United + States","region_code":"WV","region":"West Virginia","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.061}},{"country_code":"US","country":"United + States","region_code":"NE","region":"Nebraska","minimum_rate":{"label":"State + Tax","rate":0.055},"average_rate":{"label":"Tax","rate":0.0586}},{"country_code":"US","country":"United + States","region_code":"FL","region":"Florida","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0682}},{"country_code":"US","country":"United + States","region_code":"VT","region":"Vermont","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0604}},{"country_code":"US","country":"United + States","region_code":"TX","region":"Texas","minimum_rate":{"label":"State + Tax","rate":0.0625},"average_rate":{"label":"Tax","rate":0.0744}},{"country_code":"US","country":"United + States","region_code":"WY","region":"Wyoming","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0529}},{"country_code":"US","country":"United + States","region_code":"WI","region":"Wisconsin","minimum_rate":{"label":"State + Tax","rate":0.05},"average_rate":{"label":"Tax","rate":0.0546}},{"country_code":"US","country":"United + States","region_code":"PA","region":"Pennsylvania","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.061}},{"country_code":"US","country":"United + States","region_code":"KS","region":"Kansas","minimum_rate":{"label":"State + Tax","rate":0.065},"average_rate":{"label":"Tax","rate":0.0805}},{"country_code":"US","country":"United + States","region_code":"AR","region":"Arkansas","minimum_rate":{"label":"State + Tax","rate":0.065},"average_rate":{"label":"Tax","rate":0.0857}},{"country_code":"US","country":"United + States","region_code":"MO","region":"Missouri","minimum_rate":{"label":"State + Tax","rate":0.0423},"average_rate":{"label":"Tax","rate":0.0728}},{"country_code":"US","country":"United + States","region_code":"AZ","region":"Arizona","minimum_rate":{"label":"State + Tax","rate":0.056},"average_rate":{"label":"Tax","rate":0.0787}},{"country_code":"US","country":"United + States","region_code":"OK","region":"Oklahoma","minimum_rate":{"label":"State + Tax","rate":0.045},"average_rate":{"label":"Tax","rate":0.0786}},{"country_code":"US","country":"United + States","region_code":"MN","region":"Minnesota","minimum_rate":{"label":"State + Tax","rate":0.0688},"average_rate":{"label":"Tax","rate":0.0718}},{"country_code":"US","country":"United + States","region_code":"AK","region":"Alaska","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0154}},{"country_code":"US","country":"United + States","region_code":"ND","region":"North Dakota","minimum_rate":{"label":"State + Tax","rate":0.05},"average_rate":{"label":"Tax","rate":0.0551}},{"country_code":"US","country":"United + States","region_code":"WA","region":"Washington","minimum_rate":{"label":"State + Tax","rate":0.065},"average_rate":{"label":"Tax","rate":0.0876}},{"country_code":"US","country":"United + States","region_code":"GA","region":"Georgia","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0733}},{"country_code":"US","country":"United + States","region_code":"OH","region":"Ohio","minimum_rate":{"label":"State + Tax","rate":0.0575},"average_rate":{"label":"Tax","rate":0.071}},{"country_code":"US","country":"United + States","region_code":"CO","region":"Colorado","minimum_rate":{"label":"State + Tax","rate":0.029},"average_rate":{"label":"Tax","rate":0.0647}},{"country_code":"CA","country":"Canada","region_code":"NB","region":"New + Brunswick","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.15}},{"country_code":"US","country":"United + States","region_code":"SD","region":"South Dakota","minimum_rate":{"label":"State + Tax","rate":0.045},"average_rate":{"label":"Tax","rate":0.056}},{"country_code":"RO","country":"Romania","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.19},"average_rate":{"label":"VAT","rate":0.19}}]}' + http_version: + recorded_at: Wed, 09 Mar 2022 23:01:20 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/Taxjar_API_Request/logging_is_enabled/calls_the_logger.yml b/spec/fixtures/cassettes/Taxjar_API_Request/logging_is_enabled/calls_the_logger.yml new file mode 100644 index 00000000..78c6fc34 --- /dev/null +++ b/spec/fixtures/cassettes/Taxjar_API_Request/logging_is_enabled/calls_the_logger.yml @@ -0,0 +1,158 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.sandbox.taxjar.com/v2/summary_rates + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Nicks-Mac-mini.local 21.2.0 Darwin Kernel Version 21.2.0: + Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101 x86_64; + ruby 2.6.9-p207; OpenSSL 1.1.1m 14 Dec 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + Connection: + - close + Host: + - api.sandbox.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Cache-Control: + - max-age=0, private, must-revalidate + Content-Type: + - application/json + Etag: + - W/"e37b629cd4bb4ff95bc96bb6f5fae72b" + Vary: + - Origin + X-Request-Id: + - ffc77f74-70c9-4b67-b64c-bfebb74872ad + X-Runtime: + - '0.024112' + Content-Length: + - '16208' + Connection: + - Close + body: + encoding: ASCII-8BIT + string: '{"summary_rates":[{"country_code":"AT","country":"Austria","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"BE","country":"Belgium","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"BG","country":"Bulgaria","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"CA","country":"Canada","region_code":"AB","region":"Alberta","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST","rate":0.05}},{"country_code":"CA","country":"Canada","region_code":"BC","region":"British + Columbia","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST/PST","rate":0.12}},{"country_code":"CA","country":"Canada","region_code":"MB","region":"Manitoba","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST/PST","rate":0.13}},{"country_code":"US","country":"United + States","region_code":"SC","region":"South Carolina","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0736}},{"country_code":"CA","country":"Canada","region_code":"NS","region":"Nova + Scotia","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.15}},{"country_code":"CA","country":"Canada","region_code":"NT","region":"Northwest + Territories","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST","rate":0.05}},{"country_code":"CA","country":"Canada","region_code":"NU","region":"Nunavut","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST","rate":0.05}},{"country_code":"CA","country":"Canada","region_code":"ON","region":"Ontario","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.13}},{"country_code":"CA","country":"Canada","region_code":"QC","region":"Quebec","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST/QST","rate":0.1498}},{"country_code":"CA","country":"Canada","region_code":"YT","region":"Yukon","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST","rate":0.05}},{"country_code":"CY","country":"Cyprus","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.19},"average_rate":{"label":"VAT","rate":0.19}},{"country_code":"CZ","country":"Czech + Republic","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"DE","country":"Germany","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.19},"average_rate":{"label":"VAT","rate":0.19}},{"country_code":"DK","country":"Denmark","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.25},"average_rate":{"label":"VAT","rate":0.25}},{"country_code":"EE","country":"Estonia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"EL","country":"Greece","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.24},"average_rate":{"label":"VAT","rate":0.24}},{"country_code":"ES","country":"Spain","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"FI","country":"Finland","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.24},"average_rate":{"label":"VAT","rate":0.24}},{"country_code":"FR","country":"France","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"HR","country":"Croatia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.25},"average_rate":{"label":"VAT","rate":0.25}},{"country_code":"HU","country":"Hungary","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.27},"average_rate":{"label":"VAT","rate":0.27}},{"country_code":"IE","country":"Ireland","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.23},"average_rate":{"label":"VAT","rate":0.23}},{"country_code":"IT","country":"Italy","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.22},"average_rate":{"label":"VAT","rate":0.22}},{"country_code":"LT","country":"Lithuania","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"LU","country":"Luxembourg","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.17},"average_rate":{"label":"VAT","rate":0.17}},{"country_code":"LV","country":"Latvia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"MT","country":"Malta","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.18},"average_rate":{"label":"VAT","rate":0.18}},{"country_code":"NL","country":"Netherlands","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.21},"average_rate":{"label":"VAT","rate":0.21}},{"country_code":"PL","country":"Poland","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.23},"average_rate":{"label":"VAT","rate":0.23}},{"country_code":"PT","country":"Portugal","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.23},"average_rate":{"label":"VAT","rate":0.23}},{"country_code":"US","country":"United + States","region_code":"NC","region":"North Carolina","minimum_rate":{"label":"State + Tax","rate":0.0475},"average_rate":{"label":"Tax","rate":0.069}},{"country_code":"SE","country":"Sweden","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.25},"average_rate":{"label":"VAT","rate":0.25}},{"country_code":"SI","country":"Slovenia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.22},"average_rate":{"label":"VAT","rate":0.22}},{"country_code":"SK","country":"Slovakia","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"UK","country":"United + Kingdom","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.2},"average_rate":{"label":"VAT","rate":0.2}},{"country_code":"US","country":"United + States","region_code":"CT","region":"Connecticut","minimum_rate":{"label":"State + Tax","rate":0.0635},"average_rate":{"label":"Tax","rate":0.0635}},{"country_code":"US","country":"United + States","region_code":"DC","region":"District of Columbia","minimum_rate":{"label":"State + Tax","rate":0.0575},"average_rate":{"label":"Tax","rate":0.0575}},{"country_code":"US","country":"United + States","region_code":"DE","region":"Delaware","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0}},{"country_code":"US","country":"United + States","region_code":"HI","region":"Hawaii","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0424}},{"country_code":"US","country":"United + States","region_code":"IN","region":"Indiana","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.07}},{"country_code":"US","country":"United + States","region_code":"KY","region":"Kentucky","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.06}},{"country_code":"US","country":"United + States","region_code":"MA","region":"Massachusetts","minimum_rate":{"label":"State + Tax","rate":0.0625},"average_rate":{"label":"Tax","rate":0.0625}},{"country_code":"US","country":"United + States","region_code":"MD","region":"Maryland","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.06}},{"country_code":"US","country":"United + States","region_code":"ME","region":"Maine","minimum_rate":{"label":"State + Tax","rate":0.055},"average_rate":{"label":"Tax","rate":0.055}},{"country_code":"US","country":"United + States","region_code":"MI","region":"Michigan","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.06}},{"country_code":"US","country":"United + States","region_code":"ID","region":"Idaho","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0604}},{"country_code":"US","country":"United + States","region_code":"MT","region":"Montana","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0}},{"country_code":"CA","country":"Canada","region_code":"SK","region":"Saskatchewan","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"GST/PST","rate":0.11}},{"country_code":"US","country":"United + States","region_code":"NV","region":"Nevada","minimum_rate":{"label":"State + Tax","rate":0.046},"average_rate":{"label":"Tax","rate":0.0778}},{"country_code":"US","country":"United + States","region_code":"NH","region":"New Hampshire","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0}},{"country_code":"US","country":"United + States","region_code":"NJ","region":"New Jersey","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.0663}},{"country_code":"US","country":"United + States","region_code":"OR","region":"Oregon","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0}},{"country_code":"US","country":"United + States","region_code":"PR","region":"Puerto Rico","minimum_rate":{"label":"State + Tax","rate":0.115},"average_rate":{"label":"Tax","rate":0.115}},{"country_code":"US","country":"United + States","region_code":"RI","region":"Rhode Island","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.07}},{"country_code":"US","country":"United + States","region_code":"TN","region":"Tennessee","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.095}},{"country_code":"US","country":"United + States","region_code":"VA","region":"Virginia","minimum_rate":{"label":"State + Tax","rate":0.043},"average_rate":{"label":"Tax","rate":0.0546}},{"country_code":"US","country":"United + States","region_code":"NM","region":"New Mexico","minimum_rate":{"label":"State + Tax","rate":0.0513},"average_rate":{"label":"Tax","rate":0.0703}},{"country_code":"US","country":"United + States","region_code":"NY","region":"New York","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0819}},{"country_code":"US","country":"United + States","region_code":"LA","region":"Louisiana","minimum_rate":{"label":"State + Tax","rate":0.05},"average_rate":{"label":"Tax","rate":0.0958}},{"country_code":"US","country":"United + States","region_code":"IL","region":"Illinois","minimum_rate":{"label":"State + Tax","rate":0.0625},"average_rate":{"label":"Tax","rate":0.075}},{"country_code":"US","country":"United + States","region_code":"MS","region":"Mississippi","minimum_rate":{"label":"State + Tax","rate":0.07},"average_rate":{"label":"Tax","rate":0.0702}},{"country_code":"US","country":"United + States","region_code":"IA","region":"Iowa","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0689}},{"country_code":"US","country":"United + States","region_code":"AL","region":"Alabama","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0836}},{"country_code":"CA","country":"Canada","region_code":"NL","region":"Newfoundland + and Labrador","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.15}},{"country_code":"US","country":"United + States","region_code":"UT","region":"Utah","minimum_rate":{"label":"State + Tax","rate":0.047},"average_rate":{"label":"Tax","rate":0.0661}},{"country_code":"CA","country":"Canada","region_code":"PE","region":"Prince + Edward Island","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.15}},{"country_code":"US","country":"United + States","region_code":"CA","region":"California","minimum_rate":{"label":"State + Tax","rate":0.0625},"average_rate":{"label":"Tax","rate":0.0826}},{"country_code":"US","country":"United + States","region_code":"WV","region":"West Virginia","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.061}},{"country_code":"US","country":"United + States","region_code":"NE","region":"Nebraska","minimum_rate":{"label":"State + Tax","rate":0.055},"average_rate":{"label":"Tax","rate":0.0586}},{"country_code":"US","country":"United + States","region_code":"FL","region":"Florida","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0682}},{"country_code":"US","country":"United + States","region_code":"VT","region":"Vermont","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.0604}},{"country_code":"US","country":"United + States","region_code":"TX","region":"Texas","minimum_rate":{"label":"State + Tax","rate":0.0625},"average_rate":{"label":"Tax","rate":0.0744}},{"country_code":"US","country":"United + States","region_code":"WY","region":"Wyoming","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0529}},{"country_code":"US","country":"United + States","region_code":"WI","region":"Wisconsin","minimum_rate":{"label":"State + Tax","rate":0.05},"average_rate":{"label":"Tax","rate":0.0546}},{"country_code":"US","country":"United + States","region_code":"PA","region":"Pennsylvania","minimum_rate":{"label":"State + Tax","rate":0.06},"average_rate":{"label":"Tax","rate":0.061}},{"country_code":"US","country":"United + States","region_code":"KS","region":"Kansas","minimum_rate":{"label":"State + Tax","rate":0.065},"average_rate":{"label":"Tax","rate":0.0805}},{"country_code":"US","country":"United + States","region_code":"AR","region":"Arkansas","minimum_rate":{"label":"State + Tax","rate":0.065},"average_rate":{"label":"Tax","rate":0.0857}},{"country_code":"US","country":"United + States","region_code":"MO","region":"Missouri","minimum_rate":{"label":"State + Tax","rate":0.0423},"average_rate":{"label":"Tax","rate":0.0728}},{"country_code":"US","country":"United + States","region_code":"AZ","region":"Arizona","minimum_rate":{"label":"State + Tax","rate":0.056},"average_rate":{"label":"Tax","rate":0.0787}},{"country_code":"US","country":"United + States","region_code":"OK","region":"Oklahoma","minimum_rate":{"label":"State + Tax","rate":0.045},"average_rate":{"label":"Tax","rate":0.0786}},{"country_code":"US","country":"United + States","region_code":"MN","region":"Minnesota","minimum_rate":{"label":"State + Tax","rate":0.0688},"average_rate":{"label":"Tax","rate":0.0718}},{"country_code":"US","country":"United + States","region_code":"AK","region":"Alaska","minimum_rate":{"label":"State + Tax","rate":0.0},"average_rate":{"label":"Tax","rate":0.0154}},{"country_code":"US","country":"United + States","region_code":"ND","region":"North Dakota","minimum_rate":{"label":"State + Tax","rate":0.05},"average_rate":{"label":"Tax","rate":0.0551}},{"country_code":"US","country":"United + States","region_code":"WA","region":"Washington","minimum_rate":{"label":"State + Tax","rate":0.065},"average_rate":{"label":"Tax","rate":0.0876}},{"country_code":"US","country":"United + States","region_code":"GA","region":"Georgia","minimum_rate":{"label":"State + Tax","rate":0.04},"average_rate":{"label":"Tax","rate":0.0733}},{"country_code":"US","country":"United + States","region_code":"OH","region":"Ohio","minimum_rate":{"label":"State + Tax","rate":0.0575},"average_rate":{"label":"Tax","rate":0.071}},{"country_code":"US","country":"United + States","region_code":"CO","region":"Colorado","minimum_rate":{"label":"State + Tax","rate":0.029},"average_rate":{"label":"Tax","rate":0.0647}},{"country_code":"CA","country":"Canada","region_code":"NB","region":"New + Brunswick","minimum_rate":{"label":"GST","rate":0.05},"average_rate":{"label":"HST","rate":0.15}},{"country_code":"US","country":"United + States","region_code":"SD","region":"South Dakota","minimum_rate":{"label":"State + Tax","rate":0.045},"average_rate":{"label":"Tax","rate":0.056}},{"country_code":"RO","country":"Romania","region_code":null,"region":null,"minimum_rate":{"label":"VAT","rate":0.19},"average_rate":{"label":"VAT","rate":0.19}}]}' + http_version: + recorded_at: Wed, 09 Mar 2022 23:01:19 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/features/spree/admin/checkout.yml b/spec/fixtures/cassettes/features/spree/admin/checkout.yml new file mode 100644 index 00000000..6b1b1d49 --- /dev/null +++ b/spec/fixtures/cassettes/features/spree/admin/checkout.yml @@ -0,0 +1,183 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"to_country":"US","to_zip":"91761","to_city":"Ontario","to_state":"CA","to_street":"450 + Helen Ave","line_items":[{"id":9999,"quantity":1,"unit_price":"19.99","discount":"-0.0","product_tax_code":"TaxCode + - 932150"}],"shipping":"10.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Noahs-MacBook-Pro.local 20.6.0 Darwin Kernel Version + 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 + x86_64; ruby 2.6.7-p197; OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1116' + Connection: + - close + Date: + - Mon, 29 Nov 2021 22:11:40 GMT + X-Amzn-Requestid: + - da26345e-3a30-4899-bdd9-c72c61f63ee6 + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - Jll0iHA6oAMF4gw= + X-Amzn-Trace-Id: + - Root=1-61a5501c-0fad5b964c4a5d3b2b806fe8 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 5fe5f2a3903f1378941d92eceaf3fa17.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - uhCTR6Uy_1mhVxkM3S6DVtyPE-3V7A70nb8KOOzdBcV0R3urhN8N8Q== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":1.55,"breakdown":{"city_tax_collectable":0.0,"city_tax_rate":0.0,"city_taxable_amount":0.0,"combined_tax_rate":0.0775,"county_tax_collectable":0.2,"county_tax_rate":0.01,"county_taxable_amount":19.99,"line_items":[{"city_amount":0.0,"city_tax_rate":0.0,"city_taxable_amount":0.0,"combined_tax_rate":0.0775,"county_amount":0.2,"county_tax_rate":0.01,"county_taxable_amount":19.99,"id":"9999","special_district_amount":0.1,"special_district_taxable_amount":19.99,"special_tax_rate":0.005,"state_amount":1.25,"state_sales_tax_rate":0.0625,"state_taxable_amount":19.99,"tax_collectable":1.55,"taxable_amount":19.99}],"special_district_tax_collectable":0.1,"special_district_taxable_amount":19.99,"special_tax_rate":0.005,"state_tax_collectable":1.25,"state_tax_rate":0.0625,"state_taxable_amount":19.99,"tax_collectable":1.55,"taxable_amount":19.99},"freight_taxable":false,"has_nexus":true,"jurisdictions":{"city":"ONTARIO","country":"US","county":"SAN + BERNARDINO COUNTY","state":"CA"},"order_total_amount":29.99,"rate":0.0775,"shipping":10.0,"tax_source":"destination","taxable_amount":19.99}}' + http_version: + recorded_at: Mon, 29 Nov 2021 22:11:40 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"to_country":"US","to_zip":"91761","to_city":"Ontario","to_state":"CA","to_street":"450 + Helen Ave","line_items":[{"id":9999,"quantity":1,"unit_price":"19.99","discount":"-0.0","product_tax_code":"TaxCode + - 932150"}],"shipping":"10.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Noahs-MacBook-Pro.local 20.6.0 Darwin Kernel Version + 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 + x86_64; ruby 2.6.7-p197; OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1116' + Connection: + - close + Date: + - Mon, 29 Nov 2021 22:11:41 GMT + X-Amzn-Requestid: + - 88eab03b-0b7f-4e73-86df-c709b88b7ced + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - Jll0oGTnIAMFdCQ= + X-Amzn-Trace-Id: + - Root=1-61a5501d-7656be6f7fc77f157a7d2596 + X-Cache: + - Miss from cloudfront + Via: + - 1.1 b8c4a4ca04bb1976e020396d211bc8dd.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - WsHBNnVVzhZm0tI-pHr-mDrhBZ-j-WPRxCSaJilbnwEsbaAxJ9_Stw== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":1.55,"breakdown":{"city_tax_collectable":0.0,"city_tax_rate":0.0,"city_taxable_amount":0.0,"combined_tax_rate":0.0775,"county_tax_collectable":0.2,"county_tax_rate":0.01,"county_taxable_amount":19.99,"line_items":[{"city_amount":0.0,"city_tax_rate":0.0,"city_taxable_amount":0.0,"combined_tax_rate":0.0775,"county_amount":0.2,"county_tax_rate":0.01,"county_taxable_amount":19.99,"id":"9999","special_district_amount":0.1,"special_district_taxable_amount":19.99,"special_tax_rate":0.005,"state_amount":1.25,"state_sales_tax_rate":0.0625,"state_taxable_amount":19.99,"tax_collectable":1.55,"taxable_amount":19.99}],"special_district_tax_collectable":0.1,"special_district_taxable_amount":19.99,"special_tax_rate":0.005,"state_tax_collectable":1.25,"state_tax_rate":0.0625,"state_taxable_amount":19.99,"tax_collectable":1.55,"taxable_amount":19.99},"freight_taxable":false,"has_nexus":true,"jurisdictions":{"city":"ONTARIO","country":"US","county":"SAN + BERNARDINO COUNTY","state":"CA"},"order_total_amount":29.99,"rate":0.0775,"shipping":10.0,"tax_source":"destination","taxable_amount":19.99}}' + http_version: + recorded_at: Mon, 29 Nov 2021 22:11:41 GMT +- request: + method: post + uri: https://api.taxjar.com/v2/taxes + body: + encoding: UTF-8 + string: '{"to_country":"US","to_zip":"91761","to_city":"Ontario","to_state":"CA","to_street":"450 + Helen Ave","line_items":[{"id":9999"quantity":1,"unit_price":"19.99","discount":"-0.0","product_tax_code":"TaxCode + - 932150"}],"shipping":"10.0"}' + headers: + User-Agent: + - 'TaxJar/Ruby (Darwin Noahs-MacBook-Pro.local 20.6.0 Darwin Kernel Version + 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 + x86_64; ruby 2.6.7-p197; OpenSSL 1.1.1k 25 Mar 2021) taxjar-ruby/3.0.2' + Authorization: + - Bearer + X-Api-Version: + - '2020-08-07' + Plugin: + - supergoodsolidustaxjar + Connection: + - close + Content-Type: + - application/json; charset=UTF-8 + Host: + - api.taxjar.com + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '1116' + Connection: + - close + Date: + - Mon, 29 Nov 2021 22:11:41 GMT + X-Amzn-Requestid: + - '02825732-0cc9-4c6e-b6f2-6ed67ce73efb' + Access-Control-Allow-Origin: + - https://developers.taxjar.com + X-Amz-Apigw-Id: + - Jll0qFw2oAMF6yQ= + X-Amzn-Trace-Id: + - Root=1-61a5501d-0d2efc797598bbdd7e11fd1c + X-Cache: + - Miss from cloudfront + Via: + - 1.1 001e7070d795018d01b93988b9723743.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - SEA73-P1 + X-Amz-Cf-Id: + - 1UlaoHOP27gYr51iEpT3AIqXoIHRSxfTXYr2v17hkkM_nTmi5GWUWw== + body: + encoding: UTF-8 + string: '{"tax":{"amount_to_collect":1.55,"breakdown":{"city_tax_collectable":0.0,"city_tax_rate":0.0,"city_taxable_amount":0.0,"combined_tax_rate":0.0775,"county_tax_collectable":0.2,"county_tax_rate":0.01,"county_taxable_amount":19.99,"line_items":[{"city_amount":0.0,"city_tax_rate":0.0,"city_taxable_amount":0.0,"combined_tax_rate":0.0775,"county_amount":0.2,"county_tax_rate":0.01,"county_taxable_amount":19.99,"id":"9999","special_district_amount":0.1,"special_district_taxable_amount":19.99,"special_tax_rate":0.005,"state_amount":1.25,"state_sales_tax_rate":0.0625,"state_taxable_amount":19.99,"tax_collectable":1.55,"taxable_amount":19.99}],"special_district_tax_collectable":0.1,"special_district_taxable_amount":19.99,"special_tax_rate":0.005,"state_tax_collectable":1.25,"state_tax_rate":0.0625,"state_taxable_amount":19.99,"tax_collectable":1.55,"taxable_amount":19.99},"freight_taxable":false,"has_nexus":true,"jurisdictions":{"city":"ONTARIO","country":"US","county":"SAN + BERNARDINO COUNTY","state":"CA"},"order_total_amount":29.99,"rate":0.0775,"shipping":10.0,"tax_source":"destination","taxable_amount":19.99}}' + http_version: + recorded_at: Mon, 29 Nov 2021 22:11:41 GMT +recorded_with: VCR 4.0.0 diff --git a/spec/jobs/super_good/solidus_taxjar/replace_transaction_job_spec.rb b/spec/jobs/super_good/solidus_taxjar/replace_transaction_job_spec.rb new file mode 100644 index 00000000..b93967f3 --- /dev/null +++ b/spec/jobs/super_good/solidus_taxjar/replace_transaction_job_spec.rb @@ -0,0 +1,25 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::ReplaceTransactionJob do + describe ".perform_later" do + subject { described_class.perform_later(order) } + + let(:order) { create :order } + let(:mock_reporting) { instance_double ::SuperGood::SolidusTaxjar::Reporting } + + it "enqueues the job" do + assert_enqueued_with(job: described_class, args: [order]) do + subject + end + end + + it "replaces the transaction when it performs the job" do + allow(SuperGood::SolidusTaxjar).to receive(:reporting).and_return(mock_reporting) + expect(mock_reporting).to receive(:refund_and_create_new_transaction).with(order) + + perform_enqueued_jobs do + subject + end + end + end +end diff --git a/spec/jobs/super_good/solidus_taxjar/report_transaction_job_spec.rb b/spec/jobs/super_good/solidus_taxjar/report_transaction_job_spec.rb new file mode 100644 index 00000000..5141e79d --- /dev/null +++ b/spec/jobs/super_good/solidus_taxjar/report_transaction_job_spec.rb @@ -0,0 +1,25 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::ReportTransactionJob do + describe ".perform_later" do + subject { described_class.perform_later(order) } + + let(:order) { create :order } + let(:mock_reporting) { instance_double ::SuperGood::SolidusTaxjar::Reporting } + + it "enqueues the job" do + assert_enqueued_with(job: described_class, args: [order]) do + subject + end + end + + it "reports the transaction when it performs the job" do + allow(SuperGood::SolidusTaxjar).to receive(:reporting).and_return(mock_reporting) + expect(mock_reporting).to receive(:show_or_create_transaction).with(order) + + perform_enqueued_jobs do + subject + end + end + end +end diff --git a/spec/models/spree/order_updater_spec.rb b/spec/models/spree/order_updater_spec.rb new file mode 100644 index 00000000..69ecdc93 --- /dev/null +++ b/spec/models/spree/order_updater_spec.rb @@ -0,0 +1,12 @@ +RSpec.describe Spree::OrderUpdater do + describe '#update' do + it 'fires the order_recalculated event exactly once' do + stub_const('Spree::Event', class_spy(Spree::Event)) + order = create(:order) + + described_class.new(order).update + + expect(Spree::Event).to have_received(:fire).with('order_recalculated', order: order).once + end + end +end diff --git a/spec/models/spree/shipment_spec.rb b/spec/models/spree/shipment_spec.rb new file mode 100644 index 00000000..27f213bc --- /dev/null +++ b/spec/models/spree/shipment_spec.rb @@ -0,0 +1,16 @@ +RSpec.describe Spree::Shipment do + describe '#update' do + subject { shipment.ship } + + let(:shipment) { create(:shipment, state: 'ready', order: order) } + let(:order) { create :order_with_line_items } + + it 'fires the shipment_shipped event exactly once' do + stub_const('Spree::Event', class_spy(Spree::Event)) + + expect(Spree::Event).to receive(:fire).with('shipment_shipped', shipment: shipment).once + + subject + end + end +end diff --git a/spec/models/super_good/solidus_taxjar/configuration_spec.rb b/spec/models/super_good/solidus_taxjar/configuration_spec.rb new file mode 100644 index 00000000..df795e86 --- /dev/null +++ b/spec/models/super_good/solidus_taxjar/configuration_spec.rb @@ -0,0 +1,62 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::Configuration do + describe "is valid" do + subject { taxjar_configuration.valid? } + + let(:taxjar_configuration) { create(:taxjar_configuration) } + + it "is valid" do + expect(subject).to be_truthy + end + end + + describe "#preferred_reporting_enabled" do + subject { taxjar_configuration.preferred_reporting_enabled } + + let(:taxjar_configuration) { create(:taxjar_configuration) } + + it "has the default value" do + expect(subject).to be_falsey + end + end + + describe "#preferred_reporting_enabled=" do + subject { taxjar_configuration.update(preferred_reporting_enabled: true) } + + let(:taxjar_configuration) { create(:taxjar_configuration) } + + it "sets the value" do + expect { subject }.to change { taxjar_configuration.reload.preferred_reporting_enabled }.from(false).to(true) + end + end + + describe ".default" do + subject { described_class.default } + + context "configuration exists" do + let!(:taxjar_configuration) { create :taxjar_configuration } + + it "returns the default configuration" do + expect(subject).to eq(taxjar_configuration) + end + end + + context "configuration doesn't exist" do + it "creates the default config" do + expect { subject }.to change { described_class.count }.from(0).to(1) + end + + it "returns a config" do + expect(subject).to be_instance_of(described_class) + end + + context "calling default more than once" do + it "creates only one config" do + described_class.default + expect { subject }.to_not change { described_class.count }.from(1) + end + end + end + end +end diff --git a/spec/models/super_good/solidus_taxjar/order_transaction_spec.rb b/spec/models/super_good/solidus_taxjar/order_transaction_spec.rb new file mode 100644 index 00000000..df36451a --- /dev/null +++ b/spec/models/super_good/solidus_taxjar/order_transaction_spec.rb @@ -0,0 +1,34 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::OrderTransaction do + describe ".latest_for" do + subject { described_class.latest_for(order) } + + let(:order) { create(:order) } + + context "when there are no order transactions" do + it { is_expected.to be_nil } + end + + context "when there are one or more order transactions" do + let!(:first_order_transaction) { + create( + :taxjar_order_transaction, + transaction_date: 2.days.ago, + order: order + ) + } + let!(:latest_order_transaction) { + create( + :taxjar_order_transaction, + transaction_date: 1.days.ago, + order: order + ) + } + + it "returns the most recent order transaction" do + expect(subject).to eq latest_order_transaction + end + end + end +end diff --git a/spec/models/super_good/solidus_taxjar/transaction_sync_batch_spec.rb b/spec/models/super_good/solidus_taxjar/transaction_sync_batch_spec.rb new file mode 100644 index 00000000..75d2150c --- /dev/null +++ b/spec/models/super_good/solidus_taxjar/transaction_sync_batch_spec.rb @@ -0,0 +1,48 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::TransactionSyncBatch do + describe "#status" do + subject { transaction_sync_batch.status } + + let(:transaction_sync_batch) { create(:transaction_sync_batch, transaction_sync_logs: transaction_sync_logs) } + + context "all logs have status 'success'" do + let(:transaction_sync_logs) { build_list(:transaction_sync_log, 2, status: :success) } + + it "returns 'success'" do + expect(subject).to eq "success" + end + end + + context "one or more logs have status 'error'" do + let(:transaction_sync_logs) { [ + build(:transaction_sync_log, status: :success), + build(:transaction_sync_log, status: :error) + ] } + + it "returns 'error'" do + expect(subject).to eq "error" + end + end + + context "one or more logs have status 'processing'" do + let(:transaction_sync_logs) { [ + build(:transaction_sync_log, status: :success), + build(:transaction_sync_log, status: :processing), + build(:transaction_sync_log, status: :error) + ] } + + it "returns 'processing'" do + expect(subject).to eq "processing" + end + end + + context "with an empty batch" do + let(:transaction_sync_logs) { [] } + + it "returns 'success'" do + expect(subject).to eq "success" + end + end + end +end diff --git a/spec/requests/spree/admin/taxjar_settings_request_spec.rb b/spec/requests/spree/admin/taxjar_settings_request_spec.rb new file mode 100644 index 00000000..f3f9ef2b --- /dev/null +++ b/spec/requests/spree/admin/taxjar_settings_request_spec.rb @@ -0,0 +1,129 @@ +require 'spec_helper' + +RSpec.describe 'Admin TaxJar Settings', :vcr, :type => :request do + extend Spree::TestingSupport::AuthorizationHelpers::Request + stub_authorization! + + around do |example| + original = ActionController::Base.allow_forgery_protection + ActionController::Base.allow_forgery_protection = false + example.run + ActionController::Base.allow_forgery_protection = original + end + + describe "GET #sync_nexus_regions" do + subject { get spree.admin_taxjar_settings_sync_nexus_regions_path } + + let(:dummy_api) { instance_double(SuperGood::SolidusTaxjar::Api) } + + context "Taxjar API token is set" do + let(:api_token) { ENV.fetch("TAXJAR_API_KEY", "fake_token") } + + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("TAXJAR_API_KEY").and_return(api_token) + allow(SuperGood::SolidusTaxjar).to receive(:api).and_return(dummy_api) + allow(dummy_api).to receive(:nexus_regions).and_return([]) + end + + it "makes a request for the nexus regions" do + subject + expect(dummy_api).to have_received(:nexus_regions) + end + + it "redirects back to TaxJar settings" do + subject + expect(response).to redirect_to "/admin/taxjar_settings" + end + + context "Taxjar call is successful" do + it "flashes a success alert" do + subject + + expect(flash[:success]).to eq("Updated with new Nexus Regions") + end + end + + context "Taxjar responds with an error" do + before do + allow(dummy_api).to receive(:nexus_regions).and_raise(Taxjar::Error, "fake error message") + end + + it "flashes an error alert" do + subject + + expect(flash[:error]).to eq("fake error message") + end + end + end + + context "Taxjar API token is not set" do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("TAXJAR_API_KEY").and_return(nil) + end + + it "doesn't make a request for the nexus regions" do + allow(SuperGood::SolidusTaxjar).to receive(:api).and_return(dummy_api) + allow(dummy_api).to receive(:nexus_regions).and_return([]) + + subject + + expect(dummy_api).to_not have_received(:nexus_regions) + end + end + end + + describe "PUT #update" do + subject { put spree.admin_taxjar_settings_path params:{ super_good_solidus_taxjar_configuration:{"preferred_reporting_enabled"=>true} }} + + let(:taxjar_configuration) { create :taxjar_configuration, preferred_reporting_enabled: false } + + it "redirects back to TaxJar settings" do + subject + expect(response).to redirect_to "/admin/taxjar_settings" + end + + it "shows a flash message" do + subject + expect(flash[:success]).to eq "TaxJar settings updated!" + end + + it "updates the taxjar settings" do + expect { subject }.to change { taxjar_configuration.reload.preferred_reporting_enabled }.from(false).to(true) + end + + context "update fails" do + before do + allow(SuperGood::SolidusTaxjar).to receive(:configuration).and_return(taxjar_configuration) + allow(taxjar_configuration).to receive(:update).and_return(false) + end + + it "redirects back to TaxJar settings" do + subject + expect(response).to redirect_to "/admin/taxjar_settings" + end + + it "shows a flash message" do + subject + expect(flash[:alert]).to eq "Failed to update settings!" + end + end + end + + describe "#backfill_transactions" do + subject { post spree.admin_taxjar_settings_backfill_transactions_path } + + let(:order) { create(:shipped_order) } + + before do + create(:tax_rate, name: "Sales Tax") + # The order must be created **after** the TaxRate to have the correct totals + order + end + + it "shows the backfilled orders" do + expect(subject).to redirect_to(spree.admin_transaction_sync_batch_path(SuperGood::SolidusTaxjar::TransactionSyncBatch.last)) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ae3528d0..ef9e9490 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,7 +8,24 @@ system "bin/rake extension:test_app" unless File.exist? file } -require "solidus_dev_support/rspec/rails_helper" +require "solidus_dev_support/rspec/feature_helper" +require 'vcr' + +chrome_options = Selenium::WebDriver::Chrome::Options.new.tap do |options| + options.add_argument("--window-size=#{CAPYBARA_WINDOW_SIZE.join(',')}") + options.add_argument("--headless") + options.add_argument("--disable-gpu") +end + +version = Capybara::Selenium::Driver.load_selenium +options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options + +Capybara.register_driver :solidus_chrome_headless do |app| + Capybara::Selenium::Driver.new(app, browser: :chrome, options_key => chrome_options) +end + +factories = Dir["#{::SuperGoodSolidusTaxjar::Engine.root}/lib/super_good/solidus_taxjar/testing_support/factories/**/*_factory.rb"].sort +factories.each { |f| require f } # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -17,4 +34,31 @@ RSpec.configure do |config| config.infer_spec_type_from_file_location! config.use_transactional_fixtures = false + + config.before do + Rails.cache.clear + if RSpec.current_example.metadata[:js] && page.driver.browser.respond_to?(:url_blacklist) + page.driver.browser.url_blacklist = ['https://fonts.googleapis.com'] + end + end +end + +VCR.configure do |config| + config.cassette_library_dir = 'spec/fixtures/cassettes' + config.ignore_localhost = true + config.configure_rspec_metadata! + config.hook_into :webmock + driver_urls = Webdrivers::Common.subclasses.map do |driver| + Addressable::URI.parse(driver.base_url).host + end + config.ignore_hosts( + "chromedriver.storage.googleapis.com", + *driver_urls + ) + config.filter_sensitive_data('') { |interaction| + auths = interaction.request.headers['Authorization'].first + if (match = auths.match /^Bearer\s+([^,\s]+)/ ) + match.captures.first + end + } end diff --git a/spec/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber_spec.rb b/spec/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber_spec.rb new file mode 100644 index 00000000..247aaede --- /dev/null +++ b/spec/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber_spec.rb @@ -0,0 +1,247 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::Spree::ReportingSubscriber do + # We only want to trigger the real event action behaviour as our spec + # `subject`s. + def with_events_disabled(&block) + allow(Spree::Event).to receive(:fire).and_return(nil) + + object = yield block + + allow(Spree::Event).to receive(:fire).and_call_original + + object + end + + before do + create(:taxjar_configuration, preferred_reporting_enabled: reporting_enabled) + end + + let(:order_factory) { :order_ready_to_ship } + let(:order) { with_events_disabled { create order_factory } } + + let(:reporting) { instance_spy ::SuperGood::SolidusTaxjar::Reporting } + let(:reporting_enabled) { true } + + describe "order_recalculated is fired" do + subject { ::Spree::Event.fire "order_recalculated", order: order } + + context "when the order is completed" do + context "when the order has not been shipped" do + it "does nothing" do + expect(reporting) + .not_to receive(:refund_and_create_new_transaction) + + subject + end + end + + context "when the order's payment state is 'credit_owed'" do + let(:order) { + with_events_disabled { + create(order_factory, payment_state: "credit_owed") + } + } + + it "does nothing" do + expect(reporting) + .not_to receive(:refund_and_create_new_transaction) + + subject + end + end + + context "when the order's payment state is 'paid'" do + context "when a TaxJar transaction already exists on the order" do + let!(:taxjar_transaction) { create(:taxjar_order_transaction, order: order) } + + let(:dummy_client) { instance_double Taxjar::Client } + let(:dummy_response) { + instance_double( + ::Taxjar::Order, + amount: 110.00, + line_items: [ + Taxjar::LineItem.new( + discount: 0, + sales_tax: 9999, + unit_price: 9999 + ) + ], + sales_tax: 9999999, + shipping: 99999, + transaction_id: order.number, + transaction_date: "2015-05-15T00:00:00Z" + ) + } + + let(:new_dummy_response) { + instance_double( + ::Taxjar::Order, + amount: 333.00, + transaction_id: "#{order.number}-1", + transaction_date: "2015-05-16T00:00:00Z" + ) + } + + before do + allow(SuperGood::SolidusTaxjar::Api) + .to receive(:default_taxjar_client) + .and_return(dummy_client) + + allow(dummy_client) + .to receive(:show_order) + .with(taxjar_transaction.transaction_id) + .and_return(dummy_response) + + allow(dummy_client) + .to receive(:create_refund) + .with( + SuperGood::SolidusTaxjar::ApiParams + .refund_transaction_params(order, dummy_response) + ) + end + + context "when the TaxJar transaction is up-to-date" do + it "does nothing" do + expect(reporting) + .not_to receive(:refund_and_create_new_transaction) + + subject + end + + context "when reporting is disabled" do + let(:reporting_enabled) { false } + + it "does nothing" do + expect(reporting) + .not_to receive(:refund_and_create_new_transaction) + + subject + end + end + end + + context "when the TaxJar transaction is not up-to-date" do + before do + allow(dummy_client).to receive(:tax_for_order) + + with_events_disabled { + # We want to ensure that the order is completed, paid, and that + # the `ReportingSubscriber#amount_changed?` method returns true. + order.line_items.first.update!(price: 33) + order.recalculate + order.payments.first.update!(amount: order.total) + } + end + + it "enqueue a job to refund and create a new transaction" do + assert_enqueued_with( + job: SuperGood::SolidusTaxjar::ReplaceTransactionJob, + args: [order] + ) do + subject + end + end + + it "creates a new TaxJar order transaction" do + allow(dummy_client) + .to receive(:create_order) + .and_return(new_dummy_response) + + perform_enqueued_jobs do + expect { subject } + .to change { order.taxjar_order_transactions.count } + .from(1) + .to(2) + end + end + + context "when reporting is disabled" do + let(:reporting_enabled) { false } + + it "does nothing" do + expect(reporting) + .not_to receive(:refund_and_create_new_transaction) + + subject + end + end + end + end + + context "when a TaxJar transaction does not exist on the order" do + it "does nothing" do + expect(reporting) + .not_to receive(:refund_and_create_new_transaction) + + subject + end + + it( + "creates a new transaction", + skip: "in the future, we would like to implement a 'create or update' flow" + ) do + expect { subject } + .to change { SuperGood::SolidusTax::OrderTransaction.count } + .from(0) + .to(1) + end + end + end + + context "when the order's payment state is 'balance_due'" do + let(:order_factory) { :completed_order_with_pending_payment } + + it "does nothing" do + expect(reporting).not_to receive(:show_or_create_transaction) + subject + end + end + end + + context "when the order is not completed" do + let(:order_factory) { :order_with_totals } + + it "does nothing" do + expect(reporting).not_to receive(:show_or_create_transaction) + subject + end + end + end + + describe "shipment_shipped is fired" do + subject { Spree::Event.fire "shipment_shipped", shipment: shipment } + + before do + # Ignore other events that may be triggered by factories here. + allow(Spree::Event).to receive(:fire).with("order_recalculated") + end + + let(:shipment) { create(:shipment, state: 'ready', order: order) } + let(:order) { + with_events_disabled { create :order_with_line_items } + } + let(:reporting) { instance_spy(::SuperGood::SolidusTaxjar::Reporting) } + + context "reporting is enabled" do + it "enqueues job to report transaction" do + assert_enqueued_with( + job: SuperGood::SolidusTaxjar::ReportTransactionJob, + args: [shipment.order] + ) do + subject + end + end + end + + context "reporting is disabled" do + let(:reporting_enabled) { false } + + it "doesn't queue to report the transaction" do + subject + + assert_no_enqueued_jobs + end + end + end +end diff --git a/spec/super_good/solidus_taxjar/addresses_spec.rb b/spec/super_good/solidus_taxjar/addresses_spec.rb index 4995b121..27d7b6e2 100644 --- a/spec/super_good/solidus_taxjar/addresses_spec.rb +++ b/spec/super_good/solidus_taxjar/addresses_spec.rb @@ -10,8 +10,6 @@ address1: "475 North Beverly Drive", city: "Los Angeles", country: country_us, - first_name: "Chuck", - last_name: "Schuldiner", phone: "1-250-555-4444", state: state_california, zipcode: "90210" @@ -79,8 +77,6 @@ address1: "475 N Beverly Dr", city: "Beverly Hills", country: country_us, - first_name: "Chuck", - last_name: "Schuldiner", phone: "1-250-555-4444", state: state_california, zipcode: "90210-4606" @@ -123,8 +119,6 @@ address1: "475 N Beverly Dr", city: "Beverly Hills", country: country_us, - first_name: "Chuck", - last_name: "Schuldiner", phone: "1-250-555-4444", state: state_california, zipcode: "90210-4606" @@ -143,8 +137,6 @@ address1: "475 North Beverly Drive", city: "Los Angeles", country: country_us, - first_name: "Chuck", - last_name: "Schuldiner", phone: "1-250-555-4444", state: state_california, zipcode: "90210" @@ -212,8 +204,6 @@ address1: "475 N Beverly Dr", city: "Beverly Hills", country: country_us, - first_name: "Chuck", - last_name: "Schuldiner", phone: "1-250-555-4444", state: state_california, zipcode: "90210-4606" @@ -264,8 +254,6 @@ address1: "475 N Beverly Dr", city: "Beverly Hills", country: country_us, - first_name: "Chuck", - last_name: "Schuldiner", phone: "1-250-555-4444", state: state_california, zipcode: "90210-4606" @@ -275,8 +263,6 @@ address1: "473 N Beverly Dr", city: "Phoenix", country: country_us, - first_name: "Chuck", - last_name: "Schuldiner", phone: "1-250-555-4444", state: state_arizona, zipcode: "90213-1234" diff --git a/spec/super_good/solidus_taxjar/api_params_spec.rb b/spec/super_good/solidus_taxjar/api_params_spec.rb index 0ad0a0f8..e0cfc539 100644 --- a/spec/super_good/solidus_taxjar/api_params_spec.rb +++ b/spec/super_good/solidus_taxjar/api_params_spec.rb @@ -2,24 +2,23 @@ RSpec.describe SuperGood::SolidusTaxjar::ApiParams do let(:order) do - Spree::Order.create!( + create(:order, additional_tax_total: BigDecimal("9.87"), item_total: BigDecimal("28.00"), - line_items: [line_item], + line_items_attributes: [line_item_attributes], number: "R111222333", ship_address: ship_address, - shipment_total: BigDecimal("3.01"), store: store, total: order_total, - user_id: 12345 - ).tap do |order| - order.update! completed_at: DateTime.new(2018, 3, 6, 12, 10, 33) - end + shipments: [shipment], + user_id: 12345, + completed_at: DateTime.new(2018, 3, 6, 12, 10, 33)) end let(:order_total) { BigDecimal("123.45") } let(:store) do - Spree::Store.create!( + create( + :store, cart_tax_country_iso: "US", code: "store", mail_from_address: "contact@example.com", @@ -28,40 +27,30 @@ ) end - let(:ship_address) do - Spree::Address.create!( + let!(:ship_address) do + create( + :address, address1: "475 N Beverly Dr", + address2: nil, city: "Los Angeles", country: country_us, - first_name: "Chuck", - last_name: "Schuldiner", phone: "1-250-555-4444", - state: state_california, + state_code: "CA", zipcode: "90210" ) end let(:country_us) do - Spree::Country.create!( - iso3: "USA", + create( + :country, iso: "US", - iso_name: "UNITED STATES", - name: "United States", - numcode: 840, states_required: true ) end - let(:state_california) do - Spree::State.create!( - abbr: "CA", - country: country_us, - name: "California" - ) - end - - let(:line_item) do - Spree::LineItem.new( + let(:line_item_attributes) do + attributes_for( + :line_item, additional_tax_total: 4, price: 10, promo_total: -2, @@ -71,15 +60,35 @@ end let(:variant) do - Spree::Variant.create!( + create( + :variant, price: 10, product: product, - sku: "G00D-PR0DUCT" + sku: "G00D-PR0DUCT", + option_values: [option_value] + ) + end + + let(:option_value) do + create( + :option_value, + name: "Red", + presentation: "red", + option_type: option_type + ) + end + + let(:option_type) do + create( + :option_type, + name: "Color", + presentation: "color" ) end let(:product) do - Spree::Product.create!( + create( + :product, master: master_variant, name: "Product Name", shipping_category: shipping_category, @@ -88,12 +97,11 @@ ) end - let(:shipping_category) do - Spree::ShippingCategory.create!(name: "Default Category") - end + let(:shipping_category){ create(:shipping_category) } let(:tax_category) do - Spree::TaxCategory.create!( + create( + :tax_category, is_default: true, name: "Default", tax_code: "A_GEN_TAX" @@ -101,19 +109,21 @@ end let(:master_variant) do - Spree::Variant.new( + build( + :variant, is_master: true, price: 10 ) end let(:reimbursement) do - Spree::Reimbursement.new( + build( + :reimbursement, number: "RI123123123", order: order, return_items: [ - Spree::ReturnItem.new(additional_tax_total: 0.33), - Spree::ReturnItem.new(additional_tax_total: 33.0) + build(:return_item, additional_tax_total: 0.33), + build(:return_item, additional_tax_total: 33.0) ], total: 333.33 ) @@ -130,9 +140,30 @@ ) end - describe "#order_params" do + let(:shipment) { create(:shipment, cost: BigDecimal("3.01")) } + + before do + create :state, state_code: "CA" + create :state, state_code: "NY" + end + + describe ".order_params" do subject { described_class.order_params(order) } + before do + # The discount calculator relies on the line item adjustments existing in + # order to calculate the correct discount amount for TaxJar. + create( + :adjustment, + order: order, + adjustable: order.line_items.first, + amount: line_item_attributes[:promo_total], + source_type: "Spree::Promotion::Action::CreateItemAdjustments", + label: "Promo", + finalized: true # Prevents this adjustment from being recalculated. + ) + end + it "returns params for fetching the tax for the order" do expect(subject).to eq( customer_id: "12345", @@ -200,8 +231,9 @@ end context "when the line item has zero quantity" do - let(:line_item) do - Spree::LineItem.new( + let(:line_item_attributes) do + attributes_for( + :line_item, additional_tax_total: 4, price: 10, promo_total: -2, @@ -225,7 +257,7 @@ end end - describe "#address_params" do + describe ".address_params" do subject { described_class.address_params(ship_address) } it "returns params for fetching the tax info for that address" do @@ -241,7 +273,7 @@ end end - describe "#tax_rate_address_params" do + describe ".tax_rate_address_params" do subject { described_class.tax_rate_address_params(ship_address) } it "returns params for fetching the tax rate for that address" do @@ -259,17 +291,32 @@ end end - describe "#transaction_params" do + describe ".transaction_params" do subject { described_class.transaction_params(order) } + before do + # The discount calculator relies on the line item adjustments existing in + # order to calculate the correct discount amount for TaxJar. + create( + :adjustment, + order: order, + adjustable: order.line_items.first, + amount: line_item_attributes[:promo_total], + source_type: "Spree::Promotion::Action::CreateItemAdjustments", + label: "Promo", + finalized: true # Prevents this adjustment from being recalculated. + ) + end + it "returns params for creating/updating an order transaction" do expect(subject).to eq({ amount: BigDecimal("113.58"), customer_id: "12345", line_items: [{ discount: 2, - id: line_item.id, + id: order.line_items.first.id, product_identifier: "G00D-PR0DUCT", + description: "Product Name - color: red", product_tax_code: "A_GEN_TAX", quantity: 3, sales_tax: 4, @@ -301,8 +348,9 @@ it "sends the sales tax on the line items as zero" do expect(subject[:line_items]).to contain_exactly({ discount: 2, - id: line_item.id, + id: order.line_items.first.id, product_identifier: "G00D-PR0DUCT", + description: "Product Name - color: red", product_tax_code: "A_GEN_TAX", quantity: 3, sales_tax: 0, @@ -312,8 +360,9 @@ end context "when the line item has 0 quantity" do - let(:line_item) do - Spree::LineItem.new( + let(:line_item_attributes) do + attributes_for( + :line_item, additional_tax_total: 4, price: 10, promo_total: -2, @@ -339,6 +388,18 @@ }) end end + + context "with an optional transaction_id specified" do + subject { + described_class.transaction_params(order, custom_transaction_id) + } + + let(:custom_transaction_id) { "R0123456789" } + + it "uses the specified transaction_id" do + expect(subject).to include(transaction_id: "R0123456789") + end + end end describe "#refund_params" do @@ -361,6 +422,44 @@ end end + describe "#refund_transaction_params" do + subject { described_class.refund_transaction_params(order, taxjar_order) } + + let(:taxjar_line_item) { {id: 1, quantity: 2, unit_price: 2.00, discount: 0.50, sales_tax: 0.80} } + let(:taxjar_order) { + Taxjar::Order.new( + transaction_id: "R111222333-1", + amount: 123.45, + sales_tax: 33.33, + shipping: 3.01, + line_items: [taxjar_line_item] + ) + } + + it "returns params for creating/updating a refund" do + expect(subject).to include({ + amount: -123.45, + sales_tax: -33.33, + shipping: -3.01, + to_city: "Los Angeles", + to_country: "US", + to_state: "CA", + to_street: "475 N Beverly Dr", + to_zip: "90210", + transaction_date: "2018-03-06T12:10:33Z", + transaction_reference_id: "R111222333-1", + transaction_id: "R111222333-1-REFUND", + line_items: [{ + id: 1, + quantity: 2, + unit_price: -2.00, + discount: -0.50, + sales_tax: -0.80 + }] + }) + end + end + describe "#validate_address_params" do subject { described_class.validate_address_params(ship_address) } @@ -373,6 +472,65 @@ street: "475 N Beverly Dr" }) end + + context "with an address without a state" do + let!(:ship_address) do + create( + :address, + address1: "72 High St", + address2: nil, + city: "Birmingham", + country: country_uk, + phone: "1-250-555-4444", + state: nil, + state_name: "West Midlands", + zipcode: "B4 7TA" + ) + end + + let(:country_uk) do + create( + :country, + iso: "GB", + states_required: false + ) + end + + it "uses the state_name to build address params" do + expect(subject).to eq({ + country: "GB", + state: "West Midlands", + zip: "B4 7TA", + city: "Birmingham", + street: "72 High St" + }) + end + end + + context "an address with address2" do + let!(:ship_address) do + create( + :address, + address1: "1 World Trade CTR", + address2: "STE 45A", + city: "New York", + country: country_us, + phone: "1-250-555-4444", + state_code: "NY", + zipcode: "10007" + ) + end + + it "concatenates address1 and address2 into the street parameter" do + expect(subject).to eq({ + country: "US", + state: "NY", + zip: "10007", + city: "New York", + street: "1 World Trade CTR STE 45A" + }) + end + end end describe "#taxjar_customer_params" do diff --git a/spec/super_good/solidus_taxjar/api_spec.rb b/spec/super_good/solidus_taxjar/api_spec.rb index 802d3a8d..1f34cd90 100644 --- a/spec/super_good/solidus_taxjar/api_spec.rb +++ b/spec/super_good/solidus_taxjar/api_spec.rb @@ -1,11 +1,59 @@ require "spec_helper" RSpec.describe SuperGood::SolidusTaxjar::Api do + let(:api) { described_class.new(taxjar_client: dummy_client) } + let(:dummy_client) { instance_double ::Taxjar::Client } + + describe ".new" do + subject { described_class.new } + + before do + allow(ENV).to receive(:fetch).and_call_original + allow(ENV).to receive(:fetch).with("TAXJAR_API_KEY").and_return("taxjar_api_token") + end + + it "sets the correct headers" do + expect_any_instance_of(::Taxjar::Client).to receive(:set_api_config).with('headers', { + 'x-api-version' => '2020-08-07', + 'plugin' => 'supergoodsolidustaxjar' + }) + subject + end + end + + describe ".default_taxjar_client" do + subject { described_class.default_taxjar_client } + + before do + allow(ENV).to receive(:fetch).and_call_original + allow(ENV).to receive(:fetch).with("TAXJAR_API_KEY").and_return("taxjar_api_token") + end + + it "returns an instance of the TaxJar client" do + expect(subject).to be_an_instance_of(::Taxjar::Client) + end + end + + describe "#tax_categories" do + subject { api.tax_categories } + + let(:tax_categories) { + [ + instance_double(Taxjar::Category, name: "Clothing"), + instance_double(Taxjar::Category, name: "Digital Goods") + ] + } + + it "responds with a list of tax categories" do + allow(dummy_client).to receive(:categories).and_return(tax_categories) + + expect(subject).to eq(tax_categories) + end + end + describe "#tax_for" do subject { api.tax_for order } - let(:api) { described_class.new(taxjar_client: dummy_client) } - let(:dummy_client) { instance_double ::Taxjar::Client } let(:order) { Spree::Order.new } before do @@ -26,8 +74,6 @@ describe "tax_rate_for" do subject { api.tax_rate_for address } - let(:api) { described_class.new(taxjar_client: dummy_client) } - let(:dummy_client) { instance_double ::Taxjar::Client } let(:address) { Spree::Address.new } let(:tax_rate) { 0.04 } let(:response) { double(rate: tax_rate) } @@ -50,8 +96,6 @@ describe "#tax_rates_for" do subject { api.tax_rates_for address } - let(:api) { described_class.new(taxjar_client: dummy_client) } - let(:dummy_client) { instance_double ::Taxjar::Client } let(:address) { Spree::Address.new } before do @@ -74,28 +118,47 @@ let(:api) { described_class.new(taxjar_client: dummy_client) } let(:dummy_client) { instance_double ::Taxjar::Client } - let(:order) { Spree::Order.new } + let(:order) { create(:order_ready_to_ship, number: "R123") } + + let(:dummy_response) do + instance_double( + ::Taxjar::Order, + transaction_id: "R123", + transaction_date: "2015-05-15T00:00:00Z" + ) + end - before do - allow(SuperGood::SolidusTaxjar::ApiParams) - .to receive(:transaction_params) - .with(order) - .and_return({transaction: "params"}) + context "when the latest transaction ID is nil" do + before do + allow(SuperGood::SolidusTaxjar::ApiParams) + .to receive(:transaction_params) + .with(order, "R123") + .and_return({transaction: "params"}) - allow(dummy_client) - .to receive(:create_order) - .with({transaction: "params"}) - .and_return({some_kind_of: "response"}) + allow(dummy_client) + .to receive(:create_order) + .with({transaction: "params"}) + .and_return(dummy_response) + end + + it { is_expected.to eq(dummy_response) } end - it { is_expected.to eq({some_kind_of: "response"}) } + context "when the API call to create the transaction fails" do + before do + allow(dummy_client).to receive(:create_order).and_raise(Taxjar::Error) + end + + it "does not create an `OrderTransaction` for the order" do + expect { subject }.to raise_error(Taxjar::Error) + expect(order.taxjar_order_transactions.count).to be_zero + end + end end describe "#update_transaction_for" do subject { api.update_transaction_for order } - let(:api) { described_class.new(taxjar_client: dummy_client) } - let(:dummy_client) { instance_double ::Taxjar::Client } let(:order) { Spree::Order.new } before do @@ -113,11 +176,9 @@ it { is_expected.to eq({some_kind_of: "response"}) } end - describe "#update_transaction_for" do + describe "#delete_transaction_for" do subject { api.delete_transaction_for order } - let(:api) { described_class.new(taxjar_client: dummy_client) } - let(:dummy_client) { instance_double ::Taxjar::Client } let(:order) { Spree::Order.new(number: "R111222333") } before do @@ -130,11 +191,49 @@ it { is_expected.to eq({some_kind_of: "response"}) } end + describe "#show_latest_transaction_for" do + subject { api.show_latest_transaction_for order } + + let(:order) { Spree::Order.new(number: "R111222333") } + + context "with a persisted order transaction" do + before do + create( + :taxjar_order_transaction, + order: order, + transaction_id: "R111222333-42" + ) + end + + let(:order) { create(:order, number: "R111222333") } + + it "uses the persisted transaction_id to fetch the TaxJar transaction" do + expect(dummy_client) + .to receive(:show_order) + .with("R111222333-42") + .and_return({some_kind_of: "response"}) + expect(subject).to eq({some_kind_of: "response"}) + end + + context "TaxJar does not have an order transaction persisted" do + before do + allow(dummy_client) + .to receive(:show_order) + .and_raise(Taxjar::Error::NotFound) + end + + it { is_expected.to eq nil } + end + end + + context "without a persisted order transaction" do + it { is_expected.to eq nil } + end + end + describe "#create_refund_for" do subject { api.create_refund_for reimbursement } - let(:api) { described_class.new(taxjar_client: dummy_client) } - let(:dummy_client) { instance_double ::Taxjar::Client } let(:reimbursement) { Spree::Reimbursement.new } before do @@ -152,11 +251,69 @@ it { is_expected.to eq({some_kind_of: "response"}) } end + describe "#create_refund_transaction_for" do + subject { api.create_refund_transaction_for order } + + let(:order) { create(:order_ready_to_ship, number: "R111222333") } + + let(:taxjar_order) { + Taxjar::Order.new( + amount: 20, + sales_tax: 2, + shipping: 5 + ) + } + + before do + allow(dummy_client) + .to receive(:show_order) + .with("R111222333-10") + .and_return(taxjar_order) + + allow(SuperGood::SolidusTaxjar::ApiParams) + .to receive(:refund_transaction_params) + .with(order, taxjar_order) + .and_return({refund_transaction: "params"}) + + allow(dummy_client) + .to receive(:create_refund) + .with({refund_transaction: "params"}) + .and_return({some_kind_of: "response"}) + end + + context "when no order transaction has been persisted" do + it "raises an exception" do + expect { subject }.to raise_error( + NotImplementedError, + "No latest TaxJar order transaction for #{order.number}. " \ + "Backfilling TaxJar transaction orders from Solidus is not yet " \ + "implemented." + ) + end + end + + context "when an order transaction has been persisted" do + before do + create( + :taxjar_order_transaction, + order: order, + transaction_id: "R111222333-10" + ) + end + + it "requests the latest transaction from TaxJar" do + expect(dummy_client).to receive(:show_order).with("R111222333-10") + + subject + end + + it { is_expected.to eq({some_kind_of: "response"}) } + end + end + describe "#validate_spree_address" do subject { api.validate_spree_address spree_address } - let(:api) { described_class.new(taxjar_client: dummy_client) } - let(:dummy_client) { instance_double ::Taxjar::Client } let(:spree_address) { build :address } before do @@ -174,6 +331,18 @@ it { is_expected.to eq({some_kind_of: "response"}) } end + describe "#nexus_regions" do + subject { api.nexus_regions } + + before do + allow(dummy_client) + .to receive(:nexus_regions) + .and_return({some_kind_of: "response"}) + end + + it { is_expected.to eq({some_kind_of: "response"}) } + end + describe "#create_customer_for" do subject { api.create_customer_for user } @@ -185,13 +354,13 @@ before do allow(SuperGood::SolidusTaxjar::ApiParams) .to receive(:customer_params) - .with(taxjar_customer) - .and_return({customer: "params"}) + .with(taxjar_customer) + .and_return({customer: "params"}) allow(dummy_client) .to receive(:create_customer) - .with({customer: "params"}) - .and_return({some_kind_of: "response"}) + .with({customer: "params"}) + .and_return({some_kind_of: "response"}) end it { is_expected.to eq({some_kind_of: "response"}) } @@ -208,13 +377,13 @@ before do allow(SuperGood::SolidusTaxjar::ApiParams) .to receive(:customer_params) - .with(taxjar_customer) - .and_return({customer: "params"}) + .with(taxjar_customer) + .and_return({customer: "params"}) allow(dummy_client) .to receive(:update_customer) - .with({customer: "params"}) - .and_return({some_kind_of: "response"}) + .with({customer: "params"}) + .and_return({some_kind_of: "response"}) end it { is_expected.to eq({some_kind_of: "response"}) } @@ -231,8 +400,8 @@ before do allow(dummy_client) .to receive(:delete_customer) - .with(12345) - .and_return({some_kind_of: "response"}) + .with(12345) + .and_return({some_kind_of: "response"}) end it { is_expected.to eq({some_kind_of: "response"}) } diff --git a/spec/super_good/solidus_taxjar/backfill_transactions_spec.rb b/spec/super_good/solidus_taxjar/backfill_transactions_spec.rb new file mode 100644 index 00000000..1a4887b3 --- /dev/null +++ b/spec/super_good/solidus_taxjar/backfill_transactions_spec.rb @@ -0,0 +1,82 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::BackfillTransactions do + describe ".call" do + subject { described_class.new(api: api_spy).call } + + let!(:shipped_order) { create :shipped_order } + let(:reporting_mock) { instance_double ::SuperGood::SolidusTaxjar::Reporting } + let(:api_spy) { instance_spy(::SuperGood::SolidusTaxjar::Api) } + let(:test_transaction_id) { "R1234-transaction" } + + around do |example| + ::SuperGood::SolidusTaxjar.test_mode = true + example.run + ::SuperGood::SolidusTaxjar.test_mode = false + end + + before do + reported_order = create :shipped_order + create(:taxjar_order_transaction, order: reported_order) + create :order_ready_to_ship + + unreported_transaction = build(:taxjar_order_transaction, order: shipped_order, transaction_id: test_transaction_id) + + allow(SuperGood::SolidusTaxjar).to receive(:reporting).and_return(reporting_mock) + allow(reporting_mock).to receive(:show_or_create_transaction) do + unreported_transaction.save! + unreported_transaction + end + end + + it "reports the transaction to TaxJar" do + subject + + expect(reporting_mock).to have_received(:show_or_create_transaction).with(shipped_order) + end + + it "returns the associated transaction sync batch" do + expect(subject).to be_a(SuperGood::SolidusTaxjar::TransactionSyncBatch) + end + + it "creates a log of each synced order in the database" do + expect { subject }.to change { SuperGood::SolidusTaxjar::TransactionSyncBatch.count } + .from(0).to(1) + transaction_sync_batch = SuperGood::SolidusTaxjar::TransactionSyncBatch.last + expect(transaction_sync_batch.transaction_sync_logs.count).to eq(1) + expect(transaction_sync_batch.transaction_sync_logs.last.order).to eq(shipped_order) + end + + it "records the associated order, taxjar transaction, and status on each log" do + subject + sync_log = SuperGood::SolidusTaxjar::TransactionSyncLog.last + + expect(sync_log).to have_attributes( + order: shipped_order, + status: "success" + ) + expect(sync_log.order_transaction).not_to be_nil + end + + context "when the transaction cannot be created on TaxJar" do + before do + allow(reporting_mock) + .to receive(:show_or_create_transaction) + .and_raise(Taxjar::Error.new("api down")) + end + + it "records a failure status in the log" do + subject + sync_log = SuperGood::SolidusTaxjar::TransactionSyncLog.last + + expect(sync_log).to have_attributes( + order: shipped_order, + status: "error", + error_message: /api down/ + ) + + expect(sync_log.order_transaction).to be_nil + end + end + end +end diff --git a/spec/super_good/solidus_taxjar/calculator_helper_spec.rb b/spec/super_good/solidus_taxjar/calculator_helper_spec.rb new file mode 100644 index 00000000..b2e41cff --- /dev/null +++ b/spec/super_good/solidus_taxjar/calculator_helper_spec.rb @@ -0,0 +1,60 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::CalculatorHelper do + class TestProxy + extend SuperGood::SolidusTaxjar::CalculatorHelper + end + + describe "#incomplete_address?" do + subject { TestProxy.incomplete_address?(address) } + + context "with a missing city" do + let(:address) { build :address, city: nil } + it { is_expected.to eq(true) } + end + + context "with a missing address1" do + let(:address) { build :address, address1: nil } + it { is_expected.to eq(true) } + end + + context "with a missing zipcode" do + let(:address) { build :address, zipcode: nil } + it { is_expected.to eq(true) } + end + + context "with a missing state in CA" do + let(:address) { build :address, country_iso_code: "CA", state: nil } + it { is_expected.to eq(true) } + end + + context "with a missing state in US" do + let(:address) { build :address, country_iso_code: "US", state: nil } + it { is_expected.to eq(true) } + end + + context "with a missing state in DE" do + let(:address) { build :address, country_iso_code: "DE", state: nil } + it { is_expected.to eq(false) } + end + end + + describe "#state_required?" do + subject { TestProxy.state_required?(country) } + + context "when the address' country is Canada" do + let(:country) { Spree::Country.new(iso: "CA") } + it { is_expected.to eq(true) } + end + + context "when the address' country is the USA" do + let(:country) { Spree::Country.new(iso: "US") } + it { is_expected.to eq(true) } + end + + context "when the address' country is neither Canada nor the USA" do + let(:country) { Spree::Country.new(iso: "FR") } + it { is_expected.to eq(false) } + end + end +end diff --git a/spec/super_good/solidus_taxjar/discount_calculator_spec.rb b/spec/super_good/solidus_taxjar/discount_calculator_spec.rb index d628c4f5..5e5a095c 100644 --- a/spec/super_good/solidus_taxjar/discount_calculator_spec.rb +++ b/spec/super_good/solidus_taxjar/discount_calculator_spec.rb @@ -5,9 +5,26 @@ subject { calculator.discount } let(:calculator) { described_class.new line_item } + let(:line_item) { create :line_item } + let!(:order) { create :completed_order_with_promotion, promotion: promotion_with_adjustment, line_items: [line_item] } - let(:line_item) { ::Spree::LineItem.new(promo_total: 12.34) } + let(:cancelation_adjustment_amount) { -8 } + let(:promotion_adjustment_amount) { -2 } + let(:promotion_with_adjustment) { create :promotion_with_item_adjustment, adjustment_rate: promotion_adjustment_amount } + let!(:unit_cancellation_adjustment) { create :adjustment, order: order, adjustable: line_item, amount: cancelation_adjustment_amount, source_type: "Spree::UnitCancel" } - it { is_expected.to eq(-12.34) } + let!(:tax_adjustment) { create :tax_adjustment, order: order, adjustable: line_item, amount: 2.50 } + + it "sums the total of all non-tax adjustments" do + expect(subject).to eq(-1 * (cancelation_adjustment_amount + promotion_adjustment_amount)) + end + + context "a non-eligible adjustment exists" do + let!(:unit_cancellation_adjustment) { create :adjustment, order: order, adjustable: line_item, eligible: false, amount: cancelation_adjustment_amount, source_type: "Spree::UnitCancel" } + + it "only sums eligible adjustments" do + expect(subject).to eq(-1 * promotion_adjustment_amount) + end + end end end diff --git a/spec/super_good/solidus_taxjar/reporting_spec.rb b/spec/super_good/solidus_taxjar/reporting_spec.rb new file mode 100644 index 00000000..9ede951e --- /dev/null +++ b/spec/super_good/solidus_taxjar/reporting_spec.rb @@ -0,0 +1,137 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::Reporting do + let(:dummy_api) { instance_double ::SuperGood::SolidusTaxjar::Api } + let(:order) { build :order, completed_at: 1.days.ago } + let(:reporting) { described_class.new(api: dummy_api) } + let(:test_transaction_id) { "R1234-transaction" } + let(:test_transaction_date) { Date.new(2022, 1, 1) } + let(:taxjar_order_response_double) { + double( + "Taxjar::Order", + transaction_id: test_transaction_id, + transaction_date: test_transaction_date + ) + } + + describe "#refund_and_create_transaction" do + subject { reporting.refund_and_create_new_transaction(order) } + + let(:order) { create :order, completed_at: 1.days.ago } + + it "refunds the transaction and creates a new one in TaxJar" do + expect(dummy_api) + .to receive(:create_refund_transaction_for) + .with(order) + expect(dummy_api) + .to receive(:create_transaction_for) + .with(order) + + subject + end + + it "creates a transaction for the order" do + allow(dummy_api) + .to receive(:create_refund_transaction_for) + + allow(dummy_api) + .to receive(:create_transaction_for) + .and_return(taxjar_order_response_double) + + expect(subject).to be_a(SuperGood::SolidusTaxjar::OrderTransaction) + expect(subject.persisted?).to be_truthy + expect(subject).to have_attributes( + transaction_id: test_transaction_id, + transaction_date: test_transaction_date + ) + end + + context "when Taxjar cannot create a refund transaction", :vcr do + let(:reporting) { described_class.new } + let(:order) { create(:completed_order_with_totals) } + let!(:tax_rate) { create(:tax_rate, name: "Sales Tax") } + + # We ensure that TaxJar cannot create a refund transaction refunding it + # *before* the test scenario. + before do + SuperGood::SolidusTaxjar.reporting.show_or_create_transaction(order) + SuperGood::SolidusTaxjar.api.create_refund_transaction_for(order) + end + + it "raises an error" do + expect { subject }.to raise_error( + Taxjar::Error::UnprocessableEntity, + "Provider tranx already imported for your user account" + ) + end + + it "doesn't create a new transaction" do + expect(SuperGood::SolidusTaxjar.api) + .not_to receive(:create_transaction_for) + + begin + subject + rescue Taxjar::Error::UnprocessableEntity + nil + end + end + end + end + + describe "#show_or_create_transaction" do + subject { reporting.show_or_create_transaction(order) } + + context "the order has an existing transaction" do + before do + create :taxjar_order_transaction, transaction_id: test_transaction_id, transaction_date: test_transaction_date + end + + it "returns the existing taxjar order transaction record" do + allow(dummy_api) + .to receive(:show_latest_transaction_for) + .and_return(taxjar_order_response_double) + + subject + + expect(dummy_api) + .to have_received(:show_latest_transaction_for) + .with(order) + + expect(subject).to be_a(SuperGood::SolidusTaxjar::OrderTransaction) + expect(subject.persisted?).to be_truthy + expect(subject).to have_attributes( + transaction_id: test_transaction_id, + transaction_date: test_transaction_date + ) + end + end + + context "order doesn't have a transaction" do + let(:order) { create :order, completed_at: 1.days.ago } + + context "TaxJar has no record of the transaction" do + it "creates a transaction for the order" do + allow(dummy_api) + .to receive(:show_latest_transaction_for) + .and_return(nil) + + allow(dummy_api) + .to receive(:create_transaction_for) + .and_return(taxjar_order_response_double) + + subject + + expect(dummy_api).to have_received(:show_latest_transaction_for).with(order) + expect(dummy_api).to have_received(:create_transaction_for).with(order) + + expect(subject).to be_a(SuperGood::SolidusTaxjar::OrderTransaction) + expect(subject.persisted?).to be_truthy + expect(subject).to have_attributes( + transaction_id: test_transaction_id, + transaction_date: test_transaction_date + ) + end + end + end + end +end diff --git a/spec/super_good/solidus_taxjar/tax_calculator_spec.rb b/spec/super_good/solidus_taxjar/tax_calculator_spec.rb index 33860226..65ebc96f 100644 --- a/spec/super_good/solidus_taxjar/tax_calculator_spec.rb +++ b/spec/super_good/solidus_taxjar/tax_calculator_spec.rb @@ -77,7 +77,6 @@ context "when the order has an incomplete tax address" do let(:address) do ::Spree::Address.new( - first_name: "Ronnie James", zipcode: nil, address1: nil, city: "Beverly Hills", @@ -106,7 +105,6 @@ context "when the order has no line items" do let(:address) do ::Spree::Address.new( - first_name: "Ronnie James", zipcode: "90210", address1: "9900 Wilshire Blvd", city: "Beverly Hills", @@ -127,7 +125,6 @@ context "when the API encounters an error" do let(:address) do ::Spree::Address.new( - first_name: "Ronnie James", zipcode: "90210", address1: "9900 Wilshire Blvd", city: "Beverly Hills", @@ -159,7 +156,6 @@ context "when the order has a non-empty tax address" do let(:address) do ::Spree::Address.new( - first_name: "Ronnie James", zipcode: "90210", address1: "9900 Wilshire Blvd", city: "Beverly Hills", @@ -178,14 +174,6 @@ end context "and there is tax" do - let!(:tax_rate) do - ::Spree::TaxRate.create!( - name: "Sales Tax", - amount: 0.5, - calculator: ::Spree::Calculator.new - ) - end - let(:breakdown) do instance_double ::Taxjar::Breakdown, line_items: [taxjar_line_item], @@ -209,7 +197,7 @@ aggregate_failures do expect(item_tax.item_id).to eq 33 expect(item_tax.label).to eq "Sales Tax" - expect(item_tax.tax_rate).to eq tax_rate + expect(item_tax.tax_rate).to be_a(Spree::TaxRate) expect(item_tax.amount).to eq 6.66 expect(item_tax.included_in_price).to eq false end @@ -269,19 +257,19 @@ aggregate_failures do expect(shipment_taxes[0].item_id).to eq 1 expect(shipment_taxes[0].label).to eq "Sales Tax" - expect(shipment_taxes[0].tax_rate).to eq tax_rate + expect(shipment_taxes[0].tax_rate).to be_a(Spree::TaxRate) expect(shipment_taxes[0].amount).to eq 2.33 expect(shipment_taxes[0].included_in_price).to eq false expect(shipment_taxes[1].item_id).to eq 2 expect(shipment_taxes[1].label).to eq "Sales Tax" - expect(shipment_taxes[1].tax_rate).to eq tax_rate + expect(shipment_taxes[1].tax_rate).to be_a(Spree::TaxRate) expect(shipment_taxes[1].amount).to eq 4.33 expect(shipment_taxes[1].included_in_price).to eq false expect(shipment_taxes[2].item_id).to eq 3 expect(shipment_taxes[2].label).to eq "Sales Tax" - expect(shipment_taxes[2].tax_rate).to eq tax_rate + expect(shipment_taxes[2].tax_rate).to be_a(Spree::TaxRate) expect(shipment_taxes[2].amount).to eq 3.34 expect(shipment_taxes[2].included_in_price).to eq false end diff --git a/spec/super_good/solidus_taxjar/tax_rate_calculator_spec.rb b/spec/super_good/solidus_taxjar/tax_rate_calculator_spec.rb index 1757d196..ec762cef 100644 --- a/spec/super_good/solidus_taxjar/tax_rate_calculator_spec.rb +++ b/spec/super_good/solidus_taxjar/tax_rate_calculator_spec.rb @@ -18,7 +18,6 @@ let(:incomplete_address) do ::Spree::Address.new( - first_name: "Ronnie James", zipcode: nil, address1: nil, city: "Beverly Hills", diff --git a/spec/super_good/solidus_taxjar/transaction_id_generator_spec.rb b/spec/super_good/solidus_taxjar/transaction_id_generator_spec.rb new file mode 100644 index 00000000..ee410c58 --- /dev/null +++ b/spec/super_good/solidus_taxjar/transaction_id_generator_spec.rb @@ -0,0 +1,77 @@ +require "spec_helper" + +RSpec.describe SuperGood::SolidusTaxjar::TransactionIdGenerator do + describe ".next_transaction_id" do + subject { + described_class.next_transaction_id( + order: order, + current_transaction_id: current_transaction_id + ) + } + + let(:order) { build :order, number: order_number } + let(:order_number) { "R1234567890" } + + context "without a `current_transaction_id`" do + subject { described_class.next_transaction_id(order: order) } + + it "uses the order.number as the transaction_id" do + expect(subject).to eq("R1234567890") + end + + context "and there are dashes in the order number" do + let(:order_number) { "R123-456-7890" } + + it "uses the order.number as the transaction_id" do + expect(subject).to eq("R123-456-7890") + end + end + end + + context "with a `current_transaction_id`" do + context "when the current transaction ID does not have a suffix" do + let(:order_number) { "R1234567890" } + let(:current_transaction_id) { "R1234567890" } + + it "generates the next sequential transaction_id" do + expect(subject).to eq("R1234567890-1") + end + end + + context "when the current transaction ID has a suffix" do + let(:order_number) { "R1234567890" } + let(:current_transaction_id) { "R1234567890-2" } + + it "generates the next sequential transaction_id" do + expect(subject).to eq("R1234567890-3") + end + end + + context "when the current transaction ID contains more than one '-'" do + let(:order_number) { "R123-456-789-0" } + let(:current_transaction_id) { "R123-456-789-0-2" } + + it "generates the next sequential transaction_id" do + expect(subject).to eq("R123-456-789-0-3") + end + end + + context "when the current transaction ID suffix rolls over" do + let(:order_number) { "R1234567890" } + let(:current_transaction_id) { "R1234567890-99" } + + it "generates the next sequential transaction_id" do + expect(subject).to eq("R1234567890-100") + end + end + end + end + + describe ".refund_transaction_id" do + subject { described_class.refund_transaction_id(transaction_id) } + + let(:transaction_id) { "R1234567890" } + + it { is_expected.to eq("R1234567890-REFUND") } + end +end diff --git a/spec/super_good/solidus_taxjar_spec.rb b/spec/super_good/solidus_taxjar_spec.rb index 6236b2fd..1ba34f74 100644 --- a/spec/super_good/solidus_taxjar_spec.rb +++ b/spec/super_good/solidus_taxjar_spec.rb @@ -5,6 +5,54 @@ expect(SuperGood::SolidusTaxjar::VERSION).not_to be nil end + describe ".table_name_prefix" do + subject { described_class.table_name_prefix } + + it { is_expected.to eq("solidus_taxjar_") } + end + + describe ".api" do + subject { described_class.api } + + it "returns an instance of the api client" do + expect(subject).to be_a(SuperGood::SolidusTaxjar::Api) + end + end + + describe ".reporting" do + subject { described_class.reporting } + + it "creates a new reporting" do + expect(subject).to be_a(::SuperGood::SolidusTaxjar::Reporting) + end + end + + describe ".logger" do + subject { described_class.logger } + + let(:logger_double) { instance_double(Logger) } + + context "logger is set" do + before do + described_class.logger = logger_double + end + + after do + described_class.logger = nil + end + + it "returns the logger" do + expect(subject).to be(logger_double) + end + end + + context "no logger is set" do + it "returns the Rails logger" do + expect(subject).to be(Rails.logger) + end + end + end + describe "configuration" do describe ".cache_key" do subject { described_class.cache_key.call(order) } @@ -21,6 +69,28 @@ end end + describe ".cache_duration" do + subject { described_class.cache_duration } + + it "returns the default cache duration" do + expect(subject).to eq(3.hours) + end + + context "when set to another value" do + before do + described_class.cache_duration = 1.hour + end + + it "returns the correct cache duration" do + expect(subject).to eq(1.hour) + end + + after do + described_class.cache_duration = 3.hours + end + end + end + describe ".discount_calculator" do subject { described_class.discount_calculator } it { is_expected.to eq SuperGood::SolidusTaxjar::DiscountCalculator } @@ -31,6 +101,14 @@ it { is_expected.to eq false } end + describe ".reporting_ui_enabled" do + subject { described_class.reporting_ui_enabled } + + it "it defaults to false" do + expect(subject).to eq false + end + end + describe ".exception_handler" do subject { described_class.exception_handler.call(exception) } @@ -73,5 +151,34 @@ let(:spree_line_item) { Spree::LineItem.new } it { is_expected.to eq "Sales Tax" } end + + describe ".shipping_calculator" do + subject { described_class.shipping_calculator.call(order) } + + let(:order) { create :order } + let(:shipment) { create :shipment, order: order, cost: 20 } + + before do + create :adjustment, order: order, adjustable: shipment, amount: -10, eligible: true, source: create(:shipping_rate, shipment: shipment) + end + + it "returns the shipment total including promotions" do + expect(subject).to eq(10) + end + end + + describe ".job_queue" do + subject { described_class.job_queue } + + it { is_expected.to eq :default } + end + + describe ".configuration" do + subject { described_class.configuration } + + it "returns a Configuration instance" do + expect(subject).to be_instance_of(SuperGood::SolidusTaxjar::Configuration) + end + end end end diff --git a/spec/taxjar/api/request_spec.rb b/spec/taxjar/api/request_spec.rb new file mode 100644 index 00000000..9ea6e0a1 --- /dev/null +++ b/spec/taxjar/api/request_spec.rb @@ -0,0 +1,52 @@ +RSpec.describe Taxjar::API::Request, :vcr do + subject { Taxjar::API::Request.new(client, :get, '/v2/summary_rates', 'summary_rates').perform } + + let(:logger) { double(Logger) } + let(:client) { Taxjar::Client.new(api_key: ENV["TAXJAR_API_KEY"], api_url: "https://api.sandbox.taxjar.com") } + + before do + allow(SuperGood::SolidusTaxjar).to receive(:logger).and_return(logger) + end + + + context "logging is enabled" do + before do + allow(SuperGood::SolidusTaxjar).to receive(:logging_enabled).and_return(true) + end + + it "calls the logger", :aggregate_failures do + allow(logger).to receive(:debug) + allow(logger).to receive(:info) + + subject + + # When recording a cassette with VCR for this spec, one extra log + # statement is generated. In general we expect this to be called twice, + # but specify at least 2 times so cassettes can be re-recorded. + expect(logger).to have_received(:debug).at_least(2).times do |&block| + expect(block.call) + .to match("Host: api.sandbox.taxjar.com") + .or(match(/"summary_rates":/)) + end + + expect(logger).to have_received(:info).at_least(2).times do |&block| + expect(block.call) + .to match("> GET https://api.sandbox.taxjar.com/v2/summary_rates") + .or(match("< 200 OK")) + end + end + end + + context "logging is disabled" do + before do + allow(SuperGood::SolidusTaxjar).to receive(:logging_enabled).and_return(false) + end + + it "doesn't call the logger" do + expect(logger).to_not receive(:debug) + expect(logger).to_not receive(:info) + + subject + end + end +end diff --git a/super_good_solidus_taxjar.gemspec b/super_good-solidus_taxjar.gemspec similarity index 90% rename from super_good_solidus_taxjar.gemspec rename to super_good-solidus_taxjar.gemspec index d5e6783a..106328fc 100644 --- a/super_good_solidus_taxjar.gemspec +++ b/super_good-solidus_taxjar.gemspec @@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "super_good/solidus_taxjar/version" Gem::Specification.new do |spec| - spec.name = "super_good_solidus_taxjar" + spec.name = "super_good-solidus_taxjar" spec.version = SuperGood::SolidusTaxjar::VERSION spec.authors = ["Jared Norman"] spec.email = ["jared@super.gd"] @@ -13,7 +13,7 @@ Gem::Specification.new do |spec| spec.homepage = "https://github.com/SuperGoodSoft/solidus_taxjar" spec.license = 'BSD-3-Clause' - spec.required_ruby_version = Gem::Requirement.new('>= 2.5') + spec.required_ruby_version = ">= 2.5.0" # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency "solidus_dev_support" spec.add_development_dependency "bundler" - spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rake" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency "rspec-rails" spec.add_development_dependency "vcr", "~> 4.0"