spree/spree — pull request #3567 by radar (2013-08-15 07:08:18+00:00)
Title: Adjustment refactoring

/cc @BDQ, @jhawthorn, @gmacdougall, @cbrunsdon, et. al.

Some background on this PR can be found in [Sean's post to spree-user](https://groups.google.com/forum/#!searchin/spree-user/Promotions/spree-user/DaMRgT7JnK8/n7VOsB1xfmEJ).

This is my attempt at refactoring the adjustments system within Spree. Here's a couple of key points:

**Originator for adjustments is _gone_**. This caused a lot of confusion and it turns out it's no longer needed. What we have on adjustments instead are the `source` association which is where the adjustment is coming from, and the `adjustable` association which defines which object is being adjusted.

**Tax adjustments are now applied to line items and shipments individually**. Whenever a line item or shipment are updated, it will update the adjustments for those items and then update the totals for the order. This is different to the current system which updates *all* the adjustments and causes a lot of Callback Hell in between the Order model and the Adjustment model.

**Shipments have many adjustments** Previously, shipments had one and only one adjustment which was the shipping cost. Fortunately there was already a `cost` field on the shipment object which is probably a better place to store this "fauxjustment". The adjustments that link to a shipment now will be things such as discounts and credits, i.e. "Free Shipping" promotion.

**Promotion::Actions::CreateAdjustment now applies adjustments to line items rather than an order**. This is so you can clearly see how much of a discount a specific line item has received and then back out only that change if you wish. More on this later.

**Promotion events are gone, gone, gone** These caused waaaay too many headaches trying to figure out what's going on where, and so they're gone. What should be done instead is some smart hooks for specific events, but I've not gotten around to that yet.

**Adjustment eligiblity is set in the one place which needs it**. The only place this was being used was to work out which of a promotion is the better to apply to an order. Since these promotions are now moved to LineItem, this logic for setting eligibility is now moved to the `LineItem` model. I *suspect* we could improve this by moving it into some kind of service class since `LineItem` and `Shipment` instances should calculate the best promotion exactly the same way.

----

Now, what can you do to help us? Easy.

1. Come up with some new test cases in **Core** which show edgecases in the adjustment system. Be _imaginative_.
2. Do that aforementioned moving of the "best promotion selection" for line items and shipments into that service object.
2. Move the CreateAdjustment promotion action to a new class (`CreateAdjustmentOnLineItems`?) and re-instate the ability to apply an adjustment to an order, not to its line items.
4. Fix the API test which creates a shipment without a cost. Cost is now required for shipments, as it's used to calculate the final total for the order.
5. Remove `shipment.adjustment` mentions in the codebase where you find them and replace them with `shipment.cost` mentions.
3. Make the frontend display the adjustment totals for each line item in a sensible manner. Previously this was using `price_adjustment_totals`, but this is now gone. I like how it was doing it before, but we should also be display a shipment total in that `checkout/_summary` partial, along with something similar to `price_adjustment_totals`, but for shipments. This will also need to show the order's adjustments.

What this'll mean is that the user will see:

* Line item costs
* Line item adjustments, grouped by label
* Shipment costs
* Shipping adjustments
* Order adjustments
* Order total

You could also read through the commits and see if I've done anything silly.

If you're going to take on any of the units of work listed above, please submit PRs to my adjustment-refactoring branch and I'll review and merge them there.

Thanks!