Skip to content
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

Don't display inactive payment methods on frontend or backend #1801

Merged
merged 4 commits into from
Apr 10, 2017

Conversation

luukveenis
Copy link

@luukveenis luukveenis commented Mar 27, 2017

The issue

After upgrading to Solidus 2.1, I noticed that my inactive payment methods were being displayed on the payment page in checkout and the payments#new page in the admin. The issue was introduced in #1540 because the Spree::PaymentMethod.available scope used to filter payment methods by active, but the new available_to_users and available_to_admin scopes only look at their respective attributes on the payment method. The Spree::Order#available_payment_methods method wasn't updated to include the active scope accordingly. Similarly, the load_data method in the admin payment methods controller is missing the active scope.

While writing the failing spec for this, I noticed that the available_to_store scope was not behaving as expected. It returned self if the store did not have any payment methods, which presumably was expected to return the current scope, but actually returns the Spree::PaymentMethod class constant. This wasn't breaking specs because we chained other scopes that would return payment methods as expected.

The fix

I've added the active scope to #available_payment_methods and in #load_data to ensure we don't include inactive payment methods.

I also updated the available_to_store scope to raise an ArgumentError if the store provided is nil, and return all payment methods in the current scope if the store doesn't have any payment methods. I added specs that should catch this issue in the future.

scope :available_to_store, -> (store) { (store.present? && store.payment_methods.empty?) ? self : store.payment_methods }
scope :available_to_store, -> (store) do
raise ArgumentError, "You must provide a store" if store.nil?
(store.payment_methods.empty?) ? all : where(id: store.payment_method_ids)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use parentheses around a method call.
Omit parentheses for ternary conditions.

@@ -17,7 +17,10 @@ class PaymentMethod < Spree::Base
scope :active, -> { where(active: true) }
scope :available_to_users, -> { where(available_to_users: true) }
scope :available_to_admin, -> { where(available_to_admin: true) }
scope :available_to_store, -> (store) { (store.present? && store.payment_methods.empty?) ? self : store.payment_methods }
scope :available_to_store, -> (store) do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use spaces between -> and opening brace in lambda literals

@luukveenis luukveenis force-pushed the hide-inactive-payment-methods branch from 5eee81d to f400308 Compare March 27, 2017 16:53
scope :available_to_store, ->(store) do
raise ArgumentError, "You must provide a store" if store.nil?
store.payment_methods.empty? ? all : where(id: store.payment_method_ids)
end
Copy link
Contributor

@mtomov mtomov Mar 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole method is not very clearly written. It's sort of all in-reverse. I think it should be just:

scope :available_to_store, -> (store) { where(store: store) }

That should also work if store is nil.

The raise ArgumentError is not much improvement over the Undefined method payment_methods on Nil:Class that one would usually get.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback!

Unfortunately where(store: store) doesn't work, because the payment method <-> store association is a has_many :through.

The reason for the conditional is to keep the existing, or at least expected, behaviour where if the store doesn't have any payment methods, we return all of them instead of an empty collection.

I agree the ArgumentError is not a drastic improvement. I discussed this with @jhawthorn on Friday and would be okay just letting it error the usual way too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That won't quite work due to the nature of the relationship between stores and payment methods. It could be writen as:

scope :available_to_store, -> (store) { joins(:stores).merge(Spree::Store.where(id: store.id)) }

However, this would mean a divergence from how we typically handle store associations. Normally, if a store doesn't have anything associated to it we assume everything belongs to that store. I believe this decision was made a while back to help handle single store apps by ensuring everything continued to work without having to create the missing associations.

While I'm definitely not opposed to changing that behavior, it would require a larger amount of work and a conscious decision to have all single store apps require some additional configuration behind the scenes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely you are both right, I did not see it through, and I am not that familiar with that part of the code. Thanks for the explanation!

Copy link
Contributor

@jhawthorn jhawthorn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Luuk

@jhawthorn jhawthorn added the type:bug Error, flaw or fault label Mar 27, 2017
@jhawthorn
Copy link
Contributor

This was a 2.1 regression, and I think we should backport it for a 2.1.1 release.

@jhawthorn jhawthorn added this to the 2.2.0 milestone Apr 5, 2017
@luukveenis luukveenis changed the title Don't include inactive payment methods in Spree::Order#available_payment_methods Don't display inactive payment methods on frontend or backend Apr 7, 2017
Copy link
Contributor

@jordan-brough jordan-brough left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find, thanks for the fixing this and writing this up!

One nitpick request for removing the nil check but very 👍 other than that.

@@ -17,7 +17,10 @@ class PaymentMethod < Spree::Base
scope :active, -> { where(active: true) }
scope :available_to_users, -> { where(available_to_users: true) }
scope :available_to_admin, -> { where(available_to_admin: true) }
scope :available_to_store, -> (store) { (store.present? && store.payment_methods.empty?) ? self : store.payment_methods }
scope :available_to_store, ->(store) do
raise ArgumentError, "You must provide a store" if store.nil?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the ArgumentError is not a drastic improvement. I discussed this with @jhawthorn on Friday and would be okay just letting it error the usual way too.

^ I agree with removing the nil check. The prior code didn't actually protect against nils and as @mtomov mentioned it doesn't really add much and we don't do it in most places

Copy link
Contributor

@jordan-brough jordan-brough left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing my "Changes Requested" to "Approved" because the nil check isn't a blocker IMO. Personally I'd prefer to see it removed though.

Copy link
Contributor

@mamhoff mamhoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @luukveenis for doing this. I'd like to have pending calls on the commits that introduce failing specs, and their respective removals in the commits that fix them. Otherwise, great! :)

@@ -538,6 +538,16 @@ def merge!(other_order, user = nil)
expect(order.available_payment_methods).to include(payment_method)
end

it "does not include inactive payment methods" do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec surely wants to have a pending call to make sure this commit doesn't break the suite, no?

@payment_method = create(:check_payment_method, available_to_admin: true)
end

it "loads the payment method" do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have pending calls for both failing specs in this commit?

Luuk Veenis added 4 commits April 10, 2017 12:37
I noticed that inactive payment methods were not being filtered out on
the frontend. This used to happen in `Spree::PaymentMethod.available`,
but in solidusio#1540 this filter was lost.
We use `Spree::Order#available_payment_methods` to determine which
payment methods should be displayed on the payment page in checkout. The
changes in solidusio#1540 accidentally dropped the scoping for active payment
methods, causing inactive payment methods to show up on the frontend.

This also required fixing the `available_to_store` scope, which behaved
incorrectly. Calling `self` from inside the scope returns the
`Spree::PaymentMethod` class itself and not the current scoping as the
code implied. This scope will now raise an error if the store parameter
is `nil`, return all payment methods in the current scope if the store
has no payment methods, and return the payment methods for the store if
it does have some.
Payment methods available on the backend used to be scope to active
methods only. Unfortunately that functionality was lost in solidusio#1540 because
the `available_to_admin` scope isn't limited to active payment methods.
This was the original behaviour in Solidus < 2.1, but the active scope
got lost in solidusio#1540. Payment methods used to use the `available` filter
which was scoped to active payment methods only. The `available_to_admin`
scope isn't limited active payment methods, so we need to add it here.
@jhawthorn jhawthorn force-pushed the hide-inactive-payment-methods branch from 77fc23f to 34a50ad Compare April 10, 2017 19:54
@jhawthorn jhawthorn dismissed mamhoff’s stale review April 10, 2017 20:12

I've rebased to add :pending to the failing specs (and removed when fixed)

@jhawthorn jhawthorn merged commit d6a715a into solidusio:master Apr 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:bug Error, flaw or fault
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants