core/app/models/spree/order_merger.rb
module Spree
class OrderMerger
attr_accessor :order
delegate :updater, to: :order
def initialize(order)
@order = order
end
def merge!(other_order, user = nil)
other_order.line_items.each do |other_order_line_item|
next unless other_order_line_item.currency == order.currency
current_line_item = find_matching_line_item(other_order_line_item)
handle_merge(current_line_item, other_order_line_item)
end
set_user(user)
persist_merge
# So that the destroy doesn't take out line items which may have been re-assigned
other_order.line_items.reload
other_order.destroy
end
# Compare the line item of the other order with mine.
# Make sure you allow any extensions to chime in on whether or
# not the extension-specific parts of the line item match
def find_matching_line_item(other_order_line_item)
order.line_items.detect do |my_li|
my_li.variant == other_order_line_item.variant &&
Spree::Dependencies.cart_compare_line_items_service.constantize.new.call(order: order,
line_item: my_li,
options: other_order_line_item.serializable_hash).value
end
end
def set_user(user = nil)
order.associate_user!(user) if !order.user && !user.blank?
end
# The idea is the end developer can choose to override the merge
# to their own choosing. Default is merge with errors.
def handle_merge(current_line_item, other_order_line_item)
if current_line_item
current_line_item.quantity += other_order_line_item.quantity
handle_error(current_line_item) unless current_line_item.save
else
other_order_line_item.order_id = order.id
other_order_line_item.adjustments.update_all(order_id: order.id)
handle_error(other_order_line_item) unless other_order_line_item.save
end
end
# Change the error messages as you choose.
def handle_error(line_item)
order.errors.add(:base, line_item.errors.full_messages)
end
def persist_merge
updater.update_item_count
updater.update_item_total
updater.persist_totals
end
end
end