1 <?php
2 3 4 5 6 7 8 9
10
11 if ( ! defined( 'ABSPATH' ) ) exit;
12
13 if ( ! class_exists( 'WC_Settings_Tax' ) ) :
14
15 16 17
18 class WC_Settings_Tax extends WC_Settings_Page {
19
20 21 22
23 public function __construct() {
24 $this->id = 'tax';
25 $this->label = __( 'Tax', 'woocommerce' );
26
27 add_filter( 'woocommerce_settings_tabs_array', array( $this, 'add_settings_page' ), 20 );
28 add_action( 'woocommerce_sections_' . $this->id, array( $this, 'output_sections' ) );
29 add_action( 'woocommerce_settings_' . $this->id, array( $this, 'output' ) );
30 add_action( 'woocommerce_settings_save_' . $this->id, array( $this, 'save' ) );
31 }
32
33 34 35 36 37
38 public function get_sections() {
39 $sections = array(
40 '' => __( 'Tax Options', 'woocommerce' ),
41 'standard' => __( 'Standard Rates', 'woocommerce' )
42 );
43
44
45 $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) );
46
47 if ( $tax_classes )
48 foreach ( $tax_classes as $class )
49 $sections[ sanitize_title( $class ) ] = sprintf( __( '%s Rates', 'woocommerce' ), $class );
50
51 return $sections;
52 }
53
54 55 56 57 58
59 public function get_settings() {
60 $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
61 $classes_options = array();
62 if ( $tax_classes )
63 foreach ( $tax_classes as $class )
64 $classes_options[ sanitize_title( $class ) ] = esc_html( $class );
65
66 return apply_filters('woocommerce_tax_settings', array(
67
68 array( 'title' => __( 'Tax Options', 'woocommerce' ), 'type' => 'title','desc' => '', 'id' => 'tax_options' ),
69
70 array(
71 'title' => __( 'Enable Taxes', 'woocommerce' ),
72 'desc' => __( 'Enable taxes and tax calculations', 'woocommerce' ),
73 'id' => 'woocommerce_calc_taxes',
74 'default' => 'no',
75 'type' => 'checkbox'
76 ),
77
78 array(
79 'title' => __( 'Prices Entered With Tax', 'woocommerce' ),
80 'id' => 'woocommerce_prices_include_tax',
81 'default' => 'no',
82 'type' => 'radio',
83 'desc_tip' => __( 'This option is important as it will affect how you input prices. Changing it will not update existing products.', 'woocommerce' ),
84 'options' => array(
85 'yes' => __( 'Yes, I will enter prices inclusive of tax', 'woocommerce' ),
86 'no' => __( 'No, I will enter prices exclusive of tax', 'woocommerce' )
87 ),
88 ),
89
90 array(
91 'title' => __( 'Calculate Tax Based On:', 'woocommerce' ),
92 'id' => 'woocommerce_tax_based_on',
93 'desc_tip' => __( 'This option determines which address is used to calculate tax.', 'woocommerce' ),
94 'default' => 'shipping',
95 'type' => 'select',
96 'options' => array(
97 'shipping' => __( 'Customer shipping address', 'woocommerce' ),
98 'billing' => __( 'Customer billing address', 'woocommerce' ),
99 'base' => __( 'Shop base address', 'woocommerce' )
100 ),
101 ),
102
103 array(
104 'title' => __( 'Default Customer Address:', 'woocommerce' ),
105 'id' => 'woocommerce_default_customer_address',
106 'desc_tip' => __( 'This option determines the customers default address (before they input their own).', 'woocommerce' ),
107 'default' => 'base',
108 'type' => 'select',
109 'options' => array(
110 '' => __( 'No address', 'woocommerce' ),
111 'base' => __( 'Shop base address', 'woocommerce' ),
112 ),
113 ),
114
115 array(
116 'title' => __( 'Shipping Tax Class:', 'woocommerce' ),
117 'desc' => __( 'Optionally control which tax class shipping gets, or leave it so shipping tax is based on the cart items themselves.', 'woocommerce' ),
118 'id' => 'woocommerce_shipping_tax_class',
119 'css' => 'min-width:150px;',
120 'default' => 'title',
121 'type' => 'select',
122 'options' => array( '' => __( 'Shipping tax class based on cart items', 'woocommerce' ), 'standard' => __( 'Standard', 'woocommerce' ) ) + $classes_options,
123 'desc_tip' => true,
124 ),
125
126 array(
127 'title' => __( 'Rounding', 'woocommerce' ),
128 'desc' => __( 'Round tax at subtotal level, instead of rounding per line', 'woocommerce' ),
129 'id' => 'woocommerce_tax_round_at_subtotal',
130 'default' => 'no',
131 'type' => 'checkbox',
132 ),
133
134 array(
135 'title' => __( 'Additional Tax Classes', 'woocommerce' ),
136 'desc' => __( 'List additional tax classes below (1 per line). This is in addition to the default <code>Standard Rate</code>. Tax classes can be assigned to products.', 'woocommerce' ),
137 'id' => 'woocommerce_tax_classes',
138 'css' => 'width:100%; height: 65px;',
139 'type' => 'textarea',
140 'default' => sprintf( __( 'Reduced Rate%sZero Rate', 'woocommerce' ), PHP_EOL )
141 ),
142
143 array(
144 'title' => __( 'Display prices in the shop:', 'woocommerce' ),
145 'id' => 'woocommerce_tax_display_shop',
146 'default' => 'excl',
147 'type' => 'select',
148 'options' => array(
149 'incl' => __( 'Including tax', 'woocommerce' ),
150 'excl' => __( 'Excluding tax', 'woocommerce' ),
151 )
152 ),
153
154 array(
155 'title' => __( 'Price display suffix:', 'woocommerce' ),
156 'id' => 'woocommerce_price_display_suffix',
157 'default' => '',
158 'type' => 'text',
159 'desc' => __( 'Define text to show after your product prices. This could be, for example, "inc. Vat" to explain your pricing. You can also have prices substituted here using one of the following: <code>{price_including_tax}, {price_excluding_tax}</code>.', 'woocommerce' ),
160 ),
161
162 array(
163 'title' => __( 'Display prices during cart/checkout:', 'woocommerce' ),
164 'id' => 'woocommerce_tax_display_cart',
165 'default' => 'excl',
166 'type' => 'select',
167 'options' => array(
168 'incl' => __( 'Including tax', 'woocommerce' ),
169 'excl' => __( 'Excluding tax', 'woocommerce' ),
170 ),
171 'autoload' => false
172 ),
173
174 array(
175 'title' => __( 'Display tax totals:', 'woocommerce' ),
176 'id' => 'woocommerce_tax_total_display',
177 'default' => 'itemized',
178 'type' => 'select',
179 'options' => array(
180 'single' => __( 'As a single total', 'woocommerce' ),
181 'itemized' => __( 'Itemized', 'woocommerce' ),
182 ),
183 'autoload' => false
184 ),
185
186 array( 'type' => 'sectionend', 'id' => 'tax_options' ),
187
188 ));
189 }
190
191 192 193
194 public function output() {
195 global $current_section;
196
197 $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) );
198
199 if ( $current_section == 'standard' || in_array( $current_section, array_map( 'sanitize_title', $tax_classes ) ) ) {
200 $this->output_tax_rates();
201 } else {
202 $settings = $this->get_settings();
203
204 WC_Admin_Settings::output_fields( $settings );
205 }
206 }
207
208 209 210
211 public function save() {
212 global $current_section, $wpdb;
213
214 if ( ! $current_section ) {
215
216 $settings = $this->get_settings();
217 WC_Admin_Settings::save_fields( $settings );
218
219 } else {
220
221 $this->save_tax_rates();
222
223 }
224
225 $wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('_transient_wc_tax_rates_%') OR `option_name` LIKE ('_transient_timeout_wc_tax_rates_%')" );
226 }
227
228 229 230
231 public function output_tax_rates() {
232 global $woocommerce, $current_section, $wpdb;
233
234 $page = ! empty( $_GET['p'] ) ? absint( $_GET['p'] ) : 1;
235 $limit = 100;
236 $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) );
237 $current_class = '';
238
239 foreach( $tax_classes as $class )
240 if ( sanitize_title( $class ) == $current_section )
241 $current_class = $class;
242 ?>
243 <h3><?php printf( __( 'Tax Rates for the "%s" Class', 'woocommerce' ), $current_class ? esc_html( $current_class ) : __( 'Standard', 'woocommerce' ) ); ?></h3>
244 <p><?php printf( __( 'Define tax rates for countries and states below. <a href="%s">See here</a> for available alpha-2 country codes.', 'woocommerce' ), 'http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes' ); ?></p>
245 <table class="wc_tax_rates wc_input_table sortable widefat">
246 <thead>
247 <tr>
248 <th class="sort"> </th>
249
250 <th width="8%"><?php _e( 'Country Code', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e('A 2 digit country code, e.g. US. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
251
252 <th width="8%"><?php _e( 'State Code', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e('A 2 digit state code, e.g. AL. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
253
254 <th><?php _e( 'ZIP/Postcode', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e('Postcode for this rule. Semi-colon (;) separate multiple values. Leave blank to apply to all areas. Wildcards (*) can be used. Ranges for numeric postcodes (e.g. 12345-12350) will be expanded into individual postcodes.', 'woocommerce'); ?>">[?]</span></th>
255
256 <th><?php _e( 'City', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e('Cities for this rule. Semi-colon (;) separate multiple values. Leave blank to apply to all cities.', 'woocommerce'); ?>">[?]</span></th>
257
258 <th width="8%"><?php _e( 'Rate %', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e( 'Enter a tax rate (percentage) to 4 decimal places.', 'woocommerce' ); ?>">[?]</span></th>
259
260 <th width="8%"><?php _e( 'Tax Name', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e('Enter a name for this tax rate.', 'woocommerce'); ?>">[?]</span></th>
261
262 <th width="8%"><?php _e( 'Priority', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e('Choose a priority for this tax rate. Only 1 matching rate per priority will be used. To define multiple tax rates for a single area you need to specify a different priority per rate.', 'woocommerce'); ?>">[?]</span></th>
263
264 <th width="8%"><?php _e( 'Compound', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e('Choose whether or not this is a compound rate. Compound tax rates are applied on top of other tax rates.', 'woocommerce'); ?>">[?]</span></th>
265
266 <th width="8%"><?php _e( 'Shipping', 'woocommerce' ); ?> <span class="tips" data-tip="<?php _e('Choose whether or not this tax rate also gets applied to shipping.', 'woocommerce'); ?>">[?]</span></th>
267
268 </tr>
269 </thead>
270 <tfoot>
271 <tr>
272 <th colspan="10">
273 <a href="#" class="button plus insert"><?php _e( 'Insert row', 'woocommerce' ); ?></a>
274 <a href="#" class="button minus remove_tax_rates"><?php _e( 'Remove selected row(s)', 'woocommerce' ); ?></a>
275
276 <div class="pagination">
277 <?php
278 echo str_replace( 'page-numbers', 'page-numbers button', paginate_links( array(
279 'base' => add_query_arg( 'p', '%#%' ),
280 'type' => 'plain',
281 'prev_text' => '«',
282 'next_text' => '»',
283 'total' => ceil( absint( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(tax_rate_id) FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class = %s;", sanitize_title( $current_class ) ) ) ) / $limit ),
284 'current' => $page
285 ) ) );
286 ?>
287 </div>
288
289 <a href="#" download="tax_rates.csv" class="button export"><?php _e( 'Export CSV', 'woocommerce' ); ?></a>
290 <a href="<?php echo admin_url( 'admin.php?import=woocommerce_tax_rate_csv' ); ?>" class="button import"><?php _e( 'Import CSV', 'woocommerce' ); ?></a>
291 </th>
292 </tr>
293 </tfoot>
294 <tbody id="rates">
295 <?php
296 $rates = $wpdb->get_results( $wpdb->prepare(
297 "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates
298 WHERE tax_rate_class = %s
299 ORDER BY tax_rate_order
300 LIMIT %d, %d
301 " ,
302 sanitize_title( $current_class ),
303 ( $page - 1 ) * $limit,
304 $limit
305 ) );
306
307 foreach ( $rates as $rate ) {
308 ?>
309 <tr>
310 <td class="sort"><input type="hidden" class="remove_tax_rate" name="remove_tax_rate[<?php echo $rate->tax_rate_id ?>]" value="0" /></td>
311
312 <td class="country" width="8%">
313 <input type="text" value="<?php echo esc_attr( $rate->tax_rate_country ) ?>" placeholder="*" name="tax_rate_country[<?php echo $rate->tax_rate_id ?>]" />
314 </td>
315
316 <td class="state" width="8%">
317 <input type="text" value="<?php echo esc_attr( $rate->tax_rate_state ) ?>" placeholder="*" name="tax_rate_state[<?php echo $rate->tax_rate_id ?>]" />
318 </td>
319
320 <td class="postcode">
321 <input type="text" value="<?php
322 $locations = $wpdb->get_col( $wpdb->prepare( "SELECT location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type='postcode' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) );
323
324 echo esc_attr( implode( '; ', $locations ) );
325 ?>" placeholder="*" data-name="tax_rate_postcode[<?php echo $rate->tax_rate_id ?>]" />
326 </td>
327
328 <td class="city">
329 <input type="text" value="<?php
330 $locations = $wpdb->get_col( $wpdb->prepare( "SELECT location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type='city' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) );
331 echo esc_attr( implode( '; ', $locations ) );
332 ?>" placeholder="*" data-name="tax_rate_city[<?php echo $rate->tax_rate_id ?>]" />
333 </td>
334
335 <td class="rate" width="8%">
336 <input type="number" step="any" min="0" value="<?php echo esc_attr( $rate->tax_rate ) ?>" placeholder="0" name="tax_rate[<?php echo $rate->tax_rate_id ?>]" />
337 </td>
338
339 <td class="name" width="8%">
340 <input type="text" value="<?php echo esc_attr( $rate->tax_rate_name ) ?>" name="tax_rate_name[<?php echo $rate->tax_rate_id ?>]" />
341 </td>
342
343 <td class="priority" width="8%">
344 <input type="number" step="1" min="1" value="<?php echo esc_attr( $rate->tax_rate_priority ) ?>" name="tax_rate_priority[<?php echo $rate->tax_rate_id ?>]" />
345 </td>
346
347 <td class="compound" width="8%">
348 <input type="checkbox" class="checkbox" name="tax_rate_compound[<?php echo $rate->tax_rate_id ?>]" <?php checked( $rate->tax_rate_compound, '1' ); ?> />
349 </td>
350
351 <td class="apply_to_shipping" width="8%">
352 <input type="checkbox" class="checkbox" name="tax_rate_shipping[<?php echo $rate->tax_rate_id ?>]" <?php checked($rate->tax_rate_shipping, '1' ); ?> />
353 </td>
354 </tr>
355 <?php
356 }
357 ?>
358 </tbody>
359 </table>
360 <script type="text/javascript">
361 jQuery( function() {
362 jQuery('.wc_tax_rates .remove_tax_rates').click(function() {
363 var $tbody = jQuery('.wc_tax_rates').find('tbody');
364 if ( $tbody.find('tr.current').size() > 0 ) {
365 $current = $tbody.find('tr.current');
366 $current.find('input').val('');
367 $current.find('input.remove_tax_rate').val('1');
368
369 $current.each(function(){
370 if ( jQuery(this).is('.new') )
371 jQuery(this).remove();
372 else
373 jQuery(this).hide();
374 });
375 } else {
376 alert('<?php echo esc_js( __( 'No row(s) selected', 'woocommerce' ) ); ?>');
377 }
378 return false;
379 });
380
381 jQuery('.wc_tax_rates .export').click(function() {
382
383 var csv_data = "data:application/csv;charset=utf-8,<?php _e( 'Country Code', 'woocommerce' ); ?>,<?php _e( 'State Code', 'woocommerce' ); ?>,<?php _e( 'ZIP/Postcode', 'woocommerce' ); ?>,<?php _e( 'City', 'woocommerce' ); ?>,<?php _e( 'Rate %', 'woocommerce' ); ?>,<?php _e( 'Tax Name', 'woocommerce' ); ?>,<?php _e( 'Priority', 'woocommerce' ); ?>,<?php _e( 'Compound', 'woocommerce' ); ?>,<?php _e( 'Shipping', 'woocommerce' ); ?>,<?php _e( 'Tax Class', 'woocommerce' ); ?>\n";
384
385 jQuery('#rates tr:visible').each(function() {
386 var row = '';
387 jQuery(this).find('td:not(.sort) input').each(function() {
388
389 if ( jQuery(this).is('.checkbox') ) {
390
391 if ( jQuery(this).is(':checked') ) {
392 val = 1;
393 } else {
394 val = 0;
395 }
396
397 } else {
398
399 var val = jQuery(this).val();
400
401 if ( ! val )
402 val = jQuery(this).attr('placeholder');
403 }
404
405 row = row + val + ',';
406 });
407 row = row + '<?php echo $current_class; ?>';
408
409 csv_data = csv_data + row + "\n";
410 });
411
412 jQuery(this).attr( 'href', encodeURI( csv_data ) );
413
414 return true;
415 });
416
417 jQuery('.wc_tax_rates .insert').click(function() {
418 var $tbody = jQuery('.wc_tax_rates').find('tbody');
419 var size = $tbody.find('tr').size();
420 var code = '<tr class="new">\
421 <td class="sort"> </td>\
422 <td class="country" width="8%">\
423 <input type="text" placeholder="*" name="tax_rate_country[new][' + size + ']" />\
424 </td>\
425 <td class="state" width="8%">\
426 <input type="text" placeholder="*" name="tax_rate_state[new][' + size + ']" />\
427 </td>\
428 <td class="postcode">\
429 <input type="text" placeholder="*" name="tax_rate_postcode[new][' + size + ']" />\
430 </td>\
431 <td class="city">\
432 <input type="text" placeholder="*" name="tax_rate_city[new][' + size + ']" />\
433 </td>\
434 <td class="rate" width="8%">\
435 <input type="number" step="any" min="0" placeholder="0" name="tax_rate[new][' + size + ']" />\
436 </td>\
437 <td class="name" width="8%">\
438 <input type="text" name="tax_rate_name[new][' + size + ']" />\
439 </td>\
440 <td class="priority" width="8%">\
441 <input type="number" step="1" min="1" value="1" name="tax_rate_priority[new][' + size + ']" />\
442 </td>\
443 <td class="compound" width="8%">\
444 <input type="checkbox" class="checkbox" name="tax_rate_compound[new][' + size + ']" />\
445 </td>\
446 <td class="apply_to_shipping" width="8%">\
447 <input type="checkbox" class="checkbox" name="tax_rate_shipping[new][' + size + ']" checked="checked" />\
448 </td>\
449 </tr>';
450
451 if ( $tbody.find('tr.current').size() > 0 ) {
452 $tbody.find('tr.current').after( code );
453 } else {
454 $tbody.append( code );
455 }
456
457 jQuery( "td.country input" ).autocomplete({
458 source: availableCountries,
459 minLength: 3
460 });
461
462 jQuery( "td.state input" ).autocomplete({
463 source: availableStates,
464 minLength: 3
465 });
466
467 return false;
468 });
469
470 jQuery('.wc_tax_rates td.postcode, .wc_tax_rates td.city').find('input').change(function() {
471 jQuery(this).attr( 'name', jQuery(this).attr( 'data-name' ) );
472 });
473
474 var availableCountries = [<?php
475 $countries = array();
476 foreach ( WC()->countries->get_allowed_countries() as $value => $label )
477 $countries[] = '{ label: "' . $label . '", value: "' . $value . '" }';
478 echo implode( ', ', $countries );
479 ?>];
480
481 var availableStates = [<?php
482 $countries = array();
483 foreach ( WC()->countries->get_allowed_country_states() as $value => $label )
484 foreach ( $label as $code => $state )
485 $countries[] = '{ label: "' . $state . '", value: "' . $code . '" }';
486 echo implode( ', ', $countries );
487 ?>];
488
489 jQuery( "td.country input" ).autocomplete({
490 source: availableCountries,
491 minLength: 3
492 });
493
494 jQuery( "td.state input" ).autocomplete({
495 source: availableStates,
496 minLength: 3
497 });
498 });
499 </script>
500 <?php
501 }
502
503 504 505
506 public function save_tax_rates() {
507 global $wpdb, $current_section;
508
509
510 $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) );
511 $current_class = '';
512
513 foreach( $tax_classes as $class )
514 if ( sanitize_title( $class ) == $current_section )
515 $current_class = $class;
516
517
518 $tax_rate_country = isset( $_POST['tax_rate_country'] ) ? $_POST['tax_rate_country'] : array();
519 $tax_rate_state = isset( $_POST['tax_rate_state'] ) ? $_POST['tax_rate_state'] : array();
520 $tax_rate_postcode = isset( $_POST['tax_rate_postcode'] ) ? $_POST['tax_rate_postcode'] : array();
521 $tax_rate_city = isset( $_POST['tax_rate_city'] ) ? $_POST['tax_rate_city'] : array();
522 $tax_rate = isset( $_POST['tax_rate'] ) ? $_POST['tax_rate'] : array();
523 $tax_rate_name = isset( $_POST['tax_rate_name'] ) ? $_POST['tax_rate_name'] : array();
524 $tax_rate_priority = isset( $_POST['tax_rate_priority'] ) ? $_POST['tax_rate_priority'] : array();
525 $tax_rate_compound = isset( $_POST['tax_rate_compound'] ) ? $_POST['tax_rate_compound'] : array();
526 $tax_rate_shipping = isset( $_POST['tax_rate_shipping'] ) ? $_POST['tax_rate_shipping'] : array();
527
528 $i = 0;
529
530
531 foreach ( $tax_rate_country as $key => $value ) {
532
533
534 if ( $key == 'new' ) {
535
536 foreach ( $value as $new_key => $new_value ) {
537
538
539 $country = strtoupper( wc_clean( $tax_rate_country[ $key ][ $new_key ] ) );
540 $state = strtoupper( wc_clean( $tax_rate_state[ $key ][ $new_key ] ) );
541 $postcode = wc_clean( $tax_rate_postcode[ $key ][ $new_key ] );
542 $city = wc_clean( $tax_rate_city[ $key ][ $new_key ] );
543 $rate = number_format( wc_clean( $tax_rate[ $key ][ $new_key ] ), 4, '.', '' );
544 $name = wc_clean( $tax_rate_name[ $key ][ $new_key ] );
545 $priority = absint( wc_clean( $tax_rate_priority[ $key ][ $new_key ] ) );
546 $compound = isset( $tax_rate_compound[ $key ][ $new_key ] ) ? 1 : 0;
547 $shipping = isset( $tax_rate_shipping[ $key ][ $new_key ] ) ? 1 : 0;
548
549 if ( ! $name )
550 $name = __( 'Tax', 'woocommerce' );
551
552 if ( $country == '*' )
553 $country = '';
554
555 if ( $state == '*' )
556 $state = '';
557
558 $tax_rate = array(
559 'tax_rate_country' => $country,
560 'tax_rate_state' => $state,
561 'tax_rate' => $rate,
562 'tax_rate_name' => $name,
563 'tax_rate_priority' => $priority,
564 'tax_rate_compound' => $compound,
565 'tax_rate_shipping' => $shipping,
566 'tax_rate_order' => $i,
567 'tax_rate_class' => sanitize_title( $current_class )
568 );
569
570 $wpdb->insert( $wpdb->prefix . "woocommerce_tax_rates", $tax_rate );
571
572 $tax_rate_id = $wpdb->insert_id;
573
574 do_action( 'woocommerce_tax_rate_added', $tax_rate_id, $tax_rate );
575
576 if ( ! empty( $postcode ) ) {
577 $postcodes = explode( ';', $postcode );
578 $postcodes = array_map( 'strtoupper', array_map( 'wc_clean', $postcodes ) );
579
580 $postcode_query = array();
581
582 foreach( $postcodes as $postcode )
583 if ( strstr( $postcode, '-' ) ) {
584 $postcode_parts = explode( '-', $postcode );
585
586 if ( is_numeric( $postcode_parts[0] ) && is_numeric( $postcode_parts[1] ) && $postcode_parts[1] > $postcode_parts[0] ) {
587 for ( $i = $postcode_parts[0]; $i <= $postcode_parts[1]; $i ++ ) {
588 if ( ! $i )
589 continue;
590
591 if ( strlen( $i ) < strlen( $postcode_parts[0] ) )
592 $i = str_pad( $i, strlen( $postcode_parts[0] ), "0", STR_PAD_LEFT );
593
594 $postcode_query[] = "( '" . esc_sql( $i ) . "', $tax_rate_id, 'postcode' )";
595 }
596 }
597 } else {
598 if ( $postcode )
599 $postcode_query[] = "( '" . esc_sql( $postcode ) . "', $tax_rate_id, 'postcode' )";
600 }
601
602 $wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES " . implode( ',', $postcode_query ) );
603 }
604
605 if ( ! empty( $city ) ) {
606 $cities = explode( ';', $city );
607 $cities = array_map( 'strtoupper', array_map( 'wc_clean', $cities ) );
608 foreach( $cities as $city ) {
609 $wpdb->insert(
610 $wpdb->prefix . "woocommerce_tax_rate_locations",
611 array(
612 'location_code' => $city,
613 'tax_rate_id' => $tax_rate_id,
614 'location_type' => 'city',
615 )
616 );
617 }
618 }
619
620 $i++;
621 }
622
623
624 } else {
625
626 $tax_rate_id = absint( $key );
627
628 if ( $_POST['remove_tax_rate'][ $key ] == 1 ) {
629 do_action( 'woocommerce_tax_rate_deleted', $tax_rate_id );
630
631 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d;", $tax_rate_id ) );
632 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $tax_rate_id ) );
633
634 continue;
635 }
636
637
638 $country = strtoupper( wc_clean( $tax_rate_country[ $key ] ) );
639 $state = strtoupper( wc_clean( $tax_rate_state[ $key ] ) );
640 $rate = number_format( (double) wc_clean( $tax_rate[ $key ] ), 4, '.', '' );
641 $name = wc_clean( $tax_rate_name[ $key ] );
642 $priority = absint( wc_clean( $tax_rate_priority[ $key ] ) );
643 $compound = isset( $tax_rate_compound[ $key ] ) ? 1 : 0;
644 $shipping = isset( $tax_rate_shipping[ $key ] ) ? 1 : 0;
645
646 if ( ! $name )
647 $name = __( 'Tax', 'woocommerce' );
648
649 if ( $country == '*' )
650 $country = '';
651
652 if ( $state == '*' )
653 $state = '';
654
655 $tax_rate = array(
656 'tax_rate_country' => $country,
657 'tax_rate_state' => $state,
658 'tax_rate' => $rate,
659 'tax_rate_name' => $name,
660 'tax_rate_priority' => $priority,
661 'tax_rate_compound' => $compound,
662 'tax_rate_shipping' => $shipping,
663 'tax_rate_order' => $i,
664 'tax_rate_class' => sanitize_title( $current_class )
665 );
666
667 $wpdb->update(
668 $wpdb->prefix . "woocommerce_tax_rates",
669 $tax_rate,
670 array(
671 'tax_rate_id' => $tax_rate_id
672 )
673 );
674
675 do_action( 'woocommerce_tax_rate_updated', $tax_rate_id, $tax_rate );
676
677 if ( isset( $tax_rate_postcode[ $key ] ) ) {
678
679 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = 'postcode';", $tax_rate_id ) );
680
681
682 $postcode = wc_clean( $tax_rate_postcode[ $key ] );
683 $postcodes = explode( ';', $postcode );
684 $postcodes = array_map( 'strtoupper', array_map( 'wc_clean', $postcodes ) );
685
686 $postcode_query = array();
687
688 foreach( $postcodes as $postcode )
689 if ( strstr( $postcode, '-' ) ) {
690 $postcode_parts = explode( '-', $postcode );
691
692 if ( is_numeric( $postcode_parts[0] ) && is_numeric( $postcode_parts[1] ) && $postcode_parts[1] > $postcode_parts[0] ) {
693 for ( $i = $postcode_parts[0]; $i <= $postcode_parts[1]; $i ++ ) {
694 if ( ! $i )
695 continue;
696
697 if ( strlen( $i ) < strlen( $postcode_parts[0] ) )
698 $i = str_pad( $i, strlen( $postcode_parts[0] ), "0", STR_PAD_LEFT );
699
700 $postcode_query[] = "( '" . esc_sql( $i ) . "', $tax_rate_id, 'postcode' )";
701 }
702 }
703 } else {
704 if ( $postcode )
705 $postcode_query[] = "( '" . esc_sql( $postcode ) . "', $tax_rate_id, 'postcode' )";
706 }
707
708 $wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES " . implode( ',', $postcode_query ) );
709
710 }
711
712 if ( isset( $tax_rate_city[ $key ] ) ) {
713
714 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = 'city';", $tax_rate_id ) );
715
716
717 $city = wc_clean( $tax_rate_city[ $key ] );
718 $cities = explode( ';', $city );
719 $cities = array_map( 'strtoupper', array_map( 'wc_clean', $cities ) );
720 foreach( $cities as $city ) {
721 if ( $city ) {
722 $wpdb->insert(
723 $wpdb->prefix . "woocommerce_tax_rate_locations",
724 array(
725 'location_code' => $city,
726 'tax_rate_id' => $tax_rate_id,
727 'location_type' => 'city',
728 )
729 );
730 }
731 }
732 }
733
734 $i++;
735 }
736 }
737 }
738
739 }
740
741 endif;
742
743 return new WC_Settings_Tax();
744