Commit 14431d6daf6 for woocommerce
commit 14431d6daf63a4e8b906d8c95656e7fd230e9ac0
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date: Thu Sep 17 14:23:07 2026 +0300
Add stock notification settings and volume to the usage tracker (#68814)
Add stock notification settings and volume to the usage tracker snapshot
diff --git a/plugins/woocommerce/changelog/wooplug-4995-stock-notifications-tracker-data b/plugins/woocommerce/changelog/wooplug-4995-stock-notifications-tracker-data
new file mode 100644
index 00000000000..08e4333859a
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-4995-stock-notifications-tracker-data
@@ -0,0 +1,4 @@
+Significance: minor
+Type: enhancement
+
+Add customer stock notifications settings and notification counts to the WooCommerce usage tracker snapshot.
diff --git a/plugins/woocommerce/src/Internal/DataStores/StockNotifications/StockNotificationsDataStore.php b/plugins/woocommerce/src/Internal/DataStores/StockNotifications/StockNotificationsDataStore.php
index 3a79724c604..33b79561e17 100644
--- a/plugins/woocommerce/src/Internal/DataStores/StockNotifications/StockNotificationsDataStore.php
+++ b/plugins/woocommerce/src/Internal/DataStores/StockNotifications/StockNotificationsDataStore.php
@@ -627,4 +627,27 @@ CREATE TABLE $meta_table_name (
return $results;
}
+
+ /**
+ * Count the notifications of each status.
+ *
+ * @return array<string, int> Map of status to the number of notifications in it.
+ */
+ public function count_by_status(): array {
+ global $wpdb;
+
+ $table = $this->get_table_name();
+
+ $results = $wpdb->get_results(
+ "SELECT status, COUNT(id) AS total FROM $table GROUP BY status", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ ARRAY_A
+ );
+
+ $counts = array();
+ foreach ( (array) $results as $result ) {
+ $counts[ (string) $result['status'] ] = (int) $result['total'];
+ }
+
+ return $counts;
+ }
}
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/StockNotifications.php b/plugins/woocommerce/src/Internal/StockNotifications/StockNotifications.php
index b5640de6796..7509274cf85 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/StockNotifications.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/StockNotifications.php
@@ -120,6 +120,7 @@ class StockNotifications implements RegisterHooksInterface {
$container->get( PrivacyEraser::class );
$container->get( DataRetentionController::class );
$container->get( EmailActionController::class );
+ $container->get( Telemetry::class );
$container->get( ProductPageIntegration::class );
$container->get( FormHandlerService::class );
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Telemetry.php b/plugins/woocommerce/src/Internal/StockNotifications/Telemetry.php
new file mode 100644
index 00000000000..987e695e7a7
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Telemetry.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * Telemetry for the customer stock notifications feature.
+ */
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Internal\StockNotifications;
+
+use Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificationsDataStore;
+use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Tracker snapshot for the customer stock notifications feature.
+ */
+class Telemetry {
+
+ /**
+ * Constructor.
+ */
+ public function __construct() {
+ add_filter( 'woocommerce_tracker_data', array( $this, 'add_snapshot_to_tracker_data' ), 10, 1 );
+ }
+
+ /**
+ * Append the stock notifications snapshot to WC_Tracker's payload.
+ *
+ * Params are untyped and coerced in the body because any third-party callback
+ * can pass a different value along the filter chain.
+ *
+ * @internal
+ *
+ * @param mixed $data The aggregated tracker data.
+ * @return mixed
+ */
+ public function add_snapshot_to_tracker_data( $data ) {
+ if ( ! is_array( $data ) ) {
+ return $data;
+ }
+
+ $data['customer_stock_notifications'] = self::collect_snapshot();
+
+ return $data;
+ }
+
+ /**
+ * Collect the stock notifications snapshot fields.
+ *
+ * @return array<string, mixed>
+ */
+ public static function collect_snapshot(): array {
+ return array(
+ 'settings' => self::get_settings(),
+ 'notifications' => self::get_notification_counts(),
+ );
+ }
+
+ /**
+ * The feature's settings, as merchants configured them.
+ *
+ * @return array<string, mixed>
+ */
+ private static function get_settings(): array {
+ return array(
+ 'allow_signups' => Config::allows_signups() ? 'yes' : 'no',
+ 'require_double_opt_in' => Config::requires_double_opt_in() ? 'yes' : 'no',
+ 'require_account' => Config::requires_account() ? 'yes' : 'no',
+ 'unverified_deletions_days_threshold' => Config::get_unverified_deletion_days_threshold(),
+ 'hide_out_of_stock_items' => get_option( 'woocommerce_hide_out_of_stock_items', 'no' ),
+ );
+ }
+
+ /**
+ * The number of notifications in total and per status.
+ *
+ * @return array<string, int>
+ */
+ private static function get_notification_counts(): array {
+ $counts_by_status = wc_get_container()->get( StockNotificationsDataStore::class )->count_by_status();
+
+ $counts = array( 'total' => array_sum( $counts_by_status ) );
+ foreach ( NotificationStatus::get_valid_statuses() as $status ) {
+ $counts[ $status ] = $counts_by_status[ $status ] ?? 0;
+ }
+
+ return $counts;
+ }
+}
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/TelemetryTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/TelemetryTests.php
new file mode 100644
index 00000000000..668a7bdb8ff
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/TelemetryTests.php
@@ -0,0 +1,85 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\StockNotifications;
+
+use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
+use Automattic\WooCommerce\Internal\StockNotifications\Notification;
+use Automattic\WooCommerce\Internal\StockNotifications\Telemetry;
+
+/**
+ * Telemetry tests.
+ */
+class TelemetryTests extends \WC_Unit_Test_Case {
+
+ use StockNotificationsFeatureTrait;
+
+ /**
+ * Set up the test.
+ */
+ public function setUp(): void {
+ parent::setUp();
+ $this->enable_stock_notifications_feature();
+ }
+
+ /**
+ * Tear down the test.
+ */
+ public function tearDown(): void {
+ delete_option( 'woocommerce_customer_stock_notifications_allow_signups' );
+ delete_option( 'woocommerce_customer_stock_notifications_require_double_opt_in' );
+ delete_option( 'woocommerce_customer_stock_notifications_require_account' );
+ $this->restore_stock_notifications_feature_option();
+ parent::tearDown();
+ }
+
+ /**
+ * The snapshot reports the feature's settings as the merchant configured them.
+ */
+ public function test_snapshot_reports_settings() {
+ update_option( 'woocommerce_customer_stock_notifications_allow_signups', 'yes' );
+ update_option( 'woocommerce_customer_stock_notifications_require_double_opt_in', 'yes' );
+ update_option( 'woocommerce_customer_stock_notifications_require_account', 'no' );
+
+ $settings = Telemetry::collect_snapshot()['settings'];
+
+ $this->assertEquals( 'yes', $settings['allow_signups'] );
+ $this->assertEquals( 'yes', $settings['require_double_opt_in'] );
+ $this->assertEquals( 'no', $settings['require_account'] );
+ $this->assertArrayHasKey( 'unverified_deletions_days_threshold', $settings );
+ $this->assertArrayHasKey( 'hide_out_of_stock_items', $settings );
+ }
+
+ /**
+ * The snapshot counts the notifications in total and per status.
+ */
+ public function test_snapshot_reports_notification_counts() {
+ $this->create_notification( NotificationStatus::ACTIVE );
+ $this->create_notification( NotificationStatus::ACTIVE );
+ $this->create_notification( NotificationStatus::SENT );
+ $this->create_notification( NotificationStatus::CANCELLED );
+
+ $counts = Telemetry::collect_snapshot()['notifications'];
+
+ $this->assertEquals( 4, $counts['total'] );
+ $this->assertEquals( 2, $counts[ NotificationStatus::ACTIVE ] );
+ $this->assertEquals( 1, $counts[ NotificationStatus::SENT ] );
+ $this->assertEquals( 1, $counts[ NotificationStatus::CANCELLED ] );
+ $this->assertEquals( 0, $counts[ NotificationStatus::PENDING ] );
+ }
+
+ /**
+ * Create a saved notification in the given status.
+ *
+ * @param string $status The notification status.
+ * @return void
+ */
+ private function create_notification( string $status ): void {
+ $notification = new Notification();
+ $notification->set_product_id( 1 );
+ $notification->set_user_email( uniqid( 'shopper' ) . '@example.com' );
+ $notification->set_status( $status );
+ $notification->save();
+ }
+}