Skip to content
This repository was archived by the owner on May 21, 2025. It is now read-only.

Removing wc-checkout-draft status from the get related order IDs method #626

Merged

Conversation

wjrosa
Copy link
Contributor

@wjrosa wjrosa commented May 22, 2024

Fixes 4181-gh-woocommerce/woocommerce-subscriptions

Description

If a user upgrades their Subscription multiple times in a single month, the prices appear incorrectly calculated. This is happening since the introduction of the new order status: wc-checkout-draft (it was introduced with the blocks theme, more information here).

The reason was that when calculating the switch fee, we were retrieving the last switch order, and that was including the current draft order (from the current checkout).

The fix filters the new status from the main method (get_related_order_ids).

How to test this PR

  • Create a variable subscription product with three variations. All should have the same matching billing period.
  • Subscribe at the least expensive variation
  • Upgrade to the second-most expensive variation (this prorated sign-up fee is calculated correctly)
  • Upgrade to the most expensive variation
  • On trunk, this signup fee will be calculated incorrectly
  • On this branch the correct value should be displayed

Product impact

  • Added changelog entry (or does not apply)
  • Will this PR affect WooCommerce Subscriptions? yes
  • Will this PR affect WooCommerce Payments? yes

@wjrosa wjrosa self-assigned this May 23, 2024
@wjrosa wjrosa requested a review from mattallan May 23, 2024 14:53
@mattallan
Copy link
Contributor

This is happening since the introduction of the new order status: wc-checkout-draft (it was introduced with the blocks theme, more information here).

I haven't looked at the code or tested this yet but I had a general question. Does this mean the bug is only reproducible with the block Checkout and not when using the shortcode checkout?

@wjrosa
Copy link
Contributor Author

wjrosa commented May 24, 2024

This is happening since the introduction of the new order status: wc-checkout-draft (it was introduced with the blocks theme, more information here).

I haven't looked at the code or tested this yet but I had a general question. Does this mean the bug is only reproducible with the block Checkout and not when using the shortcode checkout?

Hi Matt! For this part of the fix, yes. Still it would not be enough to fix the main issue (without the other PR).

@wjrosa wjrosa requested a review from james-allan June 6, 2024 17:21
@james-allan james-allan removed the request for review from mattallan June 13, 2024 03:48
Copy link
Contributor

@james-allan james-allan left a comment

Choose a reason for hiding this comment

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

@wjrosa I think this approach has some issues with it. I left a comment inline about why I think excluding the wc-checkout-draft status from this function may be problematic.

I'm curious though. In the PR description you mention this:

The reason was that when calculating the switch fee, we were retrieving the last switch order, and that was including the current draft order (from the current checkout).

I wonder if we just need to exclude draft switch orders at that point, not from the cache.

@wjrosa
Copy link
Contributor Author

wjrosa commented Jun 13, 2024

@wjrosa I think this approach has some issues with it. I left a comment inline about why I think excluding the wc-checkout-draft status from this function may be problematic.

I'm curious though. In the PR description you mention this:

The reason was that when calculating the switch fee, we were retrieving the last switch order, and that was including the current draft order (from the current checkout).

I wonder if we just need to exclude draft switch orders at that point, not from the cache.

Oh Ok, James! I understand your concern. I decided to include a new param $include_draft in these functions to keep them working just like before. On the subscriptions repo, I will open another PR changing only the get_last_switch_order method to pass false to this new param. What do you think? Draft PR is here

@wjrosa wjrosa requested a review from james-allan June 13, 2024 18:07
@james-allan
Copy link
Contributor

@wjrosa I don't think will work as intended. If you run this code snippet for example, it will return the newly created renewal order even though it has the checkout draft status.

$subscription = wcs_get_subscription( 7634 );

$renewal_order = wcs_create_renewal_order( $subscription );
$renewal_order->set_status( 'wc-checkout-draft' );
$renewal_order->save();

echo $subscription->get_last_order( 'all', 'renewal', false );

This is because this PR introduces a new arg and then passes that arg all the way down to the cache level.

WC_Subscription::get_last_order()
   ↳ WC_Subscription::get_related_order_ids()
      ↳ WCS_Related_Order_Store::get_related_order_ids()
         ↳ parent::get_related_order_ids()

The problem is that WCS_Related_Order_Store::get_related_order_ids() returns the values in the cache if they exists (ie this whole section of the function, including your change, is bypassed). The parent::get_related_order_ids() function is only called to populate the cache.

So, because our cache is already populated, parent::get_related_order_ids() is not called and the status filter isn't applied.

I'd recommend that we don't pass this arg that far down. IMO WCS_Related_Order_Store should be left unchanged. It is responsible for maintaining the cache of raw related order IDs - unfettered and unfiltered. Code that sits on top of the cache and utilises those values can do whatever they want to filter the result as they see fit though. So, you could pass it down to the WC_Subscription::get_related_order_ids() function, however that function only deals with IDs and I don't think we want to change that by fetching subscription objects for performance reasons - that's also more the responsibility of get_related_orders().

With all of that in mind, I don't think we should really pass this parameter down past get_last_order(). I think get_last_order() could just loop over the results after the array is sorted and then find the first order that matches the criteria.

Also, what do you think about broadening this param? Something like $exclude_statuses = [] will help make it a bit more usable for the longer term if we ever needed to find the last paid order we could do $subscription->get_last_order( 'id', 'renewal', [ 'pending', 'failed' ] )

Let me know if that makes sense. 😅 I'm happy to work on an example.

@wjrosa
Copy link
Contributor Author

wjrosa commented Jun 14, 2024

@wjrosa I don't think will work as intended. If you run this code snippet for example, it will return the newly created renewal order even though it has the checkout draft status.

$subscription = wcs_get_subscription( 7634 );

$renewal_order = wcs_create_renewal_order( $subscription );
$renewal_order->set_status( 'wc-checkout-draft' );
$renewal_order->save();

echo $subscription->get_last_order( 'all', 'renewal', false );

This is because this PR introduces a new arg and then passes that arg all the way down to the cache level.

WC_Subscription::get_last_order()
   ↳ WC_Subscription::get_related_order_ids()
      ↳ WCS_Related_Order_Store::get_related_order_ids()
         ↳ parent::get_related_order_ids()

The problem is that WCS_Related_Order_Store::get_related_order_ids() returns the values in the cache if they exists (ie this whole section of the function, including your change, is bypassed). The parent::get_related_order_ids() function is only called to populate the cache.

So, because our cache is already populated, parent::get_related_order_ids() is not called and the status filter isn't applied.

I'd recommend that we don't pass this arg that far down. IMO WCS_Related_Order_Store should be left unchanged. It is responsible for maintaining the cache of raw related order IDs - unfettered and unfiltered. Code that sits on top of the cache and utilises those values can do whatever they want to filter the result as they see fit though. So, you could pass it down to the WC_Subscription::get_related_order_ids() function, however that function only deals with IDs and I don't think we want to change that by fetching subscription objects for performance reasons - that's also more the responsibility of get_related_orders().

With all of that in mind, I don't think we should really pass this parameter down past get_last_order(). I think get_last_order() could just loop over the results after the array is sorted and then find the first order that matches the criteria.

Also, what do you think about broadening this param? Something like $exclude_statuses = [] will help make it a bit more usable for the longer term if we ever needed to find the last paid order we could do $subscription->get_last_order( 'id', 'renewal', [ 'pending', 'failed' ] )

Let me know if that makes sense. 😅 I'm happy to work on an example.

Thanks for the detailed argument, James! It makes total sense to me. I have updated the main implementation following your suggestion. I will update the woocommerce-subscriptions PR now to make use of it.

Copy link
Contributor

@james-allan james-allan left a comment

Choose a reason for hiding this comment

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

This looks pretty good @wjrosa, I've just left a couple of comments.

Co-authored-by: James Allan <[email protected]>
@wjrosa wjrosa requested a review from james-allan August 15, 2024 12:11
@wjrosa
Copy link
Contributor Author

wjrosa commented Aug 15, 2024

This looks pretty good @wjrosa, I've just left a couple of comments.

Thanks for the suggestions, James! I have accepted both and updated the branch with trunk.

@wjrosa wjrosa merged commit 687d500 into trunk Aug 16, 2024
9 checks passed
@wjrosa wjrosa deleted the fix/remove-new-draft-checkout-status-from-get-related-order-ids branch August 16, 2024 14:26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants