-
-
Notifications
You must be signed in to change notification settings - Fork 729
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6643 from coopdevs/customer-balance-frontoffice
Customer balance frontoffice
- Loading branch information
Showing
13 changed files
with
471 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# frozen_string_literal: true | ||
|
||
# Fetches complete orders of the specified user including their balance as a computed column | ||
class CompleteOrdersWithBalance | ||
def initialize(user) | ||
@user = user | ||
end | ||
|
||
def query | ||
OutstandingBalance.new(sorted_finalized_orders).query | ||
end | ||
|
||
private | ||
|
||
def sorted_finalized_orders | ||
@user.orders | ||
.finalized | ||
.select('spree_orders.*') | ||
.order(completed_at: :desc) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# frozen_string_literal: true | ||
|
||
# Fetches the customers of the specified enterprise including the aggregated balance across the | ||
# customer's orders. That is, we get the total balance for each customer with this enterprise. | ||
class CustomersWithBalance | ||
def initialize(enterprise) | ||
@enterprise = enterprise | ||
end | ||
|
||
def query | ||
Customer.of(enterprise). | ||
joins(left_join_complete_orders). | ||
group("customers.id"). | ||
select("customers.*"). | ||
select(outstanding_balance_sum) | ||
end | ||
|
||
private | ||
|
||
attr_reader :enterprise | ||
|
||
def outstanding_balance_sum | ||
"SUM(#{OutstandingBalance.new.statement}) AS balance_value" | ||
end | ||
|
||
# The resulting orders are in states that belong after the checkout. Only these can be considered | ||
# for a customer's balance. | ||
def left_join_complete_orders | ||
<<-SQL.strip_heredoc | ||
LEFT JOIN spree_orders ON spree_orders.customer_id = customers.id | ||
AND #{finalized_states.to_sql} | ||
SQL | ||
end | ||
|
||
def finalized_states | ||
states = Spree::Order::FINALIZED_STATES.map { |state| Arel::Nodes.build_quoted(state) } | ||
Arel::Nodes::In.new(Spree::Order.arel_table[:state], states) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# 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 CompleteOrdersWithBalance or CustomersWithBalance as examples. | ||
class OutstandingBalance | ||
# 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 query | ||
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.strip_heredoc | ||
CASE WHEN state IN #{non_fulfilled_states_group.to_sql} THEN payment_total | ||
WHEN state IS NOT NULL THEN payment_total - 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'spec_helper' | ||
|
||
describe CompleteOrdersWithBalance do | ||
let(:complete_orders_with_balance) { described_class.new(user) } | ||
|
||
describe '#query' do | ||
let(:user) { order.user } | ||
let(:outstanding_balance) { instance_double(OutstandingBalance) } | ||
|
||
context 'when the user has complete orders' do | ||
let(:order) do | ||
create(:order, state: 'complete', total: 2.0, payment_total: 1.0, completed_at: 2.day.ago) | ||
end | ||
let!(:other_order) do | ||
create( | ||
:order, | ||
user: user, | ||
state: 'complete', | ||
total: 2.0, | ||
payment_total: 1.0, | ||
completed_at: 1.days.ago | ||
) | ||
end | ||
|
||
it 'calls OutstandingBalance#query' do | ||
allow(OutstandingBalance).to receive(:new).and_return(outstanding_balance) | ||
expect(outstanding_balance).to receive(:query) | ||
|
||
complete_orders_with_balance.query | ||
end | ||
|
||
it 'returns complete orders including their balance' do | ||
order = complete_orders_with_balance.query.first | ||
expect(order[:balance_value]).to eq(-1.0) | ||
end | ||
|
||
it 'sorts them by their completed_at with the most recent first' do | ||
orders = complete_orders_with_balance.query | ||
expect(orders.pluck(:id)).to eq([other_order.id, order.id]) | ||
end | ||
end | ||
|
||
context 'when the user has no complete orders' do | ||
let(:order) { create(:order) } | ||
|
||
it 'calls OutstandingBalance' do | ||
allow(OutstandingBalance).to receive(:new).and_return(outstanding_balance) | ||
expect(outstanding_balance).to receive(:query) | ||
|
||
complete_orders_with_balance.query | ||
end | ||
|
||
it 'returns an empty array' do | ||
order = complete_orders_with_balance.query | ||
expect(order).to be_empty | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.