Commit a026c842dc7 for woocommerce

commit a026c842dc7cb67211f5d29d04e943d2503d4c3d
Author: Irfan Ömür <omur.irfan@gmail.com>
Date:   Tue Sep 29 22:41:04 2026 +0300

    Dispatch new order push notifications before the rest of the batch (#68356)

    * Dispatch new order push notifications before the rest of the batch

    * Add changelog entry

    * Update the since version of the new order sort to 11.3.0

diff --git a/plugins/woocommerce/changelog/fix-push-notification-order-first b/plugins/woocommerce/changelog/fix-push-notification-order-first
new file mode 100644
index 00000000000..a96b3003f65
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-push-notification-order-first
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Send the new order push notification before the stock ones queued by the same order, so devices play the new order sound.
diff --git a/plugins/woocommerce/src/Internal/PushNotifications/Services/PendingNotificationStore.php b/plugins/woocommerce/src/Internal/PushNotifications/Services/PendingNotificationStore.php
index aaf0b4521f0..bfd8e95e6d9 100644
--- a/plugins/woocommerce/src/Internal/PushNotifications/Services/PendingNotificationStore.php
+++ b/plugins/woocommerce/src/Internal/PushNotifications/Services/PendingNotificationStore.php
@@ -8,6 +8,7 @@ defined( 'ABSPATH' ) || exit;

 use Automattic\WooCommerce\Internal\PushNotifications\DataStores\PushTokensDataStore;
 use Automattic\WooCommerce\Internal\PushNotifications\Dispatchers\InternalNotificationDispatcher;
+use Automattic\WooCommerce\Internal\PushNotifications\Notifications\NewOrderNotification;
 use Automattic\WooCommerce\Internal\PushNotifications\Notifications\Notification;
 use Automattic\WooCommerce\Internal\PushNotifications\PushNotifications;
 use Automattic\WooCommerce\Internal\PushNotifications\Services\NotificationProcessor;
@@ -181,7 +182,7 @@ class PendingNotificationStore {

 		$this->record_trigger_times();

-		$this->dispatcher->dispatch( array_values( $this->pending ) );
+		$this->dispatcher->dispatch( $this->sort_with_new_orders_first( array_values( $this->pending ) ) );

 		$this->enabled = false;
 		$this->pending = array();
@@ -237,6 +238,33 @@ class PendingNotificationStore {
 		}
 	}

+	/**
+	 * Moves new order notifications to the front of the batch.
+	 *
+	 * Android only plays a sound for the first notification of a burst, and
+	 * stock is reduced before the order notification is queued, so the order
+	 * must go first for its sound to play.
+	 *
+	 * @param Notification[] $notifications The notifications about to be dispatched.
+	 * @return Notification[]
+	 *
+	 * @since 11.3.0
+	 */
+	private function sort_with_new_orders_first( array $notifications ): array {
+		$new_orders = array();
+		$others     = array();
+
+		foreach ( $notifications as $notification ) {
+			if ( NewOrderNotification::TYPE === $notification->get_type() ) {
+				$new_orders[] = $notification;
+			} else {
+				$others[] = $notification;
+			}
+		}
+
+		return array_merge( $new_orders, $others );
+	}
+
 	/**
 	 * Returns the number of pending notifications.
 	 *
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Services/PendingNotificationStoreTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Services/PendingNotificationStoreTest.php
index 528288b2c6a..53994eeaa92 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Services/PendingNotificationStoreTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Services/PendingNotificationStoreTest.php
@@ -291,6 +291,48 @@ class PendingNotificationStoreTest extends WC_Unit_Test_Case {
 		remove_action( 'shutdown', array( $store, 'dispatch_all' ) );
 	}

+	/**
+	 * @testdox Should dispatch new order notifications before the rest of the batch.
+	 */
+	public function test_dispatch_all_sends_new_orders_first(): void {
+		$dispatched = array();
+		$dispatcher = $this->createMock( InternalNotificationDispatcher::class );
+		$dispatcher->expects( $this->once() )
+			->method( 'dispatch' )
+			->willReturnCallback(
+				function ( array $notifications ) use ( &$dispatched ) {
+					$dispatched = $notifications;
+				}
+			);
+
+		$store = new PendingNotificationStore();
+		$store->init( $dispatcher, $this->create_data_store_with_tokens( true ) );
+		$store->register();
+		$store->add( $this->create_stock_mock( 42, StockNotification::EVENT_LOW_STOCK ) );
+		$store->add( $this->create_stock_mock( 43, StockNotification::EVENT_LOW_STOCK ) );
+		$store->add( $this->create_order_mock( 1 ) );
+
+		$store->dispatch_all();
+
+		remove_action( 'shutdown', array( $store, 'dispatch_all' ) );
+
+		$dispatched_order = array_map(
+			function ( $notification ) {
+				return $notification->get_type() . ':' . $notification->get_resource_id();
+			},
+			$dispatched
+		);
+
+		$this->assertSame(
+			array(
+				NewOrderNotification::TYPE . ':1',
+				StockNotification::TYPE . ':42',
+				StockNotification::TYPE . ':43',
+			),
+			$dispatched_order
+		);
+	}
+
 	/**
 	 * @testdox Should not call the dispatcher when there are no pending notifications.
 	 */