Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions core/app/helpers/spree/core/controller_helpers/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,8 @@ def associate_user
end

def set_current_order
if spree_current_user && current_order
spree_current_user.orders.by_store(current_store).incomplete.where(frontend_viewable: true).where('id != ?', current_order.id).find_each do |order|
current_order.merge!(order, spree_current_user)
end
Spree::Config.mergeable_orders_finder_class.new(context: self).call.find_each do |order|
current_order.merge!(order, spree_current_user)
end
end

Expand Down
44 changes: 44 additions & 0 deletions core/app/models/spree/mergeable_orders_finder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module Spree
# Finds orders to merge when a user logs in.
#
# Configurable via {Spree::Config#mergeable_orders_finder_class}.
# Default behavior finds all incomplete orders from the same store.
#
# @example Custom finder for recent orders only
# class RecentOrdersFinder
# def initialize(context:)
# @user = context.spree_current_user
# @store = context.current_store
# @current_order = context.current_order
# end
#
# def call
# @user.orders.by_store(@store).incomplete
# .where.not(id: @current_order.id)
# .where('created_at > ?', 7.days.ago)
# end
# end
#
# Spree::Config.mergeable_orders_finder_class = RecentOrdersFinder
class MergeableOrdersFinder
# @param context [Object] an object that responds to spree_current_user,
# current_store, and current_order (typically a controller)
def initialize(context:)
@user = context.spree_current_user
@store = context.current_store
@current_order = context.current_order
end

# Returns orders that should be merged into the current order
#
# @return [ActiveRecord::Relation<Spree::Order>] incomplete orders from the
# same store
def call
return Spree::Order.none unless @user && @current_order

@user.orders.by_store(@store).incomplete.where(frontend_viewable: true).where.not(id: @current_order.id)
end
end
end
7 changes: 7 additions & 0 deletions core/lib/spree/app_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,13 @@ def default_pricing_options
# as Spree::OrderMerger.
class_name_attribute :order_merger_class, default: 'Spree::OrderMerger'

# Allows providing your own class for selecting which orders to merge.
#
# @!attribute [rw] mergeable_orders_finder_class
# @return [Class] a class with the same public interfaces as
# Spree::MergeableOrdersFinder.
class_name_attribute :mergeable_orders_finder_class, default: 'Spree::MergeableOrdersFinder'

# Allows providing your own class for adding default payments to a user's
# order from their "wallet".
#
Expand Down
4 changes: 4 additions & 0 deletions core/spec/lib/spree/app_configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
expect(prefs.promotions).to be_a Spree::Core::NullPromotionConfiguration
end

it 'uses mergeable orders finder class by default' do
expect(prefs.mergeable_orders_finder_class).to eq Spree::MergeableOrdersFinder
end

context "deprecated preferences" do
around do |example|
Spree.deprecator.silence do
Expand Down
56 changes: 56 additions & 0 deletions core/spec/models/spree/mergeable_orders_finder_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

require 'rails_helper'

module Spree
RSpec.describe MergeableOrdersFinder do
let(:user) { create(:user) }
let(:store) { create(:store) }
let(:current_order) { create(:order, user: user, store: store) }
let(:context) { double('context', spree_current_user: user, current_store: store, current_order: current_order) }
let(:subject) { Spree::MergeableOrdersFinder.new(context: context) }

describe '#call' do
let!(:incomplete_order1) { create(:order, user: user, store: store, state: 'cart') }
let!(:incomplete_order2) { create(:order, user: user, store: store, state: 'address') }
let!(:complete_order) { create(:order, user: user, store: store, state: 'complete').touch(:completed_at) }
let!(:other_store_order) { create(:order, user: user, state: 'cart') }

it 'returns incomplete orders from the same store' do
orders = subject.call
expect(orders).to include(incomplete_order1, incomplete_order2)
expect(orders).not_to include(current_order, complete_order, other_store_order)
end

context 'when user is nil' do
let(:context) { double('context', spree_current_user: nil, current_store: store, current_order: current_order) }

it 'returns empty relation' do
orders = subject.call
expect(orders).to be_empty
expect(orders).to eq(Spree::Order.none)
end
end

context 'when current_order is nil' do
let(:context) { double('context', spree_current_user: user, current_store: store, current_order: nil) }

it 'returns empty relation' do
orders = subject.call
expect(orders).to be_empty
expect(orders).to eq(Spree::Order.none)
end
end

context 'when both user and current_order are nil' do
let(:context) { double('context', spree_current_user: nil, current_store: store, current_order: nil) }

it 'returns empty relation' do
orders = subject.call
expect(orders).to be_empty
expect(orders).to eq(Spree::Order.none)
end
end
end
end
end
35 changes: 35 additions & 0 deletions core/spec/models/spree/order_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,41 @@ def merge!(other_order, user = nil)
expect(order1.merge!(order2, user)).to eq([order1, order2, user])
end
end

describe 'mergeable_orders_finder_class customization' do
let(:user) { create(:user) }
let(:store) { create(:store) }
let(:current_order) { create(:order, user: user, store: store) }
let(:context) { double('context', spree_current_user: user, current_store: store, current_order: current_order) }
let(:test_mergeable_orders_finder_class) do
Class.new do
def initialize(context:)
@user = context.spree_current_user
@store = context.current_store
@current_order = context.current_order
end

def call
@user.orders.by_store(@store).where.not(id: @current_order.id).where('created_at > ?', 7.days.ago)
end
end
end

before do
stub_spree_preferences(mergeable_orders_finder_class: test_mergeable_orders_finder_class)
end

subject(:finder) { Spree::Config.mergeable_orders_finder_class.new(context: context) }

it 'uses the configured mergeable orders finder' do
old_order = create(:order, user: user, store: store, created_at: 8.days.ago)
recent_order = create(:order, user: user, store: store, created_at: 3.days.ago)

orders = finder.call
expect(orders).to include(recent_order)
expect(orders).not_to include(old_order, current_order)
end
end
end

describe "#ensure_updated_shipments" do
Expand Down
Loading