Commit 1d6df22e27e for woocommerce
commit 1d6df22e27ecdbfb185520f11c1a77724d6d87e2
Author: Faisal Ahammad <faisalahammad24@gmail.com>
Date: Thu Aug 13 01:02:00 2026 +0600
Add notice to highlight the refund fix tool (#66265)
* add(analytics): add notice to highlight refund fix tool
- Add FullRefundFixDataToolNotice inbox note pointing merchants to the
existing full refund data fix tool on Status > Tools
- Widen Analytics::should_show_refund_fix_tool() to public static so
the note can reuse the same eligibility check as the tool itself
- Register the note in Events.php and auto-remove it once the store
no longer has legacy refund data
Merchants had no way to discover the refund fix tool added in a
previous release since it only lives on Status > Tools. This adds an
inbox notice nudging them there when their store actually needs it.
Closes #62151
* Fix refund fix tool notice to auto-hide when data fixed
Replace deprecated Features::is_enabled with FeaturesUtil::feature_is_enabled, bump @since to 11.2.0, and decouple is_applicable from the tool-row visibility flag so the notice hides once the underlying refund data is fixed rather than lingering until the tool row is dismissed.
* fix(analytics): narrow should_show_refund_fix_tool visibility to internal use
- Revert should_show_refund_fix_tool() from public to private static, it is
now only referenced inside Analytics.
- Mark FullRefundFixDataToolNotice as @internal.
Addresses PR feedback.
Refs #66265
diff --git a/plugins/woocommerce/changelog/fix-62151-refund-fix-tool-notice b/plugins/woocommerce/changelog/fix-62151-refund-fix-tool-notice
new file mode 100644
index 00000000000..2b76f2d7769
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-62151-refund-fix-tool-notice
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add inbox notice pointing merchants to the full refund data fix tool
diff --git a/plugins/woocommerce/src/Internal/Admin/Analytics.php b/plugins/woocommerce/src/Internal/Admin/Analytics.php
index 63bcc8934b3..07b7bdc2cc1 100644
--- a/plugins/woocommerce/src/Internal/Admin/Analytics.php
+++ b/plugins/woocommerce/src/Internal/Admin/Analytics.php
@@ -77,7 +77,7 @@ class Analytics {
// flag is cleared (clearing happens before the first batch is queued).
add_action( 'woocommerce_analytics_refund_fix_batch', array( $this, 'process_refund_fix_batch' ) );
- if ( $this->should_show_refund_fix_tool() ) {
+ if ( self::should_show_refund_fix_tool() ) {
add_filter( 'woocommerce_debug_tools', array( $this, 'register_full_refund_fix_data_tool' ) );
add_action( 'admin_footer', array( $this, 'output_refund_fix_tool_js' ) );
add_action( 'wp_ajax_woocommerce_check_refund_fix_needed', array( $this, 'ajax_check_refund_fix_needed' ) );
@@ -209,7 +209,7 @@ class Analytics {
*
* @return bool
*/
- private function should_show_refund_fix_tool(): bool {
+ private static function should_show_refund_fix_tool(): bool {
return ! OrderUtil::uses_new_full_refund_data()
|| 'yes' === get_option( 'woocommerce_analytics_show_old_refund_data_tool' );
}
diff --git a/plugins/woocommerce/src/Internal/Admin/Events.php b/plugins/woocommerce/src/Internal/Admin/Events.php
index 5ba281f0571..1217d773e18 100644
--- a/plugins/woocommerce/src/Internal/Admin/Events.php
+++ b/plugins/woocommerce/src/Internal/Admin/Events.php
@@ -14,6 +14,7 @@ use Automattic\WooCommerce\Internal\Admin\Notes\EditProductsOnTheMove;
use Automattic\WooCommerce\Internal\Admin\Notes\EmailImprovements;
use Automattic\WooCommerce\Internal\Admin\Notes\EUVATNumber;
use Automattic\WooCommerce\Internal\Admin\Notes\FirstProduct;
+use Automattic\WooCommerce\Internal\Admin\Notes\FullRefundFixDataToolNotice;
use Automattic\WooCommerce\Internal\Admin\Notes\InstallJPAndWCSPlugins;
use Automattic\WooCommerce\Internal\Admin\Notes\LaunchChecklist;
use Automattic\WooCommerce\Internal\Admin\Notes\MagentoMigration;
@@ -72,6 +73,7 @@ class Events {
EmailImprovements::class,
EUVATNumber::class,
FirstProduct::class,
+ FullRefundFixDataToolNotice::class,
LaunchChecklist::class,
MagentoMigration::class,
ManageOrdersOnTheGo::class,
@@ -199,6 +201,7 @@ class Events {
protected function possibly_delete_notes() {
PaymentsRemindMeLater::delete_if_not_applicable();
PaymentsMoreInfoNeeded::delete_if_not_applicable();
+ FullRefundFixDataToolNotice::delete_if_not_applicable();
}
/**
diff --git a/plugins/woocommerce/src/Internal/Admin/Notes/FullRefundFixDataToolNotice.php b/plugins/woocommerce/src/Internal/Admin/Notes/FullRefundFixDataToolNotice.php
new file mode 100644
index 00000000000..e311e23e830
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/Admin/Notes/FullRefundFixDataToolNotice.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * WooCommerce Admin Full Refund Fix Data Tool Notice Provider.
+ *
+ * Adds a note to the merchant's inbox pointing to the full refund fix tool on
+ * the WooCommerce > Status > Tools page.
+ */
+
+declare( strict_types=1 );
+
+namespace Automattic\WooCommerce\Internal\Admin\Notes;
+
+defined( 'ABSPATH' ) || exit;
+
+use Automattic\WooCommerce\Admin\Notes\Note;
+use Automattic\WooCommerce\Admin\Notes\NoteTraits;
+use Automattic\WooCommerce\Utilities\FeaturesUtil;
+use Automattic\WooCommerce\Utilities\OrderUtil;
+
+/**
+ * FullRefundFixDataToolNotice
+ *
+ * @internal
+ * @since 11.2.0
+ */
+class FullRefundFixDataToolNotice {
+ /**
+ * Note traits.
+ */
+ use NoteTraits;
+
+ /**
+ * Name of the note for use in the database.
+ */
+ const NOTE_NAME = 'wc-admin-full-refund-fix-data-tool';
+
+ /**
+ * Should this note exist?
+ *
+ * @return bool
+ */
+ public static function is_applicable() {
+ if ( ! FeaturesUtil::feature_is_enabled( 'analytics' ) ) {
+ return false;
+ }
+
+ // The notice follows the underlying data state, not the tool-row
+ // visibility flag (`woocommerce_analytics_show_old_refund_data_tool`),
+ // so it auto-hides once the fix has been applied.
+ return ! OrderUtil::uses_new_full_refund_data();
+ }
+
+ /**
+ * Get the note.
+ *
+ * @return Note|null
+ */
+ public static function get_note() {
+ if ( ! self::is_applicable() ) {
+ return null;
+ }
+
+ $note = new Note();
+
+ $note->set_title( __( 'Fix your refund data in Analytics', 'woocommerce' ) );
+ $note->set_content(
+ __( 'We found some refunded orders where the full refund amount was not recorded correctly in your Analytics reports. Use the full refund fix tool on the Status page to re-import the affected data.', 'woocommerce' )
+ );
+ $note->set_content_data( (object) array() );
+ $note->set_type( Note::E_WC_ADMIN_NOTE_WARNING );
+ $note->set_name( self::NOTE_NAME );
+ $note->set_source( 'woocommerce-admin' );
+
+ $note->add_action(
+ 'full-refund-fix-data-tool_view',
+ __( 'Fix refund data', 'woocommerce' ),
+ admin_url( 'admin.php?page=wc-status&tab=tools' ),
+ Note::E_WC_ADMIN_NOTE_UNACTIONED,
+ true
+ );
+
+ return $note;
+ }
+}
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Notes/FullRefundFixDataToolNoticeTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Notes/FullRefundFixDataToolNoticeTest.php
new file mode 100644
index 00000000000..1249f970b51
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Notes/FullRefundFixDataToolNoticeTest.php
@@ -0,0 +1,167 @@
+<?php
+/**
+ * Tests for FullRefundFixDataToolNotice class.
+ */
+
+declare( strict_types=1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\Admin\Notes;
+
+use Automattic\WooCommerce\Internal\Admin\Notes\FullRefundFixDataToolNotice;
+use Automattic\WooCommerce\Admin\Notes\Note;
+use WC_Unit_Test_Case;
+
+/**
+ * Class FullRefundFixDataToolNoticeTest
+ */
+class FullRefundFixDataToolNoticeTest extends WC_Unit_Test_Case {
+
+ /**
+ * Test is_applicable returns false when the analytics feature is disabled.
+ */
+ public function test_is_applicable_returns_false_when_analytics_disabled() {
+ update_option( 'woocommerce_analytics_enabled', 'no' );
+ delete_option( 'woocommerce_db_version' );
+ update_option( 'woocommerce_analytics_uses_old_full_refund_data', 'yes' );
+
+ $this->assertFalse( FullRefundFixDataToolNotice::is_applicable() );
+ }
+
+ /**
+ * Test is_applicable returns false for stores without legacy refund data.
+ */
+ public function test_is_applicable_returns_false_without_legacy_refund_data() {
+ update_option( 'woocommerce_db_version', '10.2.0' );
+ delete_option( 'woocommerce_analytics_uses_old_full_refund_data' );
+ delete_option( 'woocommerce_analytics_show_old_refund_data_tool' );
+
+ $this->assertFalse( FullRefundFixDataToolNotice::is_applicable() );
+ }
+
+ /**
+ * Test is_applicable returns true when the store has legacy refund data
+ * and the DB schema is below the threshold where new data applies.
+ */
+ public function test_is_applicable_returns_true_with_legacy_refund_data() {
+ delete_option( 'woocommerce_db_version' );
+ update_option( 'woocommerce_analytics_uses_old_full_refund_data', 'yes' );
+
+ $this->assertTrue( FullRefundFixDataToolNotice::is_applicable() );
+ }
+
+ /**
+ * Test is_applicable returns false once the underlying data has been fixed
+ * (legacy flag cleared) even if the tool row is still flagged as visible
+ * pending merchant dismissal. The notice follows the data, not the tool row.
+ */
+ public function test_is_applicable_returns_false_once_data_fixed_even_if_tool_still_visible() {
+ update_option( 'woocommerce_db_version', '10.2.0' );
+ delete_option( 'woocommerce_analytics_uses_old_full_refund_data' );
+ update_option( 'woocommerce_analytics_show_old_refund_data_tool', 'yes' );
+
+ $this->assertFalse( FullRefundFixDataToolNotice::is_applicable() );
+ }
+
+ /**
+ * Test get_note returns note with expected content and action when applicable.
+ */
+ public function test_get_note_returns_note_when_applicable() {
+ delete_option( 'woocommerce_db_version' );
+ update_option( 'woocommerce_analytics_uses_old_full_refund_data', 'yes' );
+
+ $note = FullRefundFixDataToolNotice::get_note();
+
+ $this->assertInstanceOf( Note::class, $note );
+ $this->assertEquals( 'Fix your refund data in Analytics', $note->get_title() );
+ $this->assertEquals( Note::E_WC_ADMIN_NOTE_WARNING, $note->get_type() );
+
+ $actions = $note->get_actions();
+ $this->assertCount( 1, $actions, 'Note should have 1 action' );
+ $this->assertEquals( 'full-refund-fix-data-tool_view', $actions[0]->name );
+ $this->assertStringContainsString( 'page=wc-status', $actions[0]->query );
+ $this->assertStringContainsString( 'tab=tools', $actions[0]->query );
+ }
+
+ /**
+ * Test get_note returns null when not applicable.
+ */
+ public function test_get_note_returns_null_when_not_applicable() {
+ update_option( 'woocommerce_db_version', '10.2.0' );
+ delete_option( 'woocommerce_analytics_uses_old_full_refund_data' );
+ delete_option( 'woocommerce_analytics_show_old_refund_data_tool' );
+
+ $this->assertNull( FullRefundFixDataToolNotice::get_note() );
+ }
+
+ /**
+ * Test that the note is added via possibly_add_note for stores with legacy refund data.
+ */
+ public function test_possibly_add_note_adds_note_when_applicable() {
+ delete_option( 'woocommerce_db_version' );
+ update_option( 'woocommerce_analytics_uses_old_full_refund_data', 'yes' );
+
+ FullRefundFixDataToolNotice::possibly_add_note();
+
+ $data_store = \WC_Data_Store::load( 'admin-note' );
+ $note_ids = $data_store->get_notes_with_name( FullRefundFixDataToolNotice::NOTE_NAME );
+
+ $this->assertNotEmpty( $note_ids, 'Note should be created when the store has legacy refund data' );
+ }
+
+ /**
+ * Test that possibly_add_note prevents duplicates.
+ */
+ public function test_possibly_add_note_prevents_duplicates() {
+ delete_option( 'woocommerce_db_version' );
+ update_option( 'woocommerce_analytics_uses_old_full_refund_data', 'yes' );
+
+ FullRefundFixDataToolNotice::possibly_add_note();
+ FullRefundFixDataToolNotice::possibly_add_note();
+
+ $data_store = \WC_Data_Store::load( 'admin-note' );
+ $note_ids = $data_store->get_notes_with_name( FullRefundFixDataToolNotice::NOTE_NAME );
+
+ $this->assertCount( 1, $note_ids, 'Only one note should exist' );
+ }
+
+ /**
+ * Test that the note is removed once the store no longer has legacy refund data.
+ */
+ public function test_delete_if_not_applicable_removes_note_once_fixed() {
+ delete_option( 'woocommerce_db_version' );
+ update_option( 'woocommerce_analytics_uses_old_full_refund_data', 'yes' );
+ FullRefundFixDataToolNotice::possibly_add_note();
+
+ update_option( 'woocommerce_db_version', '10.2.0' );
+ delete_option( 'woocommerce_analytics_uses_old_full_refund_data' );
+ delete_option( 'woocommerce_analytics_show_old_refund_data_tool' );
+ FullRefundFixDataToolNotice::delete_if_not_applicable();
+
+ $data_store = \WC_Data_Store::load( 'admin-note' );
+ $note_ids = $data_store->get_notes_with_name( FullRefundFixDataToolNotice::NOTE_NAME );
+
+ $this->assertEmpty( $note_ids, 'Note should be removed once the store no longer has legacy refund data' );
+ }
+
+ /**
+ * Clean up after each test.
+ */
+ public function tearDown(): void {
+ parent::tearDown();
+
+ $data_store = \WC_Data_Store::load( 'admin-note' );
+ $note_ids = $data_store->get_notes_with_name( FullRefundFixDataToolNotice::NOTE_NAME );
+
+ foreach ( $note_ids as $note_id ) {
+ $note = \Automattic\WooCommerce\Admin\Notes\Notes::get_note( $note_id );
+ if ( $note ) {
+ $note->delete();
+ }
+ }
+
+ delete_option( 'woocommerce_analytics_enabled' );
+ delete_option( 'woocommerce_db_version' );
+ delete_option( 'woocommerce_analytics_uses_old_full_refund_data' );
+ delete_option( 'woocommerce_analytics_show_old_refund_data_tool' );
+ }
+}