/** * Output widget. * * @see WP_Widget * * @param array $args * @param array $instance */ public function widget($args, $instance) { global $wp, $wp_the_query; if (!is_post_type_archive('product') && !is_tax(get_object_taxonomies('product'))) { return; } if (!$wp_the_query->post_count) { return; } $min_price = isset($_GET['min_price']) ? esc_attr($_GET['min_price']) : ''; $max_price = isset($_GET['max_price']) ? esc_attr($_GET['max_price']) : ''; wp_enqueue_script('wc-price-slider'); // Find min and max price in current result set $prices = $this->get_filtered_price(); $min = floor($prices->min_price); $max = ceil($prices->max_price); if ($min === $max) { return; } $this->widget_start($args, $instance); if ('' === get_option('permalink_structure')) { $form_action = remove_query_arg(array('page', 'paged'), add_query_arg($wp->query_string, '', home_url($wp->request))); } else { $form_action = preg_replace('%\\/page/[0-9]+%', '', home_url(trailingslashit($wp->request))); } /** * Adjust max if the store taxes are not displayed how they are stored. * Min is left alone because the product may not be taxable. * Kicks in when prices excluding tax are displayed including tax. */ if (wc_tax_enabled() && 'incl' === get_option('woocommerce_tax_display_shop') && !wc_prices_include_tax()) { $tax_classes = array_merge(array(''), WC_Tax::get_tax_classes()); $class_max = $max; foreach ($tax_classes as $tax_class) { if ($tax_rates = WC_Tax::get_rates($tax_class)) { $class_max = $max + WC_Tax::get_tax_total(WC_Tax::calc_exclusive_tax($max, $tax_rates)); } } $max = $class_max; } echo '<form method="get" action="' . esc_url($form_action) . '"> <div class="price_slider_wrapper"> <div class="price_slider" style="display:none;"></div> <div class="price_slider_amount"> <input type="text" id="min_price" name="min_price" value="' . esc_attr($min_price) . '" data-min="' . esc_attr(apply_filters('woocommerce_price_filter_widget_min_amount', $min)) . '" placeholder="' . esc_attr__('Min price', 'woocommerce') . '" /> <input type="text" id="max_price" name="max_price" value="' . esc_attr($max_price) . '" data-max="' . esc_attr(apply_filters('woocommerce_price_filter_widget_max_amount', $max)) . '" placeholder="' . esc_attr__('Max price', 'woocommerce') . '" /> <button type="submit" class="button">' . __('Filter', 'woocommerce') . '</button> <div class="price_label" style="display:none;"> ' . __('Price:', 'woocommerce') . ' <span class="from"></span> — <span class="to"></span> </div> ' . wc_query_string_form_fields(null, array('min_price', 'max_price'), '', true) . ' <div class="clear"></div> </div> </div> </form>'; $this->widget_end($args); }
/** * Calculate fees */ public function calculate_fees() { // Reset fees before calculation $this->fee_total = 0; $this->fees = array(); // Fire an action where developers can add their fees do_action('woocommerce_cart_calculate_fees', $this); // If fees were added, total them and calculate tax if (!empty($this->fees)) { foreach ($this->fees as $fee_key => $fee) { $this->fee_total += $fee->amount; if ($fee->taxable) { // Get tax rates $tax_rates = $this->tax->get_rates($fee->tax_class); $fee_taxes = $this->tax->calc_tax($fee->amount, $tax_rates, false); if (!empty($fee_taxes)) { // Set the tax total for this fee $this->fees[$fee_key]->tax = array_sum($fee_taxes); // Set tax data - Since 2.2 $this->fees[$fee_key]->tax_data = $fee_taxes; // Tax rows - merge the totals we just got foreach (array_keys($this->taxes + $fee_taxes) as $key) { $this->taxes[$key] = (isset($fee_taxes[$key]) ? $fee_taxes[$key] : 0) + (isset($this->taxes[$key]) ? $this->taxes[$key] : 0); } } } } } }
/** * Get the product row subtotal. * * Gets the tax etc to avoid rounding issues. * * When on the checkout (review order), this will get the subtotal based on the customer's tax rate rather than the base rate * * @params object product * @params int quantity * @return string formatted price */ function get_product_subtotal($_product, $quantity) { global $woocommerce; $price = $_product->get_price(); $taxable = $_product->is_taxable(); $base_tax_rates = $this->tax->get_shop_base_rate($_product->tax_class); $tax_rates = $this->tax->get_rates($_product->get_tax_class()); // This will get the base rate unless we're on the checkout page // Taxable if ($taxable) { if (($this->display_cart_ex_tax || $woocommerce->customer->is_vat_exempt()) && $this->prices_include_tax) { $base_taxes = $this->tax->calc_tax($price * $quantity, $base_tax_rates, true); $base_tax_amount = array_sum($base_taxes); $row_price = $price * $quantity - $base_tax_amount; $product_subtotal = woocommerce_price($row_price); $product_subtotal .= ' <small class="tax_label">' . $woocommerce->countries->ex_tax_or_vat() . '</small>'; } elseif (!$this->display_cart_ex_tax && $tax_rates !== $base_tax_rates && $this->prices_include_tax) { $base_taxes = $this->tax->calc_tax($price * $quantity, $base_tax_rates, true, true); $modded_taxes = $this->tax->calc_tax($price * $quantity - array_sum($base_taxes), $tax_rates, false); $row_price = $price * $quantity - array_sum($base_taxes) + array_sum($modded_taxes); $product_subtotal = woocommerce_price($row_price); if (!$this->prices_include_tax) { $product_subtotal .= ' <small class="tax_label">' . $woocommerce->countries->inc_tax_or_vat() . '</small>'; } } else { $row_price = $price * $quantity; $product_subtotal = woocommerce_price($row_price); } // Non taxable } else { $row_price = $price * $quantity; $product_subtotal = woocommerce_price($row_price); } return apply_filters('woocommerce_cart_product_subtotal', $product_subtotal, $_product, $quantity, $this); }
/** * Get the product row subtotal. * * Gets the tax etc to avoid rounding issues. * * When on the checkout (review order), this will get the subtotal based on the customer's tax rate rather than the base rate * * @params object product * @params int quantity * @return string formatted price */ public function get_product_subtotal($_product, $quantity) { global $woocommerce; $price = $_product->get_price(); $taxable = $_product->is_taxable(); $base_tax_rates = $this->tax->get_shop_base_rate($_product->tax_class); $tax_rates = $this->tax->get_rates($_product->get_tax_class()); // This will get the base rate unless we're on the checkout page // Taxable if ($taxable) { if ($this->tax_display_cart == 'excl') { $row_price = $_product->get_price_excluding_tax($quantity); $product_subtotal = woocommerce_price($row_price); if ($this->prices_include_tax && $this->tax_total > 0) { $product_subtotal .= ' <small class="tax_label">' . $woocommerce->countries->ex_tax_or_vat() . '</small>'; } } else { $row_price = $_product->get_price_including_tax($quantity); $product_subtotal = woocommerce_price($row_price); if (!$this->prices_include_tax && $this->tax_total > 0) { $product_subtotal .= ' <small class="tax_label">' . $woocommerce->countries->inc_tax_or_vat() . '</small>'; } } // Non-taxable } else { $row_price = $price * $quantity; $product_subtotal = woocommerce_price($row_price); } return apply_filters('woocommerce_cart_product_subtotal', $product_subtotal, $_product, $quantity, $this); }
/** * Get rates. */ public function test_get_rates() { global $wpdb; $wpdb->query("DELETE FROM {$wpdb->prefix}woocommerce_tax_rates"); $wpdb->query("DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations"); $customer_location = WC_Tax::get_tax_location(); $tax_rate = array('tax_rate_country' => $customer_location[0], 'tax_rate_state' => '', 'tax_rate' => '20.0000', 'tax_rate_name' => 'VAT', 'tax_rate_priority' => '1', 'tax_rate_compound' => '0', 'tax_rate_shipping' => '1', 'tax_rate_order' => '1', 'tax_rate_class' => ''); $tax_rate_id = WC_Tax::_insert_tax_rate($tax_rate); $tax_rates = WC_Tax::get_rates(); $this->assertEquals($tax_rates, array($tax_rate_id => array('rate' => '20.0000', 'label' => 'VAT', 'shipping' => 'yes', 'compound' => 'no'))); WC_Tax::_delete_tax_rate($tax_rate_id); }
/** * Calculate fees */ public function calculate_fees() { // Fire an action where developers can add their fees do_action('woocommerce_cart_calculate_fees', $this); // If fees were added, total them and calculate tax if ($fees = $this->get_fees()) { foreach ($fees as $fee) { $this->fee_total += $fee->amount; if ($fee->taxable) { // Get tax rates $tax_rates = $this->tax->get_rates($fee->tax_class); $fee_taxes = $this->tax->calc_tax($fee->amount, $tax_rates, false); if (!empty($fee_taxes)) { // Tax rows - merge the totals we just got foreach (array_keys($this->taxes) as $key) { $this->taxes[$key] = (isset($fee_taxes[$key]) ? $fee_taxes[$key] : 0) + (isset($this->taxes[$key]) ? $this->taxes[$key] : 0); } } } } } }
private function calc_tax_rate($product) { static $tax_rates = array(); $item_tax_rates = array(); $compound_tax_rates = 0; $regular_tax_rates = 0; if (empty($tax_rates[$product->get_tax_class()])) { $tax_rates[$product->get_tax_class()] = WC_Tax::get_rates($product->get_tax_class()); } $item_tax_rates = $tax_rates[$product->get_tax_class()]; $regular_tax_rates = $compound_tax_rates = 0; foreach ($item_tax_rates as $key => $rate) { if ($rate['compound'] == 'yes') { $compound_tax_rates = $compound_tax_rates + $rate['rate']; } else { $regular_tax_rates = $regular_tax_rates + $rate['rate']; } } $regular_tax_rate = 1 + $regular_tax_rates / 100; $compound_tax_rate = 1 + $compound_tax_rates / 100; $the_rate = 0; foreach ($item_tax_rates as $key => $rate) { if (!isset($taxes[$key])) { $taxes[$key] = 0; } $the_rate = $rate['rate']; // 100; if ($rate['compound'] == 'yes') { //$the_price = $price; // $the_rate = $the_rate / $compound_tax_rate; } else { //$the_price = $non_compound_price; // $the_rate = $the_rate / $regular_tax_rate; } } return $the_rate; }
/** * Returns the price (including tax). Uses customer tax rates. Can work for a specific $qty for more accurate taxes. * * @param string $price to calculate, left blank to just use get_price() * @return string */ public function get_price_including_tax($qty = 1, $price = '') { if ($price === '') { $price = $this->get_price(); } if ($this->is_taxable()) { if (get_option('woocommerce_prices_include_tax') === 'no') { $tax_rates = WC_Tax::get_rates($this->get_tax_class()); $taxes = WC_Tax::calc_tax($price * $qty, $tax_rates, false); $tax_amount = WC_Tax::get_tax_total($taxes); $price = round($price * $qty + $tax_amount, wc_get_price_decimals()); } else { $tax_rates = WC_Tax::get_rates($this->get_tax_class()); $base_tax_rates = WC_Tax::get_base_tax_rates($this->tax_class); if (!empty(WC()->customer) && WC()->customer->is_vat_exempt()) { $base_taxes = WC_Tax::calc_tax($price * $qty, $base_tax_rates, true); $base_tax_amount = array_sum($base_taxes); $price = round($price * $qty - $base_tax_amount, wc_get_price_decimals()); /** * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing with out of base locations. * e.g. If a product costs 10 including tax, all users will pay 10 regardless of location and taxes. * This feature is experimental @since 2.4.7 and may change in the future. Use at your risk. */ } elseif ($tax_rates !== $base_tax_rates && apply_filters('woocommerce_adjust_non_base_location_prices', true)) { $base_taxes = WC_Tax::calc_tax($price * $qty, $base_tax_rates, true); $modded_taxes = WC_Tax::calc_tax($price * $qty - array_sum($base_taxes), $tax_rates, false); $price = round($price * $qty - array_sum($base_taxes) + array_sum($modded_taxes), wc_get_price_decimals()); } else { $price = $price * $qty; } } } else { $price = $price * $qty; } return apply_filters('woocommerce_get_price_including_tax', $price, $qty, $this); }
/** * Get the tax rates/percentages for a given tax class * @param string $tax_class tax class slug * @return string $tax_rates imploded list of tax rates */ public function get_tax_rate($tax_class, $line_total, $line_tax) { if (version_compare(WOOCOMMERCE_VERSION, '2.1') >= 0) { // WC 2.1 or newer is used if ($line_tax == 0) { return '-'; // no need to determine tax rate... } // if (empty($tax_class)) // $tax_class = 'standard';// does not appear to work anymore - get_rates does accept an empty tax_class though! $tax = new WC_Tax(); $taxes = $tax->get_rates($tax_class); $tax_rates = array(); foreach ($taxes as $tax) { $tax_rates[$tax['label']] = round($tax['rate'], 2) . '%'; } if (empty($tax_rates)) { // one last try: manually calculate if ($line_total != 0) { $tax_rates[] = round($line_tax / $line_total * 100, 1) . '%'; } else { $tax_rates[] = '-'; } } $tax_rates = implode(' ,', $tax_rates); } else { // Backwards compatibility: calculate tax from line items if ($line_total != 0) { $tax_rates = round($line_tax / $line_total * 100, 1) . '%'; } else { $tax_rates = '-'; } } return $tax_rates; }
/** * Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale. * * Can be filtered by plugins which modify costs, but otherwise will include the raw meta costs unlike get_price() which runs costs through the woocommerce_get_price filter. * This is to ensure modified prices are not cached, unless intended. * * @param bool $display Are prices for display? If so, taxes will be calculated. * @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID. */ public function get_variation_prices($display = false) { global $wp_filter; /** * Transient name for storing prices for this product. * Max transient length is 45, -10 for get_transient_version. * @var string * @since 2.5.0 a single transient is used per product for all prices, rather than many transients per product. */ $transient_name = 'wc_var_prices' . $this->id . '_' . WC_Cache_Helper::get_transient_version('product'); /** * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters. * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique. * @var string */ if ($display) { $price_hash = array(true, WC_Tax::get_rates(), get_option('woocommerce_tax_display_shop')); } else { $price_hash = array(false); } foreach ($wp_filter as $key => $val) { if (in_array($key, array('woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price'))) { $price_hash[$key] = $val; } } $price_hash = md5(json_encode(apply_filters('woocommerce_get_variation_prices_hash', $price_hash, $this, $display))); // If the value has already been generated, return it now if (!empty($this->prices_array[$price_hash])) { return $this->prices_array[$price_hash]; } // Get value of transient $this->prices_array = array_filter((array) get_transient($transient_name)); // If the prices are not stored for this hash, generate them if (empty($this->prices_array[$price_hash])) { $prices = array(); $regular_prices = array(); $sale_prices = array(); $variation_ids = $this->get_children(true); foreach ($variation_ids as $variation_id) { if ($variation = $this->get_child($variation_id)) { $price = apply_filters('woocommerce_variation_prices_price', $variation->price, $variation, $this); $regular_price = apply_filters('woocommerce_variation_prices_regular_price', $variation->regular_price, $variation, $this); $sale_price = apply_filters('woocommerce_variation_prices_sale_price', $variation->sale_price, $variation, $this); // If sale price does not equal price, the product is not yet on sale if ($sale_price === $regular_price || $sale_price !== $price) { $sale_price = $regular_price; } // If we are getting prices for display, we need to account for taxes if ($display) { if ('incl' === get_option('woocommerce_tax_display_shop')) { $price = '' === $price ? '' : $variation->get_price_including_tax(1, $price); $regular_price = '' === $regular_price ? '' : $variation->get_price_including_tax(1, $regular_price); $sale_price = '' === $sale_price ? '' : $variation->get_price_including_tax(1, $sale_price); } else { $price = '' === $price ? '' : $variation->get_price_excluding_tax(1, $price); $regular_price = '' === $regular_price ? '' : $variation->get_price_excluding_tax(1, $regular_price); $sale_price = '' === $sale_price ? '' : $variation->get_price_excluding_tax(1, $sale_price); } } $prices[$variation_id] = $price; $regular_prices[$variation_id] = $regular_price; $sale_prices[$variation_id] = $sale_price; } } asort($prices); asort($regular_prices); asort($sale_prices); $this->prices_array[$price_hash] = array('price' => $prices, 'regular_price' => $regular_prices, 'sale_price' => $sale_prices); set_transient($transient_name, $this->prices_array, DAY_IN_SECONDS * 30); } /** * Give plugins one last chance to filter the variation prices array which is being returned. */ return $this->prices_array[$price_hash] = apply_filters('woocommerce_variation_prices', $this->prices_array[$price_hash], $this, $display); }
/** * Returns the price (including tax). Uses customer tax rates. Can work for a specific $qty for more accurate taxes. * * @access public * @return string */ function get_price_including_tax($qty = 1) { global $woocommerce; $_tax = new WC_Tax(); $price = $this->get_price(); if ($this->is_taxable()) { if (get_option('woocommerce_prices_include_tax') == 'no') { $tax_rates = $_tax->get_rates($this->get_tax_class()); $taxes = $_tax->calc_tax($price * $qty, $tax_rates, false); $tax_amount = $_tax->get_tax_total($taxes); $price = round($price * $qty + $tax_amount, 2); } else { $tax_rates = $_tax->get_rates($this->get_tax_class()); $base_tax_rates = $_tax->get_shop_base_rate($this->tax_class); if ($woocommerce->customer->is_vat_exempt()) { $base_taxes = $_tax->calc_tax($price * $qty, $base_tax_rates, true); $base_tax_amount = array_sum($base_taxes); $price = round($price * $qty - $base_tax_amount, 2); } elseif ($tax_rates !== $base_tax_rates) { $base_taxes = $_tax->calc_tax($price * $qty, $base_tax_rates, true, true); $modded_taxes = $_tax->calc_tax($price * $qty - array_sum($base_taxes), $tax_rates, false); $price = round($price * $qty - array_sum($base_taxes) + array_sum($modded_taxes), 2); } else { $price = $price * $qty; } } } else { $price = $price * $qty; } return apply_filters('woocommerce_get_price_including_tax', $price, $qty, $this); }
/** * Recalculate fee taxes to split tax based on different tax rates contained within cart * * @param WC_Cart $cart */ public function do_fee_tax_calculation(WC_Cart $cart) { if (get_option('woocommerce_gzd_fee_tax') != 'yes') { return; } if (!empty($cart->fees)) { $tax_shares = wc_gzd_get_cart_tax_share('fee'); foreach ($cart->fees as $key => $fee) { if (!$fee->taxable && get_option('woocommerce_gzd_fee_tax_force') != 'yes') { continue; } // Calculate gross price if necessary if ($fee->taxable) { $fee_tax_rates = WC_Tax::get_rates($fee->tax_class); $fee_tax = WC_Tax::calc_tax($fee->amount, $fee_tax_rates, false); $fee->amount += array_sum($fee_tax); } // Set fee to nontaxable to avoid WooCommerce default tax calculation $fee->taxable = false; // Calculate tax class share if (!empty($tax_shares)) { $fee_taxes = array(); foreach ($tax_shares as $rate => $class) { $tax_rates = WC_Tax::get_rates($rate); $tax_shares[$rate]['fee_tax_share'] = $fee->amount * $class['share']; $tax_shares[$rate]['fee_tax'] = WC_Tax::calc_tax($fee->amount * $class['share'], $tax_rates, true); $fee_taxes += $tax_shares[$rate]['fee_tax']; } foreach ($tax_shares as $rate => $class) { $cart->fees[$key]->tax_data = $cart->fees[$key]->tax_data + $class['fee_tax']; } // Add fee taxes to cart taxes foreach (array_keys($cart->taxes + $fee_taxes) as $sub) { $cart->taxes[$sub] = (isset($fee_taxes[$sub]) ? $fee_taxes[$sub] : 0) + (isset($cart->taxes[$sub]) ? $cart->taxes[$sub] : 0); } // Update fee $cart->fees[$key]->tax = array_sum($cart->fees[$key]->tax_data); $cart->fees[$key]->amount = $cart->fees[$key]->amount - $cart->fees[$key]->tax; } } } }
/** * Gets a product's tax description (if is taxable) * * @return mixed string if is taxable else returns false */ public function get_tax_info() { $_tax = new WC_Tax(); $tax_notice = false; if ($this->is_taxable()) { $tax_display_mode = get_option('woocommerce_tax_display_shop'); $tax_rates = $_tax->get_rates($this->get_tax_class()); if (!empty($tax_rates)) { $tax_rates = array_values($tax_rates); // If is variable or is virtual vat exception dont show exact tax rate if ($this->is_virtual_vat_exception() || $this->is_type('variable')) { $tax_notice = $tax_display_mode == 'incl' && !WC()->customer->is_vat_exempt() ? __('incl. VAT', 'woocommerce-germanized') : __('excl. VAT', 'woocommerce-germanized'); } else { $tax_notice = $tax_display_mode == 'incl' && !WC()->customer->is_vat_exempt() ? sprintf(__('incl. %s%% VAT', 'woocommerce-germanized'), wc_gzd_format_tax_rate_percentage($tax_rates[0]['rate'])) : sprintf(__('excl. %s%% VAT', 'woocommerce-germanized'), wc_gzd_format_tax_rate_percentage($tax_rates[0]['rate'])); } } } return apply_filters('woocommerce_gzd_product_tax_info', $tax_notice, $this); }
/** * Return a meta query for filtering by price. * @return array */ private function price_filter_meta_query() { if (isset($_GET['max_price']) || isset($_GET['min_price'])) { $min = isset($_GET['min_price']) ? floatval($_GET['min_price']) : 0; $max = isset($_GET['max_price']) ? floatval($_GET['max_price']) : 9999999999.0; /** * Adjust if the store taxes are not displayed how they are stored. * Max is left alone because the filter was already increased. * Kicks in when prices excluding tax are displayed including tax. */ if (wc_tax_enabled() && 'incl' === get_option('woocommerce_tax_display_shop') && !wc_prices_include_tax()) { $tax_classes = array_merge(array(''), WC_Tax::get_tax_classes()); $class_min = $min; foreach ($tax_classes as $tax_class) { if ($tax_rates = WC_Tax::get_rates($tax_class)) { $class_min = $min - WC_Tax::get_tax_total(WC_Tax::calc_exclusive_tax($min, $tax_rates)); } } $min = $class_min; } return array('key' => '_price', 'value' => array($min, $max), 'compare' => 'BETWEEN', 'type' => 'DECIMAL', 'price_filter' => true); } return array(); }
/** * Sets fee for a specific gateway * * @param object $gateway */ public function set_fee($gateway) { $is_taxable = $gateway->get_option('fee_is_taxable', 'no') == 'no' ? false : true; $fee = $gateway->get_option('fee'); if ($is_taxable) { $tax_rates = WC_Tax::get_rates(); $fee_taxes = WC_Tax::calc_tax($fee, $tax_rates, true); $fee = $fee - array_sum($fee_taxes); } WC()->cart->add_fee(__('Payment charge', 'woocommerce-germanized'), $fee, $is_taxable); }
/** * Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale. * * Can be filtered by plugins which modify costs, but otherwise will include the raw meta costs unlike get_price() which runs costs through the woocommerce_get_price filter. * This is to ensure modified prices are not cached, unless intended. * * @param bool $display Are prices for display? If so, taxes will be calculated. * @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID. */ public function get_variation_prices($display = false) { global $wp_filter; /** * Transient name for storing prices for this product (note: Max transient length is 45) * @since 2.5.0 a single transient is used per product for all prices, rather than many transients per product. */ $transient_name = 'wc_var_prices_' . $this->id; /** * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters. * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique. * @var string */ if ($display) { $price_hash = array(get_option('woocommerce_tax_display_shop', 'excl'), WC_Tax::get_rates()); } else { $price_hash = array(false); } $filter_names = array('woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price'); foreach ($filter_names as $filter_name) { if (!empty($wp_filter[$filter_name])) { $price_hash[$filter_name] = array(); foreach ($wp_filter[$filter_name] as $priority => $callbacks) { $price_hash[$filter_name][] = array_values(wp_list_pluck($callbacks, 'function')); } } } $price_hash = md5(json_encode(apply_filters('woocommerce_get_variation_prices_hash', $price_hash, $this, $display))); // If the value has already been generated, we don't need to grab the values again. if (empty($this->prices_array[$price_hash])) { // Get value of transient $prices_array = array_filter((array) json_decode(strval(get_transient($transient_name)), true)); // If the product version has changed, reset cache if (empty($prices_array['version']) || $prices_array['version'] !== WC_Cache_Helper::get_transient_version('product')) { $this->prices_array = array('version' => WC_Cache_Helper::get_transient_version('product')); } // If the prices are not stored for this hash, generate them if (empty($prices_array[$price_hash])) { $prices = array(); $regular_prices = array(); $sale_prices = array(); $variation_ids = $this->get_children(true); foreach ($variation_ids as $variation_id) { if ($variation = $this->get_child($variation_id)) { $price = apply_filters('woocommerce_variation_prices_price', $variation->price, $variation, $this); $regular_price = apply_filters('woocommerce_variation_prices_regular_price', $variation->regular_price, $variation, $this); $sale_price = apply_filters('woocommerce_variation_prices_sale_price', $variation->sale_price, $variation, $this); // Skip empty prices if ('' === $price) { continue; } // If sale price does not equal price, the product is not yet on sale if ($sale_price === $regular_price || $sale_price !== $price) { $sale_price = $regular_price; } // If we are getting prices for display, we need to account for taxes if ($display) { if ('incl' === get_option('woocommerce_tax_display_shop')) { $price = '' === $price ? '' : $variation->get_price_including_tax(1, $price); $regular_price = '' === $regular_price ? '' : $variation->get_price_including_tax(1, $regular_price); $sale_price = '' === $sale_price ? '' : $variation->get_price_including_tax(1, $sale_price); } else { $price = '' === $price ? '' : $variation->get_price_excluding_tax(1, $price); $regular_price = '' === $regular_price ? '' : $variation->get_price_excluding_tax(1, $regular_price); $sale_price = '' === $sale_price ? '' : $variation->get_price_excluding_tax(1, $sale_price); } } $prices[$variation_id] = wc_format_decimal($price, wc_get_price_decimals()); $regular_prices[$variation_id] = wc_format_decimal($regular_price, wc_get_price_decimals()); $sale_prices[$variation_id] = wc_format_decimal($sale_price . '.00', wc_get_price_decimals()); } } asort($prices); asort($regular_prices); asort($sale_prices); $prices_array[$price_hash] = array('price' => $prices, 'regular_price' => $regular_prices, 'sale_price' => $sale_prices); set_transient($transient_name, json_encode($prices_array), DAY_IN_SECONDS * 30); } /** * Give plugins one last chance to filter the variation prices array which has been generated. */ $this->prices_array[$price_hash] = apply_filters('woocommerce_variation_prices', $prices_array[$price_hash], $this, $display); } /** * Return the values. */ return $this->prices_array[$price_hash]; }
/** * Get an array of all sale and regular prices from all variations. * @since WooCommerce 2.4 * @param array() sale and regular prices for default location * @param WC_Product_Variable * @param bool Are prices for display? If so, taxes will be calculated. * @return array() */ public function get_variation_prices_array($prices_array, $product, $display) { if ($this->customer->group_key) { $cache_key = 'var_prices_' . md5(json_encode(array($product->id, $display ? WC_Tax::get_rates() : '', $this->customer->group_key, WC_Cache_Helper::get_transient_version('product')))); if (false === ($prices_array = get_transient($cache_key))) { $prices = array(); $regular_prices = array(); $sale_prices = array(); $tax_display_mode = get_option('woocommerce_tax_display_shop'); foreach ($product->get_children(true) as $variation_id) { if ($variation = $product->get_child($variation_id)) { $price = $variation->get_price(); $regular_price = $variation->get_regular_price(); $sale_price = $variation->get_sale_price(); // If sale price does not equal price, the product is not yet on sale if ($price != $sale_price) { $sale_price = $regular_price; } // If we are getting prices for display, we need to account for taxes if ($display) { $price = $tax_display_mode == 'incl' ? $variation->get_price_including_tax(1, $price) : $variation->get_price_excluding_tax(1, $price); $regular_price = $tax_display_mode == 'incl' ? $variation->get_price_including_tax(1, $regular_price) : $variation->get_price_excluding_tax(1, $regular_price); $sale_price = $tax_display_mode == 'incl' ? $variation->get_price_including_tax(1, $sale_price) : $variation->get_price_excluding_tax(1, $sale_price); } $prices[$variation_id] = $price; $regular_prices[$variation_id] = $regular_price; $sale_prices[$variation_id] = $sale_price; } } asort($prices); asort($regular_prices); asort($sale_prices); $prices_array = array('price' => $prices, 'regular_price' => $regular_prices, 'sale_price' => $sale_prices); set_transient($cache_key, $prices_array, DAY_IN_SECONDS * 30); } } return $prices_array; }
/** * Output widget. * * @see WP_Widget * * @param array $args * @param array $instance */ public function widget($args, $instance) { global $wp, $wp_the_query; if (!is_post_type_archive('product') && !is_tax(get_object_taxonomies('product'))) { return; } if (!$wp_the_query->post_count) { return; } // Remember current filters/search if ('' == get_option('permalink_structure')) { $link_url = remove_query_arg(array('page', 'paged'), add_query_arg($wp->query_string, '', home_url($wp->request))); } else { $link_url = preg_replace('%\\/page/[0-9]+%', '', home_url(trailingslashit($wp->request))); } if (get_search_query()) { $link_url = add_query_arg('s', get_search_query(), $link_url); } if (!empty($_GET['post_type'])) { $link_url = add_query_arg('post_type', urlencode($_GET['post_type']), $link_url); } if (!empty($_GET['product_cat'])) { $link_url = add_query_arg('product_cat', urlencode($_GET['product_cat']), $link_url); } if (!empty($_GET['product_tag'])) { $link_url = add_query_arg('product_tag', urlencode($_GET['product_tag']), $link_url); } if (!empty($_GET['orderby'])) { $link_url = add_query_arg('orderby', urlencode($_GET['orderby']), $link_url); } if ($_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes()) { foreach ($_chosen_attributes as $attribute => $data) { $taxonomy_filter = 'filter_' . str_replace('pa_', '', $attribute); $link_url = add_query_arg($taxonomy_filter, urlencode(implode(',', $data['terms'])), $link_url); if ('or' == $data['query_type']) { $link_url = add_query_arg(str_replace('pa_', 'query_type_', $attribute), 'or', $link_url); } } } // Find min and max price in current result set $prices = $this->get_filtered_price(); $min = floor($prices->min_price); $max = ceil($prices->max_price); if ($min === $max) { return; } $this->widget_start($args, $instance); /** * Adjust max if the store taxes are not displayed how they are stored. * Min is left alone because the product may not be taxable. * Kicks in when prices excluding tax are displayed including tax. */ if (wc_tax_enabled() && 'incl' === get_option('woocommerce_tax_display_shop') && !wc_prices_include_tax()) { $tax_classes = array_merge(array(''), WC_Tax::get_tax_classes()); $class_max = $max; foreach ($tax_classes as $tax_class) { if ($tax_rates = WC_Tax::get_rates($tax_class)) { $class_max = $max + WC_Tax::get_tax_total(WC_Tax::calc_exclusive_tax($max, $tax_rates)); } } $max = $class_max; } $minprice = isset($_GET['min_price']) ? esc_attr($_GET['min_price']) : ''; $maxprice = isset($_GET['max_price']) ? esc_attr($_GET['max_price']) : ''; $output = ''; $min_price = 0; $range_size = intval($instance['range_size']); $max_ranges = intval($instance['max_ranges']) - 1; $count = 0; if (strlen($minprice) > 0) { $output .= '<li><a href="' . esc_url($link_url) . '">' . esc_html__('All', 'cruxstore') . '</a></li>'; } else { $output .= '<li class="selected">' . esc_html__('All', 'cruxstore') . '</li>'; } while ($count <= $max_ranges) { $step = $min_price; $min_price += $range_size; if ($count != $max_ranges) { if ($min_price > $max) { $min_price = $max; } $link = add_query_arg(array('min_price' => $step, 'max_price' => $min_price), $link_url); $price_text = wc_price($step) . ' - ' . wc_price($min_price); } else { $link = add_query_arg(array('min_price' => $step, 'max_price' => $max), $link_url); $price_text = wc_price($step) . '+'; } if ($step == $minprice && $min_price == $maxprice) { $output .= '<li class="selected">' . $price_text . '</li>'; } else { $output .= '<li><a href="' . esc_url($link) . '">' . $price_text . '</a></li>'; } $count++; if ($min_price == $max) { break; } } printf('<ul>%s</ul>', $output); $this->widget_end($args); }
function vtprd_get_price_including_tax_forced($product_id, $price, $product) { global $post, $wpdb, $woocommerce, $vtprd_cart, $vtprd_cart_item, $vtprd_setup_options, $vtprd_info; //error_log( print_r( 'vtprd_get_price_including_tax_forced, Begin price= ' .$price .' Product= ' .$product_id , true ) ); //changed $this-> to $product-> //use $discount_price as basi $qty = 1; $_tax = new WC_Tax(); if ($product->is_taxable()) { if (get_option('woocommerce_prices_include_tax') === 'no') { $tax_rates = $_tax->get_rates($product->get_tax_class()); $taxes = $_tax->calc_tax($price * $qty, $tax_rates, false); $tax_amount = $_tax->get_tax_total($taxes); $price = round($price * $qty + $tax_amount, absint(get_option('woocommerce_price_num_decimals'))); } else { $tax_rates = $_tax->get_rates($product->get_tax_class()); $base_tax_rates = $_tax->get_shop_base_rate($product->tax_class); // if ( ! empty( $woocommerce->customer ) && $woocommerce->customer->is_vat_exempt() ) { //v1.0.7.5 if (vtprd_maybe_customer_tax_exempt()) { //v1.0.7.9 $base_taxes = $_tax->calc_tax($price * $qty, $base_tax_rates, true); $base_tax_amount = array_sum($base_taxes); $price = round($price * $qty - $base_tax_amount, absint(get_option('woocommerce_price_num_decimals'))); } elseif ($tax_rates !== $base_tax_rates) { $base_taxes = $_tax->calc_tax($price * $qty, $base_tax_rates, true); $modded_taxes = $_tax->calc_tax($price * $qty - array_sum($base_taxes), $tax_rates, false); $price = round($price * $qty - array_sum($base_taxes) + array_sum($modded_taxes), absint(get_option('woocommerce_price_num_decimals'))); } else { $taxes = $_tax->calc_tax($price * $qty, $tax_rates, false); $tax_amount = $_tax->get_tax_total($taxes); $price = round($price * $qty + $tax_amount, absint(get_option('woocommerce_price_num_decimals'))); } } } else { $price = $price * $qty; } return $price; }
<tr class="tax-total"> <td><?php echo esc_html( WC()->countries->tax_or_vat() ); ?></td> <td><?php wc_cart_totals_taxes_total_html(); ?></td> </tr> <?php endif; ?> <?php endif; ?> <?php do_action( 'woocommerce_review_order_before_order_total' ); ?> <tr class="order-total"> <th><?php _e( 'Gesamtsumme', 'woocommerce' ); if ( wc_tax_enabled() && WC()->cart->tax_display_cart == 'incl' ) { $tax_string_array = array(); if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) { $_tax = new WC_Tax();//looking for appropriate vat for specific product $rates = array_shift($_tax->get_rates( $_product->get_tax_class() )); foreach ( WC()->cart->get_tax_totals() as $code => $tax ){ $tax_string_array[] = sprintf( '%s %s',round($rates['rate']).'%', $tax->label ); } } else { $tax_string_array[] = sprintf( '%s %s', wc_price( WC()->cart->get_taxes_total( true, true ) ), WC()->countries->tax_or_vat() ); } if ( ! empty( $tax_string_array ) ) { $value .= '<small class="includes_tax">' . sprintf( __( '(inkl. %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) ) . '</small>'; } } echo $value; ?></th> <td><?php wc_cart_totals_order_total_html(); ?></td>
/** * Get Price Range for given product ids. * If filtered is true then return price range for filtered products, * otherwise return price range for all products. * * @param boolean $filtered * @return array */ public function getPriceRange($filtered = true) { if ($filtered === true) { $price_range = $this->filteredProductsPriceRange(); } else { $price_range = $this->unfilteredProductsPriceRange(); } if (sizeof($price_range) > 2) { $min = $max = false; foreach ($price_range as $price) { if ($min === false || $min > (int) $price) { $min = floor($price); } if ($max === false || $max < (int) $price) { $max = ceil($price); } } // if tax enabled and shop page shows price including tax if (wc_tax_enabled() && 'incl' === get_option('woocommerce_tax_display_shop') && !wc_prices_include_tax()) { $tax_classes = array_merge(array(''), WC_Tax::get_tax_classes()); foreach ($tax_classes as $tax_class) { $tax_rates = WC_Tax::get_rates($tax_class); $class_min = $min + WC_Tax::get_tax_total(WC_Tax::calc_exclusive_tax($min, $tax_rates)); $class_max = $max + WC_Tax::get_tax_total(WC_Tax::calc_exclusive_tax($max, $tax_rates)); $min = $max = false; if ($min === false || $min > (int) $class_min) { $min = floor($class_min); } if ($max === false || $max < (int) $class_max) { $max = ceil($class_max); } } } // if WooCommerce Currency Switcher plugin is activated if (class_exists('WOOCS')) { $woocs = new WOOCS(); $chosen_currency = $woocs->get_woocommerce_currency(); $currencies = $woocs->get_currencies(); if (sizeof($currencies) > 0) { foreach ($currencies as $currency) { if ($currency['name'] == $chosen_currency) { $rate = $currency['rate']; } } $min = floor($min * $rate); $max = ceil($max * $rate); } } if ($min == $max) { // empty array return array(); } else { // array with min and max values return array($min, $max); } } else { // empty array return array(); } }
/** * widget function. * * @see WP_Widget * * @param array $args * @param array $instance */ public function widget($args, $instance) { global $_chosen_attributes, $wpdb, $wp; if (!is_post_type_archive('product') && !is_tax(get_object_taxonomies('product'))) { return; } if (sizeof(WC()->query->unfiltered_product_ids) == 0) { return; // None shown - return } $min_price = isset($_GET['min_price']) ? esc_attr($_GET['min_price']) : ''; $max_price = isset($_GET['max_price']) ? esc_attr($_GET['max_price']) : ''; wp_enqueue_script('wc-price-slider'); // Remember current filters/search $fields = ''; if (get_search_query()) { $fields .= '<input type="hidden" name="s" value="' . get_search_query() . '" />'; } if (!empty($_GET['post_type'])) { $fields .= '<input type="hidden" name="post_type" value="' . esc_attr($_GET['post_type']) . '" />'; } if (!empty($_GET['product_cat'])) { $fields .= '<input type="hidden" name="product_cat" value="' . esc_attr($_GET['product_cat']) . '" />'; } if (!empty($_GET['product_tag'])) { $fields .= '<input type="hidden" name="product_tag" value="' . esc_attr($_GET['product_tag']) . '" />'; } if (!empty($_GET['orderby'])) { $fields .= '<input type="hidden" name="orderby" value="' . esc_attr($_GET['orderby']) . '" />'; } if ($_chosen_attributes) { foreach ($_chosen_attributes as $attribute => $data) { $taxonomy_filter = 'filter_' . str_replace('pa_', '', $attribute); $fields .= '<input type="hidden" name="' . esc_attr($taxonomy_filter) . '" value="' . esc_attr(implode(',', $data['terms'])) . '" />'; if ('or' == $data['query_type']) { $fields .= '<input type="hidden" name="' . esc_attr(str_replace('pa_', 'query_type_', $attribute)) . '" value="or" />'; } } } if (0 === sizeof(WC()->query->layered_nav_product_ids)) { $min = floor($wpdb->get_var("\n\t\t\t\tSELECT min(meta_value + 0)\n\t\t\t\tFROM {$wpdb->posts} as posts\n\t\t\t\tLEFT JOIN {$wpdb->postmeta} as postmeta ON posts.ID = postmeta.post_id\n\t\t\t\tWHERE meta_key IN ('" . implode("','", array_map('esc_sql', apply_filters('woocommerce_price_filter_meta_keys', array('_price', '_min_variation_price')))) . "')\n\t\t\t\tAND meta_value != ''\n\t\t\t")); $max = ceil($wpdb->get_var("\n\t\t\t\tSELECT max(meta_value + 0)\n\t\t\t\tFROM {$wpdb->posts} as posts\n\t\t\t\tLEFT JOIN {$wpdb->postmeta} as postmeta ON posts.ID = postmeta.post_id\n\t\t\t\tWHERE meta_key IN ('" . implode("','", array_map('esc_sql', apply_filters('woocommerce_price_filter_meta_keys', array('_price')))) . "')\n\t\t\t")); } else { $min = floor($wpdb->get_var("\n\t\t\t\tSELECT min(meta_value + 0)\n\t\t\t\tFROM {$wpdb->posts} as posts\n\t\t\t\tLEFT JOIN {$wpdb->postmeta} as postmeta ON posts.ID = postmeta.post_id\n\t\t\t\tWHERE meta_key IN ('" . implode("','", array_map('esc_sql', apply_filters('woocommerce_price_filter_meta_keys', array('_price', '_min_variation_price')))) . "')\n\t\t\t\tAND meta_value != ''\n\t\t\t\tAND (\n\t\t\t\t\tposts.ID IN (" . implode(',', array_map('absint', WC()->query->layered_nav_product_ids)) . ")\n\t\t\t\t\tOR (\n\t\t\t\t\t\tposts.post_parent IN (" . implode(',', array_map('absint', WC()->query->layered_nav_product_ids)) . ")\n\t\t\t\t\t\tAND posts.post_parent != 0\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t")); $max = ceil($wpdb->get_var("\n\t\t\t\tSELECT max(meta_value + 0)\n\t\t\t\tFROM {$wpdb->posts} as posts\n\t\t\t\tLEFT JOIN {$wpdb->postmeta} as postmeta ON posts.ID = postmeta.post_id\n\t\t\t\tWHERE meta_key IN ('" . implode("','", array_map('esc_sql', apply_filters('woocommerce_price_filter_meta_keys', array('_price')))) . "')\n\t\t\t\tAND (\n\t\t\t\t\tposts.ID IN (" . implode(',', array_map('absint', WC()->query->layered_nav_product_ids)) . ")\n\t\t\t\t\tOR (\n\t\t\t\t\t\tposts.post_parent IN (" . implode(',', array_map('absint', WC()->query->layered_nav_product_ids)) . ")\n\t\t\t\t\t\tAND posts.post_parent != 0\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t")); } if ($min == $max) { return; } $this->widget_start($args, $instance); if ('' == get_option('permalink_structure')) { $form_action = remove_query_arg(array('page', 'paged'), add_query_arg($wp->query_string, '', home_url($wp->request))); } else { $form_action = preg_replace('%\\/page/[0-9]+%', '', home_url(trailingslashit($wp->request))); } if (wc_tax_enabled() && 'incl' === get_option('woocommerce_tax_display_shop') && !wc_prices_include_tax()) { $tax_classes = array_merge(array(''), WC_Tax::get_tax_classes()); $min = 0; foreach ($tax_classes as $tax_class) { $tax_rates = WC_Tax::get_rates($tax_class); $class_min = $min + WC_Tax::get_tax_total(WC_Tax::calc_exclusive_tax($min, $tax_rates)); $class_max = $max + WC_Tax::get_tax_total(WC_Tax::calc_exclusive_tax($max, $tax_rates)); if ($min === 0 || $class_min < $min) { $min = $class_min; } if ($class_max > $max) { $max = $class_max; } } } echo '<form method="get" action="' . esc_url($form_action) . '"> <div class="price_slider_wrapper"> <div class="price_slider" style="display:none;"></div> <div class="price_slider_amount"> <input type="text" id="min_price" name="min_price" value="' . esc_attr($min_price) . '" data-min="' . esc_attr(apply_filters('woocommerce_price_filter_widget_min_amount', $min)) . '" placeholder="' . esc_attr__('Min price', 'woocommerce') . '" /> <input type="text" id="max_price" name="max_price" value="' . esc_attr($max_price) . '" data-max="' . esc_attr(apply_filters('woocommerce_price_filter_widget_max_amount', $max)) . '" placeholder="' . esc_attr__('Max price', 'woocommerce') . '" /> <button type="submit" class=" filter">' . __('Filter', 'woocommerce') . '</button> <div class="price_label price-input" style="display:none;"> ' . __('Price:', 'woocommerce') . ' <span class="from"></span> — <span class="to"></span> </div> ' . $fields . ' <div class="clear"></div> </div> </div> </form>'; $this->widget_end($args); }
/** * Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale. * * Can be filtered by plugins which modify costs, but otherwise will include the raw meta costs unlike get_price() which runs costs through the woocommerce_get_price filter. * This is to ensure modified prices are not cached, unless intended. * * @param bool $display Are prices for display? If so, taxes will be calculated. * @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID. */ public function get_variation_prices($display = false) { global $wp_filter; /** * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters. * Max transient length is 45, -10 for get_transient_version. * @var string */ $hash = array($this->id, $display, $display ? WC_Tax::get_rates() : array()); foreach ($wp_filter as $key => $val) { if (in_array($key, array('woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price'))) { $hash[$key] = $val; } } /** * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique. */ $hash = apply_filters('woocommerce_get_variation_prices_hash', $hash, $this, $display); $cache_key = 'wc_var_prices' . substr(md5(json_encode($hash)), 0, 22) . WC_Cache_Helper::get_transient_version('product'); $this->prices_array = get_transient($cache_key); if (empty($this->prices_array)) { $prices = array(); $regular_prices = array(); $sale_prices = array(); $tax_display_mode = get_option('woocommerce_tax_display_shop'); $variation_ids = $this->get_children(true); foreach ($variation_ids as $variation_id) { if ($variation = $this->get_child($variation_id)) { $price = apply_filters('woocommerce_variation_prices_price', $variation->price, $variation, $this); $regular_price = apply_filters('woocommerce_variation_prices_regular_price', $variation->regular_price, $variation, $this); $sale_price = apply_filters('woocommerce_variation_prices_sale_price', $variation->sale_price, $variation, $this); // If sale price does not equal price, the product is not yet on sale if ($sale_price === $regular_price || $sale_price !== $price) { $sale_price = $regular_price; } // If we are getting prices for display, we need to account for taxes if ($display) { if ('incl' === $tax_display_mode) { $price = '' === $price ? '' : $variation->get_price_including_tax(1, $price); $regular_price = '' === $regular_price ? '' : $variation->get_price_including_tax(1, $regular_price); $sale_price = '' === $sale_price ? '' : $variation->get_price_including_tax(1, $sale_price); } else { $price = '' === $price ? '' : $variation->get_price_excluding_tax(1, $price); $regular_price = '' === $regular_price ? '' : $variation->get_price_excluding_tax(1, $regular_price); $sale_price = '' === $sale_price ? '' : $variation->get_price_excluding_tax(1, $sale_price); } } $prices[$variation_id] = $price; $regular_prices[$variation_id] = $regular_price; $sale_prices[$variation_id] = $sale_price; } } asort($prices); asort($regular_prices); asort($sale_prices); $this->prices_array = array('price' => $prices, 'regular_price' => $regular_prices, 'sale_price' => $sale_prices); set_transient($cache_key, $this->prices_array, DAY_IN_SECONDS * 30); } /** * Give plugins one last chance to filter the variation prices array. */ return $this->prices_array = apply_filters('woocommerce_variation_prices', $this->prices_array, $this, $display); }
/** * Return a meta query for filtering by price. * @return array */ private function price_filter_meta_query() { if (isset($_GET['max_price']) || isset($_GET['min_price'])) { $min = isset($_GET['min_price']) ? floatval($_GET['min_price']) : 0; $max = isset($_GET['max_price']) ? floatval($_GET['max_price']) : 9999999999.0; // If displaying prices in the shop including taxes, but prices don't include taxes.. if (wc_tax_enabled() && 'incl' === get_option('woocommerce_tax_display_shop') && !wc_prices_include_tax()) { $tax_classes = array_merge(array(''), WC_Tax::get_tax_classes()); foreach ($tax_classes as $tax_class) { $tax_rates = WC_Tax::get_rates($tax_class); $class_min = $min - WC_Tax::get_tax_total(WC_Tax::calc_inclusive_tax($min, $tax_rates)); $class_max = $max - WC_Tax::get_tax_total(WC_Tax::calc_inclusive_tax($max, $tax_rates)); if ($class_min < $min) { $min = $class_min; } if ($class_max > $max) { $max = $class_max; } } } return array('key' => '_price', 'value' => array($min, $max), 'compare' => 'BETWEEN', 'type' => 'DECIMAL', 'price_filter' => true); } return array(); }
/** * Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale. * * Can be filtered by plugins which modify costs, but otherwise will include the raw meta costs unlike get_price() which runs costs through the woocommerce_get_price filter. * This is to ensure modified prices are not cached, unless intended. * * @since 2.7.0 * @param WC_Product * @param bool $include_taxes If taxes should be calculated or not. */ private function read_price_data(&$product, $include_taxes = false) { global $wp_filter; /** * Transient name for storing prices for this product (note: Max transient length is 45) * @since 2.5.0 a single transient is used per product for all prices, rather than many transients per product. */ $transient_name = 'wc_var_prices_' . $product->get_id(); /** * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters. * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique. * @var string */ $price_hash = $include_taxes ? array(get_option('woocommerce_tax_display_shop', 'excl'), WC_Tax::get_rates()) : array(false); $filter_names = array('woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price'); foreach ($filter_names as $filter_name) { if (!empty($wp_filter[$filter_name])) { $price_hash[$filter_name] = array(); foreach ($wp_filter[$filter_name] as $priority => $callbacks) { $price_hash[$filter_name][] = array_values(wp_list_pluck($callbacks, 'function')); } } } $price_hash[] = WC_Cache_Helper::get_transient_version('product'); $price_hash = md5(json_encode(apply_filters('woocommerce_get_variation_prices_hash', $price_hash, $product, $include_taxes))); /** * $this->prices_array is an array of values which may have been modified from what is stored in transients - this may not match $transient_cached_prices_array. * If the value has already been generated, we don't need to grab the values again so just return them. They are already filtered. */ if (!empty($this->prices_array[$price_hash])) { if ($include_taxes) { $product->set_variation_prices_including_taxes($this->prices_array[$price_hash]); } else { $product->set_variation_prices($this->prices_array[$price_hash]); } /** * No locally cached value? Get the data from the transient or generate it. */ } else { // Get value of transient $transient_cached_prices_array = array_filter((array) json_decode(strval(get_transient($transient_name)), true)); // If the product version has changed since the transient was last saved, reset the transient cache. if (empty($transient_cached_prices_array['version']) || WC_Cache_Helper::get_transient_version('product') !== $transient_cached_prices_array['version']) { $transient_cached_prices_array = array('version' => WC_Cache_Helper::get_transient_version('product')); } // If the prices are not stored for this hash, generate them and add to the transient. if (empty($transient_cached_prices_array[$price_hash])) { $prices = array(); $regular_prices = array(); $sale_prices = array(); $variation_ids = $product->get_visible_children(); foreach ($variation_ids as $variation_id) { if ($variation = wc_get_product($variation_id)) { $price = apply_filters('woocommerce_variation_prices_price', $variation->get_price('edit'), $variation, $product); $regular_price = apply_filters('woocommerce_variation_prices_regular_price', $variation->get_regular_price('edit'), $variation, $product); $sale_price = apply_filters('woocommerce_variation_prices_sale_price', $variation->get_sale_price('edit'), $variation, $product); // Skip empty prices if ('' === $price) { continue; } // If sale price does not equal price, the product is not yet on sale if ($sale_price === $regular_price || $sale_price !== $price) { $sale_price = $regular_price; } // If we are getting prices for display, we need to account for taxes if ($include_taxes) { if ('incl' === get_option('woocommerce_tax_display_shop')) { $price = '' === $price ? '' : wc_get_price_including_tax($variation, array('qty' => 1, 'price' => $price)); $regular_price = '' === $regular_price ? '' : wc_get_price_including_tax($variation, array('qty' => 1, 'price' => $regular_price)); $sale_price = '' === $sale_price ? '' : wc_get_price_including_tax($variation, array('qty' => 1, 'price' => $sale_price)); } else { $price = '' === $price ? '' : wc_get_price_excluding_tax($variation, array('qty' => 1, 'price' => $price)); $regular_price = '' === $regular_price ? '' : wc_get_price_excluding_tax($variation, array('qty' => 1, 'price' => $regular_price)); $sale_price = '' === $sale_price ? '' : wc_get_price_excluding_tax($variation, array('qty' => 1, 'price' => $sale_price)); } } $prices[$variation_id] = wc_format_decimal($price, wc_get_price_decimals()); $regular_prices[$variation_id] = wc_format_decimal($regular_price, wc_get_price_decimals()); $sale_prices[$variation_id] = wc_format_decimal($sale_price . '.00', wc_get_price_decimals()); } } asort($prices); asort($regular_prices); asort($sale_prices); $transient_cached_prices_array[$price_hash] = array('price' => $prices, 'regular_price' => $regular_prices, 'sale_price' => $sale_prices); set_transient($transient_name, json_encode($transient_cached_prices_array), DAY_IN_SECONDS * 30); } /** * Give plugins one last chance to filter the variation prices array which has been generated and store locally to the class. * This value may differ from the transient cache. It is filtered once before storing locally. */ $this->prices_array[$price_hash] = apply_filters('woocommerce_variation_prices', $transient_cached_prices_array[$price_hash], $product, $include_taxes); if ($include_taxes) { $product->set_variation_prices_including_taxes($this->prices_array[$price_hash]); } else { $product->set_variation_prices($this->prices_array[$price_hash]); } } }
/** * Calculate totals for cart. Implements vat exception for digital products. */ public function calculate_totals() { $this->reset(); $this->coupons = $this->get_coupons(); do_action('woocommerce_before_calculate_totals', $this); if (sizeof($this->get_cart()) == 0) { $this->set_session(); return; } $tax_rates = array(); $shop_tax_rates = array(); /** * Calculate subtotals for items. This is done first so that discount logic can use the values. */ foreach ($this->get_cart() as $cart_item_key => $values) { $_product = $values['data']; // Count items + weight $this->cart_contents_weight += $_product->get_weight() * $values['quantity']; $this->cart_contents_count += $values['quantity']; // Prices $line_price = $_product->get_price() * $values['quantity']; $line_subtotal = 0; $line_subtotal_tax = 0; /** * No tax to calculate */ if (!$_product->is_taxable()) { // Subtotal is the undiscounted price $this->subtotal += $line_price; $this->subtotal_ex_tax += $line_price; /** * Prices include tax * * To prevent rounding issues we need to work with the inclusive price where possible * otherwise we'll see errors such as when working with a 9.99 inc price, 20% VAT which would * be 8.325 leading to totals being 1p off * * Pre tax coupons come off the price the customer thinks they are paying - tax is calculated * afterwards. * * e.g. $100 bike with $10 coupon = customer pays $90 and tax worked backwards from that */ } elseif ($this->prices_include_tax) { // Get base tax rates if (empty($shop_tax_rates[$_product->tax_class])) { $shop_tax_rates[$_product->tax_class] = WC_Tax::get_base_tax_rates($_product->tax_class); } // Get item tax rates if (empty($tax_rates[$_product->get_tax_class()])) { $tax_rates[$_product->get_tax_class()] = WC_Tax::get_rates($_product->get_tax_class()); } $base_tax_rates = $shop_tax_rates[$_product->tax_class]; $item_tax_rates = $tax_rates[$_product->get_tax_class()]; /** * ADJUST TAX - Calculations when base tax is not equal to the item tax */ if ($item_tax_rates !== $base_tax_rates) { // Work out a new base price without the shop's base tax $taxes = WC_Tax::calc_tax($line_price, $base_tax_rates, true, true); // Digital VAT exception if ($this->is_virtual_taxable() && $_product->gzd_product->is_virtual_vat_exception()) { $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates, true, true); } // Now we have a new item price (excluding TAX) $line_subtotal = $line_price - array_sum($taxes); // Now add modified taxes $tax_result = WC_Tax::calc_tax($line_subtotal, $item_tax_rates); $line_subtotal_tax = array_sum($tax_result); /** * Regular tax calculation (customer inside base and the tax class is unmodified */ } else { // Calc tax normally $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates, true); $line_subtotal_tax = array_sum($taxes); $line_subtotal = $line_price - array_sum($taxes); } /** * Prices exclude tax * * This calculation is simpler - work with the base, untaxed price. */ } else { // Get item tax rates if (empty($tax_rates[$_product->get_tax_class()])) { $tax_rates[$_product->get_tax_class()] = WC_Tax::get_rates($_product->get_tax_class()); } $item_tax_rates = $tax_rates[$_product->get_tax_class()]; // Base tax for line before discount - we will store this in the order data $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates); $line_subtotal_tax = array_sum($taxes); $line_subtotal = $line_price; } // Add to main subtotal $this->subtotal += $line_subtotal + $line_subtotal_tax; $this->subtotal_ex_tax += $line_subtotal; } /** * Calculate totals for items */ foreach ($this->get_cart() as $cart_item_key => $values) { $_product = $values['data']; // Prices $base_price = $_product->get_price(); $line_price = $_product->get_price() * $values['quantity']; // Tax data $taxes = array(); $discounted_taxes = array(); /** * No tax to calculate */ if (!$_product->is_taxable()) { // Discounted Price (price with any pre-tax discounts applied) $discounted_price = $this->get_discounted_price($values, $base_price, true); $line_subtotal_tax = 0; $line_subtotal = $line_price; $line_tax = 0; $line_total = WC_Tax::round($discounted_price * $values['quantity']); /** * Prices include tax */ } elseif ($this->prices_include_tax) { $base_tax_rates = $shop_tax_rates[$_product->tax_class]; $item_tax_rates = $tax_rates[$_product->get_tax_class()]; /** * ADJUST TAX - Calculations when base tax is not equal to the item tax */ if ($item_tax_rates !== $base_tax_rates) { // Work out a new base price without the shop's base tax $taxes = WC_Tax::calc_tax($line_price, $base_tax_rates, true, true); // Digital tax exception if ($this->is_virtual_taxable() && $_product->gzd_product->is_virtual_vat_exception()) { $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates, true, true); } // Now we have a new item price (excluding TAX) $line_subtotal = round($line_price - array_sum($taxes), WC_ROUNDING_PRECISION); $taxes = WC_Tax::calc_tax($line_subtotal, $item_tax_rates); $line_subtotal_tax = array_sum($taxes); // Adjusted price (this is the price including the new tax rate) $adjusted_price = ($line_subtotal + $line_subtotal_tax) / $values['quantity']; // Apply discounts $discounted_price = $this->get_discounted_price($values, $adjusted_price, true); $discounted_taxes = WC_Tax::calc_tax($discounted_price * $values['quantity'], $item_tax_rates, true); $line_tax = array_sum($discounted_taxes); $line_total = $discounted_price * $values['quantity'] - $line_tax; /** * Regular tax calculation (customer inside base and the tax class is unmodified */ } else { // Work out a new base price without the item tax $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates, true); // Now we have a new item price (excluding TAX) $line_subtotal = $line_price - array_sum($taxes); $line_subtotal_tax = array_sum($taxes); // Calc prices and tax (discounted) $discounted_price = $this->get_discounted_price($values, $base_price, true); $discounted_taxes = WC_Tax::calc_tax($discounted_price * $values['quantity'], $item_tax_rates, true); $line_tax = array_sum($discounted_taxes); $line_total = $discounted_price * $values['quantity'] - $line_tax; } // Tax rows - merge the totals we just got foreach (array_keys($this->taxes + $discounted_taxes) as $key) { $this->taxes[$key] = (isset($discounted_taxes[$key]) ? $discounted_taxes[$key] : 0) + (isset($this->taxes[$key]) ? $this->taxes[$key] : 0); } /** * Prices exclude tax */ } else { $item_tax_rates = $tax_rates[$_product->get_tax_class()]; // Work out a new base price without the shop's base tax $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates); // Now we have the item price (excluding TAX) $line_subtotal = $line_price; $line_subtotal_tax = array_sum($taxes); // Now calc product rates $discounted_price = $this->get_discounted_price($values, $base_price, true); $discounted_taxes = WC_Tax::calc_tax($discounted_price * $values['quantity'], $item_tax_rates); $discounted_tax_amount = array_sum($discounted_taxes); $line_tax = $discounted_tax_amount; $line_total = $discounted_price * $values['quantity']; // Tax rows - merge the totals we just got foreach (array_keys($this->taxes + $discounted_taxes) as $key) { $this->taxes[$key] = (isset($discounted_taxes[$key]) ? $discounted_taxes[$key] : 0) + (isset($this->taxes[$key]) ? $this->taxes[$key] : 0); } } // Cart contents total is based on discounted prices and is used for the final total calculation $this->cart_contents_total += $line_total; // Store costs + taxes for lines $this->cart_contents[$cart_item_key]['line_total'] = $line_total; $this->cart_contents[$cart_item_key]['line_tax'] = $line_tax; $this->cart_contents[$cart_item_key]['line_subtotal'] = $line_subtotal; $this->cart_contents[$cart_item_key]['line_subtotal_tax'] = $line_subtotal_tax; // Store rates ID and costs - Since 2.2 $this->cart_contents[$cart_item_key]['line_tax_data'] = array('total' => $discounted_taxes, 'subtotal' => $taxes); } // Only calculate the grand total + shipping if on the cart/checkout if (is_checkout() || is_cart() || defined('WOOCOMMERCE_CHECKOUT') || defined('WOOCOMMERCE_CART')) { // Calculate the Shipping $this->calculate_shipping(); // Trigger the fees API where developers can add fees to the cart $this->calculate_fees(); // Total up/round taxes and shipping taxes if ($this->round_at_subtotal) { $this->tax_total = WC_Tax::get_tax_total($this->taxes); $this->shipping_tax_total = WC_Tax::get_tax_total($this->shipping_taxes); $this->taxes = array_map(array('WC_Tax', 'round'), $this->taxes); $this->shipping_taxes = array_map(array('WC_Tax', 'round'), $this->shipping_taxes); } else { $this->tax_total = array_sum($this->taxes); $this->shipping_tax_total = array_sum($this->shipping_taxes); } // VAT exemption done at this point - so all totals are correct before exemption if (WC()->customer->is_vat_exempt()) { $this->remove_taxes(); } // Allow plugins to hook and alter totals before final total is calculated do_action('woocommerce_calculate_totals', $this); // Grand Total - Discounted product prices, discounted tax, shipping cost + tax $this->total = max(0, apply_filters('woocommerce_calculated_total', round($this->cart_contents_total + $this->tax_total + $this->shipping_tax_total + $this->shipping_total + $this->fee_total, $this->dp), $this)); } else { // Set tax total to sum of all tax rows $this->tax_total = WC_Tax::get_tax_total($this->taxes); // VAT exemption done at this point - so all totals are correct before exemption if (WC()->customer->is_vat_exempt()) { $this->remove_taxes(); } } do_action('woocommerce_after_calculate_totals', $this); //print_r($this->taxes); $this->set_session(); }
/** * Calculates a price (could be per period price or sign-up fee) for a subscription less tax * if the subscription is taxable and the prices in the store include tax. * * Based on the WC_Product::get_price_excluding_tax() function. * * @param float $price The price to adjust based on taxes * @param WC_Product $product The product the price belongs too (needed to determine tax class) * @since 1.0 */ public static function calculate_tax_for_subscription($price, $product, $deduct_base_taxes = false) { _deprecated_function(__METHOD__, '1.5.8', 'WC_Product::get_price_including_tax()'); if ($product->is_taxable()) { $tax = new WC_Tax(); $base_tax_rates = $tax->get_shop_base_rate($product->tax_class); $tax_rates = $tax->get_rates($product->get_tax_class()); // This will get the base rate unless we're on the checkout page if ($deduct_base_taxes && get_option('woocommerce_prices_include_tax') == 'yes') { $base_taxes = $tax->calc_tax($price, $base_tax_rates, true); $taxes = $tax->calc_tax($price - array_sum($base_taxes), $tax_rates, false); } elseif (get_option('woocommerce_prices_include_tax') == 'yes') { $taxes = $tax->calc_tax($price, $base_tax_rates, true); } else { $taxes = $tax->calc_tax($price, $base_tax_rates, false); } $tax_amount = $tax->get_tax_total($taxes); } else { $tax_amount = 0; } return $tax_amount; }
/** * Price Filter post filter. * * @param array $filtered_posts * @return array */ public function price_filter($filtered_posts = array()) { global $wpdb; if (isset($_GET['max_price']) || isset($_GET['min_price'])) { $matched_products = array(); $min = isset($_GET['min_price']) ? floatval($_GET['min_price']) : 0; $max = isset($_GET['max_price']) ? floatval($_GET['max_price']) : 9999999999; // If displaying prices in the shop including taxes, but prices don't include taxes.. if (wc_tax_enabled() && 'incl' === get_option('woocommerce_tax_display_shop') && !wc_prices_include_tax()) { $tax_classes = array_merge(array(''), WC_Tax::get_tax_classes()); foreach ($tax_classes as $tax_class) { $tax_rates = WC_Tax::get_rates($tax_class); $min_class = $min - WC_Tax::get_tax_total(WC_Tax::calc_inclusive_tax($min, $tax_rates)); $max_class = $max - WC_Tax::get_tax_total(WC_Tax::calc_inclusive_tax($max, $tax_rates)); $matched_products_query = apply_filters('woocommerce_price_filter_results', $wpdb->get_results($wpdb->prepare("\n\t\t\t\t\t\tSELECT DISTINCT ID, post_parent, post_type FROM {$wpdb->posts}\n\t\t\t\t\t\tINNER JOIN {$wpdb->postmeta} pm1 ON ID = pm1.post_id\n\t\t\t\t\t\tINNER JOIN {$wpdb->postmeta} pm2 ON ID = pm2.post_id\n\t\t\t\t\t\tWHERE post_type IN ( 'product', 'product_variation' )\n\t\t\t\t\t\tAND post_status = 'publish'\n\t\t\t\t\t\tAND pm1.meta_key IN ('" . implode("','", array_map('esc_sql', apply_filters('woocommerce_price_filter_meta_keys', array('_price')))) . "')\n\t\t\t\t\t\tAND pm1.meta_value BETWEEN %f AND %f\n\t\t\t\t\t\tAND pm2.meta_key = '_tax_class'\n\t\t\t\t\t\tAND pm2.meta_value = %s\n\t\t\t\t\t", $min_class, $max_class, sanitize_title($tax_class)), OBJECT_K), $min_class, $max_class); if ($matched_products_query) { foreach ($matched_products_query as $product) { if ($product->post_type == 'product') { $matched_products[] = $product->ID; } if ($product->post_parent > 0) { $matched_products[] = $product->post_parent; } } } } } else { $matched_products_query = apply_filters('woocommerce_price_filter_results', $wpdb->get_results($wpdb->prepare("\n\t\t\t\t\tSELECT DISTINCT ID, post_parent, post_type FROM {$wpdb->posts}\n\t\t\t\t\tINNER JOIN {$wpdb->postmeta} pm1 ON ID = pm1.post_id\n\t\t\t\t\tWHERE post_type IN ( 'product', 'product_variation' )\n\t\t\t\t\tAND post_status = 'publish'\n\t\t\t\t\tAND pm1.meta_key IN ('" . implode("','", array_map('esc_sql', apply_filters('woocommerce_price_filter_meta_keys', array('_price')))) . "')\n\t\t\t\t\tAND pm1.meta_value BETWEEN %d AND %d\n\t\t\t\t", $min, $max), OBJECT_K), $min, $max); if ($matched_products_query) { foreach ($matched_products_query as $product) { if ($product->post_type == 'product') { $matched_products[] = $product->ID; } if ($product->post_parent > 0) { $matched_products[] = $product->post_parent; } } } } $matched_products = array_unique($matched_products); // Filter the id's if (0 === sizeof($filtered_posts)) { $filtered_posts = $matched_products; } else { $filtered_posts = array_intersect($filtered_posts, $matched_products); } $filtered_posts[] = 0; } return (array) $filtered_posts; }
/** * Returns the price (including tax). Uses customer tax rates. Can work for a specific $qty for more accurate taxes. * * @param string $price to calculate, left blank to just use get_price() * @return string */ public function get_price_including_tax($qty = 1, $price = '') { if ($price === '') { $price = $this->get_price(); } if ($this->is_taxable()) { if (get_option('woocommerce_prices_include_tax') === 'no') { $tax_rates = WC_Tax::get_rates($this->get_tax_class()); $taxes = WC_Tax::calc_tax($price * $qty, $tax_rates, false); $tax_amount = WC_Tax::get_tax_total($taxes); $price = round($price * $qty + $tax_amount, wc_get_price_decimals()); } else { $tax_rates = WC_Tax::get_rates($this->get_tax_class()); $base_tax_rates = WC_Tax::get_base_tax_rates($this->tax_class); if (!empty(WC()->customer) && WC()->customer->is_vat_exempt()) { $base_taxes = WC_Tax::calc_tax($price * $qty, $base_tax_rates, true); $base_tax_amount = array_sum($base_taxes); $price = round($price * $qty - $base_tax_amount, wc_get_price_decimals()); } elseif ($tax_rates !== $base_tax_rates) { $base_taxes = WC_Tax::calc_tax($price * $qty, $base_tax_rates, true); $modded_taxes = WC_Tax::calc_tax($price * $qty - array_sum($base_taxes), $tax_rates, false); $price = round($price * $qty - array_sum($base_taxes) + array_sum($modded_taxes), wc_get_price_decimals()); } else { $price = $price * $qty; } } } else { $price = $price * $qty; } return apply_filters('woocommerce_get_price_including_tax', $price, $qty, $this); }
/** * Get the tax rates/percentages for a given tax class * @param string $tax_class tax class slug * @return string $tax_rates imploded list of tax rates */ public function get_tax_rate($tax_class, $line_total, $line_tax, $line_tax_data = '') { if ($line_tax == 0) { return '-'; // no need to determine tax rate... } // first try the easy wc2.2 way, using line_tax_data if (!empty($line_tax_data) && isset($line_tax_data['total'])) { $tax_rates = array(); $line_taxes = $line_tax_data['total']; foreach ($line_taxes as $tax_id => $tax) { if (!empty($tax) && $tax != 0) { $tax_rates[] = $this->get_tax_rate_by_id($tax_id) . ' %'; } } $tax_rates = implode(' ,', $tax_rates); return $tax_rates; } if (version_compare(WOOCOMMERCE_VERSION, '2.1') >= 0 && !apply_filters('wpo_wcpdf_calculate_tax_rate', false)) { // WC 2.1 or newer is used // if (empty($tax_class)) // $tax_class = 'standard';// does not appear to work anymore - get_rates does accept an empty tax_class though! $tax = new WC_Tax(); $taxes = $tax->get_rates($tax_class); $tax_rates = array(); foreach ($taxes as $tax) { $tax_rates[$tax['label']] = round($tax['rate'], 2) . ' %'; } if (empty($tax_rates)) { // one last try: manually calculate if ($line_total != 0) { $tax_rates[] = round($line_tax / $line_total * 100, 1) . ' %'; } else { $tax_rates[] = '-'; } } $tax_rates = implode(' ,', $tax_rates); } else { // Backwards compatibility/fallback: calculate tax from line items if ($line_total != 0) { $tax_rates = round($line_tax / $line_total * 100, 1) . ' %'; } else { $tax_rates = '-'; } } return $tax_rates; }