Skip to content

Commit 95a9ea8

Browse files
authored
Merge pull request solidusio#6080 from SuperGoodSoft/refactor-line-item-total-updates
Refactor Line Item Total Calculations
2 parents ec1c421 + 7a3bfc2 commit 95a9ea8

File tree

5 files changed

+122
-31
lines changed

5 files changed

+122
-31
lines changed

core/app/models/spree/item_total.rb

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 do |adjustment|
10+
adjustment.tax? && !adjustment.marked_for_destruction?
11+
end
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::Config.item_total_class.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

core/lib/spree/app_configuration.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,13 @@ def payment_canceller
430430
# as Spree::Wallet::AddPaymentSourcesToWallet.
431431
class_name_attribute :add_payment_sources_to_wallet_class, default: 'Spree::Wallet::AddPaymentSourcesToWallet'
432432

433+
# Allows providing your own class for recalculating totals on an item.
434+
#
435+
# @!attribute [rw] item_total_class
436+
# @return [Class] a class with the same public interfaces as
437+
# Spree::ItemTotal
438+
class_name_attribute :item_total_class, default: 'Spree::ItemTotal'
439+
433440
# Allows providing your own class for calculating taxes on an order.
434441
#
435442
# This extension point is under development and may change in a future minor release.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
RSpec.describe Spree::ItemTotal do
6+
describe "#recalculate!" do
7+
subject { described_class.new(item).recalculate! }
8+
9+
let!(:item) { create :line_item, adjustments: }
10+
11+
let(:tax_rate) { create(:tax_rate) }
12+
13+
let(:arbitrary_adjustment) { create :adjustment, amount: 1, source: nil }
14+
let(:included_tax_adjustment) { create :adjustment, amount: 2, source: tax_rate, included: true }
15+
let(:additional_tax_adjustment) { create :adjustment, amount: 3, source: tax_rate, included: false }
16+
17+
context "with multiple types of adjustments" do
18+
let(:marked_for_destruction_included_tax_adjustment) { create(:adjustment, amount: 5, source: tax_rate, included: true) }
19+
let(:marked_for_destruction_additional_tax_adjustment) { create(:adjustment, amount: 7, source: tax_rate, included: false) }
20+
21+
let(:adjustments) {
22+
[
23+
arbitrary_adjustment,
24+
included_tax_adjustment,
25+
additional_tax_adjustment,
26+
marked_for_destruction_included_tax_adjustment,
27+
marked_for_destruction_additional_tax_adjustment
28+
]
29+
}
30+
31+
before do
32+
[marked_for_destruction_included_tax_adjustment, marked_for_destruction_additional_tax_adjustment]
33+
.each(&:mark_for_destruction)
34+
end
35+
36+
it "updates item totals" do
37+
expect {
38+
subject
39+
}.to change(item, :adjustment_total).from(0).to(4).
40+
and change { item.included_tax_total }.from(0).to(2).
41+
and change { item.additional_tax_total }.from(0).to(3)
42+
end
43+
end
44+
45+
context "with only an arbitrary adjustment" do
46+
let(:adjustments) { [arbitrary_adjustment] }
47+
48+
it "updates the adjustment total" do
49+
expect {
50+
subject
51+
}.to change { item.adjustment_total }.from(0).to(1)
52+
end
53+
end
54+
55+
context "with only tax adjustments" do
56+
let(:adjustments) { [included_tax_adjustment, additional_tax_adjustment] }
57+
58+
it "updates the adjustment total" do
59+
expect {
60+
subject
61+
}.to change { item.adjustment_total }.from(0).to(3).
62+
and change { item.included_tax_total }.from(0).to(2).
63+
and change { item.additional_tax_total }.from(0).to(3)
64+
end
65+
end
66+
end
67+
end

legacy_promotions/app/decorators/models/solidus_legacy_promotions/spree_order_updater_decorator.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ def update_adjustment_total
1717

1818
def update_item_totals
1919
[*line_items, *shipments].each do |item|
20+
Spree::ItemTotal.new(item).recalculate!
21+
2022
# The cancellation_total isn't persisted anywhere but is included in
21-
# the adjustment_total
23+
# the adjustment_total.
24+
#
25+
# Core doesn't have "eligible" adjustments anymore, so we need to
26+
# override the adjustment_total calculation to exclude them for legacy
27+
# promotions.
2228
item.adjustment_total = item.adjustments.
2329
select(&:eligible?).
2430
reject(&:included?).

0 commit comments

Comments
 (0)