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

Commit e22ed7c

Browse files
author
Matt Allan
authored
Merge branch 'trunk' into fix/4678-query-args
2 parents 5715001 + e12381a commit e22ed7c

30 files changed

+540
-189
lines changed

changelog.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
*** WooCommerce Subscriptions Core Changelog ***
22

3-
= 8.1.0 - 2025-xx-xx =
3+
= 8.2.0 - 2025-xx-xx =
4+
* Update - Increase the number of args accepted by wcs_get_subscriptions(), to bring about parity with wc_get_orders().
5+
6+
= 8.1.0 - 2025-03-24 =
47
* Update - Improved subscription search performance for WP Post stores by removing unnecessary _order_key and _billing_email meta queries.
58
* Update - Make it possible to dispatch the Cancelled Subscription email more than once (when initially set to pending-cancellation, and again when it reaches final cancellation).
69
* Update - Reduced duplicate queries when fetching multiple subscription related orders types.
710
* Update - Removed unnecessary get_time() calls to reduce redundant get_last_order() queries in the Subscriptions list table.
811
* Update - Improved performance on the Orders list table when rendering the Subscription Relationship column.
9-
* Update - Increase the number of args accepted by wcs_get_subscriptions(), to bring about parity with wc_get_orders().
12+
* Update - Improved performance of the Generate Related Order Cache tool found under WooCommerce > Status > Tools.
13+
* Fix - Added support for previewing payment retry emails in WooCommerce email settings.
1014
* Fix - Updated subscription email item table template to align with WooCommerce 9.7 email improvements.
1115
* Fix - Prevent PHP warning on cart page shipping method updates by removing unused method: maybe_restore_shipping_methods.
16+
* Fix - Removed unnecessary setting of renewal order paid date on status transition, relying on WooCommerce core behavior instead.
1217
* Fix - Ensure the order_awaiting_payment session arg is restored when loading a renewal cart from the session to prevent duplicate orders.
18+
* Fix - Ensure custom placeholders (time_until_renewal, customers_first_name) are included in customer notification email previews.
19+
* Fix - For stores with HPOS + compatibility mode enabled, using the bulk delete related orders cache tool was not correctly deleting the meta from the WP Posts table.
20+
* Fix - Prevent empty strings being saved in related orders cache ID meta when backfilling order data to the WP Posts table.
21+
* Fix - Correctly load product names with HTML on the cart and checkout shipping rates.
1322
* Dev - Fix Node version mismatch between package.json and .nvmrc (both are now set to v16.17.1).
1423

1524
= 8.0.1 - 2025-02-13 =

includes/class-wc-subscriptions-core-plugin.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class WC_Subscriptions_Core_Plugin {
1616
* The version of subscriptions-core library.
1717
* @var string
1818
*/
19-
protected $library_version = '8.0.1'; // WRCS: DEFINED_VERSION.
19+
protected $library_version = '8.1.0'; // WRCS: DEFINED_VERSION.
2020

2121
/**
2222
* The subscription scheduler instance.

includes/class-wc-subscriptions-renewal-order.php

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,27 +96,9 @@ public static function maybe_record_subscription_payment( $order_id, $orders_old
9696
$order_needed_payment = in_array( $orders_old_status, apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'on-hold', 'failed' ), $order ) );
9797

9898
if ( $order_completed && $order_needed_payment ) {
99-
100-
if ( wcs_is_woocommerce_pre( '3.0' ) ) {
101-
$update_post_data = array(
102-
'ID' => $order_id,
103-
'post_date' => current_time( 'mysql', 0 ),
104-
'post_date_gmt' => current_time( 'mysql', 1 ),
105-
);
106-
107-
wp_update_post( $update_post_data );
108-
update_post_meta( $order_id, '_paid_date', current_time( 'mysql' ) );
109-
} else {
110-
111-
$current_time = current_time( 'timestamp', 1 );
112-
113-
// Prior to WC 3.0, we need to update the post date (i.e. the date created) to have a reliable representation of the paid date (both because it was in GMT and because it was always set). That's not needed in WC 3.0, but some plugins and store owners still rely on it being updated, so we want to make it possible to update it with 3.0 also.
114-
if ( apply_filters( 'wcs_renewal_order_payment_update_date_created', false, $order, $subscriptions ) ) {
115-
$order->set_date_created( $current_time );
116-
}
117-
118-
// In WC 3.0, only the paid date prop represents the paid date, the post date isn't used anymore, also the paid date is stored and referenced as a MySQL date string in site timezone and a GMT timestamp
119-
$order->set_date_paid( $current_time );
99+
// Prior to WC 3.0, we need to update the post date (i.e. the date created) to have a reliable representation of the paid date (both because it was in GMT and because it was always set). That's not needed in WC 3.0, but some plugins and store owners still rely on it being updated, so we want to make it possible to update it with 3.0 also.
100+
if ( apply_filters( 'wcs_renewal_order_payment_update_date_created', false, $order, $subscriptions ) ) {
101+
$order->set_date_created( time() );
120102
$order->save();
121103
}
122104
}

includes/class-wcs-cart-renewal.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,20 @@ protected function set_order_awaiting_payment( $order_id ) {
257257
if ( is_a( $order_id, 'WC_Abstract_Order' ) ) {
258258
$order = $order_id;
259259
$order_id = $order->get_id();
260+
} elseif ( ! empty( $order_id ) ) {
261+
$order = wc_get_order( $order_id );
262+
}
263+
264+
// Only ever set the order awaiting payment to 0 or an Order ID - not a subscription.
265+
if ( $order && ! wcs_is_order( $order ) ) {
266+
return;
260267
}
261268

262269
WC()->session->set( 'order_awaiting_payment', $order_id );
263270
WC()->session->set( 'store_api_draft_order', $order_id );
264271

265272
if ( $order_id ) {
266-
// To avoid needing to load the order object, pass it if available, otherwise pass the order ID.
267-
$this->set_cart_hash( $order ?? $order_id );
273+
$this->set_cart_hash( $order );
268274
}
269275
}
270276

@@ -1687,7 +1693,7 @@ public function restore_order_awaiting_payment( $cart ) {
16871693
}
16881694

16891695
// If the current user has permission to pay for the order, restore the order awaiting payment session arg.
1690-
if ( $this->validate_current_user( $order ) ) {
1696+
if ( wcs_is_order( $order ) && $this->validate_current_user( $order ) ) {
16911697
$this->set_order_awaiting_payment( $order );
16921698
}
16931699

includes/data-stores/class-wcs-orders-table-subscription-data-store.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,11 @@ public function delete_all_metadata_by_key( $meta_key ) {
872872
global $wpdb;
873873

874874
$wpdb->delete( self::get_meta_table_name(), [ 'meta_key' => $meta_key ], [ '%s' ] ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
875+
876+
// If custom order tables is enabled and data syncing is enabled, delete the meta from the custom order tables.
877+
if ( wcs_is_custom_order_tables_usage_enabled() && wcs_is_custom_order_tables_data_sync_enabled() ) {
878+
$this->get_cpt_data_store_instance()->delete_all_metadata_by_key( $meta_key );
879+
}
875880
}
876881

877882
/**

includes/data-stores/class-wcs-related-order-store-cached-cpt.php

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ protected function update_related_order_id_cache( $subscription, array $related_
306306
}
307307
}
308308

309-
$subscription_data_store = WC_Data_Store::load( 'subscription' );
309+
$subscription_data_store = $subscription->get_data_store();
310310
$current_metadata = $this->get_related_order_metadata( $subscription, $relation_type );
311311
$new_metadata = array(
312312
'key' => $this->get_cache_meta_key( $relation_type ),
@@ -318,16 +318,24 @@ protected function update_related_order_id_cache( $subscription, array $related_
318318
$this->update_modified_date_for_related_order_cache( $subscription, $related_order_ids, $current_metadata );
319319
}
320320

321-
// Check if HPOS and data syncing is enabled then manually backfill the related orders cache values to WP Posts table.
322-
$this->maybe_backfill_related_order_cache( $subscription, $relation_type, $new_metadata );
323-
324321
// If there is metadata for this key, update it, otherwise add it.
325322
if ( $current_metadata ) {
326323
$new_metadata['id'] = $current_metadata->meta_id;
327-
return $subscription_data_store->update_meta( $subscription, (object) $new_metadata );
324+
$return = $subscription_data_store->update_meta( $subscription, (object) $new_metadata );
328325
} else {
329-
return $subscription_data_store->add_meta( $subscription, (object) $new_metadata );
326+
$return = $subscription_data_store->add_meta( $subscription, (object) $new_metadata );
330327
}
328+
329+
/**
330+
* Trigger update actions after modifying the subscription's related order cache metadata.
331+
*
332+
* This ensures that functions fired after a subscription update, such as webhooks and those in the DataSynchronizer,
333+
* which sync CPT post data to HPOS tables, are executed.
334+
*/
335+
do_action( 'woocommerce_update_order', $subscription->get_id(), $subscription );
336+
do_action( 'woocommerce_update_subscription', $subscription->get_id(), $subscription );
337+
338+
return $return;
331339
}
332340

333341
/**
@@ -344,8 +352,12 @@ protected function update_related_order_id_cache( $subscription, array $related_
344352
* @param WC_Subscription $subscription The subscription object to backfill.
345353
* @param string $relation_type The related order relationship type. Can be 'renewal', 'switch' or 'resubscribe'.
346354
* @param array $metadata The metadata to set update/add in the CPT data store. Should be an array with 'key' and 'value' keys.
355+
*
356+
* @deprecated 7.3.0 - Backfilling is already handled by the Order/Subscriptions Data Store.
347357
*/
348358
protected function maybe_backfill_related_order_cache( $subscription, $relation_type, $metadata ) {
359+
wcs_deprecated_function( __METHOD__, '7.3.0' );
360+
349361
if ( ! wcs_is_custom_order_tables_usage_enabled() || ! wcs_is_custom_order_tables_data_sync_enabled() || empty( $metadata['key'] ) ) {
350362
return;
351363
}
@@ -389,7 +401,7 @@ public function delete_caches_for_subscription( $subscription, $relation_type =
389401
$metadata = $this->get_related_order_metadata( $subscription, $possible_relation_type );
390402

391403
if ( $metadata ) {
392-
WC_Data_Store::load( 'subscription' )->delete_meta( $subscription, (object) [ 'id' => $metadata->meta_id ] );
404+
$subscription->get_data_store()->delete_meta( $subscription, (object) [ 'id' => $metadata->meta_id ] );
393405
}
394406
}
395407
}
@@ -555,7 +567,7 @@ protected function get_subscription_ids_without_cache( $relation_types = array()
555567
$ids = wcs_get_orders_with_meta_query(
556568
[
557569
'limit' => $limit,
558-
'fields' => 'ids',
570+
'return' => 'ids',
559571
'orderby' => 'ID',
560572
'order' => 'ASC',
561573
'type' => 'shop_subscription',
@@ -633,12 +645,11 @@ public function get_items_to_update() {
633645
public function update_items_cache( $subscription_id ) {
634646
$subscription = wcs_get_subscription( $subscription_id );
635647

636-
if ( $subscription ) {
637-
foreach ( $this->get_relation_types() as $relation_type ) {
638-
// Getting the related IDs also sets the cache when it's not already set
639-
$this->get_related_order_ids( $subscription, $relation_type );
640-
}
648+
if ( ! $subscription ) {
649+
return;
641650
}
651+
652+
$this->get_related_order_ids_by_types( $subscription, $this->get_relation_types() );
642653
}
643654

644655
/**
@@ -659,7 +670,7 @@ public function delete_all_caches() {
659670
*/
660671
protected function get_related_order_metadata( WC_Subscription $subscription, $relation_type, $data_store = null ) {
661672
$cache_meta_key = $this->get_cache_meta_key( $relation_type );
662-
$data_store = empty( $data_store ) ? WC_Data_Store::load( 'subscription' ) : $data_store;
673+
$data_store = $data_store ?? $subscription->get_data_store();
663674

664675
foreach ( $this->get_subscription_meta( $subscription, $data_store ) as $meta ) {
665676
if ( isset( $meta->meta_key ) && $cache_meta_key === $meta->meta_key ) {
@@ -704,11 +715,12 @@ protected function update_modified_date_for_related_order_cache( $subscription,
704715
*/
705716
private function get_subscription_meta( WC_Subscription $subscription, $data_store ) {
706717
$subscription_id = $subscription->get_id();
707-
$is_batch_processing = isset( self::$batch_processing_related_orders[ $subscription_id ] );
718+
$cache_key = $this->get_batch_processing_cache_key( $subscription, $data_store );
719+
$is_batch_processing = $this->is_batch_processing( $cache_key );
708720

709-
// If we are in batch processing mode, return the cached meta data.
710-
if ( $is_batch_processing && isset( self::$subscription_meta_cache[ $subscription_id ] ) ) {
711-
return self::$subscription_meta_cache[ $subscription_id ];
721+
// If we are in batch processing mode, and there are cached results return the cached meta data.
722+
if ( $is_batch_processing && isset( self::$subscription_meta_cache[ $cache_key ] ) ) {
723+
return self::$subscription_meta_cache[ $cache_key ];
712724
}
713725

714726
/**
@@ -724,9 +736,9 @@ private function get_subscription_meta( WC_Subscription $subscription, $data_sto
724736
$subscription_meta = $data_store->read_meta( $subscription );
725737
self::$override_ignored_props = false;
726738

727-
// If we are in batch processing mode, cache the meta data.
739+
// If we are in batch processing mode, cache the meta data so it can be returned for subsequent calls.
728740
if ( $is_batch_processing ) {
729-
self::$subscription_meta_cache[ $subscription_id ] = $subscription_meta;
741+
self::$subscription_meta_cache[ $cache_key ] = $subscription_meta;
730742
}
731743

732744
return $subscription_meta;
@@ -750,16 +762,66 @@ public function get_related_order_ids_by_types( WC_Order $subscription, $related
750762
$related_order_ids = [];
751763

752764
// Declare batch processing mode for this subscription.
753-
self::$batch_processing_related_orders[ $subscription_id ] = true;
765+
$cache_key = $this->start_batch_processing_mode( $subscription );
754766

755767
foreach ( $related_order_types as $relation_type ) {
756768
$related_order_ids[ $relation_type ] = $this->get_related_order_ids( $subscription, $relation_type );
757769
}
758770

759-
// Unset the batch processing mode for this subscription.
760-
unset( self::$batch_processing_related_orders[ $subscription_id ] );
761-
unset( self::$subscription_meta_cache[ $subscription_id ] );
771+
$this->stop_batch_processing_mode( $cache_key );
762772

763773
return $related_order_ids;
764774
}
775+
776+
/**
777+
* Starts batch processing mode for a subscription.
778+
*
779+
* @param WC_Subscription $subscription The subscription to start batch processing mode for.
780+
* @return string The cache key for the subscription.
781+
*/
782+
private function start_batch_processing_mode( $subscription ) {
783+
$cache_key = $this->get_batch_processing_cache_key( $subscription );
784+
785+
self::$batch_processing_related_orders[ $cache_key ] = true;
786+
return $cache_key;
787+
}
788+
789+
/**
790+
* Stops batch processing mode for a subscription.
791+
*
792+
* Destroys the cache and removes the cache key.
793+
*
794+
* @param string $cache_key The batch processing cache key.
795+
*/
796+
private function stop_batch_processing_mode( $cache_key ) {
797+
unset( self::$batch_processing_related_orders[ $cache_key ] );
798+
unset( self::$subscription_meta_cache[ $cache_key ] );
799+
}
800+
801+
/**
802+
* Checks if batch processing mode is active for a subscription.
803+
*
804+
* @param string $cache_key The batch processing cache key.
805+
* @return bool True if batch processing mode is active, false otherwise.
806+
*/
807+
private function is_batch_processing( $cache_key ) {
808+
return isset( self::$batch_processing_related_orders[ $cache_key ] );
809+
}
810+
811+
/**
812+
* Gets the batch processing cache key for a subscription.
813+
*
814+
* The cache key is a unique combination of the subscription ID and the data store class name.
815+
*
816+
* @param WC_Subscription $subscription The subscription to get the cache key for.
817+
* @param bool|object $data_store The data store which will be used to read the subscription meta. Defaults to the current subscription's data store.
818+
*
819+
* @return string The cache key for the subscription.
820+
*/
821+
private function get_batch_processing_cache_key( $subscription, $data_store = null ) {
822+
// If no data store is provided, use the subscription object's data store or load the default data store if no subscription data store is found.
823+
$data_store = $data_store ?? $subscription->get_data_store() ?? WC_Data_Store::load( 'subscriptions' );
824+
$data_store_class = is_a( $data_store, 'WC_Data_Store' ) ? $data_store->get_current_class_name() : '';
825+
return $data_store_class . '-' . $subscription->get_id();
826+
}
765827
}

includes/data-stores/class-wcs-subscription-data-store-cpt.php

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ class WCS_Subscription_Data_Store_CPT extends WC_Order_Data_Store_CPT implements
7777
'_subscription_switch_order_ids_cache' => 'switch_order_ids_cache',
7878
);
7979

80+
/**
81+
* The data store instance for the custom order tables.
82+
*
83+
* @var WCS_Orders_Table_Subscription_Data_Store
84+
*/
85+
protected $orders_table_data_store;
86+
8087
/**
8188
* Constructor.
8289
*/
@@ -615,6 +622,11 @@ public function delete_all_metadata_by_key( $meta_key ) {
615622
$meta_value = null; // Delete any values.
616623
$delete_all = true;
617624
delete_metadata( 'post', $id, $meta_key, $meta_value, $delete_all );
625+
626+
// If custom order tables is not enabled, but Data Syncing is enabled, delete the meta from the custom order tables.
627+
if ( ! wcs_is_custom_order_tables_usage_enabled() && wcs_is_custom_order_tables_data_sync_enabled() ) {
628+
$this->get_cot_data_store_instance()->delete_all_metadata_by_key( $meta_key );
629+
}
618630
}
619631

620632
/**
@@ -715,7 +727,10 @@ public function set_schedule_payment_retry( $subscription, $date ) {
715727
*/
716728
public function set_renewal_order_ids_cache( $subscription, $renewal_order_ids ) {
717729
$this->cleanup_backfill_related_order_cache_duplicates( $subscription, 'renewal' );
718-
update_post_meta( $subscription->get_id(), '_subscription_renewal_order_ids_cache', $renewal_order_ids );
730+
731+
if ( '' !== $renewal_order_ids ) {
732+
update_post_meta( $subscription->get_id(), '_subscription_renewal_order_ids_cache', $renewal_order_ids );
733+
}
719734
}
720735

721736
/**
@@ -729,7 +744,10 @@ public function set_renewal_order_ids_cache( $subscription, $renewal_order_ids )
729744
*/
730745
public function set_resubscribe_order_ids_cache( $subscription, $resubscribe_order_ids ) {
731746
$this->cleanup_backfill_related_order_cache_duplicates( $subscription, 'resubscribe' );
732-
update_post_meta( $subscription->get_id(), '_subscription_resubscribe_order_ids_cache', $resubscribe_order_ids );
747+
748+
if ( '' !== $resubscribe_order_ids ) {
749+
update_post_meta( $subscription->get_id(), '_subscription_resubscribe_order_ids_cache', $resubscribe_order_ids );
750+
}
733751
}
734752

735753
/**
@@ -743,7 +761,10 @@ public function set_resubscribe_order_ids_cache( $subscription, $resubscribe_ord
743761
*/
744762
public function set_switch_order_ids_cache( $subscription, $switch_order_ids ) {
745763
$this->cleanup_backfill_related_order_cache_duplicates( $subscription, 'switch' );
746-
update_post_meta( $subscription->get_id(), '_subscription_switch_order_ids_cache', $switch_order_ids );
764+
765+
if ( '' !== $switch_order_ids ) {
766+
update_post_meta( $subscription->get_id(), '_subscription_switch_order_ids_cache', $switch_order_ids );
767+
}
747768
}
748769

749770
/**
@@ -767,4 +788,17 @@ private function cleanup_backfill_related_order_cache_duplicates( $subscription,
767788
delete_post_meta( $subscription->get_id(), "_subscription_{$relationship_type}_order_ids_cache" );
768789
}
769790
}
791+
792+
/**
793+
* Get the data store instance for Order Tables data store.
794+
*
795+
* @return WCS_Orders_Table_Subscription_Data_Store
796+
*/
797+
public function get_cot_data_store_instance() {
798+
if ( ! isset( $this->orders_table_data_store ) ) {
799+
$this->orders_table_data_store = new WCS_Orders_Table_Subscription_Data_Store();
800+
}
801+
802+
return $this->orders_table_data_store;
803+
}
770804
}

0 commit comments

Comments
 (0)