1 <?php
  2 /**
  3  * WooCommerce Tax Settings
  4  *
  5  * @author      WooThemes
  6  * @category    Admin
  7  * @package     WooCommerce/Admin
  8  * @version     2.1.0
  9  */
 10 
 11 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
 12 
 13 if ( ! class_exists( 'WC_Settings_Tax' ) ) :
 14 
 15 /**
 16  * WC_Settings_Tax
 17  */
 18 class WC_Settings_Tax extends WC_Settings_Page {
 19 
 20     /**
 21      * Constructor.
 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      * Get sections
 35      *
 36      * @return array
 37      */
 38     public function get_sections() {
 39         $sections = array(
 40             ''         => __( 'Tax Options', 'woocommerce' ),
 41             'standard' => __( 'Standard Rates', 'woocommerce' )
 42         );
 43 
 44         // Get tax classes and display as links
 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      * Get settings array
 56      *
 57      * @return array
 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         )); // End tax settings
189     }
190 
191     /**
192      * Output the settings
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      * Save settings
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      * Output tax rate tables
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">&nbsp;</th>
249 
250                     <th width="8%"><?php _e( 'Country&nbsp;Code', 'woocommerce' ); ?>&nbsp;<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&nbsp;Code', 'woocommerce' ); ?>&nbsp;<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' ); ?>&nbsp;<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' ); ?>&nbsp;<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&nbsp;%', 'woocommerce' ); ?>&nbsp;<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&nbsp;Name', 'woocommerce' ); ?>&nbsp;<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' ); ?>&nbsp;<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' ); ?>&nbsp;<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' ); ?>&nbsp;<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' => '&laquo;',
282                                     'next_text' => '&raquo;',
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                         //row.substring( 0, row.length - 1 );
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">&nbsp;</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      * Save tax rates
505      */
506     public function save_tax_rates() {
507         global $wpdb, $current_section;
508 
509         // Get class
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         // Get POST data
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         // Loop posted fields
531         foreach ( $tax_rate_country as $key => $value ) {
532 
533             // new keys are inserted...
534             if ( $key == 'new' ) {
535 
536                 foreach ( $value as $new_key => $new_value ) {
537 
538                     // Sanitize + format
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             // ...whereas the others are updated
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                 // Sanitize + format
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                     // Delete old
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                     // Add changed
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                     // Delete old
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                     // Add changed
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 
WooCommerce API documentation generated by ApiGen 2.8.0