id = 'tax'; $this->label = __( 'Tax', 'woocommerce' ); add_filter( 'woocommerce_settings_tabs_array', array( $this, 'add_settings_page' ), 20 ); add_action( 'woocommerce_sections_' . $this->id, array( $this, 'output_sections' ) ); add_action( 'woocommerce_settings_' . $this->id, array( $this, 'output' ) ); add_action( 'woocommerce_settings_save_' . $this->id, array( $this, 'save' ) ); } /** * Get sections * * @return array */ public function get_sections() { $sections = array( '' => __( 'Tax Options', 'woocommerce' ), 'standard' => __( 'Standard Rates', 'woocommerce' ) ); // Get tax classes and display as links $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) ); if ( $tax_classes ) { foreach ( $tax_classes as $class ) { $sections[ sanitize_title( $class ) ] = sprintf( __( '%s Rates', 'woocommerce' ), $class ); } } return apply_filters( 'woocommerce_get_sections_' . $this->id, $sections ); } /** * Get settings array * * @return array */ public function get_settings() { $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) ); $classes_options = array(); if ( $tax_classes ) { foreach ( $tax_classes as $class ) { $classes_options[ sanitize_title( $class ) ] = esc_html( $class ); } } $settings = apply_filters('woocommerce_tax_settings', array( array( 'title' => __( 'Tax Options', 'woocommerce' ), 'type' => 'title','desc' => '', 'id' => 'tax_options' ), array( 'title' => __( 'Enable Taxes', 'woocommerce' ), 'desc' => __( 'Enable taxes and tax calculations', 'woocommerce' ), 'id' => 'woocommerce_calc_taxes', 'default' => 'no', 'type' => 'checkbox' ), array( 'title' => __( 'Prices Entered With Tax', 'woocommerce' ), 'id' => 'woocommerce_prices_include_tax', 'default' => 'no', 'type' => 'radio', 'desc_tip' => __( 'This option is important as it will affect how you input prices. Changing it will not update existing products.', 'woocommerce' ), 'options' => array( 'yes' => __( 'Yes, I will enter prices inclusive of tax', 'woocommerce' ), 'no' => __( 'No, I will enter prices exclusive of tax', 'woocommerce' ) ), ), array( 'title' => __( 'Calculate Tax Based On:', 'woocommerce' ), 'id' => 'woocommerce_tax_based_on', 'desc_tip' => __( 'This option determines which address is used to calculate tax.', 'woocommerce' ), 'default' => 'shipping', 'type' => 'select', 'options' => array( 'shipping' => __( 'Customer shipping address', 'woocommerce' ), 'billing' => __( 'Customer billing address', 'woocommerce' ), 'base' => __( 'Shop base address', 'woocommerce' ) ), ), array( 'title' => __( 'Default Customer Address:', 'woocommerce' ), 'id' => 'woocommerce_default_customer_address', 'desc_tip' => __( 'This option determines the customers default address (before they input their own).', 'woocommerce' ), 'default' => 'base', 'type' => 'select', 'options' => array( '' => __( 'No address', 'woocommerce' ), 'base' => __( 'Shop base address', 'woocommerce' ), ), ), array( 'title' => __( 'Shipping Tax Class:', 'woocommerce' ), 'desc' => __( 'Optionally control which tax class shipping gets, or leave it so shipping tax is based on the cart items themselves.', 'woocommerce' ), 'id' => 'woocommerce_shipping_tax_class', 'css' => 'min-width:150px;', 'default' => 'title', 'type' => 'select', 'options' => array( '' => __( 'Shipping tax class based on cart items', 'woocommerce' ), 'standard' => __( 'Standard', 'woocommerce' ) ) + $classes_options, 'desc_tip' => true, ), array( 'title' => __( 'Rounding', 'woocommerce' ), 'desc' => __( 'Round tax at subtotal level, instead of rounding per line', 'woocommerce' ), 'id' => 'woocommerce_tax_round_at_subtotal', 'default' => 'no', 'type' => 'checkbox', ), array( 'title' => __( 'Additional Tax Classes', 'woocommerce' ), 'desc' => __( 'List additional tax classes below (1 per line). This is in addition to the default Standard Rate. Tax classes can be assigned to products.', 'woocommerce' ), 'id' => 'woocommerce_tax_classes', 'css' => 'width:100%; height: 65px;', 'type' => 'textarea', 'default' => sprintf( __( 'Reduced Rate%sZero Rate', 'woocommerce' ), PHP_EOL ) ), array( 'title' => __( 'Display prices in the shop:', 'woocommerce' ), 'id' => 'woocommerce_tax_display_shop', 'default' => 'excl', 'type' => 'select', 'options' => array( 'incl' => __( 'Including tax', 'woocommerce' ), 'excl' => __( 'Excluding tax', 'woocommerce' ), ) ), array( 'title' => __( 'Price display suffix:', 'woocommerce' ), 'id' => 'woocommerce_price_display_suffix', 'default' => '', 'type' => 'text', '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: {price_including_tax}, {price_excluding_tax}.', 'woocommerce' ), ), array( 'title' => __( 'Display prices during cart/checkout:', 'woocommerce' ), 'id' => 'woocommerce_tax_display_cart', 'default' => 'excl', 'type' => 'select', 'options' => array( 'incl' => __( 'Including tax', 'woocommerce' ), 'excl' => __( 'Excluding tax', 'woocommerce' ), ), 'autoload' => false ), array( 'title' => __( 'Display tax totals:', 'woocommerce' ), 'id' => 'woocommerce_tax_total_display', 'default' => 'itemized', 'type' => 'select', 'options' => array( 'single' => __( 'As a single total', 'woocommerce' ), 'itemized' => __( 'Itemized', 'woocommerce' ), ), 'autoload' => false ), array( 'type' => 'sectionend', 'id' => 'tax_options' ), ) ); return apply_filters( 'woocommerce_get_settings_' . $this->id, $settings ); } /** * Output the settings */ public function output() { global $current_section; $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) ); if ( $current_section == 'standard' || in_array( $current_section, array_map( 'sanitize_title', $tax_classes ) ) ) { $this->output_tax_rates(); } else { $settings = $this->get_settings(); WC_Admin_Settings::output_fields( $settings ); } } /** * Save settings */ public function save() { global $current_section, $wpdb; if ( ! $current_section ) { $settings = $this->get_settings(); WC_Admin_Settings::save_fields( $settings ); } else { $this->save_tax_rates(); } $wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('_transient_wc_tax_rates_%') OR `option_name` LIKE ('_transient_timeout_wc_tax_rates_%')" ); } /** * Output tax rate tables */ public function output_tax_rates() { global $current_section, $wpdb; $page = ! empty( $_GET['p'] ) ? absint( $_GET['p'] ) : 1; $limit = 100; $tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) ); $current_class = ''; foreach( $tax_classes as $class ) { if ( sanitize_title( $class ) == $current_section ) { $current_class = $class; } } ?>

See here for available alpha-2 country codes.', 'woocommerce' ), 'http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes' ); ?>

get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class = %s ORDER BY tax_rate_order LIMIT %d, %d " , sanitize_title( $current_class ), ( $page - 1 ) * $limit, $limit ) ); foreach ( $rates as $rate ) { ?>
   [?]  [?]  [?]  [?]  [?]  [?]  [?]  [?]  [?]
prefix}woocommerce_tax_rate_locations WHERE location_type='postcode' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) ); echo esc_attr( implode( '; ', $locations ) ); ?>" placeholder="*" data-name="tax_rate_postcode[tax_rate_id ?>]" /> prefix}woocommerce_tax_rate_locations WHERE location_type='city' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) ); echo esc_attr( implode( '; ', $locations ) ); ?>" placeholder="*" data-name="tax_rate_city[tax_rate_id ?>]" /> tax_rate_compound, '1' ); ?> /> tax_rate_shipping, '1' ); ?> />
$value ) { // new keys are inserted... if ( $key == 'new' ) { foreach ( $value as $new_key => $new_value ) { // Sanitize + format $country = strtoupper( wc_clean( $tax_rate_country[ $key ][ $new_key ] ) ); $state = strtoupper( wc_clean( $tax_rate_state[ $key ][ $new_key ] ) ); $postcode = wc_clean( $tax_rate_postcode[ $key ][ $new_key ] ); $city = wc_clean( $tax_rate_city[ $key ][ $new_key ] ); $rate = number_format( (double) wc_clean( $tax_rate[ $key ][ $new_key ] ), 4, '.', '' ); $name = wc_clean( $tax_rate_name[ $key ][ $new_key ] ); $priority = absint( wc_clean( $tax_rate_priority[ $key ][ $new_key ] ) ); $compound = isset( $tax_rate_compound[ $key ][ $new_key ] ) ? 1 : 0; $shipping = isset( $tax_rate_shipping[ $key ][ $new_key ] ) ? 1 : 0; if ( ! $name ) { $name = __( 'Tax', 'woocommerce' ); } if ( $country == '*' ) { $country = ''; } if ( $state == '*' ) { $state = ''; } $_tax_rate = array( 'tax_rate_country' => $country, 'tax_rate_state' => $state, 'tax_rate' => $rate, 'tax_rate_name' => $name, 'tax_rate_priority' => $priority, 'tax_rate_compound' => $compound, 'tax_rate_shipping' => $shipping, 'tax_rate_order' => $i, 'tax_rate_class' => sanitize_title( $current_class ) ); $wpdb->insert( $wpdb->prefix . 'woocommerce_tax_rates', $_tax_rate ); $tax_rate_id = $wpdb->insert_id; do_action( 'woocommerce_tax_rate_added', $tax_rate_id, $_tax_rate ); if ( ! empty( $postcode ) ) { $postcodes = explode( ';', $postcode ); $postcodes = array_map( 'strtoupper', array_map( 'wc_clean', $postcodes ) ); $postcode_query = array(); foreach( $postcodes as $postcode ) { if ( strstr( $postcode, '-' ) ) { $postcode_parts = explode( '-', $postcode ); if ( is_numeric( $postcode_parts[0] ) && is_numeric( $postcode_parts[1] ) && $postcode_parts[1] > $postcode_parts[0] ) { for ( $i = $postcode_parts[0]; $i <= $postcode_parts[1]; $i ++ ) { if ( ! $i ) continue; if ( strlen( $i ) < strlen( $postcode_parts[0] ) ) $i = str_pad( $i, strlen( $postcode_parts[0] ), "0", STR_PAD_LEFT ); $postcode_query[] = "( '" . esc_sql( $i ) . "', $tax_rate_id, 'postcode' )"; } } } else { if ( $postcode ) $postcode_query[] = "( '" . esc_sql( $postcode ) . "', $tax_rate_id, 'postcode' )"; } } $wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES " . implode( ',', $postcode_query ) ); } if ( ! empty( $city ) ) { $cities = explode( ';', $city ); $cities = array_map( 'strtoupper', array_map( 'wc_clean', $cities ) ); foreach( $cities as $city ) { $wpdb->insert( $wpdb->prefix . "woocommerce_tax_rate_locations", array( 'location_code' => $city, 'tax_rate_id' => $tax_rate_id, 'location_type' => 'city', ) ); } } $i++; } // ...whereas the others are updated } else { $tax_rate_id = absint( $key ); if ( $_POST['remove_tax_rate'][ $key ] == 1 ) { do_action( 'woocommerce_tax_rate_deleted', $tax_rate_id ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d;", $tax_rate_id ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $tax_rate_id ) ); continue; } // Sanitize + format $country = strtoupper( wc_clean( $tax_rate_country[ $key ] ) ); $state = strtoupper( wc_clean( $tax_rate_state[ $key ] ) ); $rate = number_format( (double) wc_clean( $tax_rate[ $key ] ), 4, '.', '' ); $name = wc_clean( $tax_rate_name[ $key ] ); $priority = absint( wc_clean( $tax_rate_priority[ $key ] ) ); $compound = isset( $tax_rate_compound[ $key ] ) ? 1 : 0; $shipping = isset( $tax_rate_shipping[ $key ] ) ? 1 : 0; if ( ! $name ) { $name = __( 'Tax', 'woocommerce' ); } if ( $country == '*' ) { $country = ''; } if ( $state == '*' ) { $state = ''; } $_tax_rate = array( 'tax_rate_country' => $country, 'tax_rate_state' => $state, 'tax_rate' => $rate, 'tax_rate_name' => $name, 'tax_rate_priority' => $priority, 'tax_rate_compound' => $compound, 'tax_rate_shipping' => $shipping, 'tax_rate_order' => $i, 'tax_rate_class' => sanitize_title( $current_class ) ); $wpdb->update( $wpdb->prefix . "woocommerce_tax_rates", $_tax_rate, array( 'tax_rate_id' => $tax_rate_id ) ); do_action( 'woocommerce_tax_rate_updated', $tax_rate_id, $_tax_rate ); if ( isset( $tax_rate_postcode[ $key ] ) ) { // Delete old $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = 'postcode';", $tax_rate_id ) ); // Add changed $postcode = wc_clean( $tax_rate_postcode[ $key ] ); $postcodes = explode( ';', $postcode ); $postcodes = array_map( 'strtoupper', array_map( 'wc_clean', $postcodes ) ); $postcode_query = array(); foreach( $postcodes as $postcode ) { if ( strstr( $postcode, '-' ) ) { $postcode_parts = explode( '-', $postcode ); if ( is_numeric( $postcode_parts[0] ) && is_numeric( $postcode_parts[1] ) && $postcode_parts[1] > $postcode_parts[0] ) { for ( $i = $postcode_parts[0]; $i <= $postcode_parts[1]; $i ++ ) { if ( ! $i ) continue; if ( strlen( $i ) < strlen( $postcode_parts[0] ) ) $i = str_pad( $i, strlen( $postcode_parts[0] ), "0", STR_PAD_LEFT ); $postcode_query[] = "( '" . esc_sql( $i ) . "', $tax_rate_id, 'postcode' )"; } } } else { if ( $postcode ) $postcode_query[] = "( '" . esc_sql( $postcode ) . "', $tax_rate_id, 'postcode' )"; } } if ( !empty( $postcode_query ) ) { $wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES " . implode( ',', $postcode_query ) ); } } if ( isset( $tax_rate_city[ $key ] ) ) { // Delete old $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = 'city';", $tax_rate_id ) ); // Add changed $city = wc_clean( $tax_rate_city[ $key ] ); $cities = explode( ';', $city ); $cities = array_map( 'strtoupper', array_map( 'wc_clean', $cities ) ); foreach( $cities as $city ) { if ( $city ) { $wpdb->insert( $wpdb->prefix . "woocommerce_tax_rate_locations", array( 'location_code' => $city, 'tax_rate_id' => $tax_rate_id, 'location_type' => 'city', ) ); } } } $i++; } } } } endif; return new WC_Settings_Tax();