Commit f8c4691f4dc for woocommerce
commit f8c4691f4dc670ca1284efe3b8f2e8fa7da55bb9
Author: Little Fedya <littfed@gmail.com>
Date: Wed Oct 7 13:06:49 2026 +0500
fix: prevent errors when saving coupons with empty post_date_gmt (#69010)
* fix: backfill date_created when saving coupon with empty post_date_gmt
* fix: only set post_date and post_date_gmt when date_created is present
* test: ensure coupon fixture uses draft status in empty post_date_gmt test
* chore: update PHPStan baseline for coupon date_created guard
The guard in WC_Coupon_Data_Store_CPT::update() removes one of three
baselined null dereferences for each of getOffsetTimestamp() and
getTimestamp(), so lower both counts to 2.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Jan Lysý <jan.lysy@automattic.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/fix-69000-coupon-empty-post-date-gmt b/plugins/woocommerce/changelog/fix-69000-coupon-empty-post-date-gmt
new file mode 100644
index 00000000000..9bbf2112c86
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-69000-coupon-empty-post-date-gmt
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix an error that stopped some draft coupons from being saved.
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-coupon-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-coupon-data-store-cpt.php
index 3c7a3eb42b8..c4b7f552bfe 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-coupon-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-coupon-data-store-cpt.php
@@ -186,6 +186,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
*/
public function update( &$coupon ) {
$coupon->save_meta_data();
+
$changes = $coupon->get_changes();
if ( array_intersect( array( 'code', 'description', 'date_created', 'date_modified', 'status' ), array_keys( $changes ) ) ) {
@@ -193,12 +194,15 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
'post_title' => $coupon->get_code( 'edit' ),
'post_excerpt' => $coupon->get_description( 'edit' ),
'post_status' => $coupon->get_status( 'edit' ),
- 'post_date' => gmdate( 'Y-m-d H:i:s', $coupon->get_date_created( 'edit' )->getOffsetTimestamp() ),
- 'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $coupon->get_date_created( 'edit' )->getTimestamp() ),
'post_modified' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $coupon->get_date_modified( 'edit' )->getOffsetTimestamp() ) : current_time( 'mysql' ),
'post_modified_gmt' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $coupon->get_date_modified( 'edit' )->getTimestamp() ) : current_time( 'mysql', 1 ),
);
+ if ( $coupon->get_date_created( 'edit' ) ) {
+ $post_data['post_date'] = gmdate( 'Y-m-d H:i:s', $coupon->get_date_created( 'edit' )->getOffsetTimestamp() );
+ $post_data['post_date_gmt'] = gmdate( 'Y-m-d H:i:s', $coupon->get_date_created( 'edit' )->getTimestamp() );
+ }
+
/**
* When updating this object, to prevent infinite loops, use $wpdb
* to update data, since wp_update_post spawns more calls to the
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 98358c4b961..948d44c9e2b 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -16731,13 +16731,13 @@ parameters:
-
message: '#^Cannot call method getOffsetTimestamp\(\) on WC_DateTime\|null\.$#'
identifier: method.nonObject
- count: 3
+ count: 2
path: includes/data-stores/class-wc-coupon-data-store-cpt.php
-
message: '#^Cannot call method getTimestamp\(\) on WC_DateTime\|null\.$#'
identifier: method.nonObject
- count: 3
+ count: 2
path: includes/data-stores/class-wc-coupon-data-store-cpt.php
-
diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-coupon-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-coupon-data-store-cpt-test.php
index e8ab9fa67fb..e3eefc5de7f 100644
--- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-coupon-data-store-cpt-test.php
+++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-coupon-data-store-cpt-test.php
@@ -357,4 +357,35 @@ class WC_Coupon_Data_Store_CPT_Test extends WC_Unit_Test_Case {
'A subclass reading the deprecated property must keep seeing the historical accumulated list after saves.'
);
}
+
+ /**
+ * @testdox Should update coupon with empty post_date_gmt without error and without pinning date_created.
+ */
+ public function test_update_coupon_with_empty_post_date_gmt(): void {
+ global $wpdb;
+
+ $coupon = $this->create_settled_coupon();
+ $coupon_id = $coupon->get_id();
+
+ // Simulate empty post_date_gmt as found in unpublished or draft posts.
+ $wpdb->update(
+ $wpdb->posts,
+ array(
+ 'post_status' => 'draft',
+ 'post_date_gmt' => '0000-00-00 00:00:00',
+ ),
+ array( 'ID' => $coupon_id )
+ );
+ clean_post_cache( $coupon_id );
+
+ $coupon = new WC_Coupon( $coupon_id );
+ $this->assertNull( $coupon->get_date_created( 'edit' ) );
+
+ $coupon->set_description( 'Updated description' );
+ $coupon->save();
+
+ $reloaded_coupon = new WC_Coupon( $coupon_id );
+ $this->assertNull( $reloaded_coupon->get_date_created( 'edit' ) );
+ $this->assertSame( 'Updated description', $reloaded_coupon->get_description() );
+ }
}