PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.1.0
WooCommerce v11.1.0
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / admin / settings / class-wc-settings-tax.php
woocommerce / includes / admin / settings Last commit date
views 2 weeks ago class-wc-settings-accounts.php 7 months ago class-wc-settings-advanced.php 2 months ago class-wc-settings-checkout.php 5 years ago class-wc-settings-emails.php 2 weeks ago class-wc-settings-general.php 3 months ago class-wc-settings-integrations.php 1 year ago class-wc-settings-page.php 2 weeks ago class-wc-settings-payment-gateways.php 2 months ago class-wc-settings-point-of-sale.php 1 month ago class-wc-settings-products.php 1 month ago class-wc-settings-shipping.php 2 weeks ago class-wc-settings-site-visibility.php 2 years ago class-wc-settings-tax.php 1 month ago
class-wc-settings-tax.php
477 lines
1 <?php
2 /**
3 * WooCommerce Tax Settings
4 *
5 * @package WooCommerce\Admin
6 * @version 2.1.0
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 if ( class_exists( 'WC_Settings_Tax', false ) ) {
14 return new WC_Settings_Tax();
15 }
16
17 /**
18 * WC_Settings_Tax.
19 */
20 class WC_Settings_Tax extends WC_Settings_Page {
21
22 /**
23 * Constructor.
24 */
25 public function __construct() {
26 $this->id = 'tax';
27 $this->label = __( 'Tax', 'woocommerce' );
28
29 add_filter( 'woocommerce_settings_tabs_array', array( $this, 'add_settings_page' ), 20 );
30 add_action( 'woocommerce_admin_field_conflict_error', array( $this, 'conflict_error' ) );
31 if ( wc_tax_enabled() ) {
32 add_action( 'woocommerce_sections_' . $this->id, array( $this, 'output_sections' ) );
33 add_action( 'woocommerce_settings_' . $this->id, array( $this, 'output' ) );
34 add_action( 'woocommerce_settings_save_' . $this->id, array( $this, 'save' ) );
35 add_action( 'admin_notices', array( $this, 'tax_configuration_validation_notice' ) );
36 }
37 }
38
39 /**
40 * Setting page icon.
41 *
42 * @var string
43 */
44 public $icon = 'percent';
45
46 /**
47 * Creates the React mount point for the embedded banner.
48 */
49 public function conflict_error() {
50 ?>
51 <tr valign="top">
52 <th scope="row" class="titledesc woocommerce_admin_tax_settings_slotfill_th">
53 </th>
54 <td class="forminp forminp-text woocommerce_admin_tax_settings_slotfill_td">
55 <div id="wc_tax_settings_slotfill"> </div>
56 </td>
57 </tr>
58 <?php
59 }
60
61 /**
62 * Add this page to settings.
63 *
64 * @param array $pages Existing pages.
65 * @return array|mixed
66 */
67 public function add_settings_page( $pages ) {
68 if ( wc_tax_enabled() ) {
69 return parent::add_settings_page( $pages );
70 } else {
71 return $pages;
72 }
73 }
74
75 /**
76 * Get own sections.
77 *
78 * @return array
79 */
80 protected function get_own_sections() {
81 $sections = array(
82 '' => __( 'Tax options', 'woocommerce' ),
83 'standard' => __( 'Standard rates', 'woocommerce' ),
84 );
85
86 // Get tax classes and display as links.
87 $tax_classes = WC_Tax::get_tax_classes();
88
89 foreach ( $tax_classes as $class ) {
90 /* translators: $s tax rate section name */
91 $sections[ sanitize_title( $class ) ] = sprintf( __( '%s rates', 'woocommerce' ), $class );
92 }
93
94 return $sections;
95 }
96
97 /**
98 * Get settings array.
99 *
100 * @return array
101 */
102 public function get_settings_for_default_section() {
103 return include __DIR__ . '/views/settings-tax.php';
104 }
105
106 /**
107 * Output the settings.
108 */
109 public function output() {
110 global $current_section;
111
112 $tax_classes = WC_Tax::get_tax_class_slugs();
113
114 if ( 'standard' === $current_section || in_array( $current_section, array_filter( $tax_classes ), true ) ) {
115 $this->output_tax_rates();
116 } else {
117 parent::output();
118 }
119 }
120
121 /**
122 * Save settings.
123 */
124 public function save() {
125 // phpcs:disable WordPress.Security.NonceVerification.Missing
126 global $current_section;
127
128 if ( ! $current_section ) {
129 $this->save_settings_for_current_section();
130
131 if ( isset( $_POST['woocommerce_tax_classes'] ) ) {
132 $this->save_tax_classes( wp_unslash( $_POST['woocommerce_tax_classes'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
133 }
134 } elseif ( ! empty( $_POST['tax_rate_country'] ) ) {
135 $this->save_tax_rates();
136 } else {
137 $this->save_settings_for_current_section();
138 }
139
140 $this->do_update_options_action();
141
142 // Invalidate caches.
143 WC_Cache_Helper::invalidate_cache_group( 'taxes' );
144 WC_Cache_Helper::get_transient_version( 'shipping', true );
145 // phpcs:enable WordPress.Security.NonceVerification.Missing
146 }
147
148 /**
149 * Saves tax classes defined in the textarea to the tax class table instead of an option.
150 *
151 * @param string $raw_tax_classes Posted value.
152 * @return null
153 */
154 public function save_tax_classes( $raw_tax_classes ) {
155 $tax_classes = array_filter( array_map( 'trim', explode( "\n", $raw_tax_classes ) ) );
156 $existing_tax_classes = WC_Tax::get_tax_classes();
157 $removed = array_diff( $existing_tax_classes, $tax_classes );
158 $added = array_diff( $tax_classes, $existing_tax_classes );
159
160 foreach ( $removed as $name ) {
161 WC_Tax::delete_tax_class_by( 'name', $name );
162 }
163
164 foreach ( $added as $name ) {
165 $tax_class = WC_Tax::create_tax_class( $name );
166
167 // Display any error that could be triggered while creating tax classes.
168 if ( is_wp_error( $tax_class ) ) {
169 WC_Admin_Settings::add_error(
170 sprintf(
171 /* translators: 1: tax class name 2: error message */
172 esc_html__( 'Additional tax class "%1$s" couldn\'t be saved. %2$s.', 'woocommerce' ),
173 esc_html( $name ),
174 $tax_class->get_error_message()
175 )
176 );
177 }
178 }
179
180 return null;
181 }
182
183 /**
184 * Output tax rate tables.
185 */
186 public function output_tax_rates() {
187 global $current_section;
188
189 $current_class = self::get_current_tax_class();
190
191 $countries = array();
192 foreach ( WC()->countries->get_allowed_countries() as $value => $label ) {
193 $countries[] = array(
194 'value' => $value,
195 'label' => esc_js( html_entity_decode( $label ) ),
196 );
197 }
198
199 $states = array();
200 foreach ( WC()->countries->get_allowed_country_states() as $country_code => $country_states ) {
201 foreach ( $country_states as $code => $state ) {
202 $states[] = array(
203 'value' => $code,
204 'label' => esc_js( html_entity_decode( $state, ENT_QUOTES ) ),
205 'country' => $country_code,
206 );
207 }
208 }
209
210 $base_url = admin_url(
211 add_query_arg(
212 array(
213 'page' => 'wc-settings',
214 'tab' => 'tax',
215 'section' => $current_section,
216 ),
217 'admin.php'
218 )
219 );
220
221 // Localize and enqueue our js.
222 wp_localize_script(
223 'wc-settings-tax',
224 'htmlSettingsTaxLocalizeScript',
225 array(
226 'current_class' => $current_class,
227 'wc_tax_nonce' => wp_create_nonce( 'wc_tax_nonce-class:' . $current_class ),
228 'base_url' => $base_url,
229 'rates' => array_values( WC_Tax::get_rates_for_tax_class( $current_class ) ),
230 'page' => ! empty( $_GET['p'] ) ? absint( $_GET['p'] ) : 1, // phpcs:ignore WordPress.Security.NonceVerification.Recommended
231 'limit' => 100,
232 'countries' => $countries,
233 'states' => $states,
234 'default_rate' => array(
235 'tax_rate_id' => 0,
236 'tax_rate_country' => '',
237 'tax_rate_state' => '',
238 'tax_rate' => '',
239 'tax_rate_name' => '',
240 'tax_rate_priority' => 1,
241 'tax_rate_compound' => 0,
242 'tax_rate_shipping' => 1,
243 'tax_rate_order' => null,
244 'tax_rate_class' => $current_class,
245 ),
246 'strings' => array(
247 'no_rows_selected' => __( 'No row(s) selected', 'woocommerce' ),
248 'unload_confirmation_msg' => __( 'Your changed data will be lost if you leave this page without saving.', 'woocommerce' ),
249 'csv_data_cols' => array(
250 __( 'Country code', 'woocommerce' ),
251 __( 'State code', 'woocommerce' ),
252 __( 'Postcode / ZIP', 'woocommerce' ),
253 __( 'City', 'woocommerce' ),
254 __( 'Rate %', 'woocommerce' ),
255 __( 'Tax name', 'woocommerce' ),
256 __( 'Priority', 'woocommerce' ),
257 __( 'Compound', 'woocommerce' ),
258 __( 'Shipping', 'woocommerce' ),
259 __( 'Tax class', 'woocommerce' ),
260 ),
261 ),
262 )
263 );
264 wp_enqueue_script( 'wc-settings-tax' );
265
266 include __DIR__ . '/views/html-settings-tax.php';
267 }
268
269 /**
270 * Get tax class being edited.
271 *
272 * @return string
273 */
274 private static function get_current_tax_class() {
275 global $current_section;
276
277 $tax_classes = WC_Tax::get_tax_classes();
278 $current_class = '';
279
280 foreach ( $tax_classes as $class ) {
281 if ( sanitize_title( $class ) === $current_section ) {
282 $current_class = $class;
283 }
284 }
285
286 return $current_class;
287 }
288
289 /**
290 * Get a posted tax rate.
291 *
292 * @param string $key Key of tax rate in the post data array.
293 * @param int $order Position/order of rate.
294 * @param string $class Tax class for rate.
295 * @return array
296 */
297 private function get_posted_tax_rate( $key, $order, $class ) {
298 // phpcs:disable WordPress.Security.NonceVerification.Missing -- this is called from 'save_tax_rates' only, where nonce is already verified.
299 $tax_rate = array();
300 $tax_rate_keys = array(
301 'tax_rate_country',
302 'tax_rate_state',
303 'tax_rate',
304 'tax_rate_name',
305 'tax_rate_priority',
306 );
307
308 // phpcs:disable WordPress.Security.NonceVerification.Missing
309 foreach ( $tax_rate_keys as $tax_rate_key ) {
310 if ( isset( $_POST[ $tax_rate_key ], $_POST[ $tax_rate_key ][ $key ] ) ) {
311 $tax_rate[ $tax_rate_key ] = wc_clean( wp_unslash( $_POST[ $tax_rate_key ][ $key ] ) );
312 }
313 }
314
315 $tax_rate['tax_rate_compound'] = isset( $_POST['tax_rate_compound'][ $key ] ) ? 1 : 0;
316 $tax_rate['tax_rate_shipping'] = isset( $_POST['tax_rate_shipping'][ $key ] ) ? 1 : 0;
317 $tax_rate['tax_rate_order'] = $order;
318 $tax_rate['tax_rate_class'] = $class;
319 // phpcs:enable WordPress.Security.NonceVerification.Missing
320
321 return $tax_rate;
322 // phpcs:enable WordPress.Security.NonceVerification.Missing
323 }
324
325 /**
326 * Save tax rates.
327 */
328 public function save_tax_rates() {
329 // phpcs:disable WordPress.Security.NonceVerification.Missing -- this is called via "do_action('woocommerce_settings_save_'...") in base class, where nonce is verified first.
330 global $wpdb;
331
332 $current_class = sanitize_title( self::get_current_tax_class() );
333 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Missing
334 $posted_countries = wc_clean( wp_unslash( $_POST['tax_rate_country'] ) );
335
336 // get the tax rate id of the first submitted row.
337 $first_tax_rate_id = key( $posted_countries );
338
339 // get the order position of the first tax rate id.
340 $tax_rate_order = absint( $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_order FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $first_tax_rate_id ) ) );
341
342 $index = isset( $tax_rate_order ) ? $tax_rate_order : 0;
343
344 // Loop posted fields.
345 // phpcs:disable WordPress.Security.NonceVerification.Missing
346 foreach ( $posted_countries as $key => $value ) {
347 $mode = ( 0 === strpos( $key, 'new-' ) ) ? 'insert' : 'update';
348 $tax_rate = $this->get_posted_tax_rate( $key, $index ++, $current_class );
349
350 if ( 'insert' === $mode ) {
351 $tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate );
352 } elseif ( isset( $_POST['remove_tax_rate'][ $key ] ) && 1 === absint( $_POST['remove_tax_rate'][ $key ] ) ) {
353 $tax_rate_id = absint( $key );
354 WC_Tax::_delete_tax_rate( $tax_rate_id );
355 continue;
356 } else {
357 $tax_rate_id = absint( $key );
358 WC_Tax::_update_tax_rate( $tax_rate_id, $tax_rate );
359 }
360
361 if ( isset( $_POST['tax_rate_postcode'][ $key ] ) ) {
362 WC_Tax::_update_tax_rate_postcodes( $tax_rate_id, wc_clean( wp_unslash( $_POST['tax_rate_postcode'][ $key ] ) ) );
363 }
364 if ( isset( $_POST['tax_rate_city'][ $key ] ) ) {
365 WC_Tax::_update_tax_rate_cities( $tax_rate_id, wc_clean( wp_unslash( $_POST['tax_rate_city'][ $key ] ) ) );
366 }
367 }
368 // phpcs:enable WordPress.Security.NonceVerification.Missing
369 }
370
371 /**
372 * Display admin notice when tax-inclusive pricing is enabled without a base tax rate.
373 *
374 * @since 10.5.0
375 * @internal This method is public only because it is used as a hook callback.
376 *
377 * @return void
378 */
379 public function tax_configuration_validation_notice(): void {
380 // Only show on WooCommerce settings pages.
381 $screen = get_current_screen();
382 if ( ! $screen || 'woocommerce_page_wc-settings' !== $screen->id ) {
383 return;
384 }
385
386 // Don't show if taxes are disabled.
387 if ( ! wc_tax_enabled() ) {
388 return;
389 }
390
391 // Check if prices are entered with tax.
392 if ( 'yes' !== get_option( 'woocommerce_prices_include_tax' ) ) {
393 return;
394 }
395
396 /**
397 * Filters if taxes should be removed from locations outside the store base location.
398 *
399 * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing
400 * with out of base locations. e.g. If a product costs 10 including tax, all users will pay 10
401 * regardless of location and taxes.
402 *
403 * @since 2.4.7
404 *
405 * @param bool $adjust_non_base_location_prices True by default.
406 */
407 if ( ! apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) {
408 return;
409 }
410
411 // Check if base location has tax rates configured.
412 $base_location = wc_get_base_location();
413 if ( empty( $base_location['country'] ) ) {
414 return;
415 }
416
417 $has_base_rate = $this->has_standard_tax_rate_for_country( $base_location['country'] );
418
419 // If no base rates exist, show warning.
420 if ( ! $has_base_rate ) {
421 /**
422 * Filter whether to show the tax configuration incomplete notice.
423 *
424 * @since 10.5.0
425 *
426 * @param bool $show_notice Whether to show the notice. Default true.
427 */
428 if ( ! apply_filters( 'woocommerce_show_tax_configuration_notice', true ) ) {
429 return;
430 }
431 ?>
432 <div class="notice notice-warning">
433 <p>
434 <strong><?php esc_html_e( 'Tax configuration incomplete', 'woocommerce' ); ?></strong>
435 </p>
436 <p>
437 <?php
438 echo wp_kses_post(
439 sprintf(
440 /* translators: 1: country code, 2: opening link tag, 3: closing link tag */
441 __( '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' ),
442 esc_html( $base_location['country'] ),
443 '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=tax&section=standard' ) ) . '">',
444 '</a>'
445 )
446 );
447 ?>
448 </p>
449 </div>
450 <?php
451 }
452 }
453
454 /**
455 * Check if a standard tax rate exists for a given country.
456 *
457 * @since 10.5.0
458 *
459 * @param string $country Country code.
460 * @return bool True if at least one standard tax rate exists for the country.
461 */
462 private function has_standard_tax_rate_for_country( $country ) {
463 global $wpdb;
464
465 $count = $wpdb->get_var(
466 $wpdb->prepare(
467 "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_tax_rates WHERE (tax_rate_country = %s OR tax_rate_country = '') AND tax_rate_class = ''",
468 $country
469 )
470 );
471
472 return $count > 0;
473 }
474 }
475
476 return new WC_Settings_Tax();
477