PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 3.0.1
WooCommerce v3.0.1
10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / class-wc-tax.php
woocommerce / includes Last commit date
abstracts 9 years ago admin 9 years ago api 9 years ago cli 9 years ago data-stores 9 years ago emails 9 years ago gateways 9 years ago interfaces 9 years ago legacy 9 years ago libraries 9 years ago log-handlers 9 years ago payment-tokens 9 years ago shipping 9 years ago shortcodes 9 years ago theme-support 9 years ago vendor 9 years ago walkers 9 years ago widgets 9 years ago class-wc-ajax.php 9 years ago class-wc-api.php 9 years ago class-wc-auth.php 9 years ago class-wc-autoloader.php 9 years ago class-wc-background-emailer.php 9 years ago class-wc-background-updater.php 9 years ago class-wc-breadcrumb.php 9 years ago class-wc-cache-helper.php 9 years ago class-wc-cart.php 9 years ago class-wc-checkout.php 9 years ago class-wc-cli.php 9 years ago class-wc-comments.php 9 years ago class-wc-countries.php 9 years ago class-wc-coupon.php 9 years ago class-wc-customer-download.php 9 years ago class-wc-customer.php 9 years ago class-wc-data-exception.php 9 years ago class-wc-data-store.php 9 years ago class-wc-datetime.php 9 years ago class-wc-deprecated-action-hooks.php 9 years ago class-wc-deprecated-filter-hooks.php 9 years ago class-wc-download-handler.php 9 years ago class-wc-emails.php 9 years ago class-wc-embed.php 9 years ago class-wc-form-handler.php 9 years ago class-wc-frontend-scripts.php 9 years ago class-wc-geo-ip.php 9 years ago class-wc-geolocation.php 9 years ago class-wc-https.php 9 years ago class-wc-install.php 9 years ago class-wc-integrations.php 9 years ago class-wc-legacy-api.php 9 years ago class-wc-log-levels.php 9 years ago class-wc-logger.php 9 years ago class-wc-order-factory.php 9 years ago class-wc-order-item-coupon.php 9 years ago class-wc-order-item-fee.php 9 years ago class-wc-order-item-meta.php 9 years ago class-wc-order-item-product.php 9 years ago class-wc-order-item-shipping.php 9 years ago class-wc-order-item-tax.php 9 years ago class-wc-order-item.php 9 years ago class-wc-order-refund.php 9 years ago class-wc-order.php 9 years ago class-wc-payment-gateways.php 9 years ago class-wc-payment-tokens.php 9 years ago class-wc-post-data.php 9 years ago class-wc-post-types.php 9 years ago class-wc-product-attribute.php 9 years ago class-wc-product-download.php 9 years ago class-wc-product-external.php 9 years ago class-wc-product-factory.php 9 years ago class-wc-product-grouped.php 9 years ago class-wc-product-simple.php 9 years ago class-wc-product-variable.php 9 years ago class-wc-product-variation.php 9 years ago class-wc-query.php 9 years ago class-wc-register-wp-admin-settings.php 9 years ago class-wc-session-handler.php 9 years ago class-wc-shipping-rate.php 9 years ago class-wc-shipping-zone.php 9 years ago class-wc-shipping-zones.php 9 years ago class-wc-shipping.php 9 years ago class-wc-shortcodes.php 9 years ago class-wc-structured-data.php 9 years ago class-wc-tax.php 9 years ago class-wc-template-loader.php 9 years ago class-wc-tracker.php 9 years ago class-wc-validation.php 9 years ago class-wc-webhook.php 9 years ago wc-account-functions.php 9 years ago wc-attribute-functions.php 9 years ago wc-cart-functions.php 9 years ago wc-conditional-functions.php 9 years ago wc-core-functions.php 9 years ago wc-coupon-functions.php 9 years ago wc-deprecated-functions.php 9 years ago wc-formatting-functions.php 9 years ago wc-notice-functions.php 9 years ago wc-order-functions.php 9 years ago wc-order-item-functions.php 9 years ago wc-page-functions.php 9 years ago wc-product-functions.php 9 years ago wc-rest-functions.php 9 years ago wc-stock-functions.php 9 years ago wc-template-functions.php 9 years ago wc-template-hooks.php 9 years ago wc-term-functions.php 9 years ago wc-update-functions.php 9 years ago wc-user-functions.php 9 years ago wc-webhook-functions.php 9 years ago wc-widget-functions.php 9 years ago
class-wc-tax.php
1014 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit; // Exit if accessed directly
5 }
6
7 /**
8 * Performs tax calculations and loads tax rates
9 *
10 * @class WC_Tax
11 * @version 2.2.0
12 * @package WooCommerce/Classes
13 * @category Class
14 * @author WooThemes
15 */
16 class WC_Tax {
17
18 /**
19 * Precision.
20 *
21 * @var int
22 */
23 public static $precision;
24
25 /**
26 * Round at subtotal.
27 *
28 * @var bool
29 */
30 public static $round_at_subtotal;
31
32 /**
33 * Load options.
34 *
35 * @access public
36 */
37 public static function init() {
38 self::$precision = wc_get_rounding_precision();
39 self::$round_at_subtotal = 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' );
40 add_action( 'update_option_woocommerce_tax_classes', array( __CLASS__, 'maybe_remove_tax_class_rates' ), 10, 2 );
41 }
42
43 /**
44 * When the woocommerce_tax_classes option is changed, remove any orphan rates.
45 * @param string $old_value
46 * @param string $value
47 */
48 public static function maybe_remove_tax_class_rates( $old_value, $value ) {
49 $old = array_filter( array_map( 'trim', explode( "\n", $old_value ) ) );
50 $new = array_filter( array_map( 'trim', explode( "\n", $value ) ) );
51 $removed = array_filter( array_map( 'sanitize_title', array_diff( $old, $new ) ) );
52
53 if ( $removed ) {
54 global $wpdb;
55
56 foreach ( $removed as $removed_tax_class ) {
57 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class = %s;", $removed_tax_class ) );
58 $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;" );
59 }
60
61 WC_Cache_Helper::incr_cache_prefix( 'taxes' );
62 }
63 }
64
65 /**
66 * Calculate tax for a line.
67 * @param float $price Price to calc tax on
68 * @param array $rates Rates to apply
69 * @param boolean $price_includes_tax Whether the passed price has taxes included
70 * @param boolean $suppress_rounding Whether to suppress any rounding from taking place
71 * @return array Array of rates + prices after tax
72 */
73 public static function calc_tax( $price, $rates, $price_includes_tax = false, $suppress_rounding = false ) {
74 // Work in pence to X precision
75 $price = self::precision( $price );
76
77 if ( $price_includes_tax ) {
78 $taxes = self::calc_inclusive_tax( $price, $rates );
79 } else {
80 $taxes = self::calc_exclusive_tax( $price, $rates );
81 }
82
83 // Round to precision
84 if ( ! self::$round_at_subtotal && ! $suppress_rounding ) {
85 $taxes = array_map( 'round', $taxes ); // Round to precision
86 }
87
88 // Remove precision
89 $price = self::remove_precision( $price );
90 $taxes = array_map( array( __CLASS__, 'remove_precision' ), $taxes );
91
92 return apply_filters( 'woocommerce_calc_tax', $taxes, $price, $rates, $price_includes_tax, $suppress_rounding );
93 }
94
95 /**
96 * Calculate the shipping tax using a passed array of rates.
97 *
98 * @param float Price
99 * @param array Taxation Rate
100 * @return array
101 */
102 public static function calc_shipping_tax( $price, $rates ) {
103 $taxes = self::calc_exclusive_tax( $price, $rates );
104 return apply_filters( 'woocommerce_calc_shipping_tax', $taxes, $price, $rates );
105 }
106
107 /**
108 * Multiply cost by pow precision.
109 * @param float $price
110 * @return float
111 */
112 private static function precision( $price ) {
113 return $price * ( pow( 10, self::$precision ) );
114 }
115
116 /**
117 * Divide cost by pow precision.
118 * @param float $price
119 * @return float
120 */
121 private static function remove_precision( $price ) {
122 return $price / ( pow( 10, self::$precision ) );
123 }
124
125 /**
126 * Round to precision.
127 *
128 * Filter example: to return rounding to .5 cents you'd use:
129 *
130 * function euro_5cent_rounding( $in ) {
131 * return round( $in / 5, 2 ) * 5;
132 * }
133 * add_filter( 'woocommerce_tax_round', 'euro_5cent_rounding' );
134 * @return double
135 */
136 public static function round( $in ) {
137 return apply_filters( 'woocommerce_tax_round', round( $in, self::$precision ), $in );
138 }
139
140 /**
141 * Calc tax from inclusive price.
142 *
143 * @param float $price
144 * @param array $rates
145 * @return array
146 */
147 public static function calc_inclusive_tax( $price, $rates ) {
148 $taxes = array();
149
150 $regular_tax_rates = $compound_tax_rates = 0;
151
152 foreach ( $rates as $key => $rate ) {
153 if ( 'yes' === $rate['compound'] ) {
154 $compound_tax_rates = $compound_tax_rates + $rate['rate'];
155 } else {
156 $regular_tax_rates = $regular_tax_rates + $rate['rate'];
157 }
158 }
159
160 $regular_tax_rate = 1 + ( $regular_tax_rates / 100 );
161 $compound_tax_rate = 1 + ( $compound_tax_rates / 100 );
162 $non_compound_price = $price / $compound_tax_rate;
163
164 foreach ( $rates as $key => $rate ) {
165 if ( ! isset( $taxes[ $key ] ) )
166 $taxes[ $key ] = 0;
167
168 $the_rate = $rate['rate'] / 100;
169
170 if ( 'yes' === $rate['compound'] ) {
171 $the_price = $price;
172 $the_rate = $the_rate / $compound_tax_rate;
173 } else {
174 $the_price = $non_compound_price;
175 $the_rate = $the_rate / $regular_tax_rate;
176 }
177
178 $net_price = $price - ( $the_rate * $the_price );
179 $tax_amount = $price - $net_price;
180 $taxes[ $key ] += apply_filters( 'woocommerce_price_inc_tax_amount', $tax_amount, $key, $rate, $price );
181 }
182
183 return $taxes;
184 }
185
186 /**
187 * Calc tax from exclusive price.
188 *
189 * @param float $price
190 * @param array $rates
191 * @return array
192 */
193 public static function calc_exclusive_tax( $price, $rates ) {
194 $taxes = array();
195
196 if ( ! empty( $rates ) ) {
197 // Multiple taxes
198 foreach ( $rates as $key => $rate ) {
199
200 if ( 'yes' === $rate['compound'] ) {
201 continue;
202 }
203
204 $tax_amount = $price * ( $rate['rate'] / 100 );
205
206 // ADVANCED: Allow third parties to modify this rate
207 $tax_amount = apply_filters( 'woocommerce_price_ex_tax_amount', $tax_amount, $key, $rate, $price );
208
209 // Add rate
210 if ( ! isset( $taxes[ $key ] ) )
211 $taxes[ $key ] = $tax_amount;
212 else
213 $taxes[ $key ] += $tax_amount;
214 }
215
216 $pre_compound_total = array_sum( $taxes );
217
218 // Compound taxes
219 foreach ( $rates as $key => $rate ) {
220
221 if ( 'no' === $rate['compound'] ) {
222 continue;
223 }
224
225 $the_price_inc_tax = $price + ( $pre_compound_total );
226
227 $tax_amount = $the_price_inc_tax * ( $rate['rate'] / 100 );
228
229 // ADVANCED: Allow third parties to modify this rate
230 $tax_amount = apply_filters( 'woocommerce_price_ex_tax_amount', $tax_amount, $key, $rate, $price, $the_price_inc_tax, $pre_compound_total );
231
232 // Add rate
233 if ( ! isset( $taxes[ $key ] ) ) {
234 $taxes[ $key ] = $tax_amount;
235 } else {
236 $taxes[ $key ] += $tax_amount;
237 }
238 }
239 }
240
241 return $taxes;
242 }
243
244 /**
245 * Searches for all matching country/state/postcode tax rates.
246 *
247 * @param array $args
248 * @return array
249 */
250 public static function find_rates( $args = array() ) {
251 $args = wp_parse_args( $args, array(
252 'country' => '',
253 'state' => '',
254 'city' => '',
255 'postcode' => '',
256 'tax_class' => '',
257 ) );
258
259 extract( $args, EXTR_SKIP );
260
261 if ( ! $country ) {
262 return array();
263 }
264
265 $postcode = wc_normalize_postcode( wc_clean( $postcode ) );
266 $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 ) );
267 $matched_tax_rates = wp_cache_get( $cache_key, 'taxes' );
268
269 if ( false === $matched_tax_rates ) {
270 $matched_tax_rates = self::get_matched_tax_rates( $country, $state, $postcode, $city, $tax_class );
271 wp_cache_set( $cache_key, $matched_tax_rates, 'taxes' );
272 }
273
274 return apply_filters( 'woocommerce_find_rates', $matched_tax_rates, $args );
275 }
276
277 /**
278 * Searches for all matching country/state/postcode tax rates.
279 *
280 * @param array $args
281 * @return array
282 */
283 public static function find_shipping_rates( $args = array() ) {
284 $rates = self::find_rates( $args );
285 $shipping_rates = array();
286
287 if ( is_array( $rates ) ) {
288 foreach ( $rates as $key => $rate ) {
289 if ( 'yes' === $rate['shipping'] ) {
290 $shipping_rates[ $key ] = $rate;
291 }
292 }
293 }
294
295 return $shipping_rates;
296 }
297
298 /**
299 * Does the sort comparison.
300 */
301 private static function sort_rates_callback( $rate1, $rate2 ) {
302 if ( $rate1->tax_rate_priority !== $rate2->tax_rate_priority ) {
303 return $rate1->tax_rate_priority < $rate2->tax_rate_priority ? -1 : 1; // ASC
304 } elseif ( $rate1->tax_rate_country !== $rate2->tax_rate_country ) {
305 if ( '' === $rate1->tax_rate_country ) {
306 return 1;
307 }
308 if ( '' === $rate2->tax_rate_country ) {
309 return -1;
310 }
311 return strcmp( $rate1->tax_rate_country, $rate2->tax_rate_country ) > 0 ? 1 : -1;
312 } elseif ( $rate1->tax_rate_state !== $rate2->tax_rate_state ) {
313 if ( '' === $rate1->tax_rate_state ) {
314 return 1;
315 }
316 if ( '' === $rate2->tax_rate_state ) {
317 return -1;
318 }
319 return strcmp( $rate1->tax_rate_state, $rate2->tax_rate_state ) > 0 ? 1 : -1;
320 } else {
321 return $rate1->tax_rate_id < $rate2->tax_rate_id ? -1 : 1; // Identical - use ID
322 }
323 }
324
325 /**
326 * Logical sort order for tax rates based on the following in order of priority:
327 * - Priority
328 * - County code
329 * - State code
330 * @param array $rates
331 * @return array
332 */
333 private static function sort_rates( $rates ) {
334 uasort( $rates, __CLASS__ . '::sort_rates_callback' );
335 $i = 0;
336 foreach ( $rates as $key => $rate ) {
337 $rates[ $key ]->tax_rate_order = $i++;
338 }
339 return $rates;
340 }
341
342 /**
343 * Loop through a set of tax rates and get the matching rates (1 per priority).
344 *
345 * @param string $country
346 * @param string $state
347 * @param string $postcode
348 * @param string $city
349 * @param string $tax_class
350 * @return array
351 */
352 private static function get_matched_tax_rates( $country, $state, $postcode, $city, $tax_class ) {
353 global $wpdb;
354
355 // Query criteria - these will be ANDed
356 $criteria = array();
357 $criteria[] = $wpdb->prepare( "tax_rate_country IN ( %s, '' )", strtoupper( $country ) );
358 $criteria[] = $wpdb->prepare( "tax_rate_state IN ( %s, '' )", strtoupper( $state ) );
359 $criteria[] = $wpdb->prepare( "tax_rate_class = %s", sanitize_title( $tax_class ) );
360
361 // Pre-query postcode ranges for PHP based matching.
362 $postcode_search = wc_get_wildcard_postcodes( $postcode, $country );
363 $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 '%...%';" );
364
365 if ( $postcode_ranges ) {
366 $matches = wc_postcode_location_matcher( $postcode, $postcode_ranges, 'tax_rate_id', 'location_code', $country );
367 if ( ! empty( $matches ) ) {
368 foreach ( $matches as $matched_postcodes ) {
369 $postcode_search = array_merge( $postcode_search, $matched_postcodes );
370 }
371 }
372 }
373
374 $postcode_search = array_unique( $postcode_search );
375
376 /**
377 * Location matching criteria - ORed
378 * Needs to match:
379 * - rates with no postcodes and cities
380 * - rates with a matching postcode and city
381 * - rates with matching postcode, no city
382 * - rates with matching city, no postcode
383 */
384 $locations_criteria = array();
385 $locations_criteria[] = "locations.location_type IS NULL";
386 $locations_criteria[] = "
387 locations.location_type = 'postcode' AND locations.location_code IN ('" . implode( "','", array_map( 'esc_sql', $postcode_search ) ) . "')
388 AND (
389 ( locations2.location_type = 'city' AND locations2.location_code = '" . esc_sql( strtoupper( $city ) ) . "' )
390 OR NOT EXISTS (
391 SELECT sub.tax_rate_id FROM {$wpdb->prefix}woocommerce_tax_rate_locations as sub
392 WHERE sub.location_type = 'city'
393 AND sub.tax_rate_id = tax_rates.tax_rate_id
394 )
395 )
396 ";
397 $locations_criteria[] = "
398 locations.location_type = 'city' AND locations.location_code = '" . esc_sql( strtoupper( $city ) ) . "'
399 AND NOT EXISTS (
400 SELECT sub.tax_rate_id FROM {$wpdb->prefix}woocommerce_tax_rate_locations as sub
401 WHERE sub.location_type = 'postcode'
402 AND sub.tax_rate_id = tax_rates.tax_rate_id
403 )
404 ";
405 $criteria[] = '( ( ' . implode( ' ) OR ( ', $locations_criteria ) . ' ) )';
406
407 $found_rates = $wpdb->get_results( "
408 SELECT tax_rates.*
409 FROM {$wpdb->prefix}woocommerce_tax_rates as tax_rates
410 LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations ON tax_rates.tax_rate_id = locations.tax_rate_id
411 LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations2 ON tax_rates.tax_rate_id = locations2.tax_rate_id
412 WHERE 1=1 AND " . implode( ' AND ', $criteria ) . "
413 GROUP BY tax_rates.tax_rate_id
414 ORDER BY tax_rates.tax_rate_priority
415 " );
416
417 $found_rates = self::sort_rates( $found_rates );
418 $matched_tax_rates = array();
419 $found_priority = array();
420
421 foreach ( $found_rates as $found_rate ) {
422 if ( in_array( $found_rate->tax_rate_priority, $found_priority ) ) {
423 continue;
424 }
425
426 $matched_tax_rates[ $found_rate->tax_rate_id ] = array(
427 'rate' => $found_rate->tax_rate,
428 'label' => $found_rate->tax_rate_name,
429 'shipping' => $found_rate->tax_rate_shipping ? 'yes' : 'no',
430 'compound' => $found_rate->tax_rate_compound ? 'yes' : 'no',
431 );
432
433 $found_priority[] = $found_rate->tax_rate_priority;
434 }
435
436 return apply_filters( 'woocommerce_matched_tax_rates', $matched_tax_rates, $country, $state, $postcode, $city, $tax_class );
437 }
438
439 /**
440 * Get the customer tax location based on their status and the current page.
441 *
442 * Used by get_rates(), get_shipping_rates().
443 *
444 * @param $tax_class string Optional, passed to the filter for advanced tax setups.
445 * @return array
446 */
447 public static function get_tax_location( $tax_class = '' ) {
448 $location = array();
449
450 if ( ! empty( WC()->customer ) ) {
451 $location = WC()->customer->get_taxable_address();
452 } elseif ( wc_prices_include_tax() || 'base' === get_option( 'woocommerce_default_customer_address' ) || 'base' === get_option( 'woocommerce_tax_based_on' ) ) {
453 $location = array(
454 WC()->countries->get_base_country(),
455 WC()->countries->get_base_state(),
456 WC()->countries->get_base_postcode(),
457 WC()->countries->get_base_city(),
458 );
459 }
460
461 return apply_filters( 'woocommerce_get_tax_location', $location, $tax_class );
462 }
463
464 /**
465 * Get's an array of matching rates for a tax class.
466 * @param string $tax_class
467 * @return array
468 */
469 public static function get_rates( $tax_class = '' ) {
470 $tax_class = sanitize_title( $tax_class );
471 $location = self::get_tax_location( $tax_class );
472 $matched_tax_rates = array();
473
474 if ( sizeof( $location ) === 4 ) {
475 list( $country, $state, $postcode, $city ) = $location;
476
477 $matched_tax_rates = self::find_rates( array(
478 'country' => $country,
479 'state' => $state,
480 'postcode' => $postcode,
481 'city' => $city,
482 'tax_class' => $tax_class,
483 ) );
484 }
485
486 return apply_filters( 'woocommerce_matched_rates', $matched_tax_rates, $tax_class );
487 }
488
489 /**
490 * Get's an array of matching rates for the shop's base country.
491 *
492 * @param string Tax Class
493 * @return array
494 */
495 public static function get_base_tax_rates( $tax_class = '' ) {
496 return apply_filters( 'woocommerce_base_tax_rates', self::find_rates( array(
497 'country' => WC()->countries->get_base_country(),
498 'state' => WC()->countries->get_base_state(),
499 'postcode' => WC()->countries->get_base_postcode(),
500 'city' => WC()->countries->get_base_city(),
501 'tax_class' => $tax_class,
502 ) ), $tax_class );
503 }
504
505 /**
506 * Alias for get_base_tax_rates().
507 *
508 * @deprecated 2.3
509 * @param string Tax Class
510 * @return array
511 */
512 public static function get_shop_base_rate( $tax_class = '' ) {
513 return self::get_base_tax_rates( $tax_class );
514 }
515
516 /**
517 * Gets an array of matching shipping tax rates for a given class.
518 *
519 * @param string Tax Class
520 * @return mixed
521 */
522 public static function get_shipping_tax_rates( $tax_class = null ) {
523 // See if we have an explicitly set shipping tax class
524 $shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' );
525
526 if ( 'inherit' !== $shipping_tax_class ) {
527 $tax_class = $shipping_tax_class;
528 }
529
530 $location = self::get_tax_location( $tax_class );
531 $matched_tax_rates = array();
532
533 if ( sizeof( $location ) === 4 ) {
534 list( $country, $state, $postcode, $city ) = $location;
535
536 if ( ! is_null( $tax_class ) ) {
537 // This will be per item shipping
538 $matched_tax_rates = self::find_shipping_rates( array(
539 'country' => $country,
540 'state' => $state,
541 'postcode' => $postcode,
542 'city' => $city,
543 'tax_class' => $tax_class,
544 ) );
545
546 } else {
547
548 // This will be per order shipping - loop through the order and find the highest tax class rate
549 $cart_tax_classes = WC()->cart->get_cart_item_tax_classes();
550
551 // 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 'additonal tax class' section.
552 if ( sizeof( $cart_tax_classes ) > 1 && ! in_array( '', $cart_tax_classes ) ) {
553 $tax_classes = self::get_tax_class_slugs();
554
555 foreach ( $tax_classes as $tax_class ) {
556 if ( in_array( $tax_class, $cart_tax_classes ) ) {
557 $matched_tax_rates = self::find_shipping_rates( array(
558 'country' => $country,
559 'state' => $state,
560 'postcode' => $postcode,
561 'city' => $city,
562 'tax_class' => $tax_class,
563 ) );
564 break;
565 }
566 }
567
568 // If a single tax class is found, use it
569 } elseif ( sizeof( $cart_tax_classes ) == 1 ) {
570 $matched_tax_rates = self::find_shipping_rates( array(
571 'country' => $country,
572 'state' => $state,
573 'postcode' => $postcode,
574 'city' => $city,
575 'tax_class' => $cart_tax_classes[0],
576 ) );
577 }
578 }
579
580 // Get standard rate if no taxes were found
581 if ( ! sizeof( $matched_tax_rates ) ) {
582 $matched_tax_rates = self::find_shipping_rates( array(
583 'country' => $country,
584 'state' => $state,
585 'postcode' => $postcode,
586 'city' => $city,
587 ) );
588 }
589 }
590
591 return $matched_tax_rates;
592 }
593
594 /**
595 * Return true/false depending on if a rate is a compound rate.
596 *
597 * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format
598 * @return bool
599 */
600 public static function is_compound( $key_or_rate ) {
601 global $wpdb;
602
603 if ( is_object( $key_or_rate ) ) {
604 $key = $key_or_rate->tax_rate_id;
605 $compound = $key_or_rate->tax_rate_compound;
606 } else {
607 $key = $key_or_rate;
608 $compound = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_compound FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) ) ? true : false;
609 }
610
611 return (bool) apply_filters( 'woocommerce_rate_compound', $compound, $key );
612 }
613
614 /**
615 * Return a given rates label.
616 *
617 * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format
618 * @return string
619 */
620 public static function get_rate_label( $key_or_rate ) {
621 global $wpdb;
622
623 if ( is_object( $key_or_rate ) ) {
624 $key = $key_or_rate->tax_rate_id;
625 $rate_name = $key_or_rate->tax_rate_name;
626 } else {
627 $key = $key_or_rate;
628 $rate_name = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_name FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) );
629 }
630
631 if ( ! $rate_name ) {
632 $rate_name = WC()->countries->tax_or_vat();
633 }
634
635 return apply_filters( 'woocommerce_rate_label', $rate_name, $key );
636 }
637
638 /**
639 * Return a given rates percent.
640 *
641 * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format
642 * @return string
643 */
644 public static function get_rate_percent( $key_or_rate ) {
645 global $wpdb;
646
647 if ( is_object( $key_or_rate ) ) {
648 $key = $key_or_rate->tax_rate_id;
649 $tax_rate = $key_or_rate->tax_rate;
650 } else {
651 $key = $key_or_rate;
652 $tax_rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) );
653 }
654
655 return apply_filters( 'woocommerce_rate_percent', floatval( $tax_rate ) . '%', $key );
656 }
657
658 /**
659 * Get a rates code. Code is made up of COUNTRY-STATE-NAME-Priority. E.g GB-VAT-1, US-AL-TAX-1.
660 *
661 * @access public
662 * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format
663 * @return string
664 */
665 public static function get_rate_code( $key_or_rate ) {
666 global $wpdb;
667
668 if ( is_object( $key_or_rate ) ) {
669 $key = $key_or_rate->tax_rate_id;
670 $rate = $key_or_rate;
671 } else {
672 $key = $key_or_rate;
673 $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 ) );
674 }
675
676 $code_string = '';
677
678 if ( null !== $rate ) {
679 $code = array();
680 $code[] = $rate->tax_rate_country;
681 $code[] = $rate->tax_rate_state;
682 $code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX';
683 $code[] = absint( $rate->tax_rate_priority );
684 $code_string = strtoupper( implode( '-', array_filter( $code ) ) );
685 }
686
687 return apply_filters( 'woocommerce_rate_code', $code_string, $key );
688 }
689
690 /**
691 * Round tax lines and return the sum.
692 *
693 * @param array
694 * @return float
695 */
696 public static function get_tax_total( $taxes ) {
697 return array_sum( array_map( array( __CLASS__, 'round' ), $taxes ) );
698 }
699
700 /**
701 * Get store tax classes.
702 * @return array Array of class names ("Reduced rate", "Zero rate", etc).
703 */
704 public static function get_tax_classes() {
705 return array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
706 }
707
708 /**
709 * Get store tax classes as slugs.
710 *
711 * @since 3.0.0
712 * @return array Array of class slugs ("reduced-rate", "zero-rate", etc).
713 */
714 public static function get_tax_class_slugs() {
715 return array_map( 'sanitize_title', self::get_tax_classes() );
716 }
717
718 /**
719 * format the city.
720 * @param string $city
721 * @return string
722 */
723 private static function format_tax_rate_city( $city ) {
724 return strtoupper( trim( $city ) );
725 }
726
727 /**
728 * format the state.
729 * @param string $state
730 * @return string
731 */
732 private static function format_tax_rate_state( $state ) {
733 $state = strtoupper( $state );
734 return ( '*' === $state ) ? '' : $state;
735 }
736
737 /**
738 * format the country.
739 * @param string $country
740 * @return string
741 */
742 private static function format_tax_rate_country( $country ) {
743 $country = strtoupper( $country );
744 return ( '*' === $country ) ? '' : $country;
745 }
746
747 /**
748 * format the tax rate name.
749 * @param string $name
750 * @return string
751 */
752 private static function format_tax_rate_name( $name ) {
753 return $name ? $name : __( 'Tax', 'woocommerce' );
754 }
755
756 /**
757 * format the rate.
758 * @param double $rate
759 * @return string
760 */
761 private static function format_tax_rate( $rate ) {
762 return number_format( (double) $rate, 4, '.', '' );
763 }
764
765 /**
766 * format the priority.
767 * @param string $priority
768 * @return int
769 */
770 private static function format_tax_rate_priority( $priority ) {
771 return absint( $priority );
772 }
773
774 /**
775 * format the class.
776 * @param string $class
777 * @return string
778 */
779 public static function format_tax_rate_class( $class ) {
780 $class = sanitize_title( $class );
781 $classes = self::get_tax_class_slugs();
782 if ( ! in_array( $class, $classes ) ) {
783 $class = '';
784 }
785 return ( 'standard' === $class ) ? '' : $class;
786 }
787
788 /**
789 * Prepare and format tax rate for DB insertion.
790 * @param array $tax_rate
791 * @return array
792 */
793 private static function prepare_tax_rate( $tax_rate ) {
794 foreach ( $tax_rate as $key => $value ) {
795 if ( method_exists( __CLASS__, 'format_' . $key ) ) {
796 $tax_rate[ $key ] = call_user_func( array( __CLASS__, 'format_' . $key ), $value );
797 }
798 }
799 return $tax_rate;
800 }
801
802 /**
803 * Insert a new tax rate.
804 *
805 * Internal use only.
806 *
807 * @since 2.3.0
808 * @access private
809 *
810 * @param array $tax_rate
811 *
812 * @return int tax rate id
813 */
814 public static function _insert_tax_rate( $tax_rate ) {
815 global $wpdb;
816
817 $wpdb->insert( $wpdb->prefix . 'woocommerce_tax_rates', self::prepare_tax_rate( $tax_rate ) );
818
819 WC_Cache_Helper::incr_cache_prefix( 'taxes' );
820
821 do_action( 'woocommerce_tax_rate_added', $wpdb->insert_id, $tax_rate );
822
823 return $wpdb->insert_id;
824 }
825
826 /**
827 * Get tax rate.
828 *
829 * Internal use only.
830 *
831 * @since 2.5.0
832 * @access private
833 *
834 * @param int $tax_rate_id
835 * @param string $output_type
836 *
837 * @return array
838 */
839 public static function _get_tax_rate( $tax_rate_id, $output_type = ARRAY_A ) {
840 global $wpdb;
841
842 return $wpdb->get_row( $wpdb->prepare( "
843 SELECT *
844 FROM {$wpdb->prefix}woocommerce_tax_rates
845 WHERE tax_rate_id = %d
846 ", $tax_rate_id ), $output_type );
847 }
848
849 /**
850 * Update a tax rate.
851 *
852 * Internal use only.
853 *
854 * @since 2.3.0
855 * @access private
856 *
857 * @param int $tax_rate_id
858 * @param array $tax_rate
859 */
860 public static function _update_tax_rate( $tax_rate_id, $tax_rate ) {
861 global $wpdb;
862
863 $tax_rate_id = absint( $tax_rate_id );
864
865 $wpdb->update(
866 $wpdb->prefix . "woocommerce_tax_rates",
867 self::prepare_tax_rate( $tax_rate ),
868 array(
869 'tax_rate_id' => $tax_rate_id,
870 )
871 );
872
873 WC_Cache_Helper::incr_cache_prefix( 'taxes' );
874
875 do_action( 'woocommerce_tax_rate_updated', $tax_rate_id, $tax_rate );
876 }
877
878 /**
879 * Delete a tax rate from the database.
880 *
881 * Internal use only.
882 *
883 * @since 2.3.0
884 * @access private
885 *
886 * @param int $tax_rate_id
887 */
888 public static function _delete_tax_rate( $tax_rate_id ) {
889 global $wpdb;
890
891 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d;", $tax_rate_id ) );
892 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $tax_rate_id ) );
893
894 WC_Cache_Helper::incr_cache_prefix( 'taxes' );
895
896 do_action( 'woocommerce_tax_rate_deleted', $tax_rate_id );
897 }
898
899 /**
900 * Update postcodes for a tax rate in the DB.
901 *
902 * Internal use only.
903 *
904 * @since 2.3.0
905 * @access private
906 *
907 * @param int $tax_rate_id
908 * @param string $postcodes String of postcodes separated by ; characters
909 * @return string
910 */
911 public static function _update_tax_rate_postcodes( $tax_rate_id, $postcodes ) {
912 if ( ! is_array( $postcodes ) ) {
913 $postcodes = explode( ';', $postcodes );
914 }
915 // No normalization - postcodes are matched against both normal and formatted versions to support wildcards.
916 foreach ( $postcodes as $key => $postcode ) {
917 $postcodes[ $key ] = strtoupper( trim( str_replace( chr( 226 ) . chr( 128 ) . chr( 166 ), '...', $postcode ) ) );
918 }
919 self::_update_tax_rate_locations( $tax_rate_id, array_diff( array_filter( $postcodes ), array( '*' ) ), 'postcode' );
920 }
921
922 /**
923 * Update cities for a tax rate in the DB.
924 *
925 * Internal use only.
926 *
927 * @since 2.3.0
928 * @access private
929 *
930 * @param int $tax_rate_id
931 * @param string $cities
932 * @return string
933 */
934 public static function _update_tax_rate_cities( $tax_rate_id, $cities ) {
935 if ( ! is_array( $cities ) ) {
936 $cities = explode( ';', $cities );
937 }
938 $cities = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_city' ), $cities ), array( '*' ) ) );
939
940 self::_update_tax_rate_locations( $tax_rate_id, $cities, 'city' );
941 }
942
943 /**
944 * Updates locations (postcode and city).
945 *
946 * Internal use only.
947 *
948 * @since 2.3.0
949 * @access private
950 *
951 * @param int $tax_rate_id
952 * @param string $type
953 * @return string
954 */
955 private static function _update_tax_rate_locations( $tax_rate_id, $values, $type ) {
956 global $wpdb;
957
958 $tax_rate_id = absint( $tax_rate_id );
959
960 $wpdb->query(
961 $wpdb->prepare( "
962 DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = %s;
963 ", $tax_rate_id, $type
964 )
965 );
966
967 if ( sizeof( $values ) > 0 ) {
968 $sql = "( '" . implode( "', $tax_rate_id, '" . esc_sql( $type ) . "' ),( '", array_map( 'esc_sql', $values ) ) . "', $tax_rate_id, '" . esc_sql( $type ) . "' )";
969
970 $wpdb->query( "
971 INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES $sql;
972 " );
973 }
974
975 WC_Cache_Helper::incr_cache_prefix( 'taxes' );
976 }
977
978 /**
979 * Used by admin settings page.
980 *
981 * @param string $tax_class
982 *
983 * @return array|null|object
984 */
985 public static function get_rates_for_tax_class( $tax_class ) {
986 global $wpdb;
987
988 // Get all the rates and locations. Snagging all at once should significantly cut down on the number of queries.
989 $rates = self::sort_rates( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$wpdb->prefix}woocommerce_tax_rates` WHERE `tax_rate_class` = %s;", sanitize_title( $tax_class ) ) ) );
990 $locations = $wpdb->get_results( "SELECT * FROM `{$wpdb->prefix}woocommerce_tax_rate_locations`" );
991
992 if ( ! empty( $rates ) ) {
993 // Set the rates keys equal to their ids.
994 $rates = array_combine( wp_list_pluck( $rates, 'tax_rate_id' ), $rates );
995 }
996
997 // Drop the locations into the rates array.
998 foreach ( $locations as $location ) {
999 // Don't set them for unexistent rates.
1000 if ( ! isset( $rates[ $location->tax_rate_id ] ) ) {
1001 continue;
1002 }
1003 // If the rate exists, initialize the array before appending to it.
1004 if ( ! isset( $rates[ $location->tax_rate_id ]->{$location->location_type} ) ) {
1005 $rates[ $location->tax_rate_id ]->{$location->location_type} = array();
1006 }
1007 $rates[ $location->tax_rate_id ]->{$location->location_type}[] = $location->location_code;
1008 }
1009
1010 return $rates;
1011 }
1012 }
1013 WC_Tax::init();
1014