# frozen_string_literal: true # This service allows orders with eager-loaded adjustment objects to calculate various adjustment # types without triggering additional queries. # # For example; `order.adjustments.shipping.sum(:amount)` would normally trigger a new query # regardless of whether or not adjustments have been preloaded, as `#shipping` is an adjustment # scope, eg; `scope :shipping, where(originator_type: 'Spree::ShippingMethod')`. # # Here the adjustment scopes are moved to a shared module, and `adjustments.loaded?` is used to # check if the objects have already been fetched and initialized. If they have, `order.adjustments` # will be an Array, and we can select the required objects without hitting the database. If not, it # will fetch the adjustments via their scopes as normal. module Orders class FetchAdjustmentsService include AdjustmentScopes def initialize(order) @order = order end def admin_and_handling_total admin_and_handling_fees.map(&:amount).sum end def payment_fee sum_adjustments "payment_fee" end def ship_total sum_adjustments "shipping" end private attr_reader :order def adjustments order.all_adjustments end def adjustments_eager_loaded? adjustments.loaded? end def sum_adjustments(scope) collect_adjustments(scope).map(&:amount).sum end def collect_adjustments(scope) if adjustments_eager_loaded? adjustment_scope = public_send("#{scope}_scope") # Adjustments are already loaded here, this block is using `Array#select` adjustments.select do |adjustment| match_by_scope(adjustment, adjustment_scope) && match_by_scope(adjustment, eligible_scope) end else adjustments.where(nil).eligible.public_send scope end end def admin_and_handling_fees if adjustments_eager_loaded? adjustments.select do |adjustment| match_by_scope(adjustment, eligible_scope) && adjustment.originator_type == 'EnterpriseFee' && adjustment.adjustable_type != 'Spree::LineItem' end else adjustments.eligible. where("originator_type = ? AND adjustable_type != ?", 'EnterpriseFee', 'Spree::LineItem') end end def match_by_scope(adjustment, scope) adjustment.public_send(scope.keys.first) == scope.values.first end end end