get_base_tax_rates() public static method

Get's an array of matching rates for the shop's base country.
public static get_base_tax_rates ( $tax_class = '' ) : array
return array
示例#1
0
 /**
  * Get rates.
  */
 public function test_get_base_tax_rates()
 {
     global $wpdb;
     $wpdb->query("DELETE FROM {$wpdb->prefix}woocommerce_tax_rates");
     $wpdb->query("DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations");
     $tax_rate = array('tax_rate_country' => 'GB', '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_base_tax_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);
 }
 /**
  * Returns base tax rates for all tax classes
  * @return array
  */
 public static function tax_rates()
 {
     $rates = array();
     foreach (self::tax_classes() as $class => $label) {
         if ($rate = WC_Tax::get_base_tax_rates($class)) {
             // WC_Tax returns a assoc array with int as keys = world of pain in js
             // possibly change $key to $rate['id']
             $rates[$class] = $rate;
         }
     }
     return $rates;
 }
 /**
  * Returns the price (excluding tax) - ignores tax_class filters since the price may *include* tax and thus needs subtracting.
  * Uses store base 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_excluding_tax($qty = 1, $price = '')
 {
     if ($price === '') {
         $price = $this->get_price();
     }
     if ($this->is_taxable() && get_option('woocommerce_prices_include_tax') === 'yes') {
         $tax_rates = WC_Tax::get_base_tax_rates($this->tax_class);
         $taxes = WC_Tax::calc_tax($price * $qty, $tax_rates, true);
         $price = WC_Tax::round($price * $qty - array_sum($taxes));
     } else {
         $price = $price * $qty;
     }
     return apply_filters('woocommerce_get_price_excluding_tax', $price, $qty, $this);
 }
示例#4
0
 /**
  * Calculate totals for the items in the cart.
  */
 public function calculate_totals()
 {
     $this->reset();
     $this->coupons = $this->get_coupons();
     do_action('woocommerce_before_calculate_totals', $this);
     if ($this->is_empty()) {
         $this->set_session();
         return;
     }
     $tax_rates = array();
     $shop_tax_rates = array();
     $cart = $this->get_cart();
     /**
      * Calculate subtotals for items. This is done first so that discount logic can use the values.
      */
     foreach ($cart as $cart_item_key => $values) {
         $_product = $values['data'];
         $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.
              *
              * 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.
              */
             if ($item_tax_rates !== $base_tax_rates && apply_filters('woocommerce_adjust_non_base_location_prices', true)) {
                 // Work out a new base price without the shop's base tax
                 $taxes = WC_Tax::calc_tax($line_price, $base_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;
     }
     // Order cart items by price so coupon logic is 'fair' for customers and not based on order added to cart.
     uasort($cart, array($this, 'sort_by_subtotal'));
     /**
      * Calculate totals for items.
      */
     foreach ($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 = round($discounted_price * $values['quantity'], wc_get_rounding_precision());
             /**
              * 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.
              *
              * 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.
              */
             if ($item_tax_rates !== $base_tax_rates && apply_filters('woocommerce_adjust_non_base_location_prices', true)) {
                 // Work out a new base price without the shop's base tax
                 $taxes = WC_Tax::calc_tax($line_price, $base_tax_rates, true, true);
                 // Now we have a new item price (excluding TAX)
                 $line_subtotal = round($line_price - array_sum($taxes), wc_get_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 and get the discounted price FOR A SINGLE ITEM
                 $discounted_price = $this->get_discounted_price($values, $adjusted_price, true);
                 // Convert back to line price
                 $discounted_line_price = $discounted_price * $values['quantity'];
                 // Now use rounded line price to get taxes.
                 $discounted_taxes = WC_Tax::calc_tax($discounted_line_price, $item_tax_rates, true);
                 $line_tax = array_sum($discounted_taxes);
                 $line_total = $discounted_line_price - $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);
                 // Convert back to line price
                 $discounted_line_price = $discounted_price * $values['quantity'];
                 // Now use rounded line price to get taxes.
                 $discounted_taxes = WC_Tax::calc_tax($discounted_line_price, $item_tax_rates, true);
                 $line_tax = array_sum($discounted_taxes);
                 $line_total = $discounted_line_price - $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. For tax inclusive prices, we do some extra rounding logic so the stored
          * values "add up" when viewing the order in admin. This does have the disadvatage of not being able to
          * recalculate the tax total/subtotal accurately in the future, but it does ensure the data looks correct.
          *
          * Tax exclusive prices are not affected.
          */
         if (!$_product->is_taxable() || $this->prices_include_tax) {
             $this->cart_contents[$cart_item_key]['line_total'] = round($line_total + $line_tax - wc_round_tax_total($line_tax), $this->dp);
             $this->cart_contents[$cart_item_key]['line_subtotal'] = round($line_subtotal + $line_subtotal_tax - wc_round_tax_total($line_subtotal_tax), $this->dp);
             $this->cart_contents[$cart_item_key]['line_tax'] = wc_round_tax_total($line_tax);
             $this->cart_contents[$cart_item_key]['line_subtotal_tax'] = wc_round_tax_total($line_subtotal_tax);
             $this->cart_contents[$cart_item_key]['line_tax_data'] = array('total' => array_map('wc_round_tax_total', $discounted_taxes), 'subtotal' => array_map('wc_round_tax_total', $taxes));
         } else {
             $this->cart_contents[$cart_item_key]['line_total'] = $line_total;
             $this->cart_contents[$cart_item_key]['line_subtotal'] = $line_subtotal;
             $this->cart_contents[$cart_item_key]['line_tax'] = $line_tax;
             $this->cart_contents[$cart_item_key]['line_subtotal_tax'] = $line_subtotal_tax;
             $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->get_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->get_is_vat_exempt()) {
             $this->remove_taxes();
         }
     }
     do_action('woocommerce_after_calculate_totals', $this);
     $this->set_session();
 }
示例#5
0
 /**
  * Calculate totals for the items in the cart.
  */
 public function calculate_totals()
 {
     $this->reset();
     $this->coupons = $this->get_coupons();
     do_action('woocommerce_before_calculate_totals', $this);
     if ($this->is_empty()) {
         $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);
                 // 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);
                 // 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);
     }
     // Round cart contents
     $this->cart_contents_total = round($this->cart_contents_total, $this->dp);
     // 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);
     $this->set_session();
 }
/**
 * For a given product, and optionally price/qty, work out the price with tax excluded, based on store settings.
 * @since  2.7.0
 * @param  WC_Product $product
 * @param  array $args
 * @return float
 */
function wc_get_price_excluding_tax($product, $args = array())
{
    $args = wp_parse_args($args, array('qty' => '', 'price' => ''));
    $price = $args['price'] ? $args['price'] : $product->get_price();
    $qty = $args['qty'] ? $args['qty'] : 1;
    if ($product->is_taxable() && wc_prices_include_tax()) {
        $tax_rates = WC_Tax::get_base_tax_rates($product->get_tax_class(true));
        $taxes = WC_Tax::calc_tax($price * $qty, $tax_rates, true);
        $price = WC_Tax::round($price * $qty - array_sum($taxes));
    } else {
        $price = $price * $qty;
    }
    return apply_filters('woocommerce_get_price_excluding_tax', $price, $qty, $product);
}
 /**
  * Output the form
  */
 public function output()
 {
     $this->errors = array();
     $step = 1;
     try {
         if (!empty($_POST) && !check_admin_referer('create_booking_notification')) {
             throw new Exception(__('Error - please try again', 'woocommerce-bookings'));
         }
         if (!empty($_POST['create_booking'])) {
             $customer_id = absint($_POST['customer_id']);
             $bookable_product_id = absint($_POST['bookable_product_id']);
             $booking_order = wc_clean($_POST['booking_order']);
             if (!$bookable_product_id) {
                 throw new Exception(__('Please choose a bookable product', 'woocommerce-bookings'));
             }
             if ($booking_order === 'existing') {
                 $order_id = absint($_POST['booking_order_id']);
                 $booking_order = $order_id;
                 if (!$booking_order || get_post_type($booking_order) !== 'shop_order') {
                     throw new Exception(__('Invalid order ID provided', 'woocommerce-bookings'));
                 }
             }
             $step++;
             $product = get_product($bookable_product_id);
             $booking_form = new WC_Booking_Form($product);
         } elseif (!empty($_POST['create_booking_2'])) {
             $customer_id = absint($_POST['customer_id']);
             $bookable_product_id = absint($_POST['bookable_product_id']);
             $booking_order = wc_clean($_POST['booking_order']);
             $product = get_product($bookable_product_id);
             $booking_form = new WC_Booking_Form($product);
             $booking_data = $booking_form->get_posted_data($_POST);
             $booking_cost = ($cost = $booking_form->calculate_booking_cost($_POST)) && !is_wp_error($cost) ? number_format($cost, 2, '.', '') : 0;
             $create_order = false;
             if ('yes' === get_option('woocommerce_prices_include_tax')) {
                 if (version_compare(WOOCOMMERCE_VERSION, '2.3', '<')) {
                     $base_tax_rates = WC_Tax::get_shop_base_rate($product->tax_class);
                 } else {
                     $base_tax_rates = WC_Tax::get_base_tax_rates($product->tax_class);
                 }
                 $base_taxes = WC_Tax::calc_tax($booking_cost, $base_tax_rates, true);
                 $booking_cost = round($booking_cost - array_sum($base_taxes), absint(get_option('woocommerce_price_num_decimals')));
             }
             // Data to go into the booking
             $new_booking_data = array('user_id' => $customer_id, 'product_id' => $product->id, 'resource_id' => isset($booking_data['_resource_id']) ? $booking_data['_resource_id'] : '', 'persons' => $booking_data['_persons'], 'cost' => $booking_cost, 'start_date' => $booking_data['_start_date'], 'end_date' => $booking_data['_end_date'], 'all_day' => $booking_data['_all_day'] ? 1 : 0);
             // Create order
             if ($booking_order === 'new') {
                 $create_order = true;
                 $order_id = $this->create_order($booking_cost, $customer_id);
                 if (!$order_id) {
                     throw new Exception(__('Error: Could not create order', 'woocommerce-bookings'));
                 }
             } elseif ($booking_order > 0) {
                 $order_id = absint($booking_order);
                 if (!$order_id || get_post_type($order_id) !== 'shop_order') {
                     throw new Exception(__('Invalid order ID provided', 'woocommerce-bookings'));
                 }
                 $order = new WC_Order($order_id);
                 update_post_meta($order_id, '_order_total', $order->get_total() + $booking_cost);
                 update_post_meta($order_id, '_booking_order', '1');
             } else {
                 $order_id = 0;
             }
             if ($order_id) {
                 $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $product->get_title(), 'order_item_type' => 'line_item'));
                 if (!$item_id) {
                     throw new Exception(__('Error: Could not create item', 'woocommerce-bookings'));
                 }
                 // Add line item meta
                 woocommerce_add_order_item_meta($item_id, '_qty', 1);
                 woocommerce_add_order_item_meta($item_id, '_tax_class', $product->get_tax_class());
                 woocommerce_add_order_item_meta($item_id, '_product_id', $product->id);
                 woocommerce_add_order_item_meta($item_id, '_variation_id', '');
                 woocommerce_add_order_item_meta($item_id, '_line_subtotal', $booking_cost);
                 woocommerce_add_order_item_meta($item_id, '_line_total', $booking_cost);
                 woocommerce_add_order_item_meta($item_id, '_line_tax', 0);
                 woocommerce_add_order_item_meta($item_id, '_line_subtotal_tax', 0);
                 // We have an item id
                 $new_booking_data['order_item_id'] = $item_id;
                 // Add line item data
                 foreach ($booking_data as $key => $value) {
                     if (strpos($key, '_') !== 0) {
                         woocommerce_add_order_item_meta($item_id, get_wc_booking_data_label($key, $product), $value);
                     }
                 }
             }
             // Create the booking itself
             $new_booking = get_wc_booking($new_booking_data);
             $new_booking->create($create_order ? 'unpaid' : 'pending-confirmation');
             wp_safe_redirect(admin_url('post.php?post=' . ($create_order ? $order_id : $new_booking->id) . '&action=edit'));
             exit;
         }
     } catch (Exception $e) {
         $this->errors[] = $e->getMessage();
     }
     switch ($step) {
         case 1:
             include 'views/html-create-booking-page.php';
             break;
         case 2:
             include 'views/html-create-booking-page-2.php';
             break;
     }
 }
 /**
  * Restore renewal flag when cart is reset and modify Product object with renewal order related info
  *
  * @since 2.0
  */
 public function get_cart_item_from_session($cart_item_session_data, $cart_item, $key)
 {
     if (isset($cart_item[$this->cart_item_key]['subscription_id'])) {
         $cart_item_session_data[$this->cart_item_key] = $cart_item[$this->cart_item_key];
         $_product = $cart_item_session_data['data'];
         // Need to get the original subscription price, not the current price
         $subscription = wcs_get_subscription($cart_item[$this->cart_item_key]['subscription_id']);
         if ($subscription) {
             $subscription_items = $subscription->get_items();
             $item_to_renew = $subscription_items[$cart_item_session_data[$this->cart_item_key]['subscription_line_item_id']];
             $price = $item_to_renew['line_subtotal'];
             if (wc_prices_include_tax()) {
                 $base_tax_rates = WC_Tax::get_base_tax_rates($_product->tax_class);
                 $base_taxes_on_item = WC_Tax::calc_tax($price, $base_tax_rates, false, false);
                 $price += array_sum($base_taxes_on_item);
             }
             $_product->price = $price / $item_to_renew['qty'];
             // Don't carry over any sign up fee
             $_product->subscription_sign_up_fee = 0;
             $_product->post->post_title = apply_filters('woocommerce_subscriptions_renewal_product_title', $_product->get_title(), $_product);
             // Make sure the same quantity is renewed
             $cart_item_session_data['quantity'] = $item_to_renew['qty'];
         }
     }
     return $cart_item_session_data;
 }
 /**
  * Build the transaction lines
  *
  * @param array $items
  * @param String $country
  *
  * @return array
  */
 private function build_transaction_lines($items, $country)
 {
     // Transaction lines
     $transaction_lines = array();
     // Lo0p
     if (count($items) > 0) {
         // Country manager
         $country_manager = new WC_TA_Country_Manager();
         foreach ($items as $item_key => $item) {
             if (isset($item['data'])) {
                 // The transaction line
                 $transaction_line = array();
                 $product = $item['data'];
                 // Set the product type
                 $type = 'default';
                 // Check if this is a virtual product
                 if ($product->is_virtual()) {
                     $type = 'e-service';
                 } else {
                     $transaction_line['informative'] = true;
                 }
                 // Check if this is an e-book
                 $is_ebook = get_post_meta($item['id'], '_ebook', true);
                 if ('yes' === $is_ebook) {
                     $type = 'e-book';
                 }
                 // Check if Taxamo supports taxes for this country
                 if (false === $country_manager->is_tax_supported_for_country($country)) {
                     // Taxes aren't supported, set the line to informative
                     $transaction_line['informative'] = true;
                 }
                 // Set the product type
                 $transaction_line['product_type'] = $type;
                 // Custom ID
                 $transaction_line['custom_id'] = "" . $item_key;
                 // Quantity
                 $transaction_line['quantity'] = $item['quantity'];
                 // Price
                 if ($product->is_taxable()) {
                     // Always without tax
                     $transaction_line['amount'] = $item['line_total'];
                     // Get the base tax rates
                     if (method_exists('WC_Tax', 'get_base_tax_rates')) {
                         $base_tax_rates = WC_Tax::get_base_tax_rates($product->get_tax_class());
                     } else {
                         $base_tax_rates = WC_Tax::get_shop_base_rate($product->get_tax_class());
                     }
                     $base_tax_rate = array_shift($base_tax_rates);
                     // Set the tax rate
                     $transaction_line['tax_rate'] = $base_tax_rate['rate'];
                 } else {
                     // Set Price
                     $transaction_line['amount'] = $product->get_price();
                     // Set 0 tax rate
                     $transaction_line['tax_rate'] = 0;
                     // Force non taxable
                     $transaction_line['informative'] = true;
                 }
                 // What do we want? Floats! When do we want them? Now!
                 // Check if amount is set
                 if (isset($transaction_line['amount'])) {
                     $transaction_line['amount'] = floatval($transaction_line['amount']);
                 }
                 // Check if total_amount is set
                 if (isset($transaction_line['total_amount'])) {
                     $transaction_line['total_amount'] = floatval($transaction_line['total_amount']);
                 }
                 // Float the tax rate
                 $transaction_line['tax_rate'] = floatval($transaction_line['tax_rate']);
                 // Add transaction line to transaction lines
                 $transaction_lines[] = $transaction_line;
             }
         }
     }
     return $transaction_lines;
 }