{"action":"created","issue":{"url":"https://api.github.com/repos/solidusio/solidus/issues/1252","repository_url":"https://api.github.com/repos/solidusio/solidus","labels_url":"https://api.github.com/repos/solidusio/solidus/issues/1252/labels{/name}","comments_url":"https://api.github.com/repos/solidusio/solidus/issues/1252/comments","events_url":"https://api.github.com/repos/solidusio/solidus/issues/1252/events","html_url":"https://github.com/solidusio/solidus/issues/1252","id":160460105,"number":1252,"title":"Provide a way to use third party tax services","user":{"login":"jordan-brough","id":23050,"avatar_url":"https://avatars.githubusercontent.com/u/23050?v=3","gravatar_id":"","url":"https://api.github.com/users/jordan-brough","html_url":"https://github.com/jordan-brough","followers_url":"https://api.github.com/users/jordan-brough/followers","following_url":"https://api.github.com/users/jordan-brough/following{/other_user}","gists_url":"https://api.github.com/users/jordan-brough/gists{/gist_id}","starred_url":"https://api.github.com/users/jordan-brough/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jordan-brough/subscriptions","organizations_url":"https://api.github.com/users/jordan-brough/orgs","repos_url":"https://api.github.com/users/jordan-brough/repos","events_url":"https://api.github.com/users/jordan-brough/events{/privacy}","received_events_url":"https://api.github.com/users/jordan-brough/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":10,"created_at":"2016-06-15T16:01:52Z","updated_at":"2016-06-15T19:40:48Z","closed_at":null,"body":"There is no good way to use third party tax services with Solidus.  All solutions and extensions that the core team has seen so far are very hacky.  This is because Solidus does not provide a good extension point for third party tax systems.  We want to fix that.\r\n\r\nOne of the primary problems is that Solidus calculates taxes on a line-by-line basis, and third party tax systems generally calculate taxes on an entire order at once.  This means that for a order with 5 line items, for example, Solidus will trigger tax calculation 5 times.  If you submit the entire order to your third party tax service 5 times then 1) it will be fragile, 2) it will be slow, 3) some services (including Avatax) charge for every API call, so it may be costly.\r\n\r\nOne approach that some have tried is to attempt to cache api calls to the third party provider.  However, this doesn't work in a scenario like this:\r\n1) Start updating an order, e.g. applying a promo\r\n2) Update line item 1, trigger tax calculation\r\n3) Update line item 2, trigger tax calculation -- the totals for the order have now changed, so you cannot use the previously cached results\r\n4) Update line item 3, ...\r\nand so on."},"comment":{"url":"https://api.github.com/repos/solidusio/solidus/issues/comments/226297325","html_url":"https://github.com/solidusio/solidus/issues/1252#issuecomment-226297325","issue_url":"https://api.github.com/repos/solidusio/solidus/issues/1252","id":226297325,"user":{"login":"jordan-brough","id":23050,"avatar_url":"https://avatars.githubusercontent.com/u/23050?v=3","gravatar_id":"","url":"https://api.github.com/users/jordan-brough","html_url":"https://github.com/jordan-brough","followers_url":"https://api.github.com/users/jordan-brough/followers","following_url":"https://api.github.com/users/jordan-brough/following{/other_user}","gists_url":"https://api.github.com/users/jordan-brough/gists{/gist_id}","starred_url":"https://api.github.com/users/jordan-brough/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jordan-brough/subscriptions","organizations_url":"https://api.github.com/users/jordan-brough/orgs","repos_url":"https://api.github.com/users/jordan-brough/repos","events_url":"https://api.github.com/users/jordan-brough/events{/privacy}","received_events_url":"https://api.github.com/users/jordan-brough/received_events","type":"User","site_admin":false},"created_at":"2016-06-15T19:40:48Z","updated_at":"2016-06-15T19:40:48Z","body":"> **Possible problem**:  Anything using `ItemAdjustments` **outside of OrderUpdater** may be depending on `ItemAdjustments` to calculate taxes.\r\n\r\n#### Places in Solidus that invoke ItemAdjustments outside of OrderUpdater\r\n\r\n##### [Adjustment creation or deletion](https://github.com/solidusio/solidus/blob/v1.2.2/core/app/models/spree/adjustment.rb#L29):\r\n```ruby\r\nclass Adjustment\r\n  after_create :update_adjustable_adjustment_total\r\n  after_destroy :update_adjustable_adjustment_total\r\n```\r\n☝️  This shouldn't be a problem for *tax* adjustments, but it could be a problem for other adjustments -- e.g. if a promo modifies the amount on an item then we need to recalculate taxes.  We'd have to rely on `order.update!` being called after the adjustment was created. That *should* happen, but it seems like a possible problem spot for existing stores & code.\r\n\r\n##### [During `apply_free_shipping_promotions`](https://github.com/solidusio/solidus/blob/v1.2.2/core/app/models/spree/order.rb#L504):\r\n```ruby\r\nclass Order\r\n\r\n  before_transition from: :delivery, do: :apply_free_shipping_promotions\r\n\r\n  def apply_free_shipping_promotions\r\n    Spree::PromotionHandler::FreeShipping.new(self).activate\r\n    shipments.each { |shipment| ItemAdjustments.new(shipment).update }\r\n    updater.update_shipment_total\r\n    persist_totals\r\n  end\r\n```\r\n☝️ We could call `updater.update` instead of `updater.update_shipment_total`+`persist_totals` to ensure that shipment taxes got recalculated.\r\n\r\n##### [After adding or removing items](https://github.com/solidusio/solidus/blob/v1.2.2/core/app/models/spree/order_contents.rb#L65):\r\n```ruby\r\nclass OrderContents\r\n  def after_add_or_remove(...)\r\n    ...\r\n    ItemAdjustments.new(line_item).update\r\n    reload_totals\r\n```\r\n☝️ `reload_totals` there invokes `order_updater.update` so that should already be OK.\r\n\r\n##### [After changing the quantity on line items](https://github.com/solidusio/solidus/blob/v1.2.2/core/app/models/spree/line_item.rb#L156):\r\n```ruby\r\nclass LineItem\r\n\r\n  after_save :update_adjustments\r\n\r\n  def update_adjustments\r\n    if quantity_changed?\r\n      update_tax_charge # <- I think we can get rid of this...\r\n      recalculate_adjustments\r\n    end\r\n  end\r\n\r\n  def recalculate_adjustments\r\n    Spree::ItemAdjustments.new(self).update\r\n  end\r\n```\r\n☝️ This should be ok *if* all the triggering code calls `order.update!` afterward (like OrderContents#add does, for example) which is likely to be true but probably dangerous to assume.\r\n\r\n##### [After shipping cost changes]():\r\n```ruby\r\nclass Shipment < Spree::Base\r\n\r\n  after_save :update_adjustments\r\n\r\n  def update_adjustments\r\n    if cost_changed? && state != 'shipped'\r\n      recalculate_adjustments\r\n    end\r\n  end\r\n```\r\n☝️ Same thing as for LineItem."}}
{"actor":{"display_login":"jordan-brough"}}