-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a PORO for creating tax adjustments #685
Conversation
Force-pushed with no changes because of spurious failure in admin shipment spec. |
This does seem like a good target for extraction to a PORO, but the handling of the two cases (receiving either an item or an order) is a little awkward. The two cases share very little code. Maybe we could adjust the signature of the adjuster? Brainstorming a little:
|
One adjuster for two different things? It seams the only thing they share is the order tax zone. Why not an Sandi Metz is calling! Excuse me ;) |
There is too much knowledge of the two possible callers inside one class. Case statements on class types, then methods named after the caller, etc. Two separate adjusters are much more flexible, more future safe and less error prone. If we use POROs than please let us use clean object oriented design. Sorry, reading too much Sandi Metz lately. Just my 0.02 |
Thank you for chipping in! @jhawthorn Actually, they do share code, as the order adjuster doesn't actually adjust the order, but all the items (line items and shipments) contained within it. The current stuff on At the moment,
|
Changed these to single responsibility objects - I do think we can deal with the efficiency after we've defined the interfaces. |
order_tax_zone = self.tax_zone | ||
Spree::TaxRate.adjust(order_tax_zone, line_items) | ||
Spree::TaxRate.adjust(order_tax_zone, shipments) if shipments.any? | ||
Spree::Tax::Adjuster.new(self).adjust! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you want to call OrderAdjuster
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely. Thanks for the heads-up! 💯
Wheew! Green! Removing This is still lacking feedback. Once that's in, I'd start putting in unit tests and moving the Spree::TaxRate into some kind of Please please, this isn't ready for merge, but definitely ready for feedback (although you do have a couple of days as I'll be out until the weekend from tomorrow). |
Rebased on master, updated commit message on second commit. Feedback still welcome. Btw: With #530 in, I'll take a go at removing |
These last ones failed because of the Rails update (#736). Rebasing on master now, and adding a couple of performance fixes as well. |
The build failed because of phantomjs crashing :( |
Force-pushed to get a green build. Otherwise: This is ready for review. I can think of more stuff to do, but the scope of this PR is already quite large, so I'll stop for now. :) |
👍 Nice @mamhoff |
Damn. The shipping rates also have VAT, and they're not price-adjusted yet. |
I hopefully can spent some review time later this evening (CET) |
This is really long, and hard to review. I'll split this PR in two: One that just adds the POROs and uses #530 to look up zones (that's all commits up to a68d9c8), and one that actually implements MOSS with that. The one that actually implements MOSS will also have to deal with shipping rates, somehow. |
Alright |
|
||
def initialize(item, options = {}) | ||
@item = item | ||
@order = @item.order |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@item
and @order
should be attr_reader
s, so the TaxHelper
module
and potential sub classes don't rely on reading instance variables.
Did the naming changes, and also added some more tests to the adjusters. |
For the time being, these two POROs (one for orders, another for items) call the already defined methods on `Spree::TaxRate`. The idea is, though, that the responsibilities between `Spree::Tax::{Item,Order}Adjuster` and `Spree::TaxRate` are divided as follows: `Spree::TaxRate` stores and selects rates, while `Spree::Tax::{Item,Order}Adjuster` create tax adjustments.
First commit was failing because of a naming change, rebased again. |
The code in `Spree::TaxRate.adjust!` is hard to understand, and the adjusting should happen in dedicated adjuster classes. This commit ports the functionality of `TaxRate.match` over to two classes: * An `OrderAdjuster` which iterates over all items (line items and shipments) in an order and runs the * `ItemAdjuster`, which actually creates adjustments for every item. The code should read a lot easier now. In order to reduce database load, the tax rate array for an order can be passed into the `ItemAdjuster.new`. Considering the performance benefits, I think that's justifiable amount of shared knowledge between the two Adjuster classes.
TaxRate.for_zone will return all tax rates that are valid for a given zone. It does not care about the default VAT zone, as that should be handled by the Adjuster classes.
When using .with_shared_members, sometimes you pass nil - and that should just yield an empty ActiveRecord relation. Same goes for Spree::TaxRate.for_zone(nil).
This is really an intermediary step. I'm using for_zone already in Spree::TaxRate.match, which should result in a constant eight DB calls per .match call. Previously, the number of DB calls would depend on how many tax rates there are, and how these are configured (as Spree::Zone.contains? would be run for every tax rate in the system). A side effect of this refactoring is that the reimbursement specs had to be amended a little: The zones had to be created `:with_country`, because otherwise there'd be no shared members and the tax rates wouldn't apply. This actually makes the tests slightly more realistic (what is a zone without members, anyway?). Also, I had to change an expectation: The included tax total of the reimbursement was previously expected to be negative (which is something that IMO should never happen, as negative included taxes at the moment become positive additional taxes).
The order adjuster and the item adjuster share code, because I need to run some methods on the order level and cache them. This extracts these methods into a module. Inheritance would've also worked, but in this case I find a mixin more fitting as the behaviour of the two classes is still quite different.
TaxRate.match does some pretty weird things, and I want to change them. This commit moves that code to a temporary `applicable_rates` helper.
@item = item | ||
@order = @item.order | ||
# set the instance variable so `TaxRate.match` is only called when necessary | ||
@rates_for_order_zone = options[:rates_for_order_zone] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear where this is being used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are used in private methods of the included module TaxHelpers
, in order to run the database calls that only have to run once per order only once per order: https://github.com/magiclabs/solidus/blob/add-tax-adjuster/core/app/models/spree/tax/tax_helpers.rb#L28-L38.
Looks good to me. 👍 One thought, should we keep |
Well, the method signatures are a bit different. I could use just the item adjuster, but then that method will have abysmal performance. I'd rather have it entirely gone. However, if there's another person saying: Deprecate it and be nice about it - I'll do it, along with the requested YARD docs tomorrow. |
Add Spree::TaxRate.adjust as a deprecated method, and test for the deprecation warnings. The deleted specs are covered in the specs for the ItemAdjuster.
Add a PORO for creating tax adjustments
🍦 |
Nice ;) |
Adds a
Spree::Tax::ItemAdjuster
, which adjusts an item, and aSpree::Tax::OrderAdjuster
, which iterates over line items and shipments in an order and adjusts those.This PR is a prerequisite for any further taxation work.