# frozen_string_literal: true # Encapsulates the SQL statement that computes the balance of an order as a new column in the result # set. This can then be reused chaining it with the ActiveRecord::Relation objects you pass in the # constructor. # # Alternatively, you can get the SQL by calling #statement, which is suitable for more complex # cases. # # See CompleteOrdersWithBalanceQuery or CustomersWithBalanceQuery as examples. # # Note this query object and `app/models/concerns/balance.rb` should implement the same behavior # until we find a better way. If you change one, please, change the other too. class OutstandingBalanceQuery # All the states of a finished order but that shouldn't count towards the balance (the customer # didn't get the order for whatever reason). Note it does not include complete FINALIZED_NON_SUCCESSFUL_STATES = %w(canceled returned).freeze # The relation must be an ActiveRecord::Relation object with `spree_orders` in the SQL statement # FROM for #statement to work. def initialize(relation = nil) @relation = relation end def call relation.select("#{statement} AS balance_value") end # Arel doesn't support CASE statements until v7.1.0 so we'll have to wait with SQL literals # a little longer. See https://github.com/rails/arel/pull/400 for details. def statement <<~SQL.squish CASE WHEN "spree_orders"."state" IN #{non_fulfilled_states_group.to_sql} THEN "spree_orders"."payment_total" WHEN "spree_orders"."state" IS NOT NULL THEN "spree_orders"."payment_total" - "spree_orders"."total" ELSE 0 END SQL end private attr_reader :relation def non_fulfilled_states_group states = FINALIZED_NON_SUCCESSFUL_STATES.map { |state| Arel::Nodes.build_quoted(state) } Arel::Nodes::Grouping.new(states) end end