PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.4
WooCommerce v10.9.4
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 1 month ago class-wc-settings-accounts.php 5 months ago class-wc-settings-advanced.php 1 month ago class-wc-settings-checkout.php 5 years ago class-wc-settings-emails.php 1 month ago class-wc-settings-general.php 1 month ago class-wc-settings-integrations.php 1 year ago class-wc-settings-page.php 1 week ago class-wc-settings-payment-gateways.php 1 week ago class-wc-settings-point-of-sale.php 3 months ago class-wc-settings-products.php 1 month ago class-wc-settings-shipping.php 3 months ago class-wc-settings-site-visibility.php 2 years ago class-wc-settings-tax.php 5 months ago
class-wc-settings-tax.php
476 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 $label ) {
201 foreach ( $label as $code => $state ) {
202 $states[] = array(
203 'value' => $code,
204 'label' => esc_js( html_entity_decode( $state ) ),
205 );
206 }
207 }
208
209 $base_url = admin_url(
210 add_query_arg(
211 array(
212 'page' => 'wc-settings',
213 'tab' => 'tax',
214 'section' => $current_section,
215 ),
216 'admin.php'
217 )
218 );
219
220 // Localize and enqueue our js.
221 wp_localize_script(
222 'wc-settings-tax',
223 'htmlSettingsTaxLocalizeScript',
224 array(
225 'current_class' => $current_class,
226 'wc_tax_nonce' => wp_create_nonce( 'wc_tax_nonce-class:' . $current_class ),
227 'base_url' => $base_url,
228 'rates' => array_values( WC_Tax::get_rates_for_tax_class( $current_class ) ),
229 'page' => ! empty( $_GET['p'] ) ? absint( $_GET['p'] ) : 1, // phpcs:ignore WordPress.Security.NonceVerification.Recommended
230 'limit' => 100,
231 'countries' => $countries,
232 'states' => $states,
233 'default_rate' => array(
234 'tax_rate_id' => 0,
235 'tax_rate_country' => '',
236 'tax_rate_state' => '',
237 'tax_rate' => '',
238 'tax_rate_name' => '',
239 'tax_rate_priority' => 1,
240 'tax_rate_compound' => 0,
241 'tax_rate_shipping' => 1,
242 'tax_rate_order' => null,
243 'tax_rate_class' => $current_class,
244 ),
245 'strings' => array(
246 'no_rows_selected' => __( 'No row(s) selected', 'woocommerce' ),
247 'unload_confirmation_msg' => __( 'Your changed data will be lost if you leave this page without saving.', 'woocommerce' ),
248 'csv_data_cols' => array(
249 __( 'Country code', 'woocommerce' ),
250 __( 'State code', 'woocommerce' ),
251 __( 'Postcode / ZIP', 'woocommerce' ),
252 __( 'City', 'woocommerce' ),
253 __( 'Rate %', 'woocommerce' ),
254 __( 'Tax name', 'woocommerce' ),
255 __( 'Priority', 'woocommerce' ),
256 __( 'Compound', 'woocommerce' ),
257 __( 'Shipping', 'woocommerce' ),
258 __( 'Tax class', 'woocommerce' ),
259 ),
260 ),
261 )
262 );
263 wp_enqueue_script( 'wc-settings-tax' );
264
265 include __DIR__ . '/views/html-settings-tax.php';
266 }
267
268 /**
269 * Get tax class being edited.
270 *
271 * @return string
272 */
273 private static function get_current_tax_class() {
274 global $current_section;
275
276 $tax_classes = WC_Tax::get_tax_classes();
277 $current_class = '';
278
279 foreach ( $tax_classes as $class ) {
280 if ( sanitize_title( $class ) === $current_section ) {
281 $current_class = $class;
282 }
283 }
284
285 return $current_class;
286 }
287
288 /**
289 * Get a posted tax rate.
290 *
291 * @param string $key Key of tax rate in the post data array.
292 * @param int $order Position/order of rate.
293 * @param string $class Tax class for rate.
294 * @return array
295 */
296 private function get_posted_tax_rate( $key, $order, $class ) {
297 // phpcs:disable WordPress.Security.NonceVerification.Missing -- this is called from 'save_tax_rates' only, where nonce is already verified.
298 $tax_rate = array();
299 $tax_rate_keys = array(
300 'tax_rate_country',
301 'tax_rate_state',
302 'tax_rate',
303 'tax_rate_name',
304 'tax_rate_priority',
305 );
306
307 // phpcs:disable WordPress.Security.NonceVerification.Missing
308 foreach ( $tax_rate_keys as $tax_rate_key ) {
309 if ( isset( $_POST[ $tax_rate_key ], $_POST[ $tax_rate_key ][ $key ] ) ) {
310 $tax_rate[ $tax_rate_key ] = wc_clean( wp_unslash( $_POST[ $tax_rate_key ][ $key ] ) );
311 }
312 }
313
314 $tax_rate['tax_rate_compound'] = isset( $_POST['tax_rate_compound'][ $key ] ) ? 1 : 0;
315 $tax_rate['tax_rate_shipping'] = isset( $_POST['tax_rate_shipping'][ $key ] ) ? 1 : 0;
316 $tax_rate['tax_rate_order'] = $order;
317 $tax_rate['tax_rate_class'] = $class;
318 // phpcs:enable WordPress.Security.NonceVerification.Missing
319
320 return $tax_rate;
321 // phpcs:enable WordPress.Security.NonceVerification.Missing
322 }
323
324 /**
325 * Save tax rates.
326 */
327 public function save_tax_rates() {
328 // phpcs:disable WordPress.Security.NonceVerification.Missing -- this is called via "do_action('woocommerce_settings_save_'...") in base class, where nonce is verified first.
329 global $wpdb;
330
331 $current_class = sanitize_title( self::get_current_tax_class() );
332 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Missing
333 $posted_countries = wc_clean( wp_unslash( $_POST['tax_rate_country'] ) );
334
335 // get the tax rate id of the first submitted row.
336 $first_tax_rate_id = key( $posted_countries );
337
338 // get the order position of the first tax rate id.
339 $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 ) ) );
340
341 $index = isset( $tax_rate_order ) ? $tax_rate_order : 0;
342
343 // Loop posted fields.
344 // phpcs:disable WordPress.Security.NonceVerification.Missing
345 foreach ( $posted_countries as $key => $value ) {
346 $mode = ( 0 === strpos( $key, 'new-' ) ) ? 'insert' : 'update';
347 $tax_rate = $this->get_posted_tax_rate( $key, $index ++, $current_class );
348
349 if ( 'insert' === $mode ) {
350 $tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate );
351 } elseif ( isset( $_POST['remove_tax_rate'][ $key ] ) && 1 === absint( $_POST['remove_tax_rate'][ $key ] ) ) {
352 $tax_rate_id = absint( $key );
353 WC_Tax::_delete_tax_rate( $tax_rate_id );
354 continue;
355 } else {
356 $tax_rate_id = absint( $key );
357 WC_Tax::_update_tax_rate( $tax_rate_id, $tax_rate );
358 }
359
360 if ( isset( $_POST['tax_rate_postcode'][ $key ] ) ) {
361 WC_Tax::_update_tax_rate_postcodes( $tax_rate_id, wc_clean( wp_unslash( $_POST['tax_rate_postcode'][ $key ] ) ) );
362 }
363 if ( isset( $_POST['tax_rate_city'][ $key ] ) ) {
364 WC_Tax::_update_tax_rate_cities( $tax_rate_id, wc_clean( wp_unslash( $_POST['tax_rate_city'][ $key ] ) ) );
365 }
366 }
367 // phpcs:enable WordPress.Security.NonceVerification.Missing
368 }
369
370 /**
371 * Display admin notice when tax-inclusive pricing is enabled without a base tax rate.
372 *
373 * @since 10.5.0
374 * @internal This method is public only because it is used as a hook callback.
375 *
376 * @return void
377 */
378 public function tax_configuration_validation_notice(): void {
379 // Only show on WooCommerce settings pages.
380 $screen = get_current_screen();
381 if ( ! $screen || 'woocommerce_page_wc-settings' !== $screen->id ) {
382 return;
383 }
384
385 // Don't show if taxes are disabled.
386 if ( ! wc_tax_enabled() ) {
387 return;
388 }
389
390 // Check if prices are entered with tax.
391 if ( 'yes' !== get_option( 'woocommerce_prices_include_tax' ) ) {
392 return;
393 }
394
395 /**
396 * Filters if taxes should be removed from locations outside the store base location.
397 *
398 * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing
399 * with out of base locations. e.g. If a product costs 10 including tax, all users will pay 10
400 * regardless of location and taxes.
401 *
402 * @since 2.4.7
403 *
404 * @param bool $adjust_non_base_location_prices True by default.
405 */
406 if ( ! apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) {
407 return;
408 }
409
410 // Check if base location has tax rates configured.
411 $base_location = wc_get_base_location();
412 if ( empty( $base_location['country'] ) ) {
413 return;
414 }
415
416 $has_base_rate = $this->has_standard_tax_rate_for_country( $base_location['country'] );
417
418 // If no base rates exist, show warning.
419 if ( ! $has_base_rate ) {
420 /**
421 * Filter whether to show the tax configuration incomplete notice.
422 *
423 * @since 10.5.0
424 *
425 * @param bool $show_notice Whether to show the notice. Default true.
426 */
427 if ( ! apply_filters( 'woocommerce_show_tax_configuration_notice', true ) ) {
428 return;
429 }
430 ?>
431 <div class="notice notice-warning">
432 <p>
433 <strong><?php esc_html_e( 'Tax configuration incomplete', 'woocommerce' ); ?></strong>
434 </p>
435 <p>
436 <?php
437 echo wp_kses_post(
438 sprintf(
439 /* translators: 1: country code, 2: opening link tag, 3: closing link tag */
440 __( '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' ),
441 esc_html( $base_location['country'] ),
442 '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=tax&section=standard' ) ) . '">',
443 '</a>'
444 )
445 );
446 ?>
447 </p>
448 </div>
449 <?php
450 }
451 }
452
453 /**
454 * Check if a standard tax rate exists for a given country.
455 *
456 * @since 10.5.0
457 *
458 * @param string $country Country code.
459 * @return bool True if at least one standard tax rate exists for the country.
460 */
461 private function has_standard_tax_rate_for_country( $country ) {
462 global $wpdb;
463
464 $count = $wpdb->get_var(
465 $wpdb->prepare(
466 "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_tax_rates WHERE (tax_rate_country = %s OR tax_rate_country = '') AND tax_rate_class = ''",
467 $country
468 )
469 );
470
471 return $count > 0;
472 }
473 }
474
475 return new WC_Settings_Tax();
476