Commit 38131996bcb for woocommerce
commit 38131996bcb8b24e0eef5b9042318399b4b2c00d
Author: Taha Paksu <3295+tpaksu@users.noreply.github.com>
Date: Mon Mar 23 11:55:56 2026 +0300
[WOOPLUG-6354] Fix fulfillment email subject plural form for multiple items (#63765)
* [WOOPLUG-6354] fix: fulfillment email subject uses correct plural form for multiple items
The fulfillment created email always displayed "An item" in the subject
and "Your item is on the way!" in the heading regardless of item count.
Now uses plural forms when the fulfillment contains multiple items.
* Add changelog entry for fulfillment email plural fix
* Add changefile(s) from automation for the following project(s): woocommerce
* Remove manually created changelog file
* [WOOPLUG-6354] fix: count total item quantity instead of unique items for plural form
Sum qty values instead of counting unique fulfillment items so that
e.g. 2 qty of one product correctly triggers the plural subject/heading.
Adds tests for single-item-multiple-qty scenario and removes @since
from private method per WooCommerce conventions.
* [WOOPLUG-6354] fix: correct grammar in fulfillment email and bump template version
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
Co-authored-by: Ayush Pahwa <ayush.pahwa@automattic.com>
diff --git a/plugins/woocommerce/changelog/63765-wooplug-6354-order-fulfillments-email-subject-line-doesnt-use-correct b/plugins/woocommerce/changelog/63765-wooplug-6354-order-fulfillments-email-subject-line-doesnt-use-correct
new file mode 100644
index 00000000000..3ca1eaa79d9
--- /dev/null
+++ b/plugins/woocommerce/changelog/63765-wooplug-6354-order-fulfillments-email-subject-line-doesnt-use-correct
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix fulfillment created email subject and heading to use correct plural form when shipment contains multiple items.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/emails/class-wc-email-customer-fulfillment-created.php b/plugins/woocommerce/includes/emails/class-wc-email-customer-fulfillment-created.php
index e89608e86bf..b61fe72b72a 100644
--- a/plugins/woocommerce/includes/emails/class-wc-email-customer-fulfillment-created.php
+++ b/plugins/woocommerce/includes/emails/class-wc-email-customer-fulfillment-created.php
@@ -86,13 +86,36 @@ if ( ! class_exists( 'WC_Email_Customer_Fulfillment_Created', false ) ) :
$this->restore_locale();
}
+ /**
+ * Get the total quantity of items in the fulfillment.
+ *
+ * @return int
+ */
+ private function get_fulfillment_item_count() {
+ if ( ! $this->fulfillment ) {
+ return 1;
+ }
+
+ return array_reduce(
+ $this->fulfillment->get_items(),
+ function ( int $carry, array $item ) {
+ return $carry + (int) $item['qty'];
+ },
+ 0
+ );
+ }
+
/**
* Get email subject.
*
* @since 3.1.0
+ * @since 10.7.0 Added plural form for multi-item fulfillments.
* @return string
*/
public function get_default_subject() {
+ if ( $this->get_fulfillment_item_count() > 1 ) {
+ return __( 'Items from {site_title} order {order_number} have been fulfilled!', 'woocommerce' );
+ }
return __( 'An item from {site_title} order {order_number} has been fulfilled!', 'woocommerce' );
}
@@ -100,9 +123,13 @@ if ( ! class_exists( 'WC_Email_Customer_Fulfillment_Created', false ) ) :
* Get email heading.
*
* @since 3.1.0
+ * @since 10.7.0 Added plural form for multi-item fulfillments.
* @return string
*/
public function get_default_heading() {
+ if ( $this->get_fulfillment_item_count() > 1 ) {
+ return __( 'Your items are on the way!', 'woocommerce' );
+ }
return __( 'Your item is on the way!', 'woocommerce' );
}
diff --git a/plugins/woocommerce/templates/emails/email-fulfillment-details.php b/plugins/woocommerce/templates/emails/email-fulfillment-details.php
index fbdf081bc04..5a6bac3a604 100644
--- a/plugins/woocommerce/templates/emails/email-fulfillment-details.php
+++ b/plugins/woocommerce/templates/emails/email-fulfillment-details.php
@@ -12,7 +12,7 @@
*
* @see https://woocommerce.com/document/template-structure/
* @package WooCommerce\Templates\Emails
- * @version 10.1.0
+ * @version 10.7.0
*/
defined( 'ABSPATH' ) || exit;
@@ -39,7 +39,7 @@ if ( null === $fulfillment->get_date_deleted() ) {
echo wp_kses_post(
sprintf(
/* translators: %s: Link to My Account > Orders page. */
- __( 'You can access to more details of your order by visiting <a href="%s" target="_blank">My Account > Orders</a> and select the order you wish to see the latest status of the delivery.', 'woocommerce' ),
+ __( 'You can access more details of your order by visiting <a href="%s" target="_blank">My Account > Orders</a>, and selecting the order you wish to see the latest status for.', 'woocommerce' ),
site_url( 'my-account/orders/' )
)
);
diff --git a/plugins/woocommerce/tests/php/includes/emails/class-wc-email-customer-fulfillment-created-test.php b/plugins/woocommerce/tests/php/includes/emails/class-wc-email-customer-fulfillment-created-test.php
new file mode 100644
index 00000000000..3137faf4b9b
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/emails/class-wc-email-customer-fulfillment-created-test.php
@@ -0,0 +1,206 @@
+<?php
+declare( strict_types = 1 );
+
+use Automattic\WooCommerce\Admin\Features\Fulfillments\Fulfillment;
+
+/**
+ * WC_Email_Customer_Fulfillment_Created test.
+ *
+ * @covers WC_Email_Customer_Fulfillment_Created
+ */
+class WC_Email_Customer_Fulfillment_Created_Test extends \WC_Unit_Test_Case {
+
+ /**
+ * The System Under Test.
+ *
+ * @var WC_Email_Customer_Fulfillment_Created
+ */
+ private $sut;
+
+ /**
+ * Load up the email classes since they aren't loaded by default.
+ */
+ public function setUp(): void {
+ parent::setUp();
+
+ $bootstrap = \WC_Unit_Tests_Bootstrap::instance();
+ require_once $bootstrap->plugin_dir . '/includes/emails/class-wc-email.php';
+ require_once $bootstrap->plugin_dir . '/includes/emails/class-wc-email-customer-fulfillment-created.php';
+ }
+
+ /**
+ * @testdox Default subject uses singular form when fulfillment has one item.
+ */
+ public function test_default_subject_singular_for_one_item(): void {
+ $order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
+
+ $fulfillment = new Fulfillment();
+ $fulfillment->set_items(
+ array(
+ array(
+ 'item_id' => 1,
+ 'qty' => 1,
+ ),
+ )
+ );
+
+ $this->sut = new WC_Email_Customer_Fulfillment_Created();
+ $this->sut->trigger( $order->get_id(), $fulfillment, $order );
+
+ $subject = $this->sut->get_default_subject();
+
+ $this->assertStringContainsString( 'An item', $subject, 'Subject should use singular form for single item fulfillment' );
+ }
+
+ /**
+ * @testdox Default subject uses plural form when fulfillment has multiple items.
+ */
+ public function test_default_subject_plural_for_multiple_items(): void {
+ $order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
+
+ $fulfillment = new Fulfillment();
+ $fulfillment->set_items(
+ array(
+ array(
+ 'item_id' => 1,
+ 'qty' => 1,
+ ),
+ array(
+ 'item_id' => 2,
+ 'qty' => 1,
+ ),
+ )
+ );
+
+ $this->sut = new WC_Email_Customer_Fulfillment_Created();
+ $this->sut->trigger( $order->get_id(), $fulfillment, $order );
+
+ $subject = $this->sut->get_default_subject();
+
+ $this->assertStringContainsString( 'Items', $subject, 'Subject should use plural form for multi-item fulfillment' );
+ $this->assertStringNotContainsString( 'An item', $subject, 'Subject should not contain singular form for multi-item fulfillment' );
+ }
+
+ /**
+ * @testdox Default heading uses singular form when fulfillment has one item.
+ */
+ public function test_default_heading_singular_for_one_item(): void {
+ $order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
+
+ $fulfillment = new Fulfillment();
+ $fulfillment->set_items(
+ array(
+ array(
+ 'item_id' => 1,
+ 'qty' => 1,
+ ),
+ )
+ );
+
+ $this->sut = new WC_Email_Customer_Fulfillment_Created();
+ $this->sut->trigger( $order->get_id(), $fulfillment, $order );
+
+ $heading = $this->sut->get_default_heading();
+
+ $this->assertStringContainsString( 'Your item is on the way!', $heading, 'Heading should use singular form for single item fulfillment' );
+ }
+
+ /**
+ * @testdox Default heading uses plural form when fulfillment has multiple items.
+ */
+ public function test_default_heading_plural_for_multiple_items(): void {
+ $order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
+
+ $fulfillment = new Fulfillment();
+ $fulfillment->set_items(
+ array(
+ array(
+ 'item_id' => 1,
+ 'qty' => 1,
+ ),
+ array(
+ 'item_id' => 2,
+ 'qty' => 1,
+ ),
+ )
+ );
+
+ $this->sut = new WC_Email_Customer_Fulfillment_Created();
+ $this->sut->trigger( $order->get_id(), $fulfillment, $order );
+
+ $heading = $this->sut->get_default_heading();
+
+ $this->assertStringContainsString( 'Your items are on the way!', $heading, 'Heading should use plural form for multi-item fulfillment' );
+ }
+
+ /**
+ * @testdox Default subject uses plural form when one item has multiple quantity.
+ */
+ public function test_default_subject_plural_for_single_item_multiple_qty(): void {
+ $order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
+
+ $fulfillment = new Fulfillment();
+ $fulfillment->set_items(
+ array(
+ array(
+ 'item_id' => 1,
+ 'qty' => 3,
+ ),
+ )
+ );
+
+ $this->sut = new WC_Email_Customer_Fulfillment_Created();
+ $this->sut->trigger( $order->get_id(), $fulfillment, $order );
+
+ $subject = $this->sut->get_default_subject();
+
+ $this->assertStringContainsString( 'Items', $subject, 'Subject should use plural form when single item has qty > 1' );
+ $this->assertStringNotContainsString( 'An item', $subject, 'Subject should not contain singular form when single item has qty > 1' );
+ }
+
+ /**
+ * @testdox Default heading uses plural form when one item has multiple quantity.
+ */
+ public function test_default_heading_plural_for_single_item_multiple_qty(): void {
+ $order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
+
+ $fulfillment = new Fulfillment();
+ $fulfillment->set_items(
+ array(
+ array(
+ 'item_id' => 1,
+ 'qty' => 2,
+ ),
+ )
+ );
+
+ $this->sut = new WC_Email_Customer_Fulfillment_Created();
+ $this->sut->trigger( $order->get_id(), $fulfillment, $order );
+
+ $heading = $this->sut->get_default_heading();
+
+ $this->assertStringContainsString( 'Your items are on the way!', $heading, 'Heading should use plural form when single item has qty > 1' );
+ }
+
+ /**
+ * @testdox Default subject uses singular form when no fulfillment is set.
+ */
+ public function test_default_subject_singular_when_no_fulfillment(): void {
+ $this->sut = new WC_Email_Customer_Fulfillment_Created();
+
+ $subject = $this->sut->get_default_subject();
+
+ $this->assertStringContainsString( 'An item', $subject, 'Subject should default to singular form when no fulfillment is set' );
+ }
+
+ /**
+ * @testdox Default heading uses singular form when no fulfillment is set.
+ */
+ public function test_default_heading_singular_when_no_fulfillment(): void {
+ $this->sut = new WC_Email_Customer_Fulfillment_Created();
+
+ $heading = $this->sut->get_default_heading();
+
+ $this->assertStringContainsString( 'Your item is on the way!', $heading, 'Heading should default to singular form when no fulfillment is set' );
+ }
+}