Skip to content

Conversation

@Abdalsalaam
Copy link
Contributor

@Abdalsalaam Abdalsalaam commented Apr 19, 2023

Submission Review Guidelines:

Changes proposed in this Pull Request:

Closes #23796 .

How to test the changes in this Pull Request:

Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:

  1. Create a new order. Select a product as the line item, quantity one.
  2. Check the total_sales product meta (either via the product editor, or directly using a database tool of your choosing), which may look something like:
image
  1. Cancel the order, and the meta value should change to be one less (because, if the order was cancelled, the sale effectively did not take place).
image
  1. Restore to a completed status, and note that the meta value should have been adjusted upwards by one unit.
  2. Now mark the order as refunded. This time, the meta value should not change (refunds can happen for various reasons, and does not mean the sale did not take place).
  3. Trash the order. This is akin to stating the order was a mistake/should not have taken place so, in this case, we should see the total sales value being decremented by one.
  4. Restore the order. Total sales should increment by one.

@github-actions github-actions bot added plugin: woocommerce Issues related to the WooCommerce Core plugin. type: community contribution labels Apr 19, 2023
@woocommercebot woocommercebot requested review from a team and coreymckrill and removed request for a team April 19, 2023 14:29
@jorgeatorres jorgeatorres requested review from barryhughes and removed request for coreymckrill April 19, 2023 14:44
@codecov
Copy link

codecov bot commented Apr 19, 2023

Codecov Report

Merging #37842 (75f3d31) into trunk (34dd147) will decrease coverage by 0.0%.
The diff coverage is 56.2%.

❗ Current head 75f3d31 differs from pull request most recent head 9f6190c. Consider uploading reports for the commit 9f6190c to get more accurate results

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             trunk   #37842     +/-   ##
==========================================
- Coverage     51.6%    51.5%   -0.0%     
+ Complexity   17444    17280    -164     
==========================================
  Files          440      430     -10     
  Lines        80297    80039    -258     
==========================================
- Hits         41401    41230    -171     
+ Misses       38896    38809     -87     
Impacted Files Coverage Δ
...lugins/woocommerce/includes/wc-order-functions.php 74.5% <56.2%> (-0.8%) ⬇️

... and 51 files with indirect coverage changes

@barryhughes
Copy link
Member

Thanks for working on this, @Abdalsalaam.

However, on further review, we're not sure it makes sense to decrease this value when a sale is refunded, as it is used to measure the gross (not net) number of units sold. On the other hand, it does seem valid to reduce it on the following occasions:

  • If an order is deleted or cancelled (because in these cases, the sale is effectively annulled).
  • If an order is placed then subsequently one or more line item quantities are amended (as in most cases, it seems probable this would be the result of a mistake being made when the order was initially placed).

Thinking of some of the scenarios in the original issue (these are places where total_sales may be used):

  • Shortcode for displaying product with best_selling="1"
  • Sort by popularity on the product pages
  • Product widget when order by sales is selected

We fully appreciate different merchants may hold different opinions on this, but it does not seem a given that a product should not be regarded as best-selling, or unpopular, simply because it suffers a high number of refunds.

I hope that makes sense, but let me know if you have any questions or concerns about this (that is, our stance on refunds, and how they should or should not impact total_sales specifically).

Copy link
Member

@barryhughes barryhughes left a comment

Choose a reason for hiding this comment

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

Besides the feedback provided in relation to handling of refunds, it feels like there would be value in adding unit tests for this functionality. Are you comfortable adding some? If not, we may be able to help/provide guidance.

@barryhughes barryhughes added the needs: tests The issue/PR needs tests before it can move forward. label Apr 21, 2023
@Abdalsalaam Abdalsalaam requested a review from barryhughes April 24, 2023 14:16
Comment on lines 82 to 96
$order->update_status( 'processing' );
$this->assertEquals( 1, $product->get_total_sales() );

$order->update_status( 'cancelled' );
$this->assertEquals( 0, $product->get_total_sales() );

$order->update_status( 'processing' );
$this->assertEquals( 1, $product->get_total_sales() );

$order->update_status( 'completed' );
$this->assertEquals( 1, $product->get_total_sales() );

if ( $order->delete( true ) ) {
$this->assertEquals( 0, $product->get_total_sales() );
}
Copy link
Member

Choose a reason for hiding this comment

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

This is great, but can we add one or more assertions relating to refunds to show our design choices as they relate to that scenario?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@barryhughes I added 2 assertions to make sure that the transitions between refunded and processing statuses will not affect the sales count

@Abdalsalaam Abdalsalaam requested a review from barryhughes April 26, 2023 22:13
@barryhughes
Copy link
Member

Hi @Abdalsalaam!

I was doing a further pass and realized we didn't address anything relating to order deletion. For consistency, it would seem to me like the following should generally be true:

  • Trashing an order should act like a cancellation, and total_sales counts should be increased accordingly.
  • Un-trashing an order should have the opposite effect (just like when a cancelled order is set back to a completed status).

However, there are exceptions:

  • If it was already cancelled before being trashed, we probably would not want to make a further adjustment to any of the total_sales counts.
  • Similarly, if it is subsequently untrashed we wouldn't want to adjust total_sales if it is going back to a cancelled status.

With regards to permanent deletion, the following seems like a sensible policy:

  • If the order was already in the trash when it is permanently deleted, nothing more needs to happen (the total_sales adjustment will already have happened if it was needed).
  • However, if an order is permanently deleted without passing through the trash (ie, force deletion), then we would potentially need to make an adjustment to total_sales at this point. Of course, as in the earlier cases, it again would depend on the order's previous status.

What do you think?

@Abdalsalaam
Copy link
Contributor Author

Abdalsalaam commented Apr 29, 2023

Hi @Abdalsalaam!

I was doing a further pass and realized we didn't address anything relating to order deletion. For consistency, it would seem to me like the following should generally be true:

* Trashing an order should act like a cancellation, and `total_sales` counts should be increased accordingly.

* Un-trashing an order should have the opposite effect (just like when a cancelled order is set back to a completed status).

However, there are exceptions:

* If it was already cancelled before being trashed, we probably would not want to make a further adjustment to any of the `total_sales` counts.

* Similarly, if it is subsequently _untrashed_ we wouldn't want to adjust `total_sales` if it is going back to a cancelled status.

With regards to permanent deletion, the following seems like a sensible policy:

* If the order was already in the trash when it is permanently deleted, nothing more needs to happen (the `total_sales` adjustment will already have happened if it was needed).

* However, if an order is permanently deleted _without_ passing through the trash (ie, force deletion), then we would potentially need to make an adjustment to `total_sales` at this point. Of course, as in the earlier cases, it again would depend on the order's previous status.

What do you think?

@barryhughes How can we add tests for (untrash) scenario? wp_untrash_post function will work only with CPT

@barryhughes
Copy link
Member

wp_untrash_post function will work only with CPT

True!

However, we can work at a higher level of abstraction rather than directly with the WP Posts API. The WC_Object_Data_Store_Interface includes a delete() method, and this is implemented by both the CPT and HPOS data-stores (and it supports trashing as well as permanent deletion).

Does that help at all?

@Abdalsalaam
Copy link
Contributor Author

@barryhughes yes that helps, i'll check what i can do! thank you

@barryhughes
Copy link
Member

Just a friendly note that code freeze for the 7.8.0 release happens on Monday next week (2023-05-22).

This means that, realistically, if we want to include this fix in the 7.8.0 release then the PR will need to be updated and ready for a fresh review early this week (of course, it can also wait until some future point—I just wanted to give you an idea of the timelines we are working to re 7.8.0).

@Abdalsalaam
Copy link
Contributor Author

@barryhughes thanks for letting me know, I need a help on writing the test using WC_Object_Data_Store_Interface, is there an example or documented code? I couldn't find a way copatile with HPOS & CPT to "untrash" the order.

@barryhughes
Copy link
Member

Hi @Abdalsalaam,

There's a good example over here that does this by calling the order's delete method:

$order    = OrderHelper::create_order();
$order_id = $order->get_id();
$order->delete( true );

Some levels below that, the data-store's delete method is invoked:

$this->data_store->delete( $this, array( 'force_delete' => $force_delete ) );

In the test I linked to, we explicitly enable the HPOS data-store however, for this PR, i don't think that step is strictly necessary, as we already have a CI process that runs all unit tests against HPOS ("Run tests against PR in an environment with COT enabled").

@barryhughes
Copy link
Member

Apologies, I misread your question. For untrashing, this test may be a better example. You should be able to get the underlying data-store from an order object as follows:

$order->get_data_store();

@Abdalsalaam
Copy link
Contributor Author

@barryhughes I added a test for CPT

if ( $order->delete( false ) ) {
$this->assertEquals( 0, $product->get_total_sales() );

wp_untrash_post( $order->get_id() );
Copy link
Member

Choose a reason for hiding this comment

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

This won't work when running the test suite with HPOS enabled, but I see we have a gap in our implementation—bear with me while I think about addressing that.

Copy link
Member

Choose a reason for hiding this comment

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

With the latest changes in trunk, it is now possible to do $order->untrash() (datastore-agnostic untrashing, instead of relying on WP functions) which should allow us to make this test work even when HPOS is enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@barryhughes Great! thank you for letting me know.

@github-actions
Copy link
Contributor

Hi ,

Apart from reviewing the code changes, please make sure to review the testing instructions as well.

You can follow this guide to find out what good testing instructions should look like:
https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions

Copy link
Member

@barryhughes barryhughes left a comment

Choose a reason for hiding this comment

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

Great, thanks again! We need just a couple more tweaks to ensure our checks pass.

@Abdalsalaam
Copy link
Contributor Author

@barryhughes thanks a lot for your review! that was very helpful!

@barryhughes
Copy link
Member

With the most recent rebase (done to incorporate #39136), all checks are passing except for building the live branch (expected in this context).

@barryhughes
Copy link
Member

Testing instructions updated to cover some of the key evolutions in how this works.

Copy link
Member

@barryhughes barryhughes left a comment

Choose a reason for hiding this comment

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

Thanks for all the time you put into this, @Abdalsalaam. It's now good to merge.

@barryhughes barryhughes merged commit 5417750 into woocommerce:trunk Jul 7, 2023
@github-actions github-actions bot added this to the 8.0.0 milestone Jul 7, 2023
@Abdalsalaam
Copy link
Contributor Author

@barryhughes thank you for your feedback and help on it

@Abdalsalaam Abdalsalaam deleted the fix/issue-23796 branch July 7, 2023 15:03
@Konamiman
Copy link
Contributor

For the record, the code in this pull request incorrectly uses the before_delete_post hook, which doesn't work in the context of HPOS; it should use woocommerce_before_delete_order instead.

However by the time this pull request was merged Abstract_WC_Order_Data_Store_CPT::delete wasn't firing the woocommerce_trash/delete_order and woocommerce_before_trash/delete_order hooks, so strictly speaking it should have used both of before_delete_post and woocommerce_before_delete_order, depending on whether HPOS was active or not.

All of this is fixed as part of #37050: it introduces the firing of woocommerce_trash/delete_order and woocommerce_before_trash/delete_order hooks as part of Abstract_WC_Order_Data_Store_CPT::delete (see 77af853), and it hooks to woocommerce_before_delete_order instead of before_delete_post (see 387c91c).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs: tests The issue/PR needs tests before it can move forward. plugin: woocommerce Issues related to the WooCommerce Core plugin. type: community contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decrease total sales per product when an order is reversed

4 participants