Skip to content

Commit f882e10

Browse files
authored
Merge pull request #6384 from ThuktenSingye/feature/configurable-mergeable-orders-scope
Configurable Order Mergeable Scope
2 parents 4286a2a + d912145 commit f882e10

File tree

6 files changed

+148
-4
lines changed

6 files changed

+148
-4
lines changed

core/app/helpers/spree/core/controller_helpers/order.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,8 @@ def associate_user
4242
end
4343

4444
def set_current_order
45-
if spree_current_user && current_order
46-
spree_current_user.orders.by_store(current_store).incomplete.where(frontend_viewable: true).where('id != ?', current_order.id).find_each do |order|
47-
current_order.merge!(order, spree_current_user)
48-
end
45+
Spree::Config.mergeable_orders_finder_class.new(context: self).call.find_each do |order|
46+
current_order.merge!(order, spree_current_user)
4947
end
5048
end
5149

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# frozen_string_literal: true
2+
3+
module Spree
4+
# Finds orders to merge when a user logs in.
5+
#
6+
# Configurable via {Spree::Config#mergeable_orders_finder_class}.
7+
# Default behavior finds all incomplete orders from the same store.
8+
#
9+
# @example Custom finder for recent orders only
10+
# class RecentOrdersFinder
11+
# def initialize(context:)
12+
# @user = context.spree_current_user
13+
# @store = context.current_store
14+
# @current_order = context.current_order
15+
# end
16+
#
17+
# def call
18+
# @user.orders.by_store(@store).incomplete
19+
# .where.not(id: @current_order.id)
20+
# .where('created_at > ?', 7.days.ago)
21+
# end
22+
# end
23+
#
24+
# Spree::Config.mergeable_orders_finder_class = RecentOrdersFinder
25+
class MergeableOrdersFinder
26+
# @param context [Object] an object that responds to spree_current_user,
27+
# current_store, and current_order (typically a controller)
28+
def initialize(context:)
29+
@user = context.spree_current_user
30+
@store = context.current_store
31+
@current_order = context.current_order
32+
end
33+
34+
# Returns orders that should be merged into the current order
35+
#
36+
# @return [ActiveRecord::Relation<Spree::Order>] incomplete orders from the
37+
# same store
38+
def call
39+
return Spree::Order.none unless @user && @current_order
40+
41+
@user.orders.by_store(@store).incomplete.where(frontend_viewable: true).where.not(id: @current_order.id)
42+
end
43+
end
44+
end

core/lib/spree/app_configuration.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,13 @@ def default_pricing_options
409409
# as Spree::OrderMerger.
410410
class_name_attribute :order_merger_class, default: 'Spree::OrderMerger'
411411

412+
# Allows providing your own class for selecting which orders to merge.
413+
#
414+
# @!attribute [rw] mergeable_orders_finder_class
415+
# @return [Class] a class with the same public interfaces as
416+
# Spree::MergeableOrdersFinder.
417+
class_name_attribute :mergeable_orders_finder_class, default: 'Spree::MergeableOrdersFinder'
418+
412419
# Allows providing your own class for adding default payments to a user's
413420
# order from their "wallet".
414421
#

core/spec/lib/spree/app_configuration_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
expect(prefs.promotions).to be_a Spree::Core::NullPromotionConfiguration
5353
end
5454

55+
it 'uses mergeable orders finder class by default' do
56+
expect(prefs.mergeable_orders_finder_class).to eq Spree::MergeableOrdersFinder
57+
end
58+
5559
context "deprecated preferences" do
5660
around do |example|
5761
Spree.deprecator.silence do
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
module Spree
6+
RSpec.describe MergeableOrdersFinder do
7+
let(:user) { create(:user) }
8+
let(:store) { create(:store) }
9+
let(:current_order) { create(:order, user: user, store: store) }
10+
let(:context) { double('context', spree_current_user: user, current_store: store, current_order: current_order) }
11+
let(:subject) { Spree::MergeableOrdersFinder.new(context: context) }
12+
13+
describe '#call' do
14+
let!(:incomplete_order1) { create(:order, user: user, store: store, state: 'cart') }
15+
let!(:incomplete_order2) { create(:order, user: user, store: store, state: 'address') }
16+
let!(:complete_order) { create(:order, user: user, store: store, state: 'complete').touch(:completed_at) }
17+
let!(:other_store_order) { create(:order, user: user, state: 'cart') }
18+
19+
it 'returns incomplete orders from the same store' do
20+
orders = subject.call
21+
expect(orders).to include(incomplete_order1, incomplete_order2)
22+
expect(orders).not_to include(current_order, complete_order, other_store_order)
23+
end
24+
25+
context 'when user is nil' do
26+
let(:context) { double('context', spree_current_user: nil, current_store: store, current_order: current_order) }
27+
28+
it 'returns empty relation' do
29+
orders = subject.call
30+
expect(orders).to be_empty
31+
expect(orders).to eq(Spree::Order.none)
32+
end
33+
end
34+
35+
context 'when current_order is nil' do
36+
let(:context) { double('context', spree_current_user: user, current_store: store, current_order: nil) }
37+
38+
it 'returns empty relation' do
39+
orders = subject.call
40+
expect(orders).to be_empty
41+
expect(orders).to eq(Spree::Order.none)
42+
end
43+
end
44+
45+
context 'when both user and current_order are nil' do
46+
let(:context) { double('context', spree_current_user: nil, current_store: store, current_order: nil) }
47+
48+
it 'returns empty relation' do
49+
orders = subject.call
50+
expect(orders).to be_empty
51+
expect(orders).to eq(Spree::Order.none)
52+
end
53+
end
54+
end
55+
end
56+
end

core/spec/models/spree/order_spec.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,41 @@ def merge!(other_order, user = nil)
428428
expect(order1.merge!(order2, user)).to eq([order1, order2, user])
429429
end
430430
end
431+
432+
describe 'mergeable_orders_finder_class customization' do
433+
let(:user) { create(:user) }
434+
let(:store) { create(:store) }
435+
let(:current_order) { create(:order, user: user, store: store) }
436+
let(:context) { double('context', spree_current_user: user, current_store: store, current_order: current_order) }
437+
let(:test_mergeable_orders_finder_class) do
438+
Class.new do
439+
def initialize(context:)
440+
@user = context.spree_current_user
441+
@store = context.current_store
442+
@current_order = context.current_order
443+
end
444+
445+
def call
446+
@user.orders.by_store(@store).where.not(id: @current_order.id).where('created_at > ?', 7.days.ago)
447+
end
448+
end
449+
end
450+
451+
before do
452+
stub_spree_preferences(mergeable_orders_finder_class: test_mergeable_orders_finder_class)
453+
end
454+
455+
subject(:finder) { Spree::Config.mergeable_orders_finder_class.new(context: context) }
456+
457+
it 'uses the configured mergeable orders finder' do
458+
old_order = create(:order, user: user, store: store, created_at: 8.days.ago)
459+
recent_order = create(:order, user: user, store: store, created_at: 3.days.ago)
460+
461+
orders = finder.call
462+
expect(orders).to include(recent_order)
463+
expect(orders).not_to include(old_order, current_order)
464+
end
465+
end
431466
end
432467

433468
describe "#ensure_updated_shipments" do

0 commit comments

Comments
 (0)