2011-08-09 15:16:18 +00:00
< ? php
2019-01-11 12:28:52 +00:00
/**
* Tax calculation and rate finding class .
*
2020-08-05 16:36:24 +00:00
* @ package WooCommerce\Classes
2019-01-11 12:28:52 +00:00
*/
2015-11-06 09:22:19 +00:00
2020-10-01 08:57:12 +00:00
use Automattic\WooCommerce\Utilities\NumberUtil ;
2019-01-11 12:28:52 +00:00
defined ( 'ABSPATH' ) || exit ;
2015-11-06 09:22:19 +00:00
2011-08-09 15:16:18 +00:00
/**
2015-11-03 13:53:50 +00:00
* Performs tax calculations and loads tax rates
2011-08-09 15:16:18 +00:00
*
2019-01-11 12:28:52 +00:00
* @ class WC_Tax
2011-08-09 15:16:18 +00:00
*/
2012-01-27 16:38:39 +00:00
class WC_Tax {
2012-08-08 10:42:42 +00:00
2016-01-06 15:24:47 +00:00
/**
* Precision .
*
2017-12-07 15:39:10 +00:00
* @ var int
2016-01-06 15:24:47 +00:00
*/
2014-06-12 15:47:43 +00:00
public static $precision ;
2016-01-06 15:24:47 +00:00
/**
* Round at subtotal .
*
2017-12-07 15:39:10 +00:00
* @ var bool
2016-01-06 15:24:47 +00:00
*/
2017-12-04 20:33:27 +00:00
public static $round_at_subtotal = false ;
2014-09-20 19:02:51 +00:00
2012-12-13 01:23:35 +00:00
/**
2015-11-03 13:31:20 +00:00
* Load options .
2012-12-13 01:23:35 +00:00
*/
2014-06-12 15:47:43 +00:00
public static function init () {
2016-07-11 15:26:54 +00:00
self :: $precision = wc_get_rounding_precision ();
2014-06-12 15:47:43 +00:00
self :: $round_at_subtotal = 'yes' === get_option ( 'woocommerce_tax_round_at_subtotal' );
2012-12-13 01:23:35 +00:00
}
2019-07-22 12:11:11 +00:00
/**
* When the woocommerce_tax_classes option is changed , remove any orphan rates .
*
* @ deprecated 3.7 . 0
* @ param string $old_value Old rates value .
* @ param string $value New rates value .
*/
public static function maybe_remove_tax_class_rates ( $old_value , $value ) {
2019-07-29 08:56:13 +00:00
wc_deprecated_function ( 'WC_Tax::maybe_remove_tax_class_rates' , '3.7' , 'WC_Tax::delete_tax_class_by' );
$tax_classes = array_filter ( array_map ( 'trim' , explode ( " \n " , $value ) ) );
$existing_tax_classes = self :: get_tax_classes ();
$removed = array_diff ( $existing_tax_classes , $tax_classes );
foreach ( $removed as $name ) {
self :: delete_tax_class_by ( 'name' , $name );
}
2019-07-22 12:11:11 +00:00
}
2013-10-24 12:15:42 +00:00
/**
2015-11-03 13:31:20 +00:00
* Calculate tax for a line .
2017-12-04 20:33:27 +00:00
*
* @ param float $price Price to calc tax on .
* @ param array $rates Rates to apply .
* @ param boolean $price_includes_tax Whether the passed price has taxes included .
* @ param boolean $deprecated Whether to suppress any rounding from taking place . No longer used here .
* @ return array Array of rates + prices after tax .
2013-10-24 12:15:42 +00:00
*/
2017-12-04 20:33:27 +00:00
public static function calc_tax ( $price , $rates , $price_includes_tax = false , $deprecated = false ) {
2014-11-29 11:33:36 +00:00
if ( $price_includes_tax ) {
2014-06-12 15:47:43 +00:00
$taxes = self :: calc_inclusive_tax ( $price , $rates );
2014-11-29 11:33:36 +00:00
} else {
2014-06-12 15:47:43 +00:00
$taxes = self :: calc_exclusive_tax ( $price , $rates );
2014-11-29 11:33:36 +00:00
}
2017-12-04 20:33:27 +00:00
return apply_filters ( 'woocommerce_calc_tax' , $taxes , $price , $rates , $price_includes_tax , $deprecated );
2013-10-24 15:16:39 +00:00
}
2013-10-24 12:15:42 +00:00
2013-10-24 15:16:39 +00:00
/**
* Calculate the shipping tax using a passed array of rates .
*
2019-01-11 12:28:52 +00:00
* @ param float $price Shipping cost .
* @ param array $rates Taxation Rate .
* @ return array
2013-10-24 15:16:39 +00:00
*/
2014-06-12 15:47:43 +00:00
public static function calc_shipping_tax ( $price , $rates ) {
2016-11-03 11:47:12 +00:00
$taxes = self :: calc_exclusive_tax ( $price , $rates );
return apply_filters ( 'woocommerce_calc_shipping_tax' , $taxes , $price , $rates );
2013-10-24 12:15:42 +00:00
}
/**
* Round to precision .
*
* Filter example : to return rounding to . 5 cents you ' d use :
*
2016-04-29 10:44:28 +00:00
* function euro_5cent_rounding ( $in ) {
2013-10-24 12:15:42 +00:00
* return round ( $in / 5 , 2 ) * 5 ;
* }
* add_filter ( 'woocommerce_tax_round' , 'euro_5cent_rounding' );
2017-05-15 11:50:52 +00:00
*
2019-01-11 12:28:52 +00:00
* @ param float | int $in Value to round .
2017-05-15 11:50:52 +00:00
* @ return float
2013-10-24 12:15:42 +00:00
*/
2014-06-12 15:47:43 +00:00
public static function round ( $in ) {
2020-10-01 08:57:12 +00:00
return apply_filters ( 'woocommerce_tax_round' , NumberUtil :: round ( $in , wc_get_rounding_precision () ), $in );
2013-10-24 12:15:42 +00:00
}
2013-10-24 15:16:39 +00:00
/**
2015-11-03 13:31:20 +00:00
* Calc tax from inclusive price .
2013-10-24 15:16:39 +00:00
*
2018-02-24 11:23:27 +00:00
* @ param float $price Price to calculate tax for .
2018-01-24 16:26:44 +00:00
* @ param array $rates Array of tax rates .
2013-10-24 15:16:39 +00:00
* @ return array
*/
2015-06-09 13:58:27 +00:00
public static function calc_inclusive_tax ( $price , $rates ) {
2018-01-24 16:26:44 +00:00
$taxes = array ();
$compound_rates = array ();
$regular_rates = array ();
2013-10-24 12:15:42 +00:00
2018-01-24 16:26:44 +00:00
// Index array so taxes are output in correct order and see what compound/regular rates we have to calculate.
2016-09-07 22:32:24 +00:00
foreach ( $rates as $key => $rate ) {
2018-01-24 16:26:44 +00:00
$taxes [ $key ] = 0 ;
2016-09-07 22:32:24 +00:00
if ( 'yes' === $rate [ 'compound' ] ) {
2018-01-24 16:26:44 +00:00
$compound_rates [ $key ] = $rate [ 'rate' ];
2016-09-07 22:32:24 +00:00
} else {
2018-01-24 16:26:44 +00:00
$regular_rates [ $key ] = $rate [ 'rate' ];
2016-09-07 22:32:24 +00:00
}
}
2013-10-24 12:15:42 +00:00
2018-01-24 16:26:44 +00:00
$compound_rates = array_reverse ( $compound_rates , true ); // Working backwards.
2013-10-24 12:15:42 +00:00
2018-01-24 16:26:44 +00:00
$non_compound_price = $price ;
2013-10-24 12:15:42 +00:00
2018-01-24 16:26:44 +00:00
foreach ( $compound_rates as $key => $compound_rate ) {
$tax_amount = apply_filters ( 'woocommerce_price_inc_tax_amount' , $non_compound_price - ( $non_compound_price / ( 1 + ( $compound_rate / 100 ) ) ), $key , $rates [ $key ], $price );
$taxes [ $key ] += $tax_amount ;
$non_compound_price = $non_compound_price - $tax_amount ;
}
2013-10-24 12:15:42 +00:00
2018-01-24 16:26:44 +00:00
// Regular taxes.
$regular_tax_rate = 1 + ( array_sum ( $regular_rates ) / 100 );
2013-10-24 12:15:42 +00:00
2018-01-24 16:26:44 +00:00
foreach ( $regular_rates as $key => $regular_rate ) {
$the_rate = ( $regular_rate / 100 ) / $regular_tax_rate ;
$net_price = $price - ( $the_rate * $non_compound_price );
$tax_amount = apply_filters ( 'woocommerce_price_inc_tax_amount' , $price - $net_price , $key , $rates [ $key ], $price );
$taxes [ $key ] += $tax_amount ;
2013-10-24 12:15:42 +00:00
}
2019-01-11 12:38:05 +00:00
/**
* Round all taxes to precision ( 4 DP ) before passing them back . Note , this is not the same rounding
* as in the cart calculation class which , depending on settings , will round to 2 DP when calculating
* final totals . Also unlike that class , this rounds . 5 up for all cases .
*/
$taxes = array_map ( array ( __CLASS__ , 'round' ), $taxes );
2013-10-24 12:15:42 +00:00
return $taxes ;
}
2013-10-24 15:16:39 +00:00
/**
2015-11-03 13:31:20 +00:00
* Calc tax from exclusive price .
2013-10-24 15:16:39 +00:00
*
2018-02-24 11:23:27 +00:00
* @ param float $price Price to calculate tax for .
2018-01-24 16:26:44 +00:00
* @ param array $rates Array of tax rates .
2013-10-24 15:16:39 +00:00
* @ return array
*/
2015-06-09 13:58:27 +00:00
public static function calc_exclusive_tax ( $price , $rates ) {
2013-10-24 12:15:42 +00:00
$taxes = array ();
2016-06-06 18:39:23 +00:00
if ( ! empty ( $rates ) ) {
2014-12-09 09:14:25 +00:00
foreach ( $rates as $key => $rate ) {
2016-09-07 22:32:24 +00:00
if ( 'yes' === $rate [ 'compound' ] ) {
2014-12-09 09:14:25 +00:00
continue ;
2016-09-07 22:32:24 +00:00
}
2013-10-24 12:15:42 +00:00
2014-12-09 09:14:25 +00:00
$tax_amount = $price * ( $rate [ 'rate' ] / 100 );
2018-01-24 16:26:44 +00:00
$tax_amount = apply_filters ( 'woocommerce_price_ex_tax_amount' , $tax_amount , $key , $rate , $price ); // ADVANCED: Allow third parties to modify this rate.
2013-10-24 12:15:42 +00:00
2017-03-07 20:24:24 +00:00
if ( ! isset ( $taxes [ $key ] ) ) {
2014-12-09 09:14:25 +00:00
$taxes [ $key ] = $tax_amount ;
2017-03-07 20:24:24 +00:00
} else {
2014-12-09 09:14:25 +00:00
$taxes [ $key ] += $tax_amount ;
2017-03-07 20:24:24 +00:00
}
2014-12-09 09:14:25 +00:00
}
2013-10-24 12:15:42 +00:00
2014-12-09 09:14:25 +00:00
$pre_compound_total = array_sum ( $taxes );
2013-10-24 12:15:42 +00:00
2018-01-24 16:26:44 +00:00
// Compound taxes.
2013-10-24 12:15:42 +00:00
foreach ( $rates as $key => $rate ) {
2016-09-07 22:32:24 +00:00
if ( 'no' === $rate [ 'compound' ] ) {
2013-10-24 12:15:42 +00:00
continue ;
2016-09-07 22:32:24 +00:00
}
2014-02-25 11:54:58 +00:00
$the_price_inc_tax = $price + ( $pre_compound_total );
2018-01-24 16:26:44 +00:00
$tax_amount = $the_price_inc_tax * ( $rate [ 'rate' ] / 100 );
$tax_amount = apply_filters ( 'woocommerce_price_ex_tax_amount' , $tax_amount , $key , $rate , $price , $the_price_inc_tax , $pre_compound_total ); // ADVANCED: Allow third parties to modify this rate.
2013-10-24 12:15:42 +00:00
2016-09-07 22:32:24 +00:00
if ( ! isset ( $taxes [ $key ] ) ) {
2013-10-24 12:15:42 +00:00
$taxes [ $key ] = $tax_amount ;
2016-09-07 22:32:24 +00:00
} else {
2013-10-24 12:15:42 +00:00
$taxes [ $key ] += $tax_amount ;
2016-09-07 22:32:24 +00:00
}
2018-01-24 16:26:44 +00:00
$pre_compound_total = array_sum ( $taxes );
2013-10-24 12:15:42 +00:00
}
}
2019-01-11 12:38:05 +00:00
/**
* Round all taxes to precision ( 4 DP ) before passing them back . Note , this is not the same rounding
* as in the cart calculation class which , depending on settings , will round to 2 DP when calculating
* final totals . Also unlike that class , this rounds . 5 up for all cases .
*/
$taxes = array_map ( array ( __CLASS__ , 'round' ), $taxes );
2013-10-24 12:15:42 +00:00
return $taxes ;
}
2011-08-09 15:16:18 +00:00
/**
2012-08-14 20:19:41 +00:00
* Searches for all matching country / state / postcode tax rates .
2012-11-27 16:22:47 +00:00
*
2019-01-11 12:28:52 +00:00
* @ param array $args Args that determine the rate to find .
2012-09-23 16:16:39 +00:00
* @ return array
2011-08-09 15:16:18 +00:00
*/
2014-06-12 15:47:43 +00:00
public static function find_rates ( $args = array () ) {
2019-01-11 12:28:52 +00:00
$args = wp_parse_args (
$args ,
array (
'country' => '' ,
'state' => '' ,
'city' => '' ,
'postcode' => '' ,
'tax_class' => '' ,
)
);
2012-08-08 10:42:42 +00:00
2019-01-11 12:28:52 +00:00
$country = $args [ 'country' ];
$state = $args [ 'state' ];
$city = $args [ 'city' ];
$postcode = wc_normalize_postcode ( wc_clean ( $args [ 'postcode' ] ) );
$tax_class = $args [ 'tax_class' ];
2012-08-08 10:42:42 +00:00
2014-06-02 10:55:36 +00:00
if ( ! $country ) {
2012-09-23 16:16:39 +00:00
return array ();
2014-06-02 10:55:36 +00:00
}
2011-08-09 15:16:18 +00:00
2015-12-02 15:53:46 +00:00
$cache_key = WC_Cache_Helper :: get_cache_prefix ( 'taxes' ) . 'wc_tax_rates_' . md5 ( sprintf ( '%s+%s+%s+%s+%s' , $country , $state , $city , $postcode , $tax_class ) );
$matched_tax_rates = wp_cache_get ( $cache_key , 'taxes' );
2012-11-27 16:22:47 +00:00
2014-11-21 12:05:44 +00:00
if ( false === $matched_tax_rates ) {
2016-04-22 13:02:36 +00:00
$matched_tax_rates = self :: get_matched_tax_rates ( $country , $state , $postcode , $city , $tax_class );
2015-12-02 15:53:46 +00:00
wp_cache_set ( $cache_key , $matched_tax_rates , 'taxes' );
2012-09-23 16:16:39 +00:00
}
2011-12-28 23:59:33 +00:00
2014-12-02 11:57:01 +00:00
return apply_filters ( 'woocommerce_find_rates' , $matched_tax_rates , $args );
2014-11-21 12:05:44 +00:00
}
2014-01-10 22:57:19 +00:00
2014-11-21 13:06:30 +00:00
/**
* Searches for all matching country / state / postcode tax rates .
*
2019-01-11 12:28:52 +00:00
* @ param array $args Args that determine the rate to find .
2014-11-21 13:06:30 +00:00
* @ return array
*/
public static function find_shipping_rates ( $args = array () ) {
$rates = self :: find_rates ( $args );
$shipping_rates = array ();
2015-08-31 04:18:21 +00:00
if ( is_array ( $rates ) ) {
foreach ( $rates as $key => $rate ) {
if ( 'yes' === $rate [ 'shipping' ] ) {
$shipping_rates [ $key ] = $rate ;
}
2014-11-21 13:06:30 +00:00
}
}
return $shipping_rates ;
}
2016-07-27 10:30:12 +00:00
/**
2017-10-12 13:04:10 +00:00
* Does the sort comparison . Compares ( in this order ) :
2019-01-11 12:28:52 +00:00
* - Priority
* - Country
* - State
* - Number of postcodes
* - Number of cities
* - ID
2017-05-15 11:50:52 +00:00
*
2017-10-12 13:04:10 +00:00
* @ param object $rate1 First rate to compare .
* @ param object $rate2 Second rate to compare .
2017-05-15 11:50:52 +00:00
* @ return int
2016-07-27 10:30:12 +00:00
*/
private static function sort_rates_callback ( $rate1 , $rate2 ) {
if ( $rate1 -> tax_rate_priority !== $rate2 -> tax_rate_priority ) {
2019-01-11 12:28:52 +00:00
return $rate1 -> tax_rate_priority < $rate2 -> tax_rate_priority ? - 1 : 1 ; // ASC.
2017-10-12 13:04:10 +00:00
}
if ( $rate1 -> tax_rate_country !== $rate2 -> tax_rate_country ) {
2016-07-27 10:30:12 +00:00
if ( '' === $rate1 -> tax_rate_country ) {
return 1 ;
}
if ( '' === $rate2 -> tax_rate_country ) {
return - 1 ;
}
return strcmp ( $rate1 -> tax_rate_country , $rate2 -> tax_rate_country ) > 0 ? 1 : - 1 ;
2017-10-12 13:04:10 +00:00
}
if ( $rate1 -> tax_rate_state !== $rate2 -> tax_rate_state ) {
2016-07-27 10:30:12 +00:00
if ( '' === $rate1 -> tax_rate_state ) {
return 1 ;
}
if ( '' === $rate2 -> tax_rate_state ) {
return - 1 ;
}
return strcmp ( $rate1 -> tax_rate_state , $rate2 -> tax_rate_state ) > 0 ? 1 : - 1 ;
}
2017-10-12 13:04:10 +00:00
if ( isset ( $rate1 -> postcode_count , $rate2 -> postcode_count ) && $rate1 -> postcode_count !== $rate2 -> postcode_count ) {
return $rate1 -> postcode_count < $rate2 -> postcode_count ? 1 : - 1 ;
}
if ( isset ( $rate1 -> city_count , $rate2 -> city_count ) && $rate1 -> city_count !== $rate2 -> city_count ) {
return $rate1 -> city_count < $rate2 -> city_count ? 1 : - 1 ;
}
return $rate1 -> tax_rate_id < $rate2 -> tax_rate_id ? - 1 : 1 ;
2016-07-27 10:30:12 +00:00
}
2016-07-27 09:37:55 +00:00
/**
2017-10-12 13:04:10 +00:00
* Logical sort order for tax rates based on the following in order of priority .
*
* @ param array $rates Rates to be sorted .
2016-07-27 09:37:55 +00:00
* @ return array
*/
private static function sort_rates ( $rates ) {
2016-07-27 10:30:12 +00:00
uasort ( $rates , __CLASS__ . '::sort_rates_callback' );
$i = 0 ;
foreach ( $rates as $key => $rate ) {
$rates [ $key ] -> tax_rate_order = $i ++ ;
}
2016-07-27 09:37:55 +00:00
return $rates ;
}
2014-11-21 12:05:44 +00:00
/**
2015-11-03 13:31:20 +00:00
* Loop through a set of tax rates and get the matching rates ( 1 per priority ) .
2014-11-21 12:05:44 +00:00
*
2017-10-12 13:04:10 +00:00
* @ param string $country Country code to match against .
* @ param string $state State code to match against .
* @ param string $postcode Postcode to match against .
* @ param string $city City to match against .
* @ param string $tax_class Tax class to match against .
2014-11-21 12:05:44 +00:00
* @ return array
*/
2016-04-22 13:02:36 +00:00
private static function get_matched_tax_rates ( $country , $state , $postcode , $city , $tax_class ) {
2014-11-21 12:05:44 +00:00
global $wpdb ;
2014-01-10 22:57:19 +00:00
2017-10-12 13:04:10 +00:00
// Query criteria - these will be ANDed.
2016-04-22 13:02:36 +00:00
$criteria = array ();
2016-04-22 14:42:20 +00:00
$criteria [] = $wpdb -> prepare ( " tax_rate_country IN ( %s, '' ) " , strtoupper ( $country ) );
$criteria [] = $wpdb -> prepare ( " tax_rate_state IN ( %s, '' ) " , strtoupper ( $state ) );
2017-10-12 13:04:10 +00:00
$criteria [] = $wpdb -> prepare ( 'tax_rate_class = %s' , sanitize_title ( $tax_class ) );
2016-04-22 13:02:36 +00:00
2016-04-22 14:42:20 +00:00
// Pre-query postcode ranges for PHP based matching.
2016-06-16 10:28:53 +00:00
$postcode_search = wc_get_wildcard_postcodes ( $postcode , $country );
2016-05-23 16:30:37 +00:00
$postcode_ranges = $wpdb -> get_results ( " SELECT tax_rate_id, location_code FROM { $wpdb -> prefix } woocommerce_tax_rate_locations WHERE location_type = 'postcode' AND location_code LIKE '%...%'; " );
2016-04-22 14:42:20 +00:00
if ( $postcode_ranges ) {
2016-06-16 10:28:53 +00:00
$matches = wc_postcode_location_matcher ( $postcode , $postcode_ranges , 'tax_rate_id' , 'location_code' , $country );
2016-06-10 23:26:07 +00:00
if ( ! empty ( $matches ) ) {
foreach ( $matches as $matched_postcodes ) {
$postcode_search = array_merge ( $postcode_search , $matched_postcodes );
}
}
2016-04-22 13:02:36 +00:00
}
2016-06-10 23:26:07 +00:00
$postcode_search = array_unique ( $postcode_search );
2016-04-22 14:42:20 +00:00
/**
* Location matching criteria - ORed
* Needs to match :
2019-01-11 12:28:52 +00:00
* - rates with no postcodes and cities
* - rates with a matching postcode and city
* - rates with matching postcode , no city
* - rates with matching city , no postcode
2016-04-22 14:42:20 +00:00
*/
$locations_criteria = array ();
2017-10-12 13:04:10 +00:00
$locations_criteria [] = 'locations.location_type IS NULL' ;
2016-04-22 14:42:20 +00:00
$locations_criteria [] = "
locations . location_type = 'postcode' AND locations . location_code IN ( '" . implode( "' , '", array_map( ' esc_sql ', $postcode_search ) ) . "' )
AND (
( locations2 . location_type = 'city' AND locations2 . location_code = '" . esc_sql( strtoupper( $city ) ) . "' )
OR NOT EXISTS (
SELECT sub . tax_rate_id FROM { $wpdb -> prefix } woocommerce_tax_rate_locations as sub
WHERE sub . location_type = 'city'
AND sub . tax_rate_id = tax_rates . tax_rate_id
)
)
" ;
$locations_criteria [] = "
locations . location_type = 'city' AND locations . location_code = '" . esc_sql( strtoupper( $city ) ) . "'
AND NOT EXISTS (
SELECT sub . tax_rate_id FROM { $wpdb -> prefix } woocommerce_tax_rate_locations as sub
WHERE sub . location_type = 'postcode'
AND sub . tax_rate_id = tax_rates . tax_rate_id
)
" ;
2019-01-11 12:28:52 +00:00
2016-04-22 14:42:20 +00:00
$criteria [] = '( ( ' . implode ( ' ) OR ( ' , $locations_criteria ) . ' ) )' ;
2019-01-11 12:28:52 +00:00
$criteria_string = implode ( ' AND ' , $criteria );
2020-07-08 20:12:12 +00:00
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
2019-01-11 12:28:52 +00:00
$found_rates = $wpdb -> get_results (
"
2017-10-12 13:04:10 +00:00
SELECT tax_rates .* , COUNT ( locations . location_id ) as postcode_count , COUNT ( locations2 . location_id ) as city_count
2015-08-07 10:37:15 +00:00
FROM { $wpdb -> prefix } woocommerce_tax_rates as tax_rates
LEFT OUTER JOIN { $wpdb -> prefix } woocommerce_tax_rate_locations as locations ON tax_rates . tax_rate_id = locations . tax_rate_id
LEFT OUTER JOIN { $wpdb -> prefix } woocommerce_tax_rate_locations as locations2 ON tax_rates . tax_rate_id = locations2 . tax_rate_id
2019-01-11 12:28:52 +00:00
WHERE 1 = 1 AND { $criteria_string }
2017-01-31 22:58:54 +00:00
GROUP BY tax_rates . tax_rate_id
ORDER BY tax_rates . tax_rate_priority
2019-01-11 12:28:52 +00:00
"
);
// phpcs:enable
2014-09-20 19:02:51 +00:00
2016-07-27 09:37:55 +00:00
$found_rates = self :: sort_rates ( $found_rates );
2014-11-21 12:05:44 +00:00
$matched_tax_rates = array ();
$found_priority = array ();
2014-01-10 22:57:19 +00:00
2014-11-21 12:05:44 +00:00
foreach ( $found_rates as $found_rate ) {
2017-10-12 13:04:10 +00:00
if ( in_array ( $found_rate -> tax_rate_priority , $found_priority , true ) ) {
2014-11-21 12:05:44 +00:00
continue ;
2014-06-02 10:55:36 +00:00
}
2014-11-21 12:05:44 +00:00
$matched_tax_rates [ $found_rate -> tax_rate_id ] = array (
2018-07-26 12:32:48 +00:00
'rate' => ( float ) $found_rate -> tax_rate ,
2014-11-21 12:05:44 +00:00
'label' => $found_rate -> tax_rate_name ,
'shipping' => $found_rate -> tax_rate_shipping ? 'yes' : 'no' ,
2016-08-27 01:46:45 +00:00
'compound' => $found_rate -> tax_rate_compound ? 'yes' : 'no' ,
2014-11-21 12:05:44 +00:00
);
2014-01-10 22:57:19 +00:00
2014-11-21 12:05:44 +00:00
$found_priority [] = $found_rate -> tax_rate_priority ;
2014-01-10 22:57:19 +00:00
}
2014-11-21 12:05:44 +00:00
return apply_filters ( 'woocommerce_matched_tax_rates' , $matched_tax_rates , $country , $state , $postcode , $city , $tax_class );
2011-08-09 15:16:18 +00:00
}
2012-08-08 10:42:42 +00:00
2014-11-21 13:06:30 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get the customer tax location based on their status and the current page .
2014-12-09 16:41:26 +00:00
*
2015-11-03 13:31:20 +00:00
* Used by get_rates (), get_shipping_rates () .
2014-12-09 16:41:26 +00:00
*
2019-01-11 12:28:52 +00:00
* @ param string $tax_class string Optional , passed to the filter for advanced tax setups .
2017-07-27 12:48:58 +00:00
* @ param object $customer Override the customer object to get their location .
2014-11-21 13:06:30 +00:00
* @ return array
*/
2017-07-27 12:48:58 +00:00
public static function get_tax_location ( $tax_class = '' , $customer = null ) {
2014-12-09 16:41:26 +00:00
$location = array ();
2019-01-11 12:28:52 +00:00
if ( is_null ( $customer ) && WC () -> customer ) {
2017-07-27 12:48:58 +00:00
$customer = WC () -> customer ;
}
if ( ! empty ( $customer ) ) {
$location = $customer -> get_taxable_address ();
2015-04-14 09:08:27 +00:00
} elseif ( wc_prices_include_tax () || 'base' === get_option ( 'woocommerce_default_customer_address' ) || 'base' === get_option ( 'woocommerce_tax_based_on' ) ) {
2014-12-09 16:41:26 +00:00
$location = array (
2014-11-21 13:06:30 +00:00
WC () -> countries -> get_base_country (),
WC () -> countries -> get_base_state (),
WC () -> countries -> get_base_postcode (),
2016-08-27 02:08:49 +00:00
WC () -> countries -> get_base_city (),
2014-11-21 13:06:30 +00:00
);
}
2014-12-09 16:41:26 +00:00
2017-07-27 12:48:58 +00:00
return apply_filters ( 'woocommerce_get_tax_location' , $location , $tax_class , $customer );
2014-11-21 13:06:30 +00:00
}
2011-08-09 15:16:18 +00:00
/**
2012-08-14 20:19:41 +00:00
* Get ' s an array of matching rates for a tax class .
2017-07-27 12:48:58 +00:00
*
* @ param string $tax_class Tax class to get rates for .
* @ param object $customer Override the customer object to get their location .
2011-12-28 23:59:33 +00:00
* @ return array
2011-08-09 15:16:18 +00:00
*/
2017-07-27 12:48:58 +00:00
public static function get_rates ( $tax_class = '' , $customer = null ) {
2020-07-08 20:12:12 +00:00
$tax_class = sanitize_title ( $tax_class );
$location = self :: get_tax_location ( $tax_class , $customer );
return self :: get_rates_from_location ( $tax_class , $location , $customer );
}
/**
* Get ' s an arrau of matching rates from location and tax class . $customer parameter is used to preserve backward compatibility for filter .
*
* @ param string $tax_class Tax class to get rates for .
* @ param array $location Location to compute rates for . Should be in form : array ( country , state , postcode , city ) .
* @ param object $customer Only used to maintain backward compatibility for filter `woocommerce-matched_rates` .
*
2020-07-09 18:40:00 +00:00
* @ return mixed | void Tax rates .
2020-07-08 20:12:12 +00:00
*/
public static function get_rates_from_location ( $tax_class , $location , $customer = null ) {
2014-11-21 13:06:30 +00:00
$tax_class = sanitize_title ( $tax_class );
$matched_tax_rates = array ();
2012-11-27 16:22:47 +00:00
2019-01-11 12:28:52 +00:00
if ( count ( $location ) === 4 ) {
2014-11-21 13:06:30 +00:00
list ( $country , $state , $postcode , $city ) = $location ;
2012-08-08 10:42:42 +00:00
2019-01-11 12:28:52 +00:00
$matched_tax_rates = self :: find_rates (
array (
'country' => $country ,
'state' => $state ,
'postcode' => $postcode ,
'city' => $city ,
'tax_class' => $tax_class ,
)
);
2012-05-09 17:29:22 +00:00
}
2011-08-09 15:16:18 +00:00
2020-05-01 20:49:55 +00:00
return apply_filters ( 'woocommerce_matched_rates' , $matched_tax_rates , $tax_class , $customer );
2011-08-09 15:16:18 +00:00
}
2012-08-08 10:42:42 +00:00
2011-08-09 15:16:18 +00:00
/**
2012-08-14 20:19:41 +00:00
* Get 's an array of matching rates for the shop' s base country .
2011-08-09 15:16:18 +00:00
*
2019-01-11 12:28:52 +00:00
* @ param string $tax_class Tax Class .
* @ return array
2011-08-09 15:16:18 +00:00
*/
2014-11-11 11:56:13 +00:00
public static function get_base_tax_rates ( $tax_class = '' ) {
2019-01-11 12:28:52 +00:00
return apply_filters (
'woocommerce_base_tax_rates' ,
self :: find_rates (
array (
'country' => WC () -> countries -> get_base_country (),
'state' => WC () -> countries -> get_base_state (),
'postcode' => WC () -> countries -> get_base_postcode (),
'city' => WC () -> countries -> get_base_city (),
'tax_class' => $tax_class ,
)
),
$tax_class
);
2014-11-11 11:56:13 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Alias for get_base_tax_rates () .
2014-11-11 11:56:13 +00:00
*
* @ deprecated 2.3
2019-01-11 12:28:52 +00:00
* @ param string $tax_class Tax Class .
* @ return array
2014-11-11 11:56:13 +00:00
*/
public static function get_shop_base_rate ( $tax_class = '' ) {
return self :: get_base_tax_rates ( $tax_class );
2012-02-01 21:49:08 +00:00
}
2012-08-08 10:42:42 +00:00
2011-08-09 15:16:18 +00:00
/**
2012-08-14 20:19:41 +00:00
* Gets an array of matching shipping tax rates for a given class .
2011-08-09 15:16:18 +00:00
*
2017-07-27 12:48:58 +00:00
* @ param string $tax_class Tax class to get rates for .
* @ param object $customer Override the customer object to get their location .
* @ return mixed
2011-08-09 15:16:18 +00:00
*/
2017-07-27 12:48:58 +00:00
public static function get_shipping_tax_rates ( $tax_class = null , $customer = null ) {
2019-01-11 12:28:52 +00:00
// See if we have an explicitly set shipping tax class.
WIP - Product CRUD (#12065)
* Created function to get the catalog visibility options
* First methods for WP_Product crud
* Product set methods
* Fixed several erros while setting data
* First methods for WP_Product crud
* Product set methods
* Fixed several erros while setting data
* Hardcode the get_type per product class
* Initial look through getters and setters and abstract data
* Missing var
* Add related product functions and deprecate those in class.
* No need to exclude ID
* Fixed coding standards and improved the docblocks
* Get cached terms from wc_get_related_terms()
* Fixed wrong variable in wc_get_related_terms
* Use count() instead of sizeof()
* Sanitize ids later
* Remove unneeded comments
* wc_get_product_term_ids instead of related wording and use in other places.
get_the_terms is used here and also handles caching, something
wp_get_post_terms does not.
* Clean up the abstract product class a bit, deprecate two functions we have renamed, make update & create work properly, and add some tests for it.
* Bump template version
* Handle PR feedback: Remove duplicate regular_price update, allow changing of post status for products, remove deprecation for get_title since we might still offer it as a function
* Made abstract function useful
* External Product CRUD
* _virtual meta should be 'no', not taxable, in product unit test helper
* Grouped product class
* Tests
* Move children to meta and update test
* Use get_upsell_ids
* Spacing in query
* Moving and refactoring methods
* Availability html
* Tidy/add todos
* Rename method
* Put back review functions (still todo)
* missing $this
* get_price_including_tax/excluding_tax functions
* wc_get_price_to_display
* Price handling
* [Product CRUD] Variable (#12146)
* [Product CRUD] Variable Products
* Handle PR feedback.
* [Product CRUD] Grouped Handling (#12151)
* Handle grouped product saving
* Update routine
* [Product CRUD] Product crud terms (#12149)
* Category and tag id handling
* Replace template functions
* Remove todo
* Handle default name in save function
* Product crud admin save routine (#12174)
* Initial props
* Work on admin saving
* Set/get attributes
* Atom was moaning about this before but no longer.
* Update get_shipping_class
* WC_Product_Attribute
* Use getter in admin panel
* Fix attribute saving
* Spacing
* Fix comment
* wc_implode_text_attributes helper function
* [Product CRUD] Product crud admin use getters (#12196)
* Initial props
* Work on admin saving
* Set/get attributes
* Atom was moaning about this before but no longer.
* Update get_shipping_class
* WC_Product_Attribute
* Use getter in admin panel
* Fix attribute saving
* Move settings into new files
* Refactor panels and use getters
* Use getters for variation panel
* Revert save variation changes for now
* Add todos
* Fix downloads
* REST API CRUD Updates
* Additional API updates/fixes. Added some todos
* Fix final failing tests and implementing setters/getters and attributes functionality.
* Fix comparison for is_on_sale and remove download_type from WC_Product.
* Add a wc_get_products wrapper.
* Remove the download type input from the product data metabox for downloadable products. (#12221)
* [Product CRUD] Variations - setters, getters and admin. (#12228)
* Started on variation changes
* Stock functions
* Variation class
* Bulk change ->id to get_id() to fix variation form display
* Missing status
* Fix add to cart
* Start on stored data save
* save variation
* Save_variations
* Variation edit panel
* Save variations code works.
* Remove stored data code and fix save
* Improve legacy class
* wc_bool_to_string
* prepare_set_attributes
* Use wc_get_products
* More feedback fixes
* Feedback fixes
* Implement CRUD in the legacy REST API
* Handle PR feedback
* [Product CRUD] Getter setter proxy methods (#12236)
* Started on variation changes
* Stock functions
* Variation class
* Bulk change ->id to get_id() to fix variation form display
* Missing status
* Fix add to cart
* Start on stored data save
* save variation
* Save_variations
* Variation edit panel
* Save variations code works.
* Remove stored data code and fix save
* Improve legacy class
* wc_bool_to_string
* prepare_set_attributes
* Use wc_get_products
* More feedback fixes
* get_prop implementation in abstract and data classes
* Implement set_prop
* Change handling
* Array key exists
* set_object_read
* Use get_the_terms() instead of wp_get_post_terms()
wp_get_post_terms() is a wrapper around wp_get_object_terms() which does not
use the object cache, and generates a database query every time it is used.
get_the_terms() however can use data from the object cache if present.
* Allow WP_Query to preload post data, and meta in wc_get_products()
Allow WP_Query to bulk query for post data and meta if more than
just IDs are requested from wc_get_products(). Reduces query count
significantly.
* [Product CRUD] Variable, variation, notices, and stock handling (#12277)
* No longer needed
* Remove old todos
* Use getters in admin list
* Related and upsells update for CRUD
* Fix notice in gallery
* Variable fixes and todos
* Context
* Price sync
* Revert variation attributes change
* Return parent data in view context
* Defer term counting
* wc_find_matching_product_variation
* Stock manage tweaks
* Stock fixes
* Correct id
* correct id
* Better sync
* Data logic setter fix
* feedback
* First methods for WP_Product crud
* Product set methods
* Fixed several erros while setting data
* Hardcode the get_type per product class
* Initial look through getters and setters and abstract data
* Missing var
* Fixed coding standards and improved the docblocks
* Get cached terms from wc_get_related_terms()
* Fixed wrong variable in wc_get_related_terms
* Use count() instead of sizeof()
* Add related product functions and deprecate those in class.
* No need to exclude ID
* Sanitize ids later
* Clean up the abstract product class a bit, deprecate two functions we have renamed, make update & create work properly, and add some tests for it.
* Remove unneeded comments
* wc_get_product_term_ids instead of related wording and use in other places.
get_the_terms is used here and also handles caching, something
wp_get_post_terms does not.
* Handle PR feedback: Remove duplicate regular_price update, allow changing of post status for products, remove deprecation for get_title since we might still offer it as a function
* External Product CRUD
* _virtual meta should be 'no', not taxable, in product unit test helper
* Bump template version
* Made abstract function useful
* Grouped product class
* Tests
* Move children to meta and update test
* Use get_upsell_ids
* Spacing in query
* Moving and refactoring methods
* Availability html
* Tidy/add todos
* Rename method
* Put back review functions (still todo)
* missing $this
* get_price_including_tax/excluding_tax functions
* wc_get_price_to_display
* Price handling
* [Product CRUD] Variable (#12146)
* [Product CRUD] Variable Products
* Handle PR feedback.
* [Product CRUD] Grouped Handling (#12151)
* Handle grouped product saving
* Update routine
* [Product CRUD] Product crud terms (#12149)
* Category and tag id handling
* Replace template functions
* Remove todo
* Handle default name in save function
* Product crud admin save routine (#12174)
* Initial props
* Work on admin saving
* Set/get attributes
* Atom was moaning about this before but no longer.
* Update get_shipping_class
* WC_Product_Attribute
* Use getter in admin panel
* Fix attribute saving
* Spacing
* Fix comment
* wc_implode_text_attributes helper function
* [Product CRUD] Product crud admin use getters (#12196)
* Initial props
* Work on admin saving
* Set/get attributes
* Atom was moaning about this before but no longer.
* Update get_shipping_class
* WC_Product_Attribute
* Use getter in admin panel
* Fix attribute saving
* Move settings into new files
* Refactor panels and use getters
* Use getters for variation panel
* Revert save variation changes for now
* Add todos
* Fix downloads
* REST API CRUD Updates
* Additional API updates/fixes. Added some todos
* Fix final failing tests and implementing setters/getters and attributes functionality.
* Fix comparison for is_on_sale and remove download_type from WC_Product.
* Add a wc_get_products wrapper.
* Remove the download type input from the product data metabox for downloadable products. (#12221)
* [Product CRUD] Variations - setters, getters and admin. (#12228)
* Started on variation changes
* Stock functions
* Variation class
* Bulk change ->id to get_id() to fix variation form display
* Missing status
* Fix add to cart
* Start on stored data save
* save variation
* Save_variations
* Variation edit panel
* Save variations code works.
* Remove stored data code and fix save
* Improve legacy class
* wc_bool_to_string
* prepare_set_attributes
* Use wc_get_products
* More feedback fixes
* Feedback fixes
* Implement CRUD in the legacy REST API
* Handle PR feedback
* [Product CRUD] Getter setter proxy methods (#12236)
* Started on variation changes
* Stock functions
* Variation class
* Bulk change ->id to get_id() to fix variation form display
* Missing status
* Fix add to cart
* Start on stored data save
* save variation
* Save_variations
* Variation edit panel
* Save variations code works.
* Remove stored data code and fix save
* Improve legacy class
* wc_bool_to_string
* prepare_set_attributes
* Use wc_get_products
* More feedback fixes
* get_prop implementation in abstract and data classes
* Implement set_prop
* Change handling
* Array key exists
* set_object_read
* Use get_the_terms() instead of wp_get_post_terms()
wp_get_post_terms() is a wrapper around wp_get_object_terms() which does not
use the object cache, and generates a database query every time it is used.
get_the_terms() however can use data from the object cache if present.
* [Product CRUD] Variable, variation, notices, and stock handling (#12277)
* No longer needed
* Remove old todos
* Use getters in admin list
* Related and upsells update for CRUD
* Fix notice in gallery
* Variable fixes and todos
* Context
* Price sync
* Revert variation attributes change
* Return parent data in view context
* Defer term counting
* wc_find_matching_product_variation
* Stock manage tweaks
* Stock fixes
* Correct id
* correct id
* Better sync
* Data logic setter fix
* feedback
* Prevent notices
* Handle image_id from parent
* Fix error
* Remove _wc_save_product_price
* Remove todo
* Fixed wrong variation URLs
* Fixed undefined $image_id in WC_Product_Variation::get_image_id()
* Allow wc_rest_prepare_date_response() handle timestamps
* Updated get methods on REST API for variations
* Use variations CRUD to save variations metadata
* [Product CRUD] Abstract todos (#12305)
* Get dimensions and weights, with soft deprecation
* Product attributes
* Ratings
* Fix read method
* Downloads
* Feedback
* Revert "[Product CRUD] Abstract todos (#12305)"
This reverts commit 9a6136fcf88fec16f97457b7c8a4388f7587bfa2.
* Remove deprecated get_variation_id()
* New default attributes method
* [Product CRUD] Product Datastore (#12317)
* Fix up tests in the product/* folder.
* Handle data store updates for grouped, variable, external, simple, and general data store updates for products.
* Variations & variable changes.
* Update -functions.php calls to use data store.
* Add an interface for the public product data store methods.
* Finished product factory tests
* Correctly delete in the api, fix up some comments, and implement an interface for the public variable methods.
* Fix up delete in all versions of the api
* Handle feedback
* Match protected decloration to parent
* Product crud abstract todos (#12316)
* Get dimensions and weights, with soft deprecation
* Product attributes
* Ratings
* Fix read method
* Downloads
* Feedback
* Fix up store
* Fixed method returning in write context
* Fix error in variation admin
* Check for parent value - fixes tax class
* Remove old/complete todos
* Allow set tax class as "parent"
* Removed duplicated sync
* Fixed wrong variation URLs
* Fixed undefined $image_id in WC_Product_Variation::get_image_id()
* Allow wc_rest_prepare_date_response() handle timestamps
* Updated get methods on REST API for variations
* Use variations CRUD to save variations metadata
* Remove deprecated get_variation_id()
* New default attributes method
* Fixed method returning in write context
* Allow set tax class as "parent"
* Removed duplicated sync
* Fixed coding standards
* TODO is not accurate.
* Should pass WC_Product instancies to WC_Comments methods (#12327)
* Use new method in abstract order class to prevent headers sent issue in tests
* Fixed variable description in REST API
* Updated how create initial product variation
* Fixed a few fatal errors and warnings in Products CRUD (#12329)
* Fixed a few fatal errors and warnings in Products CRUD
* Fixed sync functions
* Add variations CRUD to legacy API (#12331)
* Apply crud to variable products in legacy API v1
* New REST API do not need fallback for default attributes
* Apply variations CRUD to legacy API v2
* Legacy v2 - save default attributes
* Variations in legacy API v2 do not have descriptions
* Fixed legacy API v2 variations params
* Applied variations CRUD to legacy API v3
* Sync before save in legacy apis
* Punc
* Removed API todos
* Removed test
* Products endpoint tweaks (#12354)
* Var type already normalized on CRUD
* Let Product CRUD handle with validation, sanitization and conditional checks
* Set downloads using WC_Product_Download
* Stop try catch exceptions more than one time
* Handle WC_Data_Exception in legacy API
* Complete remove products when fails on creating
* On creating I mean!
* Already have a method to complete delete products
* Fixed standards using WP CodeSniffer
* get_the_terms() returns false when empty
* get_manage_stock returns boolean
@claudiosanches
* Merge conflict
* Variations API endpoint fixes
* Product CRUD improvements (#12359)
* args is not used any more - remove todo
* Added test for attributes
* wc_get_price_excluding_tax usage
* parent usage
* Fix rating counts
* Test fixes
* Cleanup after tests
* Make sure status transition code runs even during API calls, not just in admin.
* Default visibility
* Fix attribute setting in API
* Use get name instead of get title
* variation id usage
* Improved cross sell templates
* variation_data
* Grouped product sync
* Notices
* Sync is not needed in API
* Delete
* Rename interfaces
* Update counts in data store
2016-11-16 12:38:24 +00:00
$shipping_tax_class = get_option ( 'woocommerce_shipping_tax_class' );
if ( 'inherit' !== $shipping_tax_class ) {
$tax_class = $shipping_tax_class ;
2012-05-09 17:29:22 +00:00
}
2012-08-08 10:42:42 +00:00
2017-07-27 12:48:58 +00:00
$location = self :: get_tax_location ( $tax_class , $customer );
2014-11-21 13:06:30 +00:00
$matched_tax_rates = array ();
2012-09-23 16:16:39 +00:00
2019-01-11 12:28:52 +00:00
if ( 4 === count ( $location ) ) {
2014-11-21 13:06:30 +00:00
list ( $country , $state , $postcode , $city ) = $location ;
2012-09-23 16:16:39 +00:00
2014-11-21 13:06:30 +00:00
if ( ! is_null ( $tax_class ) ) {
2019-01-11 12:28:52 +00:00
// This will be per item shipping.
$matched_tax_rates = self :: find_shipping_rates (
array (
'country' => $country ,
'state' => $state ,
'postcode' => $postcode ,
'city' => $city ,
'tax_class' => $tax_class ,
)
);
2012-09-23 16:16:39 +00:00
2017-08-07 16:49:46 +00:00
} elseif ( WC () -> cart -> get_cart () ) {
2012-08-08 10:42:42 +00:00
2019-01-11 12:28:52 +00:00
// This will be per order shipping - loop through the order and find the highest tax class rate.
2017-11-20 19:14:23 +00:00
$cart_tax_classes = WC () -> cart -> get_cart_item_tax_classes_for_shipping ();
2012-08-08 10:42:42 +00:00
2017-08-03 06:44:02 +00:00
// No tax classes = no taxable items.
if ( empty ( $cart_tax_classes ) ) {
return array ();
}
2017-07-17 10:10:52 +00:00
// If multiple classes are found, use the first one found unless a standard rate item is found. This will be the first listed in the 'additional tax class' section.
2019-01-11 12:28:52 +00:00
if ( count ( $cart_tax_classes ) > 1 && ! in_array ( '' , $cart_tax_classes , true ) ) {
2017-01-25 21:38:13 +00:00
$tax_classes = self :: get_tax_class_slugs ();
2012-09-23 16:16:39 +00:00
foreach ( $tax_classes as $tax_class ) {
2019-01-11 12:28:52 +00:00
if ( in_array ( $tax_class , $cart_tax_classes , true ) ) {
$matched_tax_rates = self :: find_shipping_rates (
array (
'country' => $country ,
'state' => $state ,
'postcode' => $postcode ,
'city' => $city ,
'tax_class' => $tax_class ,
)
);
2012-01-04 16:24:26 +00:00
break ;
2012-09-23 16:16:39 +00:00
}
}
2019-01-11 12:28:52 +00:00
} elseif ( 1 === count ( $cart_tax_classes ) ) {
// If a single tax class is found, use it.
$matched_tax_rates = self :: find_shipping_rates (
array (
'country' => $country ,
'state' => $state ,
'postcode' => $postcode ,
'city' => $city ,
'tax_class' => $cart_tax_classes [ 0 ],
)
);
2014-11-21 13:06:30 +00:00
}
2012-09-23 16:16:39 +00:00
}
2012-08-08 10:42:42 +00:00
2019-01-11 12:28:52 +00:00
// Get standard rate if no taxes were found.
if ( ! count ( $matched_tax_rates ) ) {
$matched_tax_rates = self :: find_shipping_rates (
array (
'country' => $country ,
'state' => $state ,
'postcode' => $postcode ,
'city' => $city ,
)
);
2014-11-21 13:06:30 +00:00
}
2012-09-23 16:16:39 +00:00
}
2014-11-21 13:06:30 +00:00
return $matched_tax_rates ;
2011-08-09 15:16:18 +00:00
}
2012-08-08 10:42:42 +00:00
2011-12-30 19:36:44 +00:00
/**
2012-08-14 20:19:41 +00:00
* Return true / false depending on if a rate is a compound rate .
2011-12-30 21:11:18 +00:00
*
2019-01-11 12:28:52 +00:00
* @ param mixed $key_or_rate Tax rate ID , or the db row itself in object format .
2012-08-08 10:42:42 +00:00
* @ return bool
2011-12-30 19:36:44 +00:00
*/
2017-02-20 21:50:07 +00:00
public static function is_compound ( $key_or_rate ) {
2012-12-03 12:18:34 +00:00
global $wpdb ;
2017-02-20 21:50:07 +00:00
if ( is_object ( $key_or_rate ) ) {
2019-01-11 12:28:52 +00:00
$key = $key_or_rate -> tax_rate_id ;
$compound = $key_or_rate -> tax_rate_compound ;
2017-02-20 21:50:07 +00:00
} else {
2019-01-11 12:28:52 +00:00
$key = $key_or_rate ;
$compound = ( bool ) $wpdb -> get_var ( $wpdb -> prepare ( " SELECT tax_rate_compound FROM { $wpdb -> prefix } woocommerce_tax_rates WHERE tax_rate_id = %s " , $key ) );
2017-02-20 21:50:07 +00:00
}
return ( bool ) apply_filters ( 'woocommerce_rate_compound' , $compound , $key );
2011-12-30 19:36:44 +00:00
}
2011-12-30 21:11:18 +00:00
2011-12-30 19:36:44 +00:00
/**
2012-08-14 20:19:41 +00:00
* Return a given rates label .
2011-12-30 21:11:18 +00:00
*
2019-01-11 12:28:52 +00:00
* @ param mixed $key_or_rate Tax rate ID , or the db row itself in object format .
2012-08-08 10:42:42 +00:00
* @ return string
2011-12-30 19:36:44 +00:00
*/
2014-07-23 10:30:06 +00:00
public static function get_rate_label ( $key_or_rate ) {
2013-11-18 12:30:28 +00:00
global $wpdb ;
2013-09-10 10:04:26 +00:00
2014-07-23 10:30:06 +00:00
if ( is_object ( $key_or_rate ) ) {
$key = $key_or_rate -> tax_rate_id ;
$rate_name = $key_or_rate -> tax_rate_name ;
} else {
$key = $key_or_rate ;
$rate_name = $wpdb -> get_var ( $wpdb -> prepare ( " SELECT tax_rate_name FROM { $wpdb -> prefix } woocommerce_tax_rates WHERE tax_rate_id = %s " , $key ) );
}
2013-09-10 10:04:26 +00:00
2014-07-23 10:30:06 +00:00
if ( ! $rate_name ) {
2013-11-18 12:30:28 +00:00
$rate_name = WC () -> countries -> tax_or_vat ();
2014-07-23 10:30:06 +00:00
}
2013-09-10 10:04:26 +00:00
2014-06-13 14:53:59 +00:00
return apply_filters ( 'woocommerce_rate_label' , $rate_name , $key );
2011-12-30 19:36:44 +00:00
}
2012-08-08 10:42:42 +00:00
2014-07-10 10:01:00 +00:00
/**
* Return a given rates percent .
*
2019-01-11 12:28:52 +00:00
* @ param mixed $key_or_rate Tax rate ID , or the db row itself in object format .
2014-07-10 10:01:00 +00:00
* @ return string
*/
2014-07-23 10:30:06 +00:00
public static function get_rate_percent ( $key_or_rate ) {
2019-07-11 15:21:56 +00:00
$rate_percent_value = self :: get_rate_percent_value ( $key_or_rate );
2019-12-20 17:21:08 +00:00
$tax_rate_id = is_object ( $key_or_rate ) ? $key_or_rate -> tax_rate_id : $key_or_rate ;
2019-07-11 13:56:36 +00:00
return apply_filters ( 'woocommerce_rate_percent' , $rate_percent_value . '%' , $tax_rate_id );
2019-04-10 15:27:48 +00:00
}
/**
* Return a given rates percent .
*
* @ param mixed $key_or_rate Tax rate ID , or the db row itself in object format .
* @ return float
*/
public static function get_rate_percent_value ( $key_or_rate ) {
2014-07-10 10:01:00 +00:00
global $wpdb ;
2014-07-23 10:30:06 +00:00
if ( is_object ( $key_or_rate ) ) {
$tax_rate = $key_or_rate -> tax_rate ;
} else {
$key = $key_or_rate ;
$tax_rate = $wpdb -> get_var ( $wpdb -> prepare ( " SELECT tax_rate FROM { $wpdb -> prefix } woocommerce_tax_rates WHERE tax_rate_id = %s " , $key ) );
}
2014-07-10 10:01:00 +00:00
2019-04-10 15:27:48 +00:00
return floatval ( $tax_rate );
2014-07-10 10:01:00 +00:00
}
2019-04-10 15:27:48 +00:00
2012-12-06 19:49:00 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get a rates code . Code is made up of COUNTRY - STATE - NAME - Priority . E . g GB - VAT - 1 , US - AL - TAX - 1.
2012-12-06 19:49:00 +00:00
*
2019-01-11 12:28:52 +00:00
* @ param mixed $key_or_rate Tax rate ID , or the db row itself in object format .
2013-11-27 09:03:47 +00:00
* @ return string
2012-12-06 19:49:00 +00:00
*/
2014-07-23 10:30:06 +00:00
public static function get_rate_code ( $key_or_rate ) {
2012-12-06 19:49:00 +00:00
global $wpdb ;
2014-07-23 10:30:06 +00:00
if ( is_object ( $key_or_rate ) ) {
$key = $key_or_rate -> tax_rate_id ;
$rate = $key_or_rate ;
} else {
$key = $key_or_rate ;
2017-02-13 12:58:42 +00:00
$rate = $wpdb -> get_row ( $wpdb -> prepare ( " SELECT tax_rate_country, tax_rate_state, tax_rate_name, tax_rate_priority FROM { $wpdb -> prefix } woocommerce_tax_rates WHERE tax_rate_id = %s " , $key ) );
2014-07-23 10:30:06 +00:00
}
2012-12-06 19:49:00 +00:00
2014-10-28 11:24:09 +00:00
$code_string = '' ;
if ( null !== $rate ) {
2019-01-11 12:28:52 +00:00
$code = array ();
$code [] = $rate -> tax_rate_country ;
$code [] = $rate -> tax_rate_state ;
$code [] = $rate -> tax_rate_name ? $rate -> tax_rate_name : 'TAX' ;
$code [] = absint ( $rate -> tax_rate_priority );
2014-10-28 11:24:09 +00:00
$code_string = strtoupper ( implode ( '-' , array_filter ( $code ) ) );
2014-02-17 15:11:50 +00:00
}
2014-10-28 11:24:09 +00:00
return apply_filters ( 'woocommerce_rate_code' , $code_string , $key );
2012-12-06 19:49:00 +00:00
}
2012-01-04 16:24:26 +00:00
/**
2019-01-11 12:38:05 +00:00
* Sums a set of taxes to form a single total . Values are pre - rounded to precision from 3.6 . 0.
2012-01-04 16:24:26 +00:00
*
2019-01-11 12:38:05 +00:00
* @ param array $taxes Array of taxes .
* @ return float
2012-01-04 16:24:26 +00:00
*/
2014-06-12 15:47:43 +00:00
public static function get_tax_total ( $taxes ) {
2019-01-11 12:38:05 +00:00
return array_sum ( $taxes );
2012-01-04 16:24:26 +00:00
}
2014-11-20 17:08:17 +00:00
2014-11-21 13:06:30 +00:00
/**
2019-03-20 14:21:23 +00:00
* Gets all tax rate classes from the database .
2017-10-17 05:05:48 +00:00
*
2019-03-20 14:21:23 +00:00
* @ since 3.7 . 0
* @ return array Array of tax class objects consisting of tax_rate_class_id , name , and slug .
2014-11-21 13:06:30 +00:00
*/
2019-03-20 14:21:23 +00:00
protected static function get_tax_rate_classes () {
global $wpdb ;
$cache_key = 'tax-rate-classes' ;
$tax_rate_classes = wp_cache_get ( $cache_key , 'taxes' );
if ( ! is_array ( $tax_rate_classes ) ) {
$tax_rate_classes = $wpdb -> get_results (
"
2019-03-20 15:27:53 +00:00
SELECT * FROM { $wpdb -> wc_tax_rate_classes } ORDER BY name ;
2019-03-20 14:21:23 +00:00
"
);
wp_cache_set ( $cache_key , $tax_rate_classes , 'taxes' );
}
return $tax_rate_classes ;
2017-10-17 05:05:48 +00:00
}
/**
2019-03-20 14:21:23 +00:00
* Get store tax class names .
2017-10-17 05:05:48 +00:00
*
2019-03-20 14:21:23 +00:00
* @ return array Array of class names ( " Reduced rate " , " Zero rate " , etc ) .
2017-10-17 05:05:48 +00:00
*/
2019-03-20 14:21:23 +00:00
public static function get_tax_classes () {
return wp_list_pluck ( self :: get_tax_rate_classes (), 'name' );
2014-11-21 13:06:30 +00:00
}
2017-01-25 21:38:13 +00:00
/**
* Get store tax classes as slugs .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-01-25 21:48:29 +00:00
* @ return array Array of class slugs ( " reduced-rate " , " zero-rate " , etc ) .
2017-01-25 21:38:13 +00:00
*/
public static function get_tax_class_slugs () {
2019-03-20 14:21:23 +00:00
return wp_list_pluck ( self :: get_tax_rate_classes (), 'slug' );
}
/**
* Create a new tax class .
*
* @ since 3.7 . 0
* @ param string $name Name of the tax class to add .
* @ param string $slug ( optional ) Slug of the tax class to add . Defaults to sanitized name .
* @ return WP_Error | array Returns name and slug ( array ) if the tax class is created , or WP_Error if something went wrong .
*/
public static function create_tax_class ( $name , $slug = '' ) {
global $wpdb ;
2019-01-30 14:04:21 +00:00
2019-07-09 18:26:07 +00:00
if ( empty ( $name ) ) {
return new WP_Error ( 'tax_class_invalid_name' , __ ( 'Tax class requires a valid name' , 'woocommerce' ) );
}
2019-03-20 14:21:23 +00:00
$existing = self :: get_tax_classes ();
$existing_slugs = self :: get_tax_class_slugs ();
2019-01-30 14:04:21 +00:00
2019-03-20 14:21:23 +00:00
if ( in_array ( $name , $existing , true ) ) {
return new WP_Error ( 'tax_class_exists' , __ ( 'Tax class already exists' , 'woocommerce' ) );
2019-01-30 14:04:21 +00:00
}
2019-03-20 14:21:23 +00:00
if ( ! $slug ) {
$slug = sanitize_title ( $name );
}
if ( in_array ( $slug , $existing_slugs , true ) ) {
return new WP_Error ( 'tax_class_slug_exists' , __ ( 'Tax class slug already exists' , 'woocommerce' ) );
}
$insert = $wpdb -> insert (
$wpdb -> wc_tax_rate_classes ,
array (
'name' => $name ,
'slug' => $slug ,
)
);
2019-01-30 14:04:21 +00:00
2019-03-20 14:21:23 +00:00
if ( is_wp_error ( $insert ) ) {
return new WP_Error ( 'tax_class_insert_error' , $insert -> get_error_message () );
}
wp_cache_delete ( 'tax-rate-classes' , 'taxes' );
return array (
'name' => $name ,
'slug' => $slug ,
);
}
/**
* Get an existing tax class .
*
* @ since 3.7 . 0
* @ param string $field Field to get by . Valid values are id , name , or slug .
* @ param string | int $item Item to get .
* @ return array | bool Returns the tax class as an array . False if not found .
*/
public static function get_tax_class_by ( $field , $item ) {
if ( ! in_array ( $field , array ( 'id' , 'name' , 'slug' ), true ) ) {
return new WP_Error ( 'invalid_field' , __ ( 'Invalid field' , 'woocommerce' ) );
}
2019-01-30 14:04:21 +00:00
2019-03-20 14:21:23 +00:00
if ( 'id' === $field ) {
$field = 'tax_rate_class_id' ;
2019-01-30 14:04:21 +00:00
}
2019-03-20 14:21:23 +00:00
$matches = wp_list_filter (
self :: get_tax_rate_classes (),
array (
$field => $item ,
)
);
if ( ! $matches ) {
return false ;
}
2019-03-20 15:27:53 +00:00
$tax_class = current ( $matches );
2019-03-20 14:21:23 +00:00
return array (
2019-03-20 15:27:53 +00:00
'name' => $tax_class -> name ,
'slug' => $tax_class -> slug ,
2019-03-20 14:21:23 +00:00
);
}
/**
* Delete an existing tax class .
*
* @ since 3.7 . 0
* @ param string $field Field to delete by . Valid values are id , name , or slug .
* @ param string | int $item Item to delete .
* @ return WP_Error | bool Returns true if deleted successfully , false if nothing was deleted , or WP_Error if there is an invalid request .
*/
public static function delete_tax_class_by ( $field , $item ) {
global $wpdb ;
if ( ! in_array ( $field , array ( 'id' , 'name' , 'slug' ), true ) ) {
return new WP_Error ( 'invalid_field' , __ ( 'Invalid field' , 'woocommerce' ) );
}
$tax_class = self :: get_tax_class_by ( $field , $item );
if ( ! $tax_class ) {
return new WP_Error ( 'invalid_tax_class' , __ ( 'Invalid tax class' , 'woocommerce' ) );
}
if ( 'id' === $field ) {
$field = 'tax_rate_class_id' ;
}
$delete = $wpdb -> delete (
$wpdb -> wc_tax_rate_classes ,
array (
$field => $item ,
)
);
if ( $delete ) {
// Delete associated tax rates.
$wpdb -> query ( $wpdb -> prepare ( " DELETE FROM { $wpdb -> prefix } woocommerce_tax_rates WHERE tax_rate_class = %s; " , $tax_class [ 'slug' ] ) );
$wpdb -> query ( " DELETE locations FROM { $wpdb -> prefix } woocommerce_tax_rate_locations locations LEFT JOIN { $wpdb -> prefix } woocommerce_tax_rates rates ON rates.tax_rate_id = locations.tax_rate_id WHERE rates.tax_rate_id IS NULL; " );
}
wp_cache_delete ( 'tax-rate-classes' , 'taxes' );
2019-11-28 13:03:57 +00:00
WC_Cache_Helper :: invalidate_cache_group ( 'taxes' );
2019-03-20 14:21:23 +00:00
return ( bool ) $delete ;
2017-01-25 21:38:13 +00:00
}
2014-11-20 18:41:51 +00:00
/**
2019-01-11 12:28:52 +00:00
* Format the city .
*
* @ param string $city Value to format .
2014-11-20 18:41:51 +00:00
* @ return string
*/
private static function format_tax_rate_city ( $city ) {
return strtoupper ( trim ( $city ) );
}
/**
2019-01-11 12:28:52 +00:00
* Format the state .
*
* @ param string $state Value to format .
2014-11-20 18:41:51 +00:00
* @ return string
*/
private static function format_tax_rate_state ( $state ) {
$state = strtoupper ( $state );
2016-09-07 22:32:24 +00:00
return ( '*' === $state ) ? '' : $state ;
2014-11-20 18:41:51 +00:00
}
/**
2019-01-11 12:28:52 +00:00
* Format the country .
*
* @ param string $country Value to format .
2014-11-20 18:41:51 +00:00
* @ return string
*/
private static function format_tax_rate_country ( $country ) {
$country = strtoupper ( $country );
2016-09-07 22:32:24 +00:00
return ( '*' === $country ) ? '' : $country ;
2014-11-20 18:41:51 +00:00
}
/**
2019-01-11 12:28:52 +00:00
* Format the tax rate name .
*
* @ param string $name Value to format .
2014-11-20 18:41:51 +00:00
* @ return string
*/
private static function format_tax_rate_name ( $name ) {
return $name ? $name : __ ( 'Tax' , 'woocommerce' );
}
/**
2019-01-11 12:28:52 +00:00
* Format the rate .
*
* @ param float $rate Value to format .
2015-02-03 14:32:10 +00:00
* @ return string
2014-11-20 18:41:51 +00:00
*/
private static function format_tax_rate ( $rate ) {
2019-01-11 12:28:52 +00:00
return number_format ( ( float ) $rate , 4 , '.' , '' );
2014-11-20 18:41:51 +00:00
}
/**
2019-01-11 12:28:52 +00:00
* Format the priority .
*
* @ param string $priority Value to format .
2014-11-20 18:41:51 +00:00
* @ return int
*/
private static function format_tax_rate_priority ( $priority ) {
return absint ( $priority );
}
/**
2019-01-11 12:28:52 +00:00
* Format the class .
*
* @ param string $class Value to format .
2014-11-20 18:41:51 +00:00
* @ return string
*/
2015-08-13 21:34:00 +00:00
public static function format_tax_rate_class ( $class ) {
2017-01-25 21:38:13 +00:00
$class = sanitize_title ( $class );
$classes = self :: get_tax_class_slugs ();
2019-01-11 12:28:52 +00:00
if ( ! in_array ( $class , $classes , true ) ) {
2015-08-13 20:56:50 +00:00
$class = '' ;
}
2016-09-07 22:32:24 +00:00
return ( 'standard' === $class ) ? '' : $class ;
2014-11-20 18:41:51 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Prepare and format tax rate for DB insertion .
2019-01-11 12:28:52 +00:00
*
* @ param array $tax_rate Tax rate to format .
2014-11-20 18:41:51 +00:00
* @ return array
*/
private static function prepare_tax_rate ( $tax_rate ) {
foreach ( $tax_rate as $key => $value ) {
if ( method_exists ( __CLASS__ , 'format_' . $key ) ) {
2017-09-14 22:50:25 +00:00
if ( 'tax_rate_state' === $key ) {
$tax_rate [ $key ] = call_user_func ( array ( __CLASS__ , 'format_' . $key ), sanitize_key ( $value ) );
} else {
$tax_rate [ $key ] = call_user_func ( array ( __CLASS__ , 'format_' . $key ), $value );
}
2014-11-20 18:41:51 +00:00
}
}
return $tax_rate ;
}
/**
2015-11-03 13:31:20 +00:00
* Insert a new tax rate .
2014-11-20 18:41:51 +00:00
*
* Internal use only .
*
* @ since 2.3 . 0
2015-10-08 20:24:37 +00:00
*
2019-01-11 12:28:52 +00:00
* @ param array $tax_rate Tax rate to insert .
2015-10-08 20:24:37 +00:00
* @ return int tax rate id
2014-11-20 18:41:51 +00:00
*/
public static function _insert_tax_rate ( $tax_rate ) {
global $wpdb ;
$wpdb -> insert ( $wpdb -> prefix . 'woocommerce_tax_rates' , self :: prepare_tax_rate ( $tax_rate ) );
2019-02-26 20:29:17 +00:00
$tax_rate_id = $wpdb -> insert_id ;
2019-11-28 13:03:57 +00:00
WC_Cache_Helper :: invalidate_cache_group ( 'taxes' );
2015-12-02 15:53:46 +00:00
2019-02-26 20:29:17 +00:00
do_action ( 'woocommerce_tax_rate_added' , $tax_rate_id , $tax_rate );
2014-11-20 18:41:51 +00:00
2019-02-26 20:29:17 +00:00
return $tax_rate_id ;
2014-11-20 18:41:51 +00:00
}
2015-10-08 20:24:37 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get tax rate .
2015-10-08 20:24:37 +00:00
*
* Internal use only .
*
* @ since 2.5 . 0
*
2019-01-11 12:28:52 +00:00
* @ param int $tax_rate_id Tax rate ID .
* @ param string $output_type Type of output .
2017-05-15 11:50:52 +00:00
* @ return array | object
2015-10-08 20:24:37 +00:00
*/
2016-03-09 04:11:56 +00:00
public static function _get_tax_rate ( $tax_rate_id , $output_type = ARRAY_A ) {
2015-10-08 20:24:37 +00:00
global $wpdb ;
2019-01-11 12:28:52 +00:00
return $wpdb -> get_row (
$wpdb -> prepare (
"
SELECT *
FROM { $wpdb -> prefix } woocommerce_tax_rates
WHERE tax_rate_id = % d
" ,
$tax_rate_id
),
$output_type
);
2015-10-08 20:24:37 +00:00
}
2014-11-20 18:41:51 +00:00
/**
2015-11-03 13:31:20 +00:00
* Update a tax rate .
2014-11-20 18:41:51 +00:00
*
* Internal use only .
*
* @ since 2.3 . 0
*
2019-01-11 12:28:52 +00:00
* @ param int $tax_rate_id Tax rate to update .
* @ param array $tax_rate Tax rate values .
2014-11-20 18:41:51 +00:00
*/
public static function _update_tax_rate ( $tax_rate_id , $tax_rate ) {
global $wpdb ;
$tax_rate_id = absint ( $tax_rate_id );
$wpdb -> update (
2019-01-11 12:28:52 +00:00
$wpdb -> prefix . 'woocommerce_tax_rates' ,
2014-11-20 18:41:51 +00:00
self :: prepare_tax_rate ( $tax_rate ),
array (
2016-08-27 01:46:45 +00:00
'tax_rate_id' => $tax_rate_id ,
2014-11-20 18:41:51 +00:00
)
);
2019-11-28 13:03:57 +00:00
WC_Cache_Helper :: invalidate_cache_group ( 'taxes' );
2015-12-02 15:53:46 +00:00
2014-11-20 18:41:51 +00:00
do_action ( 'woocommerce_tax_rate_updated' , $tax_rate_id , $tax_rate );
}
/**
2015-11-03 13:31:20 +00:00
* Delete a tax rate from the database .
2014-11-20 18:41:51 +00:00
*
* Internal use only .
*
* @ since 2.3 . 0
2019-01-11 12:28:52 +00:00
* @ param int $tax_rate_id Tax rate to delete .
2014-11-20 18:41:51 +00:00
*/
public static function _delete_tax_rate ( $tax_rate_id ) {
global $wpdb ;
$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 ) );
2019-11-28 13:03:57 +00:00
WC_Cache_Helper :: invalidate_cache_group ( 'taxes' );
2015-12-02 15:53:46 +00:00
2014-11-20 18:41:51 +00:00
do_action ( 'woocommerce_tax_rate_deleted' , $tax_rate_id );
}
/**
2015-11-03 13:31:20 +00:00
* Update postcodes for a tax rate in the DB .
2014-11-20 18:41:51 +00:00
*
* Internal use only .
*
* @ since 2.3 . 0
*
2019-01-11 12:28:52 +00:00
* @ param int $tax_rate_id Tax rate to update .
* @ param string $postcodes String of postcodes separated by ; characters .
2014-11-20 18:41:51 +00:00
*/
public static function _update_tax_rate_postcodes ( $tax_rate_id , $postcodes ) {
2015-08-13 21:49:59 +00:00
if ( ! is_array ( $postcodes ) ) {
$postcodes = explode ( ';' , $postcodes );
}
2016-06-16 10:28:53 +00:00
// No normalization - postcodes are matched against both normal and formatted versions to support wildcards.
2016-06-21 09:57:03 +00:00
foreach ( $postcodes as $key => $postcode ) {
$postcodes [ $key ] = strtoupper ( trim ( str_replace ( chr ( 226 ) . chr ( 128 ) . chr ( 166 ), '...' , $postcode ) ) );
}
2019-01-11 12:28:52 +00:00
self :: update_tax_rate_locations ( $tax_rate_id , array_diff ( array_filter ( $postcodes ), array ( '*' ) ), 'postcode' );
2014-11-20 18:41:51 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Update cities for a tax rate in the DB .
2014-11-20 18:41:51 +00:00
*
* Internal use only .
*
* @ since 2.3 . 0
*
2019-01-11 12:28:52 +00:00
* @ param int $tax_rate_id Tax rate to update .
* @ param string $cities Cities to set .
2014-11-20 18:41:51 +00:00
*/
public static function _update_tax_rate_cities ( $tax_rate_id , $cities ) {
2015-08-13 21:49:59 +00:00
if ( ! is_array ( $cities ) ) {
$cities = explode ( ';' , $cities );
}
$cities = array_filter ( array_diff ( array_map ( array ( __CLASS__ , 'format_tax_rate_city' ), $cities ), array ( '*' ) ) );
2014-11-20 18:41:51 +00:00
2019-01-11 12:28:52 +00:00
self :: update_tax_rate_locations ( $tax_rate_id , $cities , 'city' );
2014-11-20 21:02:10 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Updates locations ( postcode and city ) .
2014-11-20 21:02:10 +00:00
*
* Internal use only .
*
* @ since 2.3 . 0
*
2019-01-11 12:28:52 +00:00
* @ param int $tax_rate_id Tax rate ID to update .
* @ param array $values Values to set .
* @ param string $type Location type .
2014-11-20 21:02:10 +00:00
*/
2019-01-11 12:28:52 +00:00
private static function update_tax_rate_locations ( $tax_rate_id , $values , $type ) {
2014-11-20 21:02:10 +00:00
global $wpdb ;
2015-03-13 12:44:04 +00:00
$tax_rate_id = absint ( $tax_rate_id );
2014-11-20 18:41:51 +00:00
$wpdb -> query (
2019-01-11 12:28:52 +00:00
$wpdb -> prepare (
" DELETE FROM { $wpdb -> prefix } woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = %s; " ,
$tax_rate_id ,
$type
2014-11-20 18:41:51 +00:00
)
);
2019-01-11 12:28:52 +00:00
if ( count ( $values ) > 0 ) {
2014-11-20 21:02:10 +00:00
$sql = " ( ' " . implode ( " ', $tax_rate_id , ' " . esc_sql ( $type ) . " ' ),( ' " , array_map ( 'esc_sql' , $values ) ) . " ', $tax_rate_id , ' " . esc_sql ( $type ) . " ' ) " ;
2014-11-20 18:41:51 +00:00
2019-01-11 12:28:52 +00:00
$wpdb -> query ( " INSERT INTO { $wpdb -> prefix } woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES $sql ; " ); // @codingStandardsIgnoreLine.
2014-11-20 18:41:51 +00:00
}
2015-12-02 15:53:46 +00:00
2019-11-28 13:03:57 +00:00
WC_Cache_Helper :: invalidate_cache_group ( 'taxes' );
2014-11-20 18:41:51 +00:00
}
2015-08-13 21:49:59 +00:00
/**
* Used by admin settings page .
*
2019-01-11 12:28:52 +00:00
* @ param string $tax_class Tax class slug .
2015-08-13 21:49:59 +00:00
*
* @ return array | null | object
*/
public static function get_rates_for_tax_class ( $tax_class ) {
global $wpdb ;
2020-08-22 10:09:14 +00:00
$tax_class = self :: format_tax_rate_class ( $tax_class );
2015-08-13 21:49:59 +00:00
// Get all the rates and locations. Snagging all at once should significantly cut down on the number of queries.
2020-08-22 10:09:14 +00:00
$rates = $wpdb -> get_results ( $wpdb -> prepare ( " SELECT * FROM ` { $wpdb -> prefix } woocommerce_tax_rates` WHERE `tax_rate_class` = %s; " , $tax_class ) );
2015-08-13 21:49:59 +00:00
$locations = $wpdb -> get_results ( " SELECT * FROM ` { $wpdb -> prefix } woocommerce_tax_rate_locations` " );
2016-02-08 08:58:32 +00:00
if ( ! empty ( $rates ) ) {
// Set the rates keys equal to their ids.
$rates = array_combine ( wp_list_pluck ( $rates , 'tax_rate_id' ), $rates );
}
2015-08-13 21:49:59 +00:00
// Drop the locations into the rates array.
foreach ( $locations as $location ) {
// Don't set them for unexistent rates.
if ( ! isset ( $rates [ $location -> tax_rate_id ] ) ) {
continue ;
}
// If the rate exists, initialize the array before appending to it.
if ( ! isset ( $rates [ $location -> tax_rate_id ] -> { $location -> location_type } ) ) {
$rates [ $location -> tax_rate_id ] -> { $location -> location_type } = array ();
}
$rates [ $location -> tax_rate_id ] -> { $location -> location_type }[] = $location -> location_code ;
}
2017-10-12 13:04:10 +00:00
foreach ( $rates as $rate_id => $rate ) {
$rates [ $rate_id ] -> postcode_count = isset ( $rates [ $rate_id ] -> postcode ) ? count ( $rates [ $rate_id ] -> postcode ) : 0 ;
$rates [ $rate_id ] -> city_count = isset ( $rates [ $rate_id ] -> city ) ? count ( $rates [ $rate_id ] -> city ) : 0 ;
}
$rates = self :: sort_rates ( $rates );
2015-08-13 21:49:59 +00:00
return $rates ;
}
2014-02-25 11:54:58 +00:00
}
2014-09-20 19:02:51 +00:00
WC_Tax :: init ();