-
-
Notifications
You must be signed in to change notification settings - Fork 729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Customer balance frontoffice #6643
Customer balance frontoffice #6643
Conversation
aeaec4a
to
41a03ef
Compare
41a03ef
to
c8f050c
Compare
4247051
to
cc908f7
Compare
9eb1110
to
6e915d1
Compare
6e915d1
to
23cdc86
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great - easy to understand and change if necessary in the future.
I only focused on reading/assessing the code, and didn't do any manual testing to confirm that the balances are still the same; I think this will be adequately covered by Filipe or another tester later in the pipe.
app/models/spree/order.rb
Outdated
# All the states of a complete order but that shouldn't count towards the balance. Those that | ||
# the customer won't enjoy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I know what you mean by the customer won't enjoy them? Not a big deal, obviously :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then it needs to be rephrased. It's a way to say that is a state that belongs after the order is placed but in which the customer won't have the products on their hand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd appreciate it if you guys could help me find a perfect name for these two constants and the scope below. Naming is hard, you know.
@@ -0,0 +1,40 @@ | |||
# frozen_string_literal: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this pattern of pulling queries into their own classes/files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an abstraction I'd like to introduce. I've seen it in the Rails community for a long time (see 4 in https://codeclimate.com/blog/7-ways-to-decompose-fat-activerecord-models/) and although I used it before, I've been using it a lot lately, and IMO it has lots of pros.
some examples:
|
||
customers = spree_current_user.customers | ||
@shops = Enterprise | ||
.where(id: @orders.pluck(:distributor_id).uniq | customers.pluck(:enterprise_id)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if distinct
could be used here instead of uniq
, if we're going for performance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I plan to create a separate issue for this whole data fetch (among other improvements I found along the way). Stay tuned.
app/models/spree/order.rb
Outdated
# the customer won't enjoy. | ||
NON_FULFILLED_STATES = %w(canceled returned).freeze | ||
|
||
scope :in_incomplete_state, -> { where(state: PRIOR_TO_COMPLETION_STATES) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we go for a simpler name here? We could have FULFILLED_STATES
and UNFULFILLED_STATES
and the scope could be unfulfilled
?
Also, you've only used this scope in one place where you explicitly use the negation of it (with not
), so maybe the opposite scope is more useful, eg: fulfilled
?
"Fulfilled" probably doesn't convey the right idea though. Naming things is hard... maybe finalized
/ unfinalized
is better than fulfilled
/ unfulfilled
?
FINALIZED_STATES = %w(complete canceled returned).freeze
UNFINALIZED_STATES = %w(cart address delivery payment).freeze
scope :finalized, -> { where(state: FINALIZED_STATES) }
scope :unfinalized, -> { where(state: UNFINALIZED_STATES) }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, that looks better. Thanks! we missed that FINALIZED_STATES
should NOT include complete
. I'll see what I can do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think finalized states should include complete though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scope you're trying to get (below) is "all the states that are not cart, address, delivery, payment", which is: complete, canceled, returned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not in_incomplete_state
== not unfinalized
== finalized
(including complete) 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see you want a scope that just includes only canceled or returned in some cases. Maybe that's a third scope?
I think semantically, if we have a scope called "finalized", it has to include the completed state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll find another name then. Yes, we don't want two opposite scopes. They are not semantically opposite.
|
||
def sorted_complete_orders | ||
@user.orders | ||
.where.not(Spree::Order.in_incomplete_state.where_values_hash) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we go with the finalized
/ unfinalized
scopes, this whole line can can just be .finalized
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try removing the double negation once I address the scope naming but where_values_hash is the way to get the WHERE from the underlying AR Relation object to pass them to #not
. Apparently, there's no way to make #not
and #merge
work together.
Data fetching is a controller action responsibility. We shouldn't couple the controller with it too much but it should trigger it, not the view-layer.
Instead of relying on Spree::Order#outstanding_balance we make us of the result set `balance_value` computed column. So, we ask PostgreSQL to compute it instead of Ruby and then serialize it from that computed column. That's a bit faster to compute that way and let's reuse logic. We hide this new implementation under this features' toggle so it's only used when enabled. We want hit the old behaviour by default.
We don't care about the conversion from hash to JSON (that's an ActiveModel::Serializer responsibility that is thoroughly tested) but our logic so we can skip that step which only slows down tests. It consistently reduced ~1.5s on my machine but it's still too slow to wait ~8.5s to get feedback from them.
It improves the overall readability of the code and as a result, things became easier to manage already.
This duplicates the scenarios tested for CustomersWithBalance.
This comment was related to the feature we removed in openfoodfoundation#3609.
cf89864
to
391c78b
Compare
Alright @Matt-Yorkley . I think I made sense of it all in the last commit. |
We rely now on the exhaustive list of states an order can be in after checkout. What made this all a bit more messy is that I made up the "checkout" order state, likely mixing it from the payment model states. This simplifies things quite a bit and gives meaningful names to things.
391c78b
to
cc9e3fe
Compare
@@ -9,6 +9,10 @@ | |||
# | |||
# 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering about awaiting_return
and returned
now that you've added them in the scope in Spree::Order
. Are they relevant here? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For awaiting_return the customer is still in posession of the product, so until returned, we still need to consider it in the balance.
So I take it as you approve @Matt-Yorkley . I want to hear from @filipefurtad0 |
Hey @sauloperez Feature toggle comes really handy for testing this 👍 I've checked a specific customer, which has orders on different hubs, before and after toggling this feature, for the customer and hubs - this needs to be done for both "sides" of the order. I went stepwise and checked all the cases in here. Everything looks fine, but something strikes me as being introduced by this PR, which I'm not sure it is intended: -> this PR seems to introduce the balance of cancelled orders, in the customer balance. This happens for both for the admin as well as for the customer, so the values match nicely. So, let's say:
Then the customer balance is zero as well - as reported on #6366. Now, let's toggle the feature for the customer only (right side, below): We can see that the value of the order appears on the balance, on the customer's side. If we now toggle it for the hub as well: We can see it appears for the hub as well. So there is agreement on both, but I'm not sure this is expected / desired. Since #6366 is well covered with specs, I focused here on our two missing test cases which relate to:
So, this looks good to go 👍 Customer balance matched between (admin) backoffice and (user) account, all the test cases. |
this is what I've been going after with this epic (apart from the perf. issue), and it was introduced in the former PR. But I'll leave it up to @RachL to approve. Essentially, what we add that makes the balance calculations right is (...)
CASE WHEN state IN ('canceled', 'returned') THEN payment_total
WHEN state IS NOT NULL THEN payment_total - total # any other finalized state
ELSE 0 END
AS balance
(...) |
@filipefurtad0 @sauloperez is it correct if I sum up in those terms:
I thing this is a pretty good improvement? Or am I missing something? |
I think I've missed it before, but yeah:
I think so as well! Thanks for feedback! |
You were right to do so. It's a crazy topic :D |
Don't tell me 😂 |
What? Why?
Closes #6694
This reuses the new implementation of customer balance, refactoring it slightly so it fits better for this use case. Fortunately, we didn't have to change the JS implementation. We only updated the server-side data fetching.
The git history should give you all the specific details on how I accomplished this.
What should we test?
The following feature spec covers this
openfoodnetwork/spec/features/consumer/account_spec.rb
Lines 35 to 77 in 334e270
Specifically, we need to check that the numbers match between back-office and front-office and balances in /accounts are right. Remember to do so with the feature toggle on and off.
Release notes
Use new order balance calculation in the front-office user account page but hidden under a feature toggle.
Changelog Category: User facing changes