Skip to content

Commit c3214bb

Browse files
jarednormanadammathysAlistairNormanforkataHarmony Bouvier
committed
Refactor line item total calculations
While working on the in-memory updater in solidusio#5872, we found the need to change how item totals were being calculated, so that we could mark adjustments for destruction without actually destroying them, while still keeping tax adjustments intact. This change is completely backwards-compatible with the current OrderUpdater, so to reduce the scope of our PR, we wanted to make this change separately. Since the OrderUpdater is already very large, this helps reduce its responsibilities and makes it easier to test this behaviour. We don't see it as necessary to make this a configurable class, but this change leaves that option open in the future. Co-authored-by: Adam Mueller <[email protected]> Co-authored-by: Alistair Norman <[email protected]> Co-authored-by: Chris Todorov <[email protected]> Co-authored-by: Harmony Bouvier <[email protected]> Co-authored-by: Sofia Besenski <[email protected]> Co-authored-by: benjamin wil <[email protected]>
1 parent 3675af2 commit c3214bb

File tree

3 files changed

+106
-30
lines changed

3 files changed

+106
-30
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
class Spree::ItemTotal
4+
def initialize(item)
5+
@item = item
6+
end
7+
8+
def recalculate!
9+
tax_adjustments = item.adjustments.select { |adjustment|
10+
adjustment.tax? && !adjustment.marked_for_destruction?
11+
}
12+
13+
# Included tax adjustments are those which are included in the price.
14+
# These ones should not affect the eventual total price.
15+
#
16+
# Additional tax adjustments are the opposite, affecting the final total.
17+
item.included_tax_total = tax_adjustments.select(&:included?).sum(&:amount)
18+
item.additional_tax_total = tax_adjustments.reject(&:included?).sum(&:amount)
19+
20+
item.adjustment_total = item.adjustments.reject { |adjustment|
21+
adjustment.marked_for_destruction? || adjustment.included?
22+
}.sum(&:amount)
23+
end
24+
25+
private
26+
27+
attr_reader :item
28+
end

core/app/models/spree/order_updater.rb

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def recalculate_adjustments
113113
# It also fits the criteria for sales tax as outlined here:
114114
# http://www.boe.ca.gov/formspubs/pub113/
115115
update_promotions
116-
update_taxes
116+
update_tax_adjustments
117117
update_item_totals
118118
end
119119

@@ -198,21 +198,8 @@ def update_promotions
198198
Spree::Config.promotions.order_adjuster_class.new(order).call
199199
end
200200

201-
def update_taxes
201+
def update_tax_adjustments
202202
Spree::Config.tax_adjuster_class.new(order).adjust!
203-
204-
[*line_items, *shipments].each do |item|
205-
tax_adjustments = item.adjustments.select(&:tax?)
206-
# Tax adjustments come in not one but *two* exciting flavours:
207-
# Included & additional
208-
209-
# Included tax adjustments are those which are included in the price.
210-
# These ones should not affect the eventual total price.
211-
#
212-
# Additional tax adjustments are the opposite, affecting the final total.
213-
item.included_tax_total = tax_adjustments.select(&:included?).sum(&:amount)
214-
item.additional_tax_total = tax_adjustments.reject(&:included?).sum(&:amount)
215-
end
216203
end
217204

218205
def update_cancellations
@@ -221,21 +208,17 @@ def update_cancellations
221208

222209
def update_item_totals
223210
[*line_items, *shipments].each do |item|
224-
# The cancellation_total isn't persisted anywhere but is included in
225-
# the adjustment_total
226-
item.adjustment_total = item.adjustments.
227-
reject(&:included?).
228-
sum(&:amount)
229-
230-
if item.changed?
231-
item.update_columns(
232-
promo_total: item.promo_total,
233-
included_tax_total: item.included_tax_total,
234-
additional_tax_total: item.additional_tax_total,
235-
adjustment_total: item.adjustment_total,
236-
updated_at: Time.current,
237-
)
238-
end
211+
Spree::ItemTotal.new(item).recalculate!
212+
213+
next unless item.changed?
214+
215+
item.update_columns(
216+
promo_total: item.promo_total,
217+
included_tax_total: item.included_tax_total,
218+
additional_tax_total: item.additional_tax_total,
219+
adjustment_total: item.adjustment_total,
220+
updated_at: Time.current,
221+
)
239222
end
240223
end
241224
end
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
require 'rails_helper'
2+
3+
RSpec.describe Spree::ItemTotal do
4+
describe "#recalculate!" do
5+
subject { described_class.new(item).recalculate! }
6+
7+
let!(:item) { create :line_item, adjustments: }
8+
9+
let(:tax_rate) { create(:tax_rate) }
10+
11+
let(:arbitrary_adjustment) { create :adjustment, amount: 1, source: nil }
12+
let(:included_tax_adjustment) { create :adjustment, amount: 2, source: tax_rate, included: true }
13+
let(:additional_tax_adjustment) { create :adjustment, amount: 3, source: tax_rate, included: false }
14+
15+
context "with multiple types of adjustments" do
16+
let(:marked_for_destruction_included_tax_adjustment) { create(:adjustment, amount: 5, source: tax_rate, included: true) }
17+
let(:marked_for_destruction_additional_tax_adjustment) { create(:adjustment, amount: 7, source: tax_rate, included: false) }
18+
19+
let(:adjustments) {
20+
[
21+
arbitrary_adjustment,
22+
included_tax_adjustment,
23+
additional_tax_adjustment,
24+
marked_for_destruction_included_tax_adjustment,
25+
marked_for_destruction_additional_tax_adjustment
26+
]
27+
}
28+
29+
before do
30+
[marked_for_destruction_included_tax_adjustment, marked_for_destruction_additional_tax_adjustment]
31+
.each(&:mark_for_destruction)
32+
end
33+
34+
it "updates item totals" do
35+
expect {
36+
subject
37+
}.to change(item, :adjustment_total).from(0).to(4).
38+
and change { item.included_tax_total }.from(0).to(2).
39+
and change { item.additional_tax_total }.from(0).to(3)
40+
end
41+
end
42+
43+
context "with only an arbitrary adjustment" do
44+
let(:adjustments) { [arbitrary_adjustment] }
45+
46+
it "updates the adjustment total" do
47+
expect {
48+
subject
49+
}.to change { item.adjustment_total }.from(0).to(1)
50+
end
51+
end
52+
53+
context "with only tax adjustments" do
54+
let(:adjustments) { [included_tax_adjustment, additional_tax_adjustment] }
55+
56+
it "updates the adjustment total" do
57+
expect {
58+
subject
59+
}.to change { item.adjustment_total }.from(0).to(3).
60+
and change { item.included_tax_total }.from(0).to(2).
61+
and change { item.additional_tax_total }.from(0).to(3)
62+
end
63+
end
64+
end
65+
end

0 commit comments

Comments
 (0)