Commit c09daa9d69 for woocommerce
commit c09daa9d6905bad5d62df42776099c0203be83b8
Author: Fernando Espinosa <Ferdev@users.noreply.github.com>
Date: Fri Dec 12 18:07:58 2025 +0100
Fix tax-inclusive prices incorrectly inflating when no base tax rate … (#61471)
* Fix tax-inclusive prices incorrectly inflating when no base tax rate configured
When prices are entered inclusive of tax and no base tax rate exists,
wc_get_price_including_tax() was incorrectly adding customer taxes on
top of the already-inclusive price. For example, a $100 tax-inclusive
product would display as $120 for customers with 20% tax.
The function was attempting to back-calculate base taxes with an empty
rate array, resulting in $0 base taxes. It then added customer taxes
as if the price was tax-exclusive.
This fix checks if base_tax_rates is empty and preserves the original
price unchanged, as it's already correctly set as tax-inclusive.
Added comprehensive unit tests covering various tax rate scenarios and
a regression test to ensure existing behavior remains intact when both
base and customer rates are configured.
* Add changefile(s) from automation for the following project(s): woocommerce
* Address coderabbitai feedback
* Fix tax-inclusive prices inflating in cart when no base tax rate exists
Extends the fix from the initial commit to also handle cart/checkout calculations.
The previous fix addressed wc_get_price_including_tax() for product display, but
cart totals use WC_Cart_Totals::adjust_non_base_location_price() which had the
same issue: when no base tax rate exists, it was incorrectly adding customer taxes
on top of already tax-inclusive prices.
For example, a $100 tax-inclusive product would incorrectly show as $120 in the
cart for customers with 20% tax when no base rate was configured.
Changes:
- Add empty base_tax_rates check in WC_Cart_Totals::adjust_non_base_location_price()
- Add comprehensive cart integration tests for WOOPLUG-5511
* Fix broken unit test
* Fix linting errors
* Revert "Fix tax-inclusive prices inflating in cart when no base tax rate exists"
This reverts commit dccbeec9600c6112b6088ce8b00e5ad65ddad053.
* Revert "Fix tax-inclusive prices incorrectly inflating when no base tax rate configured"
This reverts commit a1f6af348865301316faa41de760b1894fb13de1.
* Add UI improvements for tax-inclusive pricing configuration
- Update setting description to mention base tax rate requirement
- Add admin notice when tax-inclusive pricing is enabled without base rate
* Add changefile(s) from automation for the following project(s): woocommerce
* Add filter to allow disabling tax configuration notice
Allow extensions and site customizations to programmatically disable the
tax configuration incomplete notice via the
`woocommerce_show_tax_configuration_notice` filter.
* Enhance Hydration class to support urls with ID and query params (#62057)
* Allow inserting more blocks inside Product Gallery Large Image (#62030)
* Allow inserting blocks to large image
* Add changelog
* Add z-index: 1 to Large Image inner blocks
* Handle Next Prev Buttons pointer events with new z-index property
* Automate cleanup of old milestones at feature freeze time (#62135)
* Only coerce order total into float when necessary (#62131)
* Instead of casting all string values to float when setting order totals, only do it when necessary.
* Fix a lint issue
* Rely on Case-Insensitive Collation for Coupon Lookups (#62145)
There's no good reason to `LOWER()` the
coupon code when checking. The collation
is virtually guaranteed to be case-insensitive.
* Add analytics-scheduled-import feature flag (#62149)
* Add analytics-scheduled-import feature flag
- Added conditional checks in OrdersScheduler to handle immediate import options based on the new 'analytics-scheduled-import' feature flag.
- Updated core.json and development.json to configure the feature flag for different environments.
- Improved maintainability by ensuring that immediate import logic only executes when the feature is enabled.
* Enhance documentation for immediate import check in OrdersScheduler
- Updated the docblock for the immediate import check method to clarify its behavior when the "analytics-scheduled-import" feature is enabled or disabled.
- Added setup and teardown methods in OrdersSchedulerTest to manage the state of the feature flag during tests, ensuring accurate test conditions.
* Add changefile(s) from automation for the following project(s): woocommerce, woocommerce/client/admin
* Add changefile(s) from automation for the following project(s): woocommerce, woocommerce/client/admin
* Fix lint
* Add changefile(s) from automation for the following project(s): woocommerce, woocommerce/client/admin
* Delete plugins/woocommerce/changelog/62148-wooa7s-797-add-analytics-scheduled-updates-feature-flag
---------
Co-authored-by: github-actions <github-actions@github.com>
* Register analytics import mode setting (#62169)
* Add woocommerce_analytics_immediate_import setting to Settings API
Registers the analytics import mode setting in the WooCommerce Admin Settings API.
The setting is only available when the 'analytics-scheduled-import' feature flag is enabled.
Setting configuration:
- Type: radio
- Values: 'yes' (immediate) or 'no' (scheduled)
- Default: 'yes' for backward compatibility
- Options: 'Scheduled (Recommended)' or 'Immediately'
The setting will be accessible via the REST API endpoint:
GET/POST /wc/v2/settings/wc_admin/woocommerce_analytics_immediate_import
Related to WOOA7S-791
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add changefile(s) from automation for the following project(s): woocommerce
* Update setting
* Update default value
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
* Don't display deprecation notice while using deprecated Navigation store internally (#62049)
* Remove auto usage of deprecated API by Woo itself
* Add chanbgelog
* Add data changelog
* Revert "Remove auto usage of deprecated API by Woo itself"
This reverts commit e5fdb800797b27b64b03f24ad8c29641731f1fca.
* Extend the deprecation notice supression
* Apply more robust way of recognizing internal function call with Symbol
* Move INTERNAL_CALL symbol so it's not exposed externally
* Remove unnecessary Product Collection margin (#62144)
* Remove unnecessary margin top for product template when there's no notices
* Add changelog
* e2e tests: Enhance CTRF report with additional environment data (#62154)
* Email Editor: fix slow image rendering (#62118)
Co-authored-by: github-actions <github-actions@github.com>
* Prepare Packages for Release (#62180)
* Automated change: Prep @woocommerce/email-editor-config for release.
* Fix Email Editor changelog
---------
Co-authored-by: lysyjan <13644846+lysyjan@users.noreply.github.com>
Co-authored-by: Jan Lysý <jan.lysy@automattic.com>
* Update email template design and improve structure for better styling control (#62051)
* Revert hardcoded email editor header font sizes
* Wrap the WooEmailTemplate in a group block for easier style application.
* Wrap the post-content block of the WooEmailTemplate in a group block for easier style application.
* Update email template structure and adjust font size translations in tests
- Wrap the post-content block in a group block for improved styling in the email template.
- Modify font size translations for 'large', 'x-large', and add 'xx-large' in the Theme_Controller_Test.
- Update content rendering tests to reflect changes in post-content block usage.
* Prevent `contentSize` not found error
* Add changelog files
* Unify post-content block lock settings by making sure the remover is disabled for the Woo Email Template.
* Revert style h1 to h6 removal
* Add block email editor support for order fulfillment notification emails (#62146)
* Opt in fulfillment emails for block email editor support.
This will allow Woo create email posts required for the editor.
* Add initial block email templates for customer fulfillment notifications
This commit introduces three new email templates for customer fulfillment:
- `customer-fulfillment-created.php` for notifying customers when their items are on the way.
- `customer-fulfillment-deleted.php` for informing customers when a shipment has been removed.
- `customer-fulfillment-updated.php` for updates regarding changes to shipment details.
These templates are designed to be customizable through the WooCommerce email editor.
* Add filter support for skipping order details in some block email content.
This will allow extensions to skip the order details do_action hook for some of their emails
* Add block email editor support for fulfillment email templates
This update introduces functionality to skip order details in the block editor for fulfillment-related emails. It also adds the ability to include fulfillment details and customer information in the general block content for the created, deleted, and updated fulfillment emails. Additionally, a method for retrieving block editor email template content has been implemented for better customization.
* Refactor fulfillment email classes to utilize a shared template for block content
This commit removes redundant methods from the fulfillment email classes and introduces a new template file for general block content used in fulfillment emails. The changes streamline the email structure and enhance customization capabilities for created, deleted, and updated fulfillment notifications.
* Add changelog and fix lint errors
* coderabbit feedback
* Optimize fetching WC emails in EmailApiController.php (#62170)
* Apply `Release` label and PR reviewer for release related PRs (#62139)
* Skip the automatic PR reviewer for release related PRs
* Asign `Release` label to all release related PRs
* Add the gh actor to changelog PRs
* Pass the ga param to the changelog command
* Fix command error
* Use woocommerce bot since it has permissions to add labels
* Remove changes for changelog
* Remove unnecessary reviewer
* Add the pr author as reviewer
* Fix PHP notice 55353 (#55354)
* Fix PHP notice 55353
Fix https://github.com/woocommerce/woocommerce/issues/55353
* Add changefile(s) from automation for the following project(s): woocommerce
* Add changefile(s) from automation for the following project(s): woocommerce
* Add a basic test
---------
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alba Rincón <albarin@users.noreply.github.com>
Co-authored-by: Marin Atanasov <8436925+tyxla@users.noreply.github.com>
* dev: Update Playwright to 1.57.0 and CTRF reporter to 0.0.27 (#62189)
* e2e tests: enhance e2e ctrf reports to include commit SHA values (#62192)
* Add the reviewer to the update changelog PRs from the release process (#62174)
* Add the reviewer to the update changelog PRs from the release process
* Release Automation: update npmjs auth to OIDC (#62056)
* Release Automation: update npmjs auth to OIDC
* Use latest NPM for the publish workflow
* PayPal Standard: Use home url for generating webhook url (#62141)
* use home url for generating webhook url
* PayPal Standard: Include WC version in PayPal custom_id (#62142)
* include wc version in custom id
* use 'v' for version
* update test
* Revert: Restart Start Test Environemnt command if it fails (#62201)
Revert "Restart Start Test Environemnt command if it fails (#62061)"
This reverts commit 6ac263b2841c4459cf7f2ba330859af8d94b754c.
* Fix block email content preview (#62206)
* Switch to using postId for checking email type.
The postId is a more authoritative way to fetch and display the associated email editor post. If the email type for the post ID does not exist, then we can be sure that the email post has not been generated.
* Implement caching for email editor post templates.
* Update test for WCTransactionalEmailPostsManager class
* Remove unused method get_email_type_class_name_from_template_name
* Add changelog
* Fix lint errors and CodeRabbit feedback
* PayPal Standard: Make auth capture logic more robust (#61963)
* store capture and authorization ids in separate order meta
* add AUTHORIZED status
* check status and auth id from order meta
* send paypal order id in auth capture request
* refactor bailing condition
* add order check
* bail if the order is not transitioning from on-hold status
* rearrange the bailing logic
* retrieve auth id from paypal order
* add status check
* backward compatibility
* revert order status check
* skip if capture id is present
* return if no paypal order id
* simplify check with renamed function
* save capture data
* fix check and add meta
* wrap in try catch
* bail if capture id is present
* guard against repeated call
* get latest item from array
* fix lint in constant file
* fix lint
* use dedicated flag meta
* add tests
* switch condition order
* add log when capture/auth ID backfilled
* string comparison
* remove unnecessary empty value assignment
* Update stable tag to 10.3.6 (trunk) (#62214)
Update stable tag to 10.3.6 on trunk
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
* Add index to user_email within woocommerce_downloadable_product_permissions to improve performance (#62101)
* Add index to user_email within woocommerce_downloadable_product_permissions to improve performance
* update version for release
---------
Co-authored-by: Michael Pretty <prettyboymp@users.noreply.github.com>
* Update changelog.txt after 10.3.6 (#62221)
Update changelog.txt after release 10.3.6
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add PHPStan analysis Github action to the WooCommerce plugin (#62064)
* Add phpstan analysis
* Update config and baseline
* Add Github action for PHPStan analysis
* Add phpstan files to the distignore
* Fix missing feature config file when using phpstan
* Add optional to feature config path
* Update memory limit and processes
* Skip php stubs with linting and update README
* Add changelog
* Update to use PHP 7.4
* Use pnpm to run phpstan instead
* Update baseline
* Update baseline with PHP 7.4 run
* Remove extension installer and load extension directly
* Update composer.lock to sync with composer.json after rebase
- Update php-stubs/wordpress-stubs constraint to ^6.8 and lock to v6.8.3
- Update woocommerce/monorepo-plugin to current branch
- Remove phpstan/extension-installer (per previous commit)
* Use newer PHP version when running phpstan
PHPStan can be configured to run on older PHP versions, but the installed PHP binary can be of a newer version
PHPStan internally assesses the code with older syntax.
This allows us to update to a newer version of - php-stubs/wordpress-stubs
* Update phpstan-baseline.neon
---------
Co-authored-by: Nikhil <nikhil.chavan@automattic.com>
Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
* Prevent Select2 styles from leaking into other plugins (#61956)
* Prevent Select2 styles from leaking into other plugins
* Add changelog file
* Add deprecated doc tag to wc_enqueue_js(). (#62219)
* Add deprecated doc tag to wc_enqueue_js().
* Update wc-core-functions.php
Expand on deprecation.
* Add changelog for layout settings fix in the email editor (#62237)
* Address review feedback for tax configuration notice
- Add wc_tax_enabled() check before showing notice
- Add woocommerce_adjust_non_base_location_prices filter check
- Fix false positive when localized tax rates exist for base country
- Update notice text with "standard tax rates" and country code
- Update desc_tip to clarify prices include base location tax rate
- Update tests to use real database queries instead of mocks
* Add changefile(s) from automation for the following project(s): @woocommerce/email-editor, @woocommerce/data, packages/php/email-editor, woocommerce, woocommerce/client/admin
* Add changefile(s) from automation for the following project(s): @woocommerce/email-editor, woocommerce
* Address PR review feedback for tax configuration notice
- Update @since tags to 10.5.0 (matching trunk version)
- Add @internal annotation to public hook callback method
- Fix SQL query to only check Standard tax rates (tax_rate_class = '')
- Add proper docblock for woocommerce_adjust_non_base_location_prices filter
* Add return type to tax_configuration_validation_notice method
Fixes PHPStan error: Method has no return type specified.
* Update E2E test expectation for tax prices tooltip
Update the expected tooltip text in the settings CRUD API test to match
the new, more informative tooltip text that explains base location tax
rate requirements.
* Rename has_tax_rate_for_country to has_standard_tax_rate_for_country
Better reflects that the method checks only for standard tax rates.
---------
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Sam Seay <samueljseay@gmail.com>
Co-authored-by: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
Co-authored-by: Jorge A. Torres <jorge.torres@automattic.com>
Co-authored-by: Radoslav Georgiev <rageorgiev@gmail.com>
Co-authored-by: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com>
Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com>
Co-authored-by: Tony Arcangelini <33258733+arcangelini@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: lysyjan <13644846+lysyjan@users.noreply.github.com>
Co-authored-by: Jan Lysý <jan.lysy@automattic.com>
Co-authored-by: Seun Olorunsola <30554163+triple0t@users.noreply.github.com>
Co-authored-by: Rostislav Wolný <1082140+costasovo@users.noreply.github.com>
Co-authored-by: Alba Rincón <albarin@users.noreply.github.com>
Co-authored-by: kkmuffme <11071985+kkmuffme@users.noreply.github.com>
Co-authored-by: Marin Atanasov <8436925+tyxla@users.noreply.github.com>
Co-authored-by: Brandon Kraft <public@brandonkraft.com>
Co-authored-by: Mayisha <33387139+Mayisha@users.noreply.github.com>
Co-authored-by: woocommercebot <30233865+woocommercebot@users.noreply.github.com>
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
Co-authored-by: Boro Sitnikovski <buritomath@gmail.com>
Co-authored-by: Michael Pretty <prettyboymp@users.noreply.github.com>
Co-authored-by: louwie17 <lourensschep@gmail.com>
Co-authored-by: Nikhil <nikhil.chavan@automattic.com>
Co-authored-by: Albert Juhé Lluveras <contact@albertjuhe.com>
diff --git a/packages/js/email-editor/changelog/61471-wooplug-5511-product-prices-entered-inclusive-of-tax-are-displayed b/packages/js/email-editor/changelog/61471-wooplug-5511-product-prices-entered-inclusive-of-tax-are-displayed
new file mode 100644
index 0000000000..257b4b41de
--- /dev/null
+++ b/packages/js/email-editor/changelog/61471-wooplug-5511-product-prices-entered-inclusive-of-tax-are-displayed
@@ -0,0 +1,4 @@
+Significance: patch
+Type: tweak
+
+Improve UX for tax-inclusive pricing configuration by adding validation notice and clearer setting description when base tax rate is not configured.
\ No newline at end of file
diff --git a/packages/js/email-editor/changelog/fix-layout-settings-not-initialized b/packages/js/email-editor/changelog/fix-layout-settings-not-initialized
new file mode 100644
index 0000000000..a36fb2f63d
--- /dev/null
+++ b/packages/js/email-editor/changelog/fix-layout-settings-not-initialized
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Handle missing layout settings in the editor settings
diff --git a/plugins/woocommerce/changelog/61471-wooplug-5511-product-prices-entered-inclusive-of-tax-are-displayed b/plugins/woocommerce/changelog/61471-wooplug-5511-product-prices-entered-inclusive-of-tax-are-displayed
new file mode 100644
index 0000000000..257b4b41de
--- /dev/null
+++ b/plugins/woocommerce/changelog/61471-wooplug-5511-product-prices-entered-inclusive-of-tax-are-displayed
@@ -0,0 +1,4 @@
+Significance: patch
+Type: tweak
+
+Improve UX for tax-inclusive pricing configuration by adding validation notice and clearer setting description when base tax rate is not configured.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/admin/settings/class-wc-settings-tax.php b/plugins/woocommerce/includes/admin/settings/class-wc-settings-tax.php
index a4b946408a..662c330514 100644
--- a/plugins/woocommerce/includes/admin/settings/class-wc-settings-tax.php
+++ b/plugins/woocommerce/includes/admin/settings/class-wc-settings-tax.php
@@ -32,6 +32,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
add_action( 'woocommerce_sections_' . $this->id, array( $this, 'output_sections' ) );
add_action( 'woocommerce_settings_' . $this->id, array( $this, 'output' ) );
add_action( 'woocommerce_settings_save_' . $this->id, array( $this, 'save' ) );
+ add_action( 'admin_notices', array( $this, 'tax_configuration_validation_notice' ) );
}
}
@@ -365,6 +366,110 @@ class WC_Settings_Tax extends WC_Settings_Page {
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
}
+
+ /**
+ * Display admin notice when tax-inclusive pricing is enabled without a base tax rate.
+ *
+ * @since 10.5.0
+ * @internal This method is public only because it is used as a hook callback.
+ *
+ * @return void
+ */
+ public function tax_configuration_validation_notice(): void {
+ // Only show on WooCommerce settings pages.
+ $screen = get_current_screen();
+ if ( ! $screen || 'woocommerce_page_wc-settings' !== $screen->id ) {
+ return;
+ }
+
+ // Don't show if taxes are disabled.
+ if ( ! wc_tax_enabled() ) {
+ return;
+ }
+
+ // Check if prices are entered with tax.
+ if ( 'yes' !== get_option( 'woocommerce_prices_include_tax' ) ) {
+ return;
+ }
+
+ /**
+ * Filters if taxes should be removed from locations outside the store base location.
+ *
+ * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing
+ * with out of base locations. e.g. If a product costs 10 including tax, all users will pay 10
+ * regardless of location and taxes.
+ *
+ * @since 2.4.7
+ *
+ * @param bool $adjust_non_base_location_prices True by default.
+ */
+ if ( ! apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) {
+ return;
+ }
+
+ // Check if base location has tax rates configured.
+ $base_location = wc_get_base_location();
+ if ( empty( $base_location['country'] ) ) {
+ return;
+ }
+
+ $has_base_rate = $this->has_standard_tax_rate_for_country( $base_location['country'] );
+
+ // If no base rates exist, show warning.
+ if ( ! $has_base_rate ) {
+ /**
+ * Filter whether to show the tax configuration incomplete notice.
+ *
+ * @since 10.5.0
+ *
+ * @param bool $show_notice Whether to show the notice. Default true.
+ */
+ if ( ! apply_filters( 'woocommerce_show_tax_configuration_notice', true ) ) {
+ return;
+ }
+ ?>
+ <div class="notice notice-warning">
+ <p>
+ <strong><?php esc_html_e( 'Tax configuration incomplete', 'woocommerce' ); ?></strong>
+ </p>
+ <p>
+ <?php
+ echo wp_kses_post(
+ sprintf(
+ /* translators: 1: country code, 2: opening link tag, 3: closing link tag */
+ __( 'You have enabled "Prices entered with tax" but have not configured a standard tax rate for your base location (%1$s). Please %2$sconfigure standard tax rates%3$s to ensure accurate tax calculations.', 'woocommerce' ),
+ esc_html( $base_location['country'] ),
+ '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=tax§ion=standard' ) ) . '">',
+ '</a>'
+ )
+ );
+ ?>
+ </p>
+ </div>
+ <?php
+ }
+ }
+
+ /**
+ * Check if a standard tax rate exists for a given country.
+ *
+ * @since 10.5.0
+ *
+ * @param string $country Country code.
+ * @return bool True if at least one standard tax rate exists for the country.
+ */
+ private function has_standard_tax_rate_for_country( $country ) {
+ global $wpdb;
+
+ $count = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_tax_rates WHERE (tax_rate_country = %s OR tax_rate_country = '') AND tax_rate_class = ''",
+ $country
+ )
+ );
+
+ return $count > 0;
+ }
}
return new WC_Settings_Tax();
diff --git a/plugins/woocommerce/includes/admin/settings/views/settings-tax.php b/plugins/woocommerce/includes/admin/settings/views/settings-tax.php
index cde4f49a3d..7a02fd7830 100644
--- a/plugins/woocommerce/includes/admin/settings/views/settings-tax.php
+++ b/plugins/woocommerce/includes/admin/settings/views/settings-tax.php
@@ -21,7 +21,7 @@ $settings = array(
'id' => 'woocommerce_prices_include_tax',
'default' => 'no',
'type' => 'radio',
- 'desc_tip' => __( 'This option is important as it will affect how you input prices. Changing it will not update existing products.', 'woocommerce' ),
+ 'desc_tip' => __( 'This option is important as it will affect how you input prices. If you select "Yes", enter prices including your base location\'s tax rate, the baseline for tax calculations. Changing this option will not update existing products.', 'woocommerce' ),
'options' => array(
'yes' => __( 'Yes, I will enter prices inclusive of tax', 'woocommerce' ),
'no' => __( 'No, I will enter prices exclusive of tax', 'woocommerce' ),
diff --git a/plugins/woocommerce/tests/e2e-pw/tests/api-tests/settings/settings-crud.test.js b/plugins/woocommerce/tests/e2e-pw/tests/api-tests/settings/settings-crud.test.js
index ee86eaf392..7ffb3601d1 100644
--- a/plugins/woocommerce/tests/e2e-pw/tests/api-tests/settings/settings-crud.test.js
+++ b/plugins/woocommerce/tests/e2e-pw/tests/api-tests/settings/settings-crud.test.js
@@ -1042,7 +1042,7 @@ test.describe( 'Settings API tests: CRUD', () => {
yes: 'Yes, I will enter prices inclusive of tax',
no: 'No, I will enter prices exclusive of tax',
},
- tip: 'This option is important as it will affect how you input prices. Changing it will not update existing products.',
+ tip: 'This option is important as it will affect how you input prices. If you select "Yes", enter prices including your base location\'s tax rate, the baseline for tax calculations. Changing this option will not update existing products.',
value: 'no',
} ),
] )
diff --git a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-tax-test.php b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-tax-test.php
index f2396fc08f..b771f77a64 100644
--- a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-tax-test.php
+++ b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-tax-test.php
@@ -172,4 +172,254 @@ class WC_Settings_Tax_Test extends WC_Settings_Unit_Test_Case {
$this->assertEquals( array( 'tax_1' ), $deleted );
$this->assertEquals( array( 'tax_4' ), $created );
}
+
+ /**
+ * @testDox 'tax_configuration_validation_notice' shows notice when tax-inclusive pricing is enabled without base rate.
+ */
+ public function test_tax_configuration_validation_notice_shows_when_prices_include_tax_but_no_base_rate() {
+ // Set up prices include tax option.
+ update_option( 'woocommerce_prices_include_tax', 'yes' );
+ update_option( 'woocommerce_calc_taxes', 'yes' );
+ update_option( 'woocommerce_default_country', 'US:CA' );
+
+ // Mock the screen to simulate being on WooCommerce settings page.
+ set_current_screen( 'woocommerce_page_wc-settings' );
+
+ // Ensure no tax rates exist for US.
+ global $wpdb;
+ $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_country = 'US' OR tax_rate_country = ''" );
+
+ $sut = new WC_Settings_Tax();
+
+ // Capture output.
+ ob_start();
+ $sut->tax_configuration_validation_notice();
+ $output = ob_get_clean();
+
+ // Assert notice is displayed with country code and "standard tax rates" text.
+ $this->assertStringContainsString( 'Tax configuration incomplete', $output );
+ $this->assertStringContainsString( 'configure standard tax rates', $output );
+ $this->assertStringContainsString( '(US)', $output );
+ $this->assertStringContainsString( 'notice-warning', $output );
+ }
+
+ /**
+ * @testDox 'tax_configuration_validation_notice' does not show notice when taxes are disabled.
+ */
+ public function test_tax_configuration_validation_notice_does_not_show_when_taxes_disabled() {
+ // Set up prices include tax but taxes disabled.
+ update_option( 'woocommerce_prices_include_tax', 'yes' );
+ update_option( 'woocommerce_calc_taxes', 'no' );
+ update_option( 'woocommerce_default_country', 'US:CA' );
+
+ // Mock the screen.
+ set_current_screen( 'woocommerce_page_wc-settings' );
+
+ // Ensure no tax rates exist for US.
+ global $wpdb;
+ $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_country = 'US' OR tax_rate_country = ''" );
+
+ $sut = new WC_Settings_Tax();
+
+ // Capture output.
+ ob_start();
+ $sut->tax_configuration_validation_notice();
+ $output = ob_get_clean();
+
+ // Assert notice is NOT displayed because taxes are disabled.
+ $this->assertEmpty( $output );
+ }
+
+ /**
+ * @testDox 'tax_configuration_validation_notice' does not show notice when base rate exists.
+ */
+ public function test_tax_configuration_validation_notice_does_not_show_when_base_rate_exists() {
+ // Set up prices include tax option.
+ update_option( 'woocommerce_prices_include_tax', 'yes' );
+ update_option( 'woocommerce_calc_taxes', 'yes' );
+ update_option( 'woocommerce_default_country', 'US:CA' );
+
+ // Mock the screen.
+ set_current_screen( 'woocommerce_page_wc-settings' );
+
+ // Insert a tax rate for US.
+ $tax_rate_id = WC_Tax::_insert_tax_rate(
+ array(
+ 'tax_rate_country' => 'US',
+ 'tax_rate_state' => 'CA',
+ 'tax_rate' => '10.0000',
+ 'tax_rate_name' => 'Test Tax',
+ 'tax_rate_priority' => 1,
+ 'tax_rate_compound' => 0,
+ 'tax_rate_shipping' => 1,
+ 'tax_rate_order' => 1,
+ 'tax_rate_class' => '',
+ )
+ );
+
+ $sut = new WC_Settings_Tax();
+
+ // Capture output.
+ ob_start();
+ $sut->tax_configuration_validation_notice();
+ $output = ob_get_clean();
+
+ // Clean up.
+ WC_Tax::_delete_tax_rate( $tax_rate_id );
+
+ // Assert notice is NOT displayed.
+ $this->assertEmpty( $output );
+ }
+
+ /**
+ * @testDox 'tax_configuration_validation_notice' does not show notice when prices are not tax-inclusive.
+ */
+ public function test_tax_configuration_validation_notice_does_not_show_when_prices_not_inclusive() {
+ // Set up prices exclude tax option.
+ update_option( 'woocommerce_prices_include_tax', 'no' );
+ update_option( 'woocommerce_calc_taxes', 'yes' );
+
+ // Mock the screen.
+ set_current_screen( 'woocommerce_page_wc-settings' );
+
+ $sut = new WC_Settings_Tax();
+
+ // Capture output.
+ ob_start();
+ $sut->tax_configuration_validation_notice();
+ $output = ob_get_clean();
+
+ // Assert notice is NOT displayed.
+ $this->assertEmpty( $output );
+ }
+
+ /**
+ * @testDox 'tax_configuration_validation_notice' does not show notice on non-WooCommerce pages.
+ */
+ public function test_tax_configuration_validation_notice_does_not_show_on_non_woocommerce_pages() {
+ // Set up prices include tax option.
+ update_option( 'woocommerce_prices_include_tax', 'yes' );
+ update_option( 'woocommerce_calc_taxes', 'yes' );
+
+ // Mock the screen to simulate being on a different admin page.
+ set_current_screen( 'dashboard' );
+
+ $sut = new WC_Settings_Tax();
+
+ // Capture output.
+ ob_start();
+ $sut->tax_configuration_validation_notice();
+ $output = ob_get_clean();
+
+ // Assert notice is NOT displayed.
+ $this->assertEmpty( $output );
+ }
+
+ /**
+ * @testDox 'tax_configuration_validation_notice' does not show notice when filtered to false.
+ */
+ public function test_tax_configuration_validation_notice_respects_filter() {
+ // Set up prices include tax option.
+ update_option( 'woocommerce_prices_include_tax', 'yes' );
+ update_option( 'woocommerce_calc_taxes', 'yes' );
+ update_option( 'woocommerce_default_country', 'US:CA' );
+
+ // Mock the screen.
+ set_current_screen( 'woocommerce_page_wc-settings' );
+
+ // Ensure no tax rates exist for US.
+ global $wpdb;
+ $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_country = 'US' OR tax_rate_country = ''" );
+
+ // Add filter to disable the notice.
+ add_filter( 'woocommerce_show_tax_configuration_notice', '__return_false' );
+
+ $sut = new WC_Settings_Tax();
+
+ // Capture output.
+ ob_start();
+ $sut->tax_configuration_validation_notice();
+ $output = ob_get_clean();
+
+ // Remove filter.
+ remove_filter( 'woocommerce_show_tax_configuration_notice', '__return_false' );
+
+ // Assert notice is NOT displayed due to filter.
+ $this->assertEmpty( $output );
+ }
+
+ /**
+ * @testDox 'tax_configuration_validation_notice' does not show notice when localized tax rate exists for base country.
+ */
+ public function test_tax_configuration_validation_notice_does_not_show_when_localized_rate_exists() {
+ // Set up prices include tax option.
+ update_option( 'woocommerce_prices_include_tax', 'yes' );
+ update_option( 'woocommerce_calc_taxes', 'yes' );
+ // Base location is DE without a specific state.
+ update_option( 'woocommerce_default_country', 'DE' );
+
+ // Mock the screen.
+ set_current_screen( 'woocommerce_page_wc-settings' );
+
+ // Insert a tax rate for DE with a specific state (more localized than base).
+ $tax_rate_id = WC_Tax::_insert_tax_rate(
+ array(
+ 'tax_rate_country' => 'DE',
+ 'tax_rate_state' => 'BE', // Berlin - more specific than base location.
+ 'tax_rate' => '19.0000',
+ 'tax_rate_name' => 'MwSt',
+ 'tax_rate_priority' => 1,
+ 'tax_rate_compound' => 0,
+ 'tax_rate_shipping' => 1,
+ 'tax_rate_order' => 1,
+ 'tax_rate_class' => '',
+ )
+ );
+
+ $sut = new WC_Settings_Tax();
+
+ // Capture output.
+ ob_start();
+ $sut->tax_configuration_validation_notice();
+ $output = ob_get_clean();
+
+ // Clean up.
+ WC_Tax::_delete_tax_rate( $tax_rate_id );
+
+ // Assert notice is NOT displayed because a rate exists for the country.
+ $this->assertEmpty( $output );
+ }
+
+ /**
+ * @testDox 'tax_configuration_validation_notice' does not show notice when adjust_non_base_location_prices is disabled.
+ */
+ public function test_tax_configuration_validation_notice_respects_adjust_non_base_location_prices_filter() {
+ // Set up prices include tax option.
+ update_option( 'woocommerce_prices_include_tax', 'yes' );
+ update_option( 'woocommerce_calc_taxes', 'yes' );
+ update_option( 'woocommerce_default_country', 'US:CA' );
+
+ // Mock the screen.
+ set_current_screen( 'woocommerce_page_wc-settings' );
+
+ // Ensure no tax rates exist for US.
+ global $wpdb;
+ $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_country = 'US' OR tax_rate_country = ''" );
+
+ // Disable non-base location price adjustments.
+ add_filter( 'woocommerce_adjust_non_base_location_prices', '__return_false' );
+
+ $sut = new WC_Settings_Tax();
+
+ // Capture output.
+ ob_start();
+ $sut->tax_configuration_validation_notice();
+ $output = ob_get_clean();
+
+ // Remove filter.
+ remove_filter( 'woocommerce_adjust_non_base_location_prices', '__return_false' );
+
+ // Assert notice is NOT displayed because price adjustment is disabled.
+ $this->assertEmpty( $output );
+ }
}