public function calculate_taxes($cart = null, $packages = null, $return_packages = false)
 {
     global $woocommerce;
     if (get_option('woocommerce_calc_taxes', 0) != 'yes') {
         if ($return_packages) {
             return $packages;
         }
         return;
     }
     $default_shipping_location = array($woocommerce->customer->get_shipping_country(), $woocommerce->customer->get_shipping_state(), $woocommerce->customer->get_shipping_postcode());
     $merge = false;
     if (!is_object($cart)) {
         $cart = $woocommerce->cart;
         $merge = true;
     }
     if (isset($_POST['action']) && $_POST['action'] == 'woocommerce_update_shipping_method') {
         return $cart;
     }
     if (!$packages) {
         $packages = $cart->get_shipping_packages();
     }
     if (count($packages) < 2) {
         return;
     }
     // clear the taxes arrays remove tax totals from the grand total
     $old_taxes = $cart->taxes;
     $old_tax_total = $cart->tax_total;
     $old_shipping_taxes = $cart->shipping_taxes;
     $old_shipping_tax_total = $cart->shipping_tax_total;
     $old_total = $cart->total;
     $cart_total_without_taxes = $old_total - ($old_tax_total + $old_shipping_tax_total);
     // deduct taxes from the subtotal
     $cart->subtotal -= $old_tax_total;
     $item_taxes = array();
     $cart_taxes = array();
     foreach ($packages as $idx => $package) {
         if (isset($package['destination']) && !$this->wcms->is_address_empty($package['destination'])) {
             $woocommerce->customer->calculated_shipping(true);
             $woocommerce->customer->set_shipping_location($package['destination']['country'], $package['destination']['state'], $package['destination']['postcode']);
         }
         $tax_rates = array();
         $shop_tax_rates = array();
         /**
          * Calculate subtotals for items. This is done first so that discount logic can use the values.
          */
         foreach ($package['contents'] as $cart_item_key => $values) {
             $_product = $values['data'];
             // Prices
             $line_price = $_product->get_price() * $values['quantity'];
             $line_subtotal = 0;
             $line_subtotal_tax = 0;
             if (!$_product->is_taxable()) {
                 $line_subtotal = $line_price;
             } elseif ($cart->prices_include_tax) {
                 // Get base tax rates
                 if (empty($shop_tax_rates[$_product->tax_class])) {
                     $shop_tax_rates[$_product->tax_class] = $cart->tax->get_shop_base_rate($_product->tax_class);
                 }
                 // Get item tax rates
                 if (empty($tax_rates[$_product->get_tax_class()])) {
                     $tax_rates[$_product->get_tax_class()] = $cart->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 = $cart->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 modifed taxes
                     $tax_result = $cart->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 = $cart->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_MS_Compatibility::get_tax_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_MS_Compatibility::calc_tax($line_price, $item_tax_rates);
                 $line_subtotal_tax = array_sum($taxes);
                 $line_subtotal = $line_price;
             }
             // Add to main subtotal
             $cart->subtotal += $line_subtotal_tax;
         }
         /**
          * Calculate totals for items
          */
         foreach ($package['contents'] 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();
             if (!$_product->is_taxable()) {
                 // Discounted Price (price with any pre-tax discounts applied)
                 $discounted_price = $cart->get_discounted_price($values, $base_price, true);
                 $line_subtotal_tax = 0;
                 $line_subtotal = $line_price;
                 $line_tax = 0;
                 $line_total = WC_MS_Compatibility::round_tax($discounted_price * $values['quantity']);
                 /**
                  * Prices include tax
                  */
             } elseif ($cart->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 = $cart->tax->calc_tax($line_price, $base_tax_rates, true, true);
                     // Now we have a new item price (excluding TAX)
                     $line_subtotal = woocommerce_round_tax_total($line_price - array_sum($taxes));
                     // Now add modifed taxes
                     $taxes = $cart->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 = $cart->get_discounted_price($values, $adjusted_price, true);
                     $discounted_taxes = $cart->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 shop's base tax
                     $taxes = $cart->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 = $cart->get_discounted_price($values, $base_price, true);
                     $discounted_taxes = $cart->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($cart_taxes + $discounted_taxes) as $key) {
                     $cart_taxes[$key] = (isset($discounted_taxes[$key]) ? $discounted_taxes[$key] : 0) + (isset($cart_taxes[$key]) ? $cart_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_MS_Compatibility::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 = $cart->get_discounted_price($values, $base_price, true);
                 $discounted_taxes = WC_MS_Compatibility::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($cart_taxes + $discounted_taxes) as $key) {
                     $cart_taxes[$key] = (isset($discounted_taxes[$key]) ? $discounted_taxes[$key] : 0) + (isset($cart_taxes[$key]) ? $cart_taxes[$key] : 0);
                 }
             }
             // Store costs + taxes for lines
             if (!isset($item_taxes[$cart_item_key])) {
                 $item_taxes[$cart_item_key]['line_total'] = $line_total;
                 $item_taxes[$cart_item_key]['line_tax'] = $line_tax;
                 $item_taxes[$cart_item_key]['line_subtotal'] = $line_subtotal;
                 $item_taxes[$cart_item_key]['line_subtotal_tax'] = $line_subtotal_tax;
                 $item_taxes[$cart_item_key]['line_tax_data'] = array('total' => $discounted_taxes, 'subtotal' => $taxes);
             } else {
                 $item_taxes[$cart_item_key]['line_total'] += $line_total;
                 $item_taxes[$cart_item_key]['line_tax'] += $line_tax;
                 $item_taxes[$cart_item_key]['line_subtotal'] += $line_subtotal;
                 $item_taxes[$cart_item_key]['line_subtotal_tax'] += $line_subtotal_tax;
                 $item_taxes[$cart_item_key]['line_tax_data']['total'] += $discounted_taxes;
                 $item_taxes[$cart_item_key]['line_tax_data']['subtotal'] += $taxes;
             }
             $packages[$idx]['contents'][$cart_item_key]['line_total'] = $line_total;
             $packages[$idx]['contents'][$cart_item_key]['line_tax'] = $line_tax;
             $packages[$idx]['contents'][$cart_item_key]['line_subtotal'] = $line_subtotal;
             $packages[$idx]['contents'][$cart_item_key]['line_subtotal_tax'] = $line_subtotal_tax;
             $packages[$idx]['contents'][$cart_item_key]['line_tax_data'] = array('total' => $discounted_taxes, 'subtotal' => $taxes);
         }
     }
     foreach ($item_taxes as $cart_item_key => $taxes) {
         if (!isset($cart->cart_contents[$cart_item_key])) {
             continue;
         }
         $product_id = $cart->cart_contents[$cart_item_key]['product_id'];
         $woocommerce->cart->recurring_cart_contents = array();
         $cart->cart_contents[$cart_item_key]['line_total'] = $taxes['line_total'];
         $cart->cart_contents[$cart_item_key]['line_tax'] = $taxes['line_tax'];
         $cart->cart_contents[$cart_item_key]['line_subtotal'] = $taxes['line_subtotal'];
         $cart->cart_contents[$cart_item_key]['line_subtotal_tax'] = $taxes['line_subtotal_tax'];
         $cart->cart_contents[$cart_item_key]['line_tax_data'] = $taxes['line_tax_data'];
         // Set recurring taxes for subscription products
         if (class_exists('WC_Subscriptions_Product') && WC_Subscriptions_Product::is_subscription($product_id)) {
             $woocommerce->cart->recurring_cart_contents[$product_id]['recurring_line_total'] = $taxes['line_total'];
             $woocommerce->cart->recurring_cart_contents[$product_id]['recurring_line_tax'] = $taxes['line_tax'];
             $woocommerce->cart->recurring_cart_contents[$product_id]['recurring_line_subtotal'] = $taxes['line_subtotal'];
             $woocommerce->cart->recurring_cart_contents[$product_id]['recurring_line_subtotal_tax'] = $taxes['line_subtotal_tax'];
         }
     }
     // Total up/round taxes and shipping taxes
     if ($cart->round_at_subtotal) {
         $cart->tax_total = WC_MS_Compatibility::get_tax_total($cart_taxes);
         $cart->taxes = array_map('WC_MS_Compatibility::round_tax', $cart_taxes);
     } else {
         $cart->tax_total = array_sum($cart_taxes);
         $cart->taxes = array_map('WC_MS_Compatibility::round_tax', $cart_taxes);
     }
     if ($merge) {
         $woocommerce->cart = $cart;
     }
     // Setting an empty default customer shipping location prevents
     // subtotal calculation from applying the incorrect taxes based
     // on the shipping address. But do not remove the shipping country
     // to satisfy the validation done on WC_Checkout
     $woocommerce->customer->calculated_shipping(false);
     $woocommerce->customer->set_shipping_location($woocommerce->customer->get_shipping_country(), '', '');
     if ($return_packages) {
         return $packages;
     }
     // store the modified packages array
     wcms_session_set('wcms_packages', $packages);
     return $cart;
 }
 /**
  * Round to precision
  * @param float $in
  * @return float
  */
 public static function round_tax($in)
 {
     if (function_exists('wc_round_tax_total')) {
         return wc_round_tax_total($in);
     } else {
         return woocommerce_round_tax_total($in);
     }
 }