diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d58dc6a..2c8e40d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## master +- [#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` 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_transaction_job.rb b/app/jobs/super_good/solidus_taxjar/report_transaction_job.rb index 623b0921..9a14b055 100644 --- a/app/jobs/super_good/solidus_taxjar/report_transaction_job.rb +++ b/app/jobs/super_good/solidus_taxjar/report_transaction_job.rb @@ -6,7 +6,7 @@ class ReportTransactionJob < ApplicationJob queue_as { SuperGood::SolidusTaxjar.job_queue } def perform(order) - SuperGood::SolidusTaxjar.reporting.report_transaction(order) + SuperGood::SolidusTaxjar.reporting.show_or_create_transaction(order) end end end diff --git a/app/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber.rb b/app/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber.rb index 9f0cc3fd..22495db8 100644 --- a/app/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber.rb +++ b/app/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber.rb @@ -7,12 +7,38 @@ module ReportingSubscriber 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 def report_transaction(event) return unless SuperGood::SolidusTaxjar.configuration.preferred_reporting_enabled + SuperGood::SolidusTaxjar::ReportTransactionJob.perform_later(event.payload[:shipment].order) end + + def replace_transaction(event) + order = event.payload[:order] + + return unless SuperGood::SolidusTaxjar.configuration.preferred_reporting_enabled + + if transaction_replaceable?(order) && amount_changed?(order) + SuperGood::SolidusTaxjar::ReplaceTransactionJob.perform_later(event.payload[:order]) + end + end + + private + + def amount_changed?(order) + SuperGood::SolidusTaxjar.api.show_latest_transaction_for(order).amount != + (order.total - order.additional_tax_total) + end + + def transaction_replaceable?(order) + order.taxjar_order_transactions.present? && + order.complete? && + order.payment_state == "paid" + end end end end diff --git a/lib/super_good/solidus_taxjar/api.rb b/lib/super_good/solidus_taxjar/api.rb index 8043b923..2b37a914 100644 --- a/lib/super_good/solidus_taxjar/api.rb +++ b/lib/super_good/solidus_taxjar/api.rb @@ -34,10 +34,18 @@ def tax_rates_for(address) end def create_transaction_for(order) - transaction_id = TransactionIdGenerator.next_transaction_id(order: order) + latest_transaction_id = + OrderTransaction.latest_for(order)&.transaction_id + + transaction_id = TransactionIdGenerator.next_transaction_id( + order: order, + current_transaction_id: latest_transaction_id + ) + response = taxjar_client.create_order( ApiParams.transaction_params(order, transaction_id) ) + order.taxjar_order_transactions.create!( transaction_id: response.transaction_id, transaction_date: response.transaction_date @@ -57,18 +65,23 @@ def show_latest_transaction_for(order) latest_transaction_id = OrderTransaction.latest_for(order)&.transaction_id - if latest_transaction_id.nil? + 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_client.show_order(latest_transaction_id) - end - - def create_refund_transaction_for(order) taxjar_order = show_latest_transaction_for(order) + taxjar_client.create_refund ApiParams.refund_transaction_params(order, taxjar_order) end diff --git a/lib/super_good/solidus_taxjar/reporting.rb b/lib/super_good/solidus_taxjar/reporting.rb index 3f5b6e9c..0e3b7b68 100644 --- a/lib/super_good/solidus_taxjar/reporting.rb +++ b/lib/super_good/solidus_taxjar/reporting.rb @@ -5,18 +5,13 @@ def initialize(api: SuperGood::SolidusTaxjar.api) @api = api end - def report_transaction(order) - begin - @api.show_latest_transaction_for(order) - rescue NotImplementedError - # FIXME: - # We can stop rescuing from `NotImplementedError` once we have - # fleshed out and implemented functionality to correctly backfill - # TaxJar order transactions and - # `SuperGood::SolidusTaxjar::OrderTransaction` records. - rescue Taxjar::Error::NotFound - @api.create_transaction_for(order) - end + def refund_and_create_new_transaction(order) + @api.create_refund_transaction_for(order) + @api.create_transaction_for(order) + end + + def show_or_create_transaction(order) + @api.show_latest_transaction_for(order) || @api.create_transaction_for(order) end end 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 index 0dea6e85..4a7958b2 100644 --- 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 @@ -3,12 +3,20 @@ order transaction_date { Date.current } - sequence(:transaction_id) { |n| - if n == 1 - order.number - else - "#{order.number}-#{n - 1}" - end + 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/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/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 index 45d23f77..5141e79d 100644 --- a/spec/jobs/super_good/solidus_taxjar/report_transaction_job_spec.rb +++ b/spec/jobs/super_good/solidus_taxjar/report_transaction_job_spec.rb @@ -15,7 +15,7 @@ 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(:report_transaction).with(order) + expect(mock_reporting).to receive(:show_or_create_transaction).with(order) perform_enqueued_jobs do subject 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 index fb69223e..247aaede 100644 --- a/spec/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber_spec.rb +++ b/spec/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber_spec.rb @@ -1,20 +1,229 @@ 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 } + 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) { create :order_with_line_items } + let(:order) { + with_events_disabled { create :order_with_line_items } + } let(:reporting) { instance_spy(::SuperGood::SolidusTaxjar::Reporting) } context "reporting is enabled" do - let(:reporting_enabled) { true } - - before do - create(:taxjar_configuration, preferred_reporting_enabled: reporting_enabled) - end - it "enqueues job to report transaction" do assert_enqueued_with( job: SuperGood::SolidusTaxjar::ReportTransactionJob, @@ -26,6 +235,8 @@ end context "reporting is disabled" do + let(:reporting_enabled) { false } + it "doesn't queue to report the transaction" do subject diff --git a/spec/super_good/solidus_taxjar/api_spec.rb b/spec/super_good/solidus_taxjar/api_spec.rb index a9d60244..a5443719 100644 --- a/spec/super_good/solidus_taxjar/api_spec.rb +++ b/spec/super_good/solidus_taxjar/api_spec.rb @@ -116,7 +116,9 @@ describe "#create_transaction_for" do subject { api.create_transaction_for order } - let(:order) { create :order, number: "R123" } + let(:api) { described_class.new(taxjar_client: dummy_client) } + let(:dummy_client) { instance_double ::Taxjar::Client } + let(:order) { create(:order_ready_to_ship, number: "R123") } let(:dummy_response) do instance_double( @@ -126,34 +128,76 @@ ) end - before do - allow(SuperGood::SolidusTaxjar::ApiParams) - .to receive(:transaction_params) - .with(order, "R123") - .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(dummy_response) + end - allow(dummy_client) - .to receive(:create_order) - .with({transaction: "params"}) - .and_return(dummy_response) - end + it { is_expected.to eq(dummy_response) } - it { is_expected.to eq(dummy_response) } + it "creates an `OrderTransaction` for the order" do + expect { subject } + .to change { order.taxjar_order_transactions.count } + .from(0) + .to(1) + end - it "creates an `OrderTransaction` for the order" do - expect { subject } - .to change { order.taxjar_order_transactions.count } - .from(0) - .to(1) + it "sets `transaction_id` and `transaction_date` on the order transaction" do + subject + expect(order.taxjar_order_transactions.first) + .to have_attributes( + transaction_id: "R123", + transaction_date: DateTime.new(2015, 5, 15, 0, 0, 0, "+0") + ) + end end - it "sets `transaction_id` and `transaction_date` on the order transaction" do - subject - expect(order.taxjar_order_transactions.first) - .to have_attributes( - transaction_id: "R123", - transaction_date: DateTime.new(2015, 5, 15, 0, 0, 0, "+0") + context "when the latest transaction ID is not the order number" do + before do + allow(dummy_client) + .to receive(:create_order) + .with( + SuperGood::SolidusTaxjar::ApiParams.transaction_params( + order, + "R123-1" + ) + ).and_return(dummy_response) + end + + let(:dummy_response) { + instance_double( + ::Taxjar::Order, + transaction_id: "R123-1", + transaction_date: "2015-05-15T00:00:00Z" + ) + } + + let!(:taxjar_order_transaction) { + create( + :taxjar_order_transaction, + order: order, + transaction_date: "2011-05-15T00:00:00Z" ) + } + + it "creates a new TaxJar transaction with the next transaction ID" do + expect(order.taxjar_order_transactions) + .to receive(:create!) + .with( + transaction_id: "R123-1", + transaction_date: "2015-05-15T00:00:00Z" + ) + + subject + end end context "when the API call to create the transaction fails" do @@ -226,17 +270,20 @@ .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 "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 + it { is_expected.to eq nil } end end diff --git a/spec/super_good/solidus_taxjar/reporting_spec.rb b/spec/super_good/solidus_taxjar/reporting_spec.rb index 5dd38e5b..f277a98e 100644 --- a/spec/super_good/solidus_taxjar/reporting_spec.rb +++ b/spec/super_good/solidus_taxjar/reporting_spec.rb @@ -1,41 +1,75 @@ require "spec_helper" RSpec.describe SuperGood::SolidusTaxjar::Reporting do - describe "#report_transaction" do - subject { described_class.new(api: dummy_api).report_transaction(order) } + 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(:dummy_api) { instance_double ::SuperGood::SolidusTaxjar::Api } - let(:order) { build :order, completed_at: 1.days.ago } + describe "#refund_and_create_transaction" do + subject { reporting.refund_and_create_new_transaction(order) } - it "updates the transaction" do - allow(dummy_api) - .to receive(:show_latest_transaction_for) + 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) - .and_return("UPDATED-TRANSACTION-ID") - expect(subject).to eq("UPDATED-TRANSACTION-ID") + subject end - context "order doesn't have a transaction" do - context "the Solidus application has no record of the transaction" do - it "does nothing (until this feature is implemented)" do - allow(dummy_api) - .to receive(:show_latest_transaction_for) - .with(order) - .and_raise(NotImplementedError) + 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.api.create_transaction_for(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 - expect(dummy_api).not_to receive(:create_transaction_for) + 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) } + + it "shows the latest transaction for the order" do + allow(dummy_api) + .to receive(:show_latest_transaction_for) + .with(order) + .and_return("UPDATED-TRANSACTION-ID") + + expect(subject).to eq("UPDATED-TRANSACTION-ID") + end + context "order doesn't have a transaction" do context "TaxJar has no record of the transaction" do it "creates the transaction for it" do allow(dummy_api) .to receive(:show_latest_transaction_for) .with(order) - .and_raise(Taxjar::Error::NotFound) + .and_return(nil) expect(dummy_api) .to receive(:create_transaction_for)