Skip to content

Commit 987fd44

Browse files
committed
Move LoadPromotions out of OrderAdjuster namespace
Loading the currently available promotions can be quite useful outside of the order adjuster. This moves the LoadPromotions class out of there and into the main `SolidusPromotions` namespace so that it's clear that this class can be used elsewhere.
1 parent bbca5cc commit 987fd44

File tree

5 files changed

+149
-122
lines changed

5 files changed

+149
-122
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
module SolidusPromotions
4+
class LoadPromotions
5+
def initialize(order:, dry_run_promotion: nil)
6+
@order = order
7+
@dry_run_promotion = dry_run_promotion
8+
end
9+
10+
def call
11+
promos = connected_order_promotions | sale_promotions
12+
promos << dry_run_promotion if dry_run_promotion
13+
promos.flat_map(&:benefits).group_by(&:preload_relations).each do |benefit_preload_relations, benefits|
14+
preload(records: benefits, associations: benefit_preload_relations)
15+
benefits.flat_map(&:conditions).group_by(&:preload_relations).each do |condition_preload_relations, conditions|
16+
preload(records: conditions, associations: condition_preload_relations)
17+
end
18+
end
19+
promos.reject { |promotion| promotion.usage_limit_exceeded?(excluded_orders: [order]) }
20+
end
21+
22+
private
23+
24+
attr_reader :order, :dry_run_promotion
25+
26+
def preload(records:, associations:)
27+
ActiveRecord::Associations::Preloader.new(records: records, associations: associations).call
28+
end
29+
30+
def connected_order_promotions
31+
eligible_connected_promotion_ids = order.solidus_order_promotions.select do |order_promotion|
32+
order_promotion.promotion.kept? && (order_promotion.promotion_code.nil? || !order_promotion.promotion_code.usage_limit_exceeded?(excluded_orders: [order]))
33+
end.map(&:promotion_id)
34+
order.solidus_promotions.active(reference_time).where(id: eligible_connected_promotion_ids).includes(promotion_includes)
35+
end
36+
37+
def sale_promotions
38+
SolidusPromotions::Promotion.kept.where(apply_automatically: true).active(reference_time).includes(promotion_includes)
39+
end
40+
41+
def reference_time
42+
order.completed_at || Time.current
43+
end
44+
45+
def promotion_includes
46+
{
47+
benefits: :conditions
48+
}
49+
end
50+
end
51+
end

promotions/app/models/solidus_promotions/order_adjuster.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ class OrderAdjuster
77
def initialize(order, dry_run_promotion: nil)
88
@order = order
99
@dry_run = !!dry_run_promotion
10-
@promotions = LoadPromotions.new(order: order, dry_run_promotion: dry_run_promotion).call
10+
@promotions = SolidusPromotions::LoadPromotions.new(
11+
order: order,
12+
dry_run_promotion: dry_run_promotion
13+
).call
1114
end
1215

1316
def call

promotions/app/models/solidus_promotions/order_adjuster/load_promotions.rb

Lines changed: 4 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,10 @@
22

33
module SolidusPromotions
44
class OrderAdjuster
5-
class LoadPromotions
6-
def initialize(order:, dry_run_promotion: nil)
7-
@order = order
8-
@dry_run_promotion = dry_run_promotion
9-
end
10-
11-
def call
12-
promos = connected_order_promotions | sale_promotions
13-
promos << dry_run_promotion if dry_run_promotion
14-
promos.flat_map(&:benefits).group_by(&:preload_relations).each do |benefit_preload_relations, benefits|
15-
preload(records: benefits, associations: benefit_preload_relations)
16-
benefits.flat_map(&:conditions).group_by(&:preload_relations).each do |condition_preload_relations, conditions|
17-
preload(records: conditions, associations: condition_preload_relations)
18-
end
19-
end
20-
promos.reject { |promotion| promotion.usage_limit_exceeded?(excluded_orders: [order]) }
21-
end
22-
23-
private
24-
25-
attr_reader :order, :dry_run_promotion
26-
27-
def preload(records:, associations:)
28-
ActiveRecord::Associations::Preloader.new(records: records, associations: associations).call
29-
end
30-
31-
def connected_order_promotions
32-
eligible_connected_promotion_ids = order.solidus_order_promotions.select do |order_promotion|
33-
order_promotion.promotion.kept? && (order_promotion.promotion_code.nil? || !order_promotion.promotion_code.usage_limit_exceeded?(excluded_orders: [order]))
34-
end.map(&:promotion_id)
35-
order.solidus_promotions.active(reference_time).where(id: eligible_connected_promotion_ids).includes(promotion_includes)
36-
end
37-
38-
def sale_promotions
39-
SolidusPromotions::Promotion.kept.where(apply_automatically: true).active(reference_time).includes(promotion_includes)
40-
end
41-
42-
def reference_time
43-
order.completed_at || Time.current
44-
end
45-
46-
def promotion_includes
47-
{
48-
benefits: :conditions
49-
}
5+
class LoadPromotions < SolidusPromotions::LoadPromotions
6+
def initialize(...)
7+
Spree.deprecator.warn("Please use SolidusPromotions::LoadPromotions instead")
8+
super
509
end
5110
end
5211
end
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# frozen_string_literal: true
2+
3+
require "rails_helper"
4+
5+
RSpec.describe SolidusPromotions::LoadPromotions do
6+
describe "selecting promotions" do
7+
subject { described_class.new(order: order).call }
8+
9+
let(:order) { create(:order) }
10+
11+
let!(:active_promotion) { create(:solidus_promotion, :with_adjustable_benefit, apply_automatically: true) }
12+
let!(:inactive_promotion) do
13+
create(:solidus_promotion, :with_adjustable_benefit, expires_at: 2.days.ago, apply_automatically: true)
14+
end
15+
let!(:connectable_promotion) { create(:solidus_promotion, :with_adjustable_benefit) }
16+
let!(:connectable_inactive_promotion) do
17+
create(:solidus_promotion, :with_adjustable_benefit, expires_at: 2.days.ago)
18+
end
19+
20+
context "no promo is connected to the order" do
21+
it "returns only active promotions" do
22+
expect(subject).to eq([active_promotion])
23+
end
24+
end
25+
26+
context "an active promo is connected to the order" do
27+
before do
28+
order.solidus_promotions << connectable_promotion
29+
end
30+
31+
it "checks active and connected promotions" do
32+
expect(subject).to include(active_promotion, connectable_promotion)
33+
end
34+
end
35+
36+
context "an inactive promo is connected to the order" do
37+
before do
38+
order.solidus_promotions << connectable_inactive_promotion
39+
end
40+
41+
it "does not check connected inactive promotions" do
42+
expect(subject).not_to include(connectable_inactive_promotion)
43+
expect(subject).to eq([active_promotion])
44+
end
45+
end
46+
47+
context "discarded promotions" do
48+
let!(:discarded_promotion) { create(:solidus_promotion, :with_adjustable_benefit, deleted_at: 1.hour.ago, apply_automatically: true) }
49+
50+
it "does not check discarded promotions" do
51+
expect(subject).not_to include(discarded_promotion)
52+
end
53+
54+
context "a discarded promo is connected to the order" do
55+
before do
56+
order.solidus_promotions << discarded_promotion
57+
end
58+
59+
it "does not check connected discarded promotions" do
60+
expect(subject).not_to include(discarded_promotion)
61+
expect(subject).to eq([active_promotion])
62+
end
63+
end
64+
end
65+
end
66+
67+
context "promotions in the past" do
68+
let(:order) { create(:order, completed_at: 7.days.ago) }
69+
let(:currently_active_promotion) { create(:solidus_promotion, :with_adjustable_benefit, starts_at: 1.hour.ago) }
70+
let(:past_promotion) { create(:solidus_promotion, :with_adjustable_benefit, starts_at: 1.year.ago, expires_at: 11.months.ago) }
71+
let(:order_promotion) { create(:solidus_promotion, :with_adjustable_benefit, starts_at: 8.days.ago, expires_at: 6.days.ago) }
72+
73+
before do
74+
order.solidus_promotions << past_promotion
75+
order.solidus_promotions << order_promotion
76+
order.solidus_promotions << currently_active_promotion
77+
end
78+
79+
subject { described_class.new(order: order).call }
80+
81+
it "only evaluates the past promotion that was active when the order was completed" do
82+
expect(subject).to eq([order_promotion])
83+
end
84+
end
85+
end

promotions/spec/models/solidus_promotions/order_adjuster/load_promotions_spec.rb

Lines changed: 5 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,83 +3,12 @@
33
require "rails_helper"
44

55
RSpec.describe SolidusPromotions::OrderAdjuster::LoadPromotions do
6-
describe "selecting promotions" do
7-
subject { described_class.new(order: order).call }
6+
let(:order) { Spree::Order.new }
87

9-
let(:order) { create(:order) }
8+
subject { described_class.new(order:) }
109

11-
let!(:active_promotion) { create(:solidus_promotion, :with_adjustable_benefit, apply_automatically: true) }
12-
let!(:inactive_promotion) do
13-
create(:solidus_promotion, :with_adjustable_benefit, expires_at: 2.days.ago, apply_automatically: true)
14-
end
15-
let!(:connectable_promotion) { create(:solidus_promotion, :with_adjustable_benefit) }
16-
let!(:connectable_inactive_promotion) do
17-
create(:solidus_promotion, :with_adjustable_benefit, expires_at: 2.days.ago)
18-
end
19-
20-
context "no promo is connected to the order" do
21-
it "returns only active promotions" do
22-
expect(subject).to eq([active_promotion])
23-
end
24-
end
25-
26-
context "an active promo is connected to the order" do
27-
before do
28-
order.solidus_promotions << connectable_promotion
29-
end
30-
31-
it "checks active and connected promotions" do
32-
expect(subject).to include(active_promotion, connectable_promotion)
33-
end
34-
end
35-
36-
context "an inactive promo is connected to the order" do
37-
before do
38-
order.solidus_promotions << connectable_inactive_promotion
39-
end
40-
41-
it "does not check connected inactive promotions" do
42-
expect(subject).not_to include(connectable_inactive_promotion)
43-
expect(subject).to eq([active_promotion])
44-
end
45-
end
46-
47-
context "discarded promotions" do
48-
let!(:discarded_promotion) { create(:solidus_promotion, :with_adjustable_benefit, deleted_at: 1.hour.ago, apply_automatically: true) }
49-
50-
it "does not check discarded promotions" do
51-
expect(subject).not_to include(discarded_promotion)
52-
end
53-
54-
context "a discarded promo is connected to the order" do
55-
before do
56-
order.solidus_promotions << discarded_promotion
57-
end
58-
59-
it "does not check connected discarded promotions" do
60-
expect(subject).not_to include(discarded_promotion)
61-
expect(subject).to eq([active_promotion])
62-
end
63-
end
64-
end
65-
end
66-
67-
context "promotions in the past" do
68-
let(:order) { create(:order, completed_at: 7.days.ago) }
69-
let(:currently_active_promotion) { create(:solidus_promotion, :with_adjustable_benefit, starts_at: 1.hour.ago) }
70-
let(:past_promotion) { create(:solidus_promotion, :with_adjustable_benefit, starts_at: 1.year.ago, expires_at: 11.months.ago) }
71-
let(:order_promotion) { create(:solidus_promotion, :with_adjustable_benefit, starts_at: 8.days.ago, expires_at: 6.days.ago) }
72-
73-
before do
74-
order.solidus_promotions << past_promotion
75-
order.solidus_promotions << order_promotion
76-
order.solidus_promotions << currently_active_promotion
77-
end
78-
79-
subject { described_class.new(order: order).call }
80-
81-
it "only evaluates the past promotion that was active when the order was completed" do
82-
expect(subject).to eq([order_promotion])
83-
end
10+
it "tells the user to use SolidusPromotions::LoadPromotions instead" do
11+
expect(Spree.deprecator).to receive(:warn).with("Please use SolidusPromotions::LoadPromotions instead")
12+
expect(subject).to be_a(SolidusPromotions::LoadPromotions)
8413
end
8514
end

0 commit comments

Comments
 (0)