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();
+	}
+}