Calculate tax for a line.
public static calc_tax ( float $price, array $rates, boolean $price_includes_tax = false, boolean $suppress_rounding = false ) : array | ||
$price | float | Price to calc tax on |
$rates | array | Rates to apply |
$price_includes_tax | boolean | Whether the passed price has taxes included |
$suppress_rounding | boolean | Whether to suppress any rounding from taking place |
return | array | Array of rates + prices after tax |
/** * 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); }
public static function woocommerce_get_price($price, $product) { global $post, $woocommerce; $baseprice = $price; $result = $baseprice; if ($post == null || !is_admin()) { if ($product->is_type('variation')) { $commission = WRP_Variations_Admin::get_commission($product, $product->variation_id); } else { $commission = self::get_commission($product); } if ($commission) { $baseprice = $product->get_regular_price(); if ($product->get_sale_price() != $product->get_regular_price() && $product->get_sale_price() == $product->price) { if (get_option("wrp-baseprice", "regular") == "sale") { $baseprice = $product->get_sale_price(); } } $product_price = $baseprice; $type = get_option("wrp-method", "rate"); $result = 0; if ($type == "rate") { // if rate and price includes taxes if ($product->is_taxable() && get_option('woocommerce_prices_include_tax') == 'yes') { $_tax = new WC_Tax(); $tax_rates = $_tax->get_shop_base_rate($product->tax_class); $taxes = $_tax->calc_tax($baseprice, $tax_rates, true); $product_price = $_tax->round($baseprice - array_sum($taxes)); } $result = self::bcmul($product_price, $commission, WOO_ROLE_PRICING_DECIMALS); if ($product->is_taxable() && get_option('woocommerce_prices_include_tax') == 'yes') { $_tax = new WC_Tax(); $tax_rates = $_tax->get_shop_base_rate($product->tax_class); $taxes = $_tax->calc_tax($result, $tax_rates, false); // important false $result = $_tax->round($result + array_sum($taxes)); } } else { $result = self::bcsub($product_price, $commission, WOO_ROLE_PRICING_DECIMALS); } } } return $result; }
public static function woocommerce_get_price($price, $product) { global $post, $woocommerce; $baseprice = $price; $result = $baseprice; if ($post == null || !is_admin()) { $roleprice = self::get_role_price($product); if (empty($roleprice)) { $regularprice = $product->get_regular_price(); if (!empty($regularprice)) { $baseprice = $regularprice; } if ($product->get_sale_price() != $product->get_regular_price() && $product->get_sale_price() == $product->price) { if (get_option("wrp-baseprice", "regular") == "sale") { $baseprice = $product->get_sale_price(); } } $product_price = $baseprice; } else { $baseprice = $roleprice; $product_price = $roleprice; } $result = 0; if ($product->is_taxable() && get_option('woocommerce_prices_include_tax') == 'yes') { $_tax = new WC_Tax(); $tax_rates = $_tax->get_shop_base_rate($product->tax_class); $taxes = $_tax->calc_tax($baseprice, $tax_rates, true); $product_price = $_tax->round($baseprice - array_sum($taxes)); } $result = $product_price; if ($product->is_taxable() && get_option('woocommerce_prices_include_tax') == 'yes') { $_tax = new WC_Tax(); $tax_rates = $_tax->get_shop_base_rate($product->tax_class); $taxes = $_tax->calc_tax($result, $tax_rates, false); // important false $result = $_tax->round($result + array_sum($taxes)); } } return $result; }
/** * 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); // Store $fee->tax = array_sum($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); } } } } }
public function __construct() { $this->order = new WC_Order(); $this->type = 'preview'; $this->number = get_option('wc_gzdp_invoice_simple') + 1; $this->recipient = array('firstname' => _x('Max', 'invoices-preview', 'woocommerce-germanized-pro'), 'lastname' => _x('Mustermann', 'invoices-preview', 'woocommerce-germanized-pro')); $this->address = implode('<br/>', array(_x('Max Mustermann', 'invoices-preview', 'woocommerce-germanized-pro'), _x('Musterstraße 35', 'invoices-preview', 'woocommerce-germanized-pro'), _x('12209 Musterstadt', 'invoices-preview', 'woocommerce-germanized-pro'))); $base_rate = WC_Tax::get_shop_base_rate(); $base_rate_id = key($base_rate); $calc_tax = true; if (get_option('woocommerce_gzd_small_enterprise') == 'yes') { $calc_tax = false; } $this->items = array(); $total = 0; $total_tax = 0; $shipping = 0; for ($i = 0; $i < 30; $i++) { $qty = 1; $subtotal_gross = 35.95; $subtotal_tax = $calc_tax ? array_sum(WC_Tax::calc_tax($subtotal_gross, $base_rate, true)) : 0; $subtotal_net = $subtotal_gross - $subtotal_tax; $subtotal = $subtotal_net; $item_total = $qty * $subtotal; $item_tax = $qty * $subtotal_tax; $item = array('name' => _x('Testprodukt', 'invoices-preview', 'woocommerce-germanized-pro'), 'item_meta' => array(), 'type' => 'line_item', 'qty' => $qty, 'line_total' => $item_total, 'line_subtotal' => $subtotal, 'line_tax' => $item_tax, 'line_subtotal_tax' => $subtotal_tax); $total += $subtotal_gross; $total_tax += $item_tax; array_push($this->items, $item); } $this->totals = array('subtotal' => $total - $shipping - $total_tax, 'total' => $total, 'shipping' => $shipping, 'tax' => $total_tax, 'discount' => 0); if ($calc_tax) { $tax_totals = array('amount' => $total_tax, 'rate_id' => $base_rate_id, 'formatted_amount' => wc_price($total_tax)); $this->tax_totals = array(); $this->tax_totals['DE'] = (object) $tax_totals; } }
/** * 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; } } } }
/** * Calc line tax. */ public static function calc_line_taxes() { global $wpdb; check_ajax_referer('calc-totals', 'security'); if (!current_user_can('edit_shop_orders')) { die(-1); } $tax = new WC_Tax(); $tax_based_on = get_option('woocommerce_tax_based_on'); $order_id = absint($_POST['order_id']); $items = array(); $country = strtoupper(esc_attr($_POST['country'])); $state = strtoupper(esc_attr($_POST['state'])); $postcode = strtoupper(esc_attr($_POST['postcode'])); $city = wc_clean(esc_attr($_POST['city'])); $order = wc_get_order($order_id); $taxes = array(); $shipping_taxes = array(); $order_item_tax_classes = array(); // Default to base if ('base' === $tax_based_on || empty($country)) { $default = wc_get_base_location(); $country = $default['country']; $state = $default['state']; $postcode = ''; $city = ''; } // Parse the jQuery serialized items parse_str($_POST['items'], $items); // Prevent undefined warnings if (!isset($items['line_tax'])) { $items['line_tax'] = array(); } if (!isset($items['line_subtotal_tax'])) { $items['line_subtotal_tax'] = array(); } $items['order_taxes'] = array(); // Action $items = apply_filters('woocommerce_ajax_calc_line_taxes', $items, $order_id, $country, $_POST); $is_vat_exempt = get_post_meta($order_id, '_is_vat_exempt', true); // Tax is calculated only if tax is enabled and order is not vat exempted if (wc_tax_enabled() && $is_vat_exempt !== 'yes') { // Get items and fees taxes if (isset($items['order_item_id'])) { $line_total = $line_subtotal = array(); foreach ($items['order_item_id'] as $item_id) { $item_id = absint($item_id); $line_total[$item_id] = isset($items['line_total'][$item_id]) ? wc_format_decimal($items['line_total'][$item_id]) : 0; $line_subtotal[$item_id] = isset($items['line_subtotal'][$item_id]) ? wc_format_decimal($items['line_subtotal'][$item_id]) : $line_total[$item_id]; $order_item_tax_classes[$item_id] = isset($items['order_item_tax_class'][$item_id]) ? sanitize_text_field($items['order_item_tax_class'][$item_id]) : ''; $product_id = $order->get_item_meta($item_id, '_product_id', true); // Get product details if (get_post_type($product_id) == 'product') { $_product = wc_get_product($product_id); $item_tax_status = $_product->get_tax_status(); } else { $item_tax_status = 'taxable'; } if ('0' !== $order_item_tax_classes[$item_id] && 'taxable' === $item_tax_status) { $tax_rates = WC_Tax::find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $order_item_tax_classes[$item_id])); $line_taxes = WC_Tax::calc_tax($line_total[$item_id], $tax_rates, false); $line_subtotal_taxes = WC_Tax::calc_tax($line_subtotal[$item_id], $tax_rates, false); // Set the new line_tax foreach ($line_taxes as $_tax_id => $_tax_value) { $items['line_tax'][$item_id][$_tax_id] = $_tax_value; } // Set the new line_subtotal_tax foreach ($line_subtotal_taxes as $_tax_id => $_tax_value) { $items['line_subtotal_tax'][$item_id][$_tax_id] = $_tax_value; } // Sum the item taxes foreach (array_keys($taxes + $line_taxes) as $key) { $taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($taxes[$key]) ? $taxes[$key] : 0); } } } } // Get shipping taxes if (isset($items['shipping_method_id'])) { $matched_tax_rates = array(); $order_item_tax_classes = array_unique(array_values($order_item_tax_classes)); // If multiple classes are found, use the first one. Don't bother with standard rate, we can get that later. if (sizeof($order_item_tax_classes) > 1 && !in_array('', $order_item_tax_classes)) { $tax_classes = WC_Tax::get_tax_classes(); foreach ($tax_classes as $tax_class) { $tax_class = sanitize_title($tax_class); if (in_array($tax_class, $order_item_tax_classes)) { $matched_tax_rates = WC_Tax::find_shipping_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $tax_class)); break; } } // If a single tax class is found, use it } elseif (sizeof($order_item_tax_classes) === 1) { $matched_tax_rates = WC_Tax::find_shipping_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $order_item_tax_classes[0])); } // Get standard rate if no taxes were found if (!sizeof($matched_tax_rates)) { $matched_tax_rates = WC_Tax::find_shipping_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city)); } $shipping_cost = $shipping_taxes = array(); foreach ($items['shipping_method_id'] as $item_id) { $item_id = absint($item_id); $shipping_cost[$item_id] = isset($items['shipping_cost'][$item_id]) ? wc_format_decimal($items['shipping_cost'][$item_id]) : 0; $_shipping_taxes = WC_Tax::calc_shipping_tax($shipping_cost[$item_id], $matched_tax_rates); // Set the new shipping_taxes foreach ($_shipping_taxes as $_tax_id => $_tax_value) { $items['shipping_taxes'][$item_id][$_tax_id] = $_tax_value; $shipping_taxes[$_tax_id] = isset($shipping_taxes[$_tax_id]) ? $shipping_taxes[$_tax_id] + $_tax_value : $_tax_value; } } } } // Remove old tax rows $order->remove_order_items('tax'); // Add tax rows foreach (array_keys($taxes + $shipping_taxes) as $tax_rate_id) { $order->add_tax($tax_rate_id, isset($taxes[$tax_rate_id]) ? $taxes[$tax_rate_id] : 0, isset($shipping_taxes[$tax_rate_id]) ? $shipping_taxes[$tax_rate_id] : 0); } // Create the new order_taxes foreach ($order->get_taxes() as $tax_id => $tax_item) { $items['order_taxes'][$tax_id] = absint($tax_item['rate_id']); } $items = apply_filters('woocommerce_ajax_after_calc_line_taxes', $items, $order_id, $country, $_POST); // Save order items wc_save_order_items($order_id, $items); // Return HTML items $order = wc_get_order($order_id); $data = get_post_meta($order_id); include 'admin/meta-boxes/views/html-order-items.php'; die; }
public function sync() { global $wp; global $wpdb; set_time_limit(0); @ini_set('display_errors', '1'); @ini_set('zlib.output_compression', 'Off'); @ini_set('output_buffering', 'Off'); @ini_set('output_handler', ''); while (ob_get_level() > 1) { @ob_end_clean(); } if (ob_get_level() > 0) { @ob_clean(); } if (!in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) { $this->sendHttpHeaders('500 Config Error', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode(array('ack' => 'failed', 'message' => 'WooCommerce Deactivated')); exit; } $type = $wp->query_vars['codisto-sync-route']; if (strtolower($_SERVER['REQUEST_METHOD']) == 'get') { if ($type == 'test' || $type == 'sync' && preg_match('/\\/sync\\/testHash\\?/', $_SERVER['REQUEST_URI'])) { if (!$this->check_hash()) { exit; } $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode(array('ack' => 'ok')); } else { if ($type === 'settings') { if (!$this->check_hash()) { exit; } $logo_url = get_header_image(); if (function_exists('site_logo')) { $logo = site_logo()->logo; $logo_id = get_theme_mod('custom_logo'); $logo_id = $logo_id ? $logo_id : $logo['id']; if ($logo_id) { $logo_url = wp_get_attachment_image_src($logo_id, 'full'); $logo_url = $logo_url[0]; } } $currency = get_option('woocommerce_currency'); $dimension_unit = get_option('woocommerce_dimension_unit'); $weight_unit = get_option('woocommerce_weight_unit'); $default_location = explode(':', get_option('woocommerce_default_country')); $country_code = isset($default_location[0]) ? $default_location[0] : ''; $state_code = isset($default_location[1]) ? $default_location[1] : ''; $response = array('ack' => 'ok', 'logo' => $logo_url, 'currency' => $currency, 'dimension_unit' => $dimension_unit, 'weight_unit' => $weight_unit, 'country_code' => $country_code, 'state_code' => $state_code); $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode($response); exit; } else { if ($type === 'tax') { if (!$this->check_hash()) { exit; } $tax_enabled = true; if (function_exists('wc_tax_enabled')) { $tax_enabled = wc_tax_enabled(); } else { $tax_enabled = get_option('woocommerce_calc_taxes') === 'yes'; } if ($tax_enabled) { $rates = $wpdb->get_results("SELECT tax_rate_country AS country, tax_rate_state AS state, tax_rate AS rate, tax_rate_name AS name, tax_rate_class AS class, tax_rate_order AS sequence, tax_rate_priority AS priority FROM `{$wpdb->prefix}woocommerce_tax_rates` ORDER BY tax_rate_order"); } else { $rates = array(); } $response = array('ack' => 'ok', 'tax_rates' => $rates); $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode($response); exit; } else { if ($type === 'products') { if (!$this->check_hash()) { exit; } $page = isset($_GET['page']) ? (int) $_GET['page'] : 0; $count = isset($_GET['count']) ? (int) $_GET['count'] : 0; $product_ids = isset($_GET['product_ids']) ? json_decode(wp_unslash($_GET['product_ids'])) : null; if (!is_null($product_ids)) { if (!is_array($product_ids)) { $product_ids = array($product_ids); } $product_ids = array_filter($product_ids, create_function('$v', 'return is_numeric($v);')); if (!isset($_GET['count'])) { $count = count($product_ids); } } $products = $wpdb->get_results($wpdb->prepare("SELECT id AS id " . "FROM `{$wpdb->prefix}posts` AS P " . "WHERE post_type = 'product' " . "\t\tAND post_status IN ('publish', 'future', 'pending', 'private') " . "\t" . (is_array($product_ids) ? 'AND id IN (' . implode(',', $product_ids) . ')' : '') . "" . "ORDER BY ID LIMIT %d, %d", $page * $count, $count)); if (!is_array($product_ids) && $page === 0) { $total_count = $wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}posts` WHERE post_type = 'product' AND post_status IN ('publish', 'future', 'pending', 'private')"); } $acf_installed = function_exists('acf'); foreach ($products as $product) { $wc_product = $this->get_product($product->id); $categoryproduct = $wc_product->get_categories(); $product->sku = $wc_product->get_sku(); $product->name = html_entity_decode(apply_filters('woocommerce_product_title', $wc_product->post->post_title, $wc_product), ENT_COMPAT | ENT_HTML401, 'UTF-8'); $product->enabled = $wc_product->is_purchasable() && ($wc_product->managing_stock() || $wc_product->is_in_stock()); $product->price = $wc_product->get_price_excluding_tax(); $product->listprice = floatval($wc_product->get_regular_price()); $product->is_taxable = $wc_product->is_taxable(); $product->tax_class = $wc_product->get_tax_class(); $product->stock_control = $wc_product->managing_stock(); $product->stock_level = $wc_product->get_stock_quantity(); if (method_exists($wc_product, 'get_type')) { $product->type = $wc_product->get_type(); } else { $product->type = $wc_product->product_type; } $product->description = apply_filters('the_content', $wc_product->post->post_content); $product->short_description = apply_filters('the_content', $wc_product->post->post_excerpt); if (method_exists($wc_product, 'get_width')) { $product->width = $wc_product->get_width(); if (!is_numeric($product->width)) { unset($product->width); } $product->height = $wc_product->get_height(); if (!is_numeric($product->height)) { unset($product->height); } $product->length = $wc_product->get_length(); if (!is_numeric($product->length)) { unset($product->length); } } else { $product->length = $wc_product->length; $product->width = $wc_product->width; $product->height = $wc_product->height; } $product->weight = $wc_product->get_weight(); if (!is_numeric($product->weight)) { unset($product->weight); } if ($product->is_taxable && 'yes' === get_option('woocommerce_prices_include_tax')) { $tax_rates = WC_Tax::get_shop_base_rate($product->tax_class); $taxes = WC_Tax::calc_tax($product->listprice, $tax_rates, true); $product->listprice = $product->listprice - array_sum($taxes); } if ($product->type == 'variable') { $product->skus = array(); foreach ($wc_product->get_children() as $child_id) { $child_product = $wc_product->get_child($child_id); $img = wp_get_attachment_image_src($child_product->get_image_id(), 'full'); $img = $img[0]; $child_product_data = array('id' => $child_id, 'sku' => $child_product->get_sku(), 'enabled' => $wc_product->is_purchasable() && ($wc_product->managing_stock() || $wc_product->is_in_stock()), 'price' => $child_product->get_price_excluding_tax(), 'listprice' => $child_product->get_regular_price(), 'is_taxable' => $child_product->is_taxable(), 'tax_class' => $child_product->get_tax_class(), 'stock_control' => $child_product->managing_stock(), 'stock_level' => $child_product->get_stock_quantity(), 'images' => array(array('source' => $img, 'sequence' => 0))); $attributes = array(); $termsmap = array(); $names = array(); foreach ($child_product->get_variation_attributes() as $name => $value) { $name = preg_replace('/(pa_)?attribute_/', '', $name); if (!isset($names[$name])) { $names[$name] = true; $terms = get_terms(array('taxonomy' => $name)); if ($terms) { foreach ($terms as $term) { $termsmap[$term->slug] = $term->name; } } } if ($value && (gettype($value) == 'string' || gettype($value) == 'integer')) { if (array_key_exists($value, $termsmap)) { $newvalue = $termsmap[$value]; } else { $newvalue = $value; } } else { $newvalue = ''; } $name = wc_attribute_label($name, $child_product); $attributes[] = array('name' => $name, 'value' => $newvalue, 'slug' => $value); } foreach (get_post_custom_keys($child_product->variation_id) as $attribute) { if (!(in_array($attribute, array('_sku', '_weight', '_length', '_width', '_height', '_thumbnail_id', '_virtual', '_downloadable', '_regular_price', '_sale_price', '_sale_price_dates_from', '_sale_price_dates_to', '_price', '_download_limit', '_download_expiry', '_file_paths', '_manage_stock', '_stock_status', '_downloadable_files', '_variation_description', '_tax_class', '_tax_status', '_stock', '_default_attributes', '_product_attributes', '_file_path', '_backorders')) || substr($attribute, 0, 4) === '_wp_' || substr($attribute, 0, 13) === 'attribute_pa_')) { $value = get_post_meta($child_product->variation_id, $attribute, false); if (is_array($value)) { if (count($value) === 1) { $value = $value[0]; } else { $value = implode(',', $value); } } $attributes[] = array('name' => $attribute, 'value' => $value, 'custom' => true); } } $child_product_data['attributes'] = $attributes; $product->skus[] = $child_product_data; } $attrs = array(); foreach ($wc_product->get_variation_attributes() as $name => $value) { $name = preg_replace('/(pa_)?attribute_/', '', $name); if (!isset($names[$name])) { $names[$name] = true; $terms = get_terms(array('taxonomy' => $name)); if ($terms) { foreach ($terms as $term) { $termsmap[$term->slug] = $term->name; } } } if ($value && (gettype($value) == 'string' || gettype($value) == 'integer')) { if (array_key_exists($value, $termsmap)) { $newvalue = $termsmap[$value]; } else { $newvalue = $value; } } else { $newvalue = ''; } $name = wc_attribute_label($name, $child_product); $attrs[] = array('name' => $name, 'value' => $newvalue, 'slug' => $value); } $product->options = $attrs; } else { if ($product->type == 'grouped') { $product->skus = array(); foreach ($wc_product->get_children() as $child_id) { $child_product = $wc_product->get_child($child_id); $child_product_data = array('id' => $child_id, 'price' => $child_product->get_price_excluding_tax(), 'sku' => $child_product->get_sku(), 'name' => $child_product->get_title()); $product->skus[] = $child_product_data; } } } $product->categories = array(); $product_categories = get_the_terms($product->id, 'product_cat'); if (is_array($product_categories)) { $sequence = 0; foreach ($product_categories as $category) { $product->categories[] = array('category_id' => $category->term_id, 'sequence' => $sequence); $sequence++; } } $image_sequence = 1; $product->images = array(); $imagesUsed = array(); $primaryimage_path = wp_get_attachment_image_src($wc_product->get_image_id(), 'full'); $primaryimage_path = $primaryimage_path[0]; if ($primaryimage_path) { $product->images[] = array('source' => $primaryimage_path, 'sequence' => 0); $imagesUsed[$primaryimage_path] = true; foreach ($wc_product->get_gallery_attachment_ids() as $image_id) { $image_path = wp_get_attachment_image_src($image_id, 'full'); $image_path = $image_path[0]; if (!array_key_exists($image_path, $imagesUsed)) { $product->images[] = array('source' => $image_path, 'sequence' => $image_sequence); $imagesUsed[$image_path] = true; $image_sequence++; } } } $product->attributes = array(); $attributesUsed = array(); foreach ($wc_product->get_attributes() as $attribute) { if (!$attribute['is_variation']) { if (!array_key_exists($attribute['name'], $attributesUsed)) { $attributesUsed[$attribute['name']] = true; $attributeName = wc_attribute_label($attribute['name']); if (!$attribute['is_taxonomy']) { $product->attributes[] = array('name' => $attributeName, 'value' => $attribute['value']); } else { $attributeValue = implode(', ', wc_get_product_terms($product->id, $attribute['name'], array('fields' => 'names'))); $product->attributes[] = array('name' => $attributeName, 'value' => $attributeValue); } } } } foreach (get_post_custom_keys($product->id) as $attribute) { if (!(substr($attribute, 0, 1) === '_' || substr($attribute, 0, 3) === 'pa_')) { if (!array_key_exists($attribute, $attributesUsed)) { $attributesUsed[$attribute] = true; $value = get_post_meta($product->id, $attribute, false); if (is_array($value)) { if (count($value) === 1) { $value = $value[0]; } else { $value = implode(',', $value); } } $product->attributes[] = array('name' => $attribute, 'value' => $value); } } } // acf if ($acf_installed) { if (function_exists('get_field_objects')) { $fields = get_field_objects($product->id); if (is_array($fields)) { foreach ($fields as $field) { if ($field['type'] == 'image') { $image_path = $field['value']['url']; if (!array_key_exists($image_path, $imagesUsed)) { $product->images[] = array('source' => $image_path, 'sequence' => $image_sequence); $imagesUsed[$image_path] = true; $image_sequence++; } } else { if ($field['type'] == 'gallery') { $gallery = $field['value']; if (is_array($gallery)) { foreach ($gallery as $image) { $image_path = $image['url']; if (!array_key_exists($image_path, $imagesUsed)) { $product->images[] = array('source' => $image_path, 'sequence' => $image_sequence); $imagesUsed[$image_path] = true; $image_sequence++; } } } } else { if (in_array($field['type'], array('textarea', 'wysiwyg', 'text', 'number', 'select', 'radio', 'checkbox', 'true_false'))) { if (!array_key_exists($field['label'], $attributesUsed)) { $attributesUsed[$field['label']] = true; $value = $field['value']; if (is_array($value)) { if (count($value) === 1) { $value = $value[0]; } else { $value = implode(',', $value); } } $product->attributes[] = array('name' => $field['name'], 'value' => $value); } } } } if (!$product->description) { if (in_array($field['type'], array('textarea', 'wysiwyg')) && $field['name'] == 'description') { $product->description = $field['value']; } } } } } } } $response = array('ack' => 'ok', 'products' => $products); if (isset($total_count)) { $response['total_count'] = $total_count; } $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode($response); exit; } else { if ($type === 'categories') { if (!$this->check_hash()) { exit; } $categories = get_categories(array('taxonomy' => 'product_cat', 'orderby' => 'term_order', 'hide_empty' => 0)); $result = array(); foreach ($categories as $category) { $result[] = array('category_id' => $category->term_id, 'name' => $category->name, 'parent_id' => $category->parent); } $response = array('ack' => 'ok', 'categories' => $result, 'total_count' => count($categories)); $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode($response); exit; } else { if ($type === 'orders') { if (!$this->check_hash()) { exit; } $page = isset($_GET['page']) ? (int) $_GET['page'] : 0; $count = isset($_GET['count']) ? (int) $_GET['count'] : 0; $orders = $wpdb->get_results($wpdb->prepare("SELECT (SELECT meta_value FROM `{$wpdb->prefix}postmeta` WHERE post_id = P.id AND meta_key = '_codisto_orderid') AS id, ID AS post_id, post_status AS status FROM `{$wpdb->prefix}posts` AS P WHERE post_type = 'shop_order' AND ID IN (SELECT post_id FROM `{$wpdb->prefix}postmeta` WHERE meta_key = '_codisto_orderid') ORDER BY ID LIMIT %d, %d", $page * $count, $count)); if ($page == 0) { $total_count = $wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}posts` AS P WHERE post_type = 'shop_order' AND ID IN (SELECT post_id FROM `{$wpdb->prefix}postmeta` WHERE meta_key = '_codisto_orderid')"); } $order_data = array(); foreach ($orders as $order) { $ship_date = get_post_meta($order->post_id, '_date_shipped', true); if ($ship_date) { if (is_numeric($ship_date)) { $ship_date = date('Y-m-d H:i:s', $ship_date); } $order->ship_date = $ship_date; } $carrier = get_post_meta($order->post_id, '_tracking_provider', true); if ($carrier) { if ($carrier === 'custom') { $carrier = get_post_meta($order->post_id, '_custom_tracking_provider', true); } if ($carrier) { $order->carrier = $carrier; } } $tracking_number = get_post_meta($order->post_id, '_tracking_number', true); if ($tracking_number) { $order->track_number = $tracking_number; } unset($order->post_id); $order_data[] = $order; } $response = array('ack' => 'ok', 'orders' => $order_data); if (isset($total_count)) { $response['total_count'] = $total_count; } $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode($response); exit; } else { if ($type == 'sync') { if ($_SERVER['HTTP_X_ACTION'] === 'TEMPLATE') { if (!$this->check_hash()) { exit; } $ebayDesignDir = WP_CONTENT_DIR . '/ebay/'; $merchantid = (int) $_GET['merchantid']; if (!$merchantid) { $merchantid = 0; } $templatedb = get_temp_dir() . '/ebay-template-' . $merchantid . '.db'; $db = new PDO('sqlite:' . $templatedb); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->setAttribute(PDO::ATTR_TIMEOUT, 60); $db->exec('PRAGMA synchronous=0'); $db->exec('PRAGMA temp_store=2'); $db->exec('PRAGMA page_size=65536'); $db->exec('PRAGMA encoding=\'UTF-8\''); $db->exec('PRAGMA cache_size=15000'); $db->exec('PRAGMA soft_heap_limit=67108864'); $db->exec('PRAGMA journal_mode=MEMORY'); $db->exec('BEGIN EXCLUSIVE TRANSACTION'); $db->exec('CREATE TABLE IF NOT EXISTS File(Name text NOT NULL PRIMARY KEY, Content blob NOT NULL, LastModified datetime NOT NULL, Changed bit NOT NULL DEFAULT -1)'); $db->exec('COMMIT TRANSACTION'); if (isset($_GET['markreceived'])) { $update = $db->prepare('UPDATE File SET LastModified = ? WHERE Name = ?'); $files = $db->query('SELECT Name FROM File WHERE Changed != 0'); $files->execute(); $db->exec('BEGIN EXCLUSIVE TRANSACTION'); while ($row = $files->fetch()) { $stat = stat(WP_CONTENT_DIR . '/ebay/' . $row['Name']); $lastModified = strftime('%Y-%m-%d %H:%M:%S', $stat['mtime']); $update->bindParam(1, $lastModified); $update->bindParam(2, $row['Name']); $update->execute(); } $db->exec('UPDATE File SET Changed = 0'); $db->exec('COMMIT TRANSACTION'); $db = null; $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, must-revalidate', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode(array('ack' => 'ok')); exit; } else { $insert = $db->prepare('INSERT OR IGNORE INTO File(Name, Content, LastModified) VALUES (?, ?, ?)'); $update = $db->prepare('UPDATE File SET Content = ?, Changed = -1 WHERE Name = ? AND LastModified != ?'); $filelist = $this->files_in_dir($ebayDesignDir); $db->exec('BEGIN EXCLUSIVE TRANSACTION'); foreach ($filelist as $key => $name) { try { $fileName = $ebayDesignDir . $name; if (!in_array($name, array('README'))) { $content = @file_get_contents($fileName); if ($content !== false) { $stat = stat($fileName); $lastModified = strftime('%Y-%m-%d %H:%M:%S', $stat['mtime']); $update->bindParam(1, $content); $update->bindParam(2, $name); $update->bindParam(3, $lastModified); $update->execute(); if ($update->rowCount() == 0) { $insert->bindParam(1, $name); $insert->bindParam(2, $content); $insert->bindParam(3, $lastModified); $insert->execute(); } } } } catch (Exception $e) { } } $db->exec('COMMIT TRANSACTION'); $tmpDb = wp_tempnam(); $db = new PDO('sqlite:' . $tmpDb); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->exec('PRAGMA synchronous=0'); $db->exec('PRAGMA temp_store=2'); $db->exec('PRAGMA page_size=512'); $db->exec('PRAGMA encoding=\'UTF-8\''); $db->exec('PRAGMA cache_size=15000'); $db->exec('PRAGMA soft_heap_limit=67108864'); $db->exec('PRAGMA journal_mode=OFF'); $db->exec('ATTACH DATABASE \'' . $templatedb . '\' AS Source'); $db->exec('CREATE TABLE File AS SELECT * FROM Source.File WHERE Changed != 0'); $db->exec('DETACH DATABASE Source'); $db->exec('VACUUM'); $fileCountStmt = $db->query('SELECT COUNT(*) AS fileCount FROM File'); $fileCountStmt->execute(); $fileCountRow = $fileCountStmt->fetch(); $fileCount = $fileCountRow['fileCount']; $db = null; if ($fileCount == 0) { $this->sendHttpHeaders('204 No Content', array('Cache-Control' => 'no-cache, must-revalidate', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); } else { $headers = array('Cache-Control' => 'no-cache, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Content-Type' => 'application/octet-stream', 'Content-Disposition' => 'attachment; filename=' . basename($tmpDb), 'Content-Length' => filesize($tmpDb)); $this->sendHttpHeaders('200 OK', $headers); while (ob_get_level() > 0) { if (!@ob_end_clean()) { break; } } flush(); readfile($tmpDb); } unlink($tmpDb); exit; } } } } } } } } } } else { if ($type === 'createorder') { if (!$this->check_hash()) { exit; } try { $xml = simplexml_load_string(file_get_contents('php://input')); $ordercontent = $xml->entry->content->children('http://api.codisto.com/schemas/2009/'); $wpdb->query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'); $wpdb->query('START TRANSACTION'); $billing_address = $ordercontent->orderaddresses->orderaddress[0]; $shipping_address = $ordercontent->orderaddresses->orderaddress[1]; $billing_first_name = $billing_last_name = ''; if (strpos($billing_address->name, ' ') !== false) { $billing_name = explode(' ', $billing_address->name, 2); $billing_first_name = $billing_name[0]; $billing_last_name = $billing_name[1]; } else { $billing_first_name = (string) $billing_address->name; } $billing_country_code = (string) $billing_address->countrycode; $billing_division = (string) $billing_address->division; $billing_states = WC()->countries->get_states($billing_country_code); if ($billing_states) { $billing_division_match = preg_replace('/\\s+/', '', strtolower($billing_division)); foreach ($billing_states as $state_code => $state_name) { if (preg_replace('/\\s+/', '', strtolower($state_name)) == $billing_division_match) { $billing_division = $state_code; break; } } } $shipping_first_name = $shipping_last_name = ''; if (strpos($shipping_address->name, ' ') !== false) { $shipping_name = explode(' ', $shipping_address->name, 2); $shipping_first_name = $shipping_name[0]; $shipping_last_name = $shipping_name[1]; } else { $shipping_first_name = (string) $shipping_address->name; } $shipping_country_code = (string) $shipping_address->countrycode; $shipping_division = (string) $shipping_address->division; if ($billing_country_code === $shipping_country_code) { $shipping_states = $billing_states; } else { $shipping_states = WC()->countries->get_states($shipping_country_code); } if ($shipping_states) { $shipping_division_match = preg_replace('/\\s+/', '', strtolower($shipping_division)); foreach ($shipping_states as $state_code => $state_name) { if (preg_replace('/\\s+/', '', strtolower($state_name)) == $shipping_division_match) { $shipping_division = $state_code; break; } } } $address_data = array('billing_first_name' => $billing_first_name, 'billing_last_name' => $billing_last_name, 'billing_company' => (string) $billing_address->companyname, 'billing_address_1' => (string) $billing_address->address1, 'billing_address_2' => (string) $billing_address->address2, 'billing_city' => (string) $billing_address->place, 'billing_postcode' => (string) $billing_address->postalcode, 'billing_state' => $billing_division, 'billing_country' => $billing_country_code, 'billing_email' => (string) $billing_address->email, 'billing_phone' => (string) $billing_address->phone, 'shipping_first_name' => $shipping_first_name, 'shipping_last_name' => $shipping_last_name, 'shipping_company' => (string) $shipping_address->companyname, 'shipping_address_1' => (string) $shipping_address->address1, 'shipping_address_2' => (string) $shipping_address->address2, 'shipping_city' => (string) $shipping_address->place, 'shipping_postcode' => (string) $shipping_address->postalcode, 'shipping_state' => $shipping_division, 'shipping_country' => $shipping_country_code, 'shipping_email' => (string) $shipping_address->email, 'shipping_phone' => (string) $shipping_address->phone); $email = (string) $billing_address->email; if (!$email) { $email = (string) $shipping_address->email; } if ($email) { $user = get_user_by('email', $email); if (!$user) { $username = (string) $ordercontent->ebayusername; if (!$username) { $username = current(explode('@', $email)); } if ($username) { $username = sanitize_user($username); } if (username_exists($username)) { $counter = 1; $newusername = $username . $counter; while (username_exists($newusername)) { $counter++; $newusername = $username . $counter; } $username = $newusername; } $password = wp_generate_password(); $customer_data = apply_filters('woocommerce_new_customer_data', array('user_login' => $username, 'user_pass' => $password, 'user_email' => $email, 'role' => 'customer')); $customer_id = wp_insert_user($customer_data); foreach ($address_data as $key => $value) { update_user_meta($customer_id, $key, $value); } do_action('woocommerce_created_customer', $customer_id, $customer_data, true); } else { $customer_id = $user->ID; } } else { $customer_id = 0; } $customer_note = @count($ordercontent->instructions) ? strval($ordercontent->instructions) : ''; $order_id = $wpdb->get_var($wpdb->prepare("SELECT ID FROM `{$wpdb->prefix}posts` AS P WHERE ID IN (SELECT post_id FROM `{$wpdb->prefix}postmeta` WHERE meta_key = '_codisto_orderid' AND meta_value = %d)", (int) $ordercontent->orderid)); $shipping = 0; $shipping_tax = 0; $cart_discount = 0; $cart_discount_tax = 0; $total = (double) $ordercontent->ordertotal; $tax = 0; if (!$order_id) { $new_order_data_callback = array($this, 'order_set_date'); add_filter('woocommerce_new_order_data', $new_order_data_callback, 1, 1); $order = wc_create_order(array('customer_id' => $customer_id, 'customer_note' => $customer_note, 'created_via' => 'eBay')); remove_filter('woocommerce_new_order_data', $new_order_data_callback); $order_id = $order->id; update_post_meta($order_id, '_codisto_orderid', (int) $ordercontent->orderid); update_post_meta($order_id, '_codisto_ebayuser', (string) $ordercontent->ebayusername); update_post_meta($order_id, '_order_currency', (string) $ordercontent->transactcurrency); update_post_meta($order_id, '_customer_ip_address', '-'); delete_post_meta($order_id, '_prices_include_tax'); do_action('woocommerce_new_order', $order_id); foreach ($ordercontent->orderlines->orderline as $orderline) { if ($orderline->productcode[0] != 'FREIGHT') { $productcode = (string) $orderline->productcode; if ($productcode == null) { $productcode = ''; } $productname = (string) $orderline->productname; if ($productname == null) { $productname = ''; } $product_id = $orderline->externalreference[0]; if ($product_id != null) { $product_id = intval($product_id); } $variation_id = 0; if (get_post_type($product_id) === 'product_variation') { $variation_id = $product_id; $product_id = wp_get_post_parent_id($variation_id); if (!is_numeric($product_id) || $product_id === 0) { $product_id = 0; $variation_id = 0; } } $qty = (int) $orderline->quantity[0]; $item_id = wc_add_order_item($order_id, array('order_item_name' => $productname, 'order_item_type' => 'line_item')); wc_add_order_item_meta($item_id, '_qty', $qty); if (!is_null($product_id) && $product_id !== 0) { wc_add_order_item_meta($item_id, '_product_id', $product_id); wc_add_order_item_meta($item_id, '_variation_id', $variation_id); wc_add_order_item_meta($item_id, '_tax_class', ''); } else { wc_add_order_item_meta($item_id, '_product_id', 0); wc_add_order_item_meta($item_id, '_variation_id', 0); wc_add_order_item_meta($item_id, '_tax_class', ''); } $line_total = wc_format_decimal((double) $orderline->linetotal); $line_total_tax = wc_format_decimal((double) $orderline->linetotalinctax - (double) $orderline->linetotal); wc_add_order_item_meta($item_id, '_line_subtotal', $line_total); wc_add_order_item_meta($item_id, '_line_total', $line_total); wc_add_order_item_meta($item_id, '_line_subtotal_tax', $line_total_tax); wc_add_order_item_meta($item_id, '_line_tax', $line_total_tax); wc_add_order_item_meta($item_id, '_line_tax_data', array('total' => array(1 => $line_total_tax), 'subtotal' => array(1 => $line_total_tax))); $tax += $line_total_tax; } else { $item_id = wc_add_order_item($order_id, array('order_item_name' => (string) $orderline->productname, 'order_item_type' => 'shipping')); wc_add_order_item_meta($item_id, 'cost', wc_format_decimal((double) $orderline->linetotal)); $shipping += (double) $orderline->linetotal; $shipping_tax += (double) $orderline->linetotalinctax - (double) $orderline->linetotal; } } if ($ordercontent->paymentstatus == 'complete') { $transaction_id = (string) $ordercontent->orderpayments[0]->orderpayment->transactionid; if ($transaction_id) { update_post_meta($order_id, '_payment_method', 'paypal'); update_post_meta($order_id, '_payment_method_title', __('PayPal', 'woocommerce')); update_post_meta($order_id, '_transaction_id', $transaction_id); } else { update_post_meta($order_id, '_payment_method', 'bacs'); update_post_meta($order_id, '_payment_method_title', __('BACS', 'woocommerce')); } // payment_complete add_post_meta($order_id, '_paid_date', current_time('mysql'), true); if (!get_post_meta($order_id, '_order_stock_reduced', true)) { $order->reduce_order_stock(); } } } else { $order = wc_get_order($order_id); foreach ($ordercontent->orderlines->orderline as $orderline) { if ($orderline->productcode[0] != 'FREIGHT') { $line_total = wc_format_decimal((double) $orderline->linetotal); $line_total_tax = wc_format_decimal((double) $orderline->linetotalinctax - (double) $orderline->linetotal); $tax += $line_total_tax; } else { $order->remove_order_items('shipping'); $item_id = wc_add_order_item($order_id, array('order_item_name' => (string) $orderline->productname, 'order_item_type' => 'shipping')); wc_add_order_item_meta($item_id, 'cost', wc_format_decimal((double) $orderline->linetotal)); $shipping += (double) $orderline->linetotal; $shipping_tax += (double) $orderline->linetotalinctax - (double) $orderline->linetotal; } } if ($ordercontent->paymentstatus == 'complete') { $transaction_id = (string) $ordercontent->orderpayments[0]->orderpayment->transactionid; if ($transaction_id) { update_post_meta($order_id, '_payment_method', 'paypal'); update_post_meta($order_id, '_payment_method_title', __('PayPal', 'woocommerce')); update_post_meta($order_id, '_transaction_id', $transaction_id); } else { update_post_meta($order_id, '_payment_method', 'bacs'); update_post_meta($order_id, '_payment_method_title', __('BACS', 'woocommerce')); } // payment_complete add_post_meta($order_id, '_paid_date', current_time('mysql'), true); if (!get_post_meta($order_id, '_order_stock_reduced', true)) { $order->reduce_order_stock(); } } } foreach ($address_data as $key => $value) { update_post_meta($order_id, '_' . $key, $value); } $order->remove_order_items('tax'); $order->add_tax(1, $tax, $shipping_tax); $order->set_total($shipping, 'shipping'); $order->set_total($shipping_tax, 'shipping_tax'); $order->set_total($cart_discount, 'cart_discount'); $order->set_total($cart_discount_tax, 'cart_discount_tax'); $order->set_total($tax, 'tax'); $order->set_total($total, 'total'); if ($ordercontent->orderstate == 'cancelled') { if (!$order->has_status('cancelled')) { // update_status $order->post_status = 'wc-cancelled'; $update_post_data = array('ID' => $order_id, 'post_status' => 'wc-cancelled', 'post_date' => current_time('mysql', 0), 'post_date_gmt' => current_time('mysql', 1)); wp_update_post($update_post_data); $order->decrease_coupon_usage_counts(); wc_delete_shop_order_transients($order_id); } } else { if ($ordercontent->orderstate == 'inprogress' || $ordercontent->orderstate == 'processing') { if ($ordercontent->paymentstatus == 'complete') { if (!$order->has_status('processing')) { // update_status $order->post_status = 'wc-processing'; $update_post_data = array('ID' => $order_id, 'post_status' => 'wc-processing', 'post_date' => current_time('mysql', 0), 'post_date_gmt' => current_time('mysql', 1)); wp_update_post($update_post_data); } } else { if (!$order->has_status('pending')) { // update_status $order->post_status = 'wc-pending'; $update_post_data = array('ID' => $order_id, 'post_status' => 'wc-pending', 'post_date' => current_time('mysql', 0), 'post_date_gmt' => current_time('mysql', 1)); wp_update_post($update_post_data); } } } else { if ($ordercontent->orderstate == 'complete') { if (!$order->has_status('completed')) { // update_status $order->post_status = 'wc-completed'; $update_post_data = array('ID' => $order_id, 'post_status' => 'wc-completed', 'post_date' => current_time('mysql', 0), 'post_date_gmt' => current_time('mysql', 1)); wp_update_post($update_post_data); $order->record_product_sales(); $order->increase_coupon_usage_counts(); update_post_meta($order_id, '_completed_date', current_time('mysql')); wc_delete_shop_order_transients($order_id); } } } } $wpdb->query('COMMIT'); $response = array('ack' => 'ok', 'orderid' => $order_id); $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode($response); exit; } catch (Exception $e) { $wpdb->query('ROLLBACK'); $response = array('ack' => 'failed', 'message' => $e->getMessage() . ' ' . $e->getFile() . ' ' . $e->getLine()); $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode($response); exit; } } else { if ($type == 'sync') { if ($_SERVER['HTTP_X_ACTION'] === 'TEMPLATE') { if (!$this->check_hash()) { exit; } $ebayDesignDir = WP_CONTENT_DIR . '/ebay/'; $tmpPath = wp_tempnam(); @file_put_contents($tmpPath, file_get_contents('php://input')); $db = new PDO('sqlite:' . $tmpPath); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->exec('PRAGMA synchronous=0'); $db->exec('PRAGMA temp_store=2'); $db->exec('PRAGMA page_size=65536'); $db->exec('PRAGMA encoding=\'UTF-8\''); $db->exec('PRAGMA cache_size=15000'); $db->exec('PRAGMA soft_heap_limit=67108864'); $db->exec('PRAGMA journal_mode=MEMORY'); $files = $db->prepare('SELECT Name, Content FROM File'); $files->execute(); $files->bindColumn(1, $name); $files->bindColumn(2, $content); while ($files->fetch()) { $fileName = $ebayDesignDir . $name; if (strpos($name, '..') === false) { if (!file_exists($fileName)) { $dir = dirname($fileName); if (!is_dir($dir)) { mkdir($dir . '/', 0755, true); } @file_put_contents($fileName, $content); } } } $db = null; unlink($tmpPath); $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $this->json_encode(array('ack' => 'ok')); exit; } } else { if ($type == 'index/calc') { $product_ids = array(); $quantities = array(); for ($i = 0;; $i++) { if (!isset($_POST['PRODUCTCODE(' . $i . ')'])) { break; } $productid = (int) $_POST['PRODUCTID(' . $i . ')']; if (!$productid) { $productcode = $_POST['PRODUCTCODE(' . $i . ')']; $productid = wc_get_product_id_by_sku($productcode); } $productqty = $_POST['PRODUCTQUANTITY(' . $i . ')']; if (!$productqty && $productqty != 0) { $productqty = 1; } WC()->cart->add_to_cart($productid, $productqty); } WC()->customer->set_location($_POST['COUNTRYCODE'], $_POST['DIVISION'], $_POST['POSTALCODE'], $_POST['PLACE']); WC()->customer->set_shipping_location($_POST['COUNTRYCODE'], $_POST['DIVISION'], $_POST['POSTALCODE'], $_POST['PLACE']); WC()->cart->calculate_totals(); WC()->cart->calculate_shipping(); $response = ''; $idx = 0; $methods = WC()->shipping()->get_shipping_methods(); foreach ($methods as $method) { if (file_exists(plugin_dir_path(__FILE__) . 'shipping/' . $method->id)) { include plugin_dir_path(__FILE__) . 'shipping/' . $method->id; } else { foreach ($method->rates as $method => $rate) { $method_name = $rate->get_label(); if (!$method_name) { $method_name = 'Shipping'; } $method_cost = $rate->cost; if (is_numeric($method_cost)) { if (isset($rate->taxes) && is_array($rate->taxes)) { foreach ($rate->taxes as $tax) { if (is_numeric($tax)) { $method_cost += $tax; } } } $response .= ($idx > 0 ? '&' : '') . 'FREIGHTNAME(' . $idx . ')=' . rawurlencode($method_name) . '&FREIGHTCHARGEINCTAX(' . $idx . ')=' . number_format((double) $method_cost, 2, '.', ''); $idx++; } } } } $this->sendHttpHeaders('200 OK', array('Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store', 'Expires' => 'Thu, 01 Jan 1970 00:00:00 GMT', 'Pragma' => 'no-cache')); echo $response; exit; } } } } }
/** * Calculate taxes for all line items and shipping, and store the totals and tax rows. * * Will use the base country unless customer addresses are set. * * @return bool success or fail */ public function calculate_taxes() { $tax_total = 0; $taxes = array(); $tax_based_on = get_option('woocommerce_tax_based_on'); if ('billing' === $tax_based_on) { $country = $this->billing_country; $state = $this->billing_state; $postcode = $this->billing_postcode; $city = $this->billing_city; } elseif ('shipping' === $tax_based_on) { $country = $this->shipping_country; $state = $this->shipping_state; $postcode = $this->shipping_postcode; $city = $this->shipping_city; } // Default to base if ('base' === $tax_based_on || empty($country)) { $default = wc_get_base_location(); $country = $default['country']; $state = $default['state']; $postcode = ''; $city = ''; } // Get items foreach ($this->get_items(array('line_item', 'fee')) as $item_id => $item) { $product = $this->get_product_from_item($item); $line_total = isset($item['line_total']) ? $item['line_total'] : 0; $line_subtotal = isset($item['line_subtotal']) ? $item['line_subtotal'] : 0; $tax_class = $item['tax_class']; $item_tax_status = $product ? $product->get_tax_status() : 'taxable'; if ('0' !== $tax_class && 'taxable' === $item_tax_status) { $tax_rates = WC_Tax::find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $tax_class)); $line_subtotal_taxes = WC_Tax::calc_tax($line_subtotal, $tax_rates, false); $line_taxes = WC_Tax::calc_tax($line_total, $tax_rates, false); $line_subtotal_tax = max(0, array_sum($line_subtotal_taxes)); $line_tax = max(0, array_sum($line_taxes)); $tax_total += $line_tax; wc_update_order_item_meta($item_id, '_line_subtotal_tax', wc_format_decimal($line_subtotal_tax)); wc_update_order_item_meta($item_id, '_line_tax', wc_format_decimal($line_tax)); wc_update_order_item_meta($item_id, '_line_tax_data', array('total' => $line_taxes, 'subtotal' => $line_subtotal_taxes)); // Sum the item taxes foreach (array_keys($taxes + $line_taxes) as $key) { $taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($taxes[$key]) ? $taxes[$key] : 0); } } } // Now calculate shipping tax $matched_tax_rates = array(); $tax_rates = WC_Tax::find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => '')); if (!empty($tax_rates)) { foreach ($tax_rates as $key => $rate) { if (isset($rate['shipping']) && 'yes' === $rate['shipping']) { $matched_tax_rates[$key] = $rate; } } } $shipping_taxes = WC_Tax::calc_shipping_tax($this->order_shipping, $matched_tax_rates); $shipping_tax_total = WC_Tax::round(array_sum($shipping_taxes)); // Save tax totals $this->set_total($shipping_tax_total, 'shipping_tax'); $this->set_total($tax_total, 'tax'); // Tax rows $this->remove_order_items('tax'); // Now merge to keep tax rows foreach (array_keys($taxes + $shipping_taxes) as $tax_rate_id) { $this->add_tax($tax_rate_id, isset($taxes[$tax_rate_id]) ? $taxes[$tax_rate_id] : 0, isset($shipping_taxes[$tax_rate_id]) ? $shipping_taxes[$tax_rate_id] : 0); } return true; }
/** * 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; }
/** * Calc line tax */ public static function calc_line_taxes() { global $wpdb; check_ajax_referer('calc-totals', 'security'); if (!current_user_can('edit_shop_orders')) { die(-1); } $tax = new WC_Tax(); $order_id = absint($_POST['order_id']); $items = array(); $country = strtoupper(esc_attr($_POST['country'])); $state = strtoupper(esc_attr($_POST['state'])); $postcode = strtoupper(esc_attr($_POST['postcode'])); $city = wc_clean(esc_attr($_POST['city'])); $order = wc_get_order($order_id); $taxes = array(); $shipping_taxes = array(); // Parse the jQuery serialized items parse_str($_POST['items'], $items); // Prevent undefined warnings if (!isset($items['line_tax'])) { $items['line_tax'] = array(); } if (!isset($items['line_subtotal_tax'])) { $items['line_subtotal_tax'] = array(); } $items['order_taxes'] = array(); // Action $items = apply_filters('woocommerce_ajax_calc_line_taxes', $items, $order_id, $country, $_POST); // Get items and fees taxes if (isset($items['order_item_id'])) { $line_total = $line_subtotal = $order_item_tax_class = array(); foreach ($items['order_item_id'] as $item_id) { $item_id = absint($item_id); $line_total[$item_id] = isset($items['line_total'][$item_id]) ? wc_format_decimal($items['line_total'][$item_id]) : 0; $line_subtotal[$item_id] = isset($items['line_subtotal'][$item_id]) ? wc_format_decimal($items['line_subtotal'][$item_id]) : $line_total[$item_id]; $order_item_tax_class[$item_id] = isset($items['order_item_tax_class'][$item_id]) ? sanitize_text_field($items['order_item_tax_class'][$item_id]) : ''; $product_id = $order->get_item_meta($item_id, '_product_id', true); // Get product details if (get_post_type($product_id) == 'product') { $_product = wc_get_product($product_id); $item_tax_status = $_product->get_tax_status(); } else { $item_tax_status = 'taxable'; } if ('0' !== $order_item_tax_class[$item_id] && 'taxable' === $item_tax_status) { $tax_rates = WC_Tax::find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $order_item_tax_class[$item_id])); $line_taxes = WC_Tax::calc_tax($line_total[$item_id], $tax_rates, false); $line_subtotal_taxes = WC_Tax::calc_tax($line_subtotal[$item_id], $tax_rates, false); // Set the new line_tax foreach ($line_taxes as $_tax_id => $_tax_value) { $items['line_tax'][$item_id][$_tax_id] = $_tax_value; } // Set the new line_subtotal_tax foreach ($line_subtotal_taxes as $_tax_id => $_tax_value) { $items['line_subtotal_tax'][$item_id][$_tax_id] = $_tax_value; } // Sum the item taxes foreach (array_keys($taxes + $line_taxes) as $key) { $taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($taxes[$key]) ? $taxes[$key] : 0); } } } } // Get shipping taxes if (isset($items['shipping_method_id'])) { $matched_tax_rates = array(); $tax_rates = WC_Tax::find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => '')); if ($tax_rates) { foreach ($tax_rates as $key => $rate) { if (isset($rate['shipping']) && 'yes' == $rate['shipping']) { $matched_tax_rates[$key] = $rate; } } } $shipping_cost = $shipping_taxes = array(); foreach ($items['shipping_method_id'] as $item_id) { $item_id = absint($item_id); $shipping_cost[$item_id] = isset($items['shipping_cost'][$item_id]) ? wc_format_decimal($items['shipping_cost'][$item_id]) : 0; $_shipping_taxes = WC_Tax::calc_shipping_tax($shipping_cost[$item_id], $matched_tax_rates); // Set the new shipping_taxes foreach ($_shipping_taxes as $_tax_id => $_tax_value) { $items['shipping_taxes'][$item_id][$_tax_id] = $_tax_value; $shipping_taxes[$_tax_id] = isset($shipping_taxes[$_tax_id]) ? $shipping_taxes[$_tax_id] + $_tax_value : $_tax_value; } } } // Remove old tax rows $order->remove_order_items('tax'); // Add tax rows foreach (array_keys($taxes + $shipping_taxes) as $tax_rate_id) { $order->add_tax($tax_rate_id, isset($taxes[$tax_rate_id]) ? $taxes[$tax_rate_id] : 0, isset($shipping_taxes[$tax_rate_id]) ? $shipping_taxes[$tax_rate_id] : 0); } // Create the new order_taxes foreach ($order->get_taxes() as $tax_id => $tax_item) { $items['order_taxes'][$tax_id] = absint($tax_item['rate_id']); } // Save order items wc_save_order_items($order_id, $items); // Return HTML items $order = wc_get_order($order_id); $data = get_post_meta($order_id); include 'admin/meta-boxes/views/html-order-items.php'; die; }
/** * Returns the price (excluding tax) - ignores tax_class filters since the price may *include* tax and thus needs subtracting. * * @access public * @return string */ function get_price_excluding_tax() { $price = $this->get_price(); if ($this->is_taxable() && get_option('woocommerce_prices_include_tax') == 'yes') { $_tax = new WC_Tax(); $tax_rates = $_tax->get_shop_base_rate($this->tax_class); $taxes = $_tax->calc_tax($price, $tax_rates, true); $tax_amount = $_tax->get_tax_total($taxes); $price = round($price - $tax_amount, 2); } return apply_filters('woocommerce_get_price_excluding_tax', $price, $this); }
function vtprd_get_price_excluding_tax_forced($product_id, $price, $product) { global $post, $wpdb, $woocommerce, $vtprd_cart, $vtprd_cart_item, $vtprd_setup_options, $vtprd_info; //changed $this-> to $product-> //use $discount_price as basis $qty = 1; if ($product->is_taxable()) { //if ( $product->is_taxable() && get_option('woocommerce_prices_include_tax') === 'yes' ) { $_tax = new WC_Tax(); $tax_rates = $_tax->get_shop_base_rate($product->tax_class); $taxes = $_tax->calc_tax($price * $qty, $tax_rates, true); $price = $_tax->round($price * $qty - array_sum($taxes)); } return $price; }
/** * 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; } }
/** * Test compound tax amounts */ public function test_calc_compound_tax() { global $wpdb; $wpdb->query("DELETE FROM {$wpdb->prefix}woocommerce_tax_rates"); $wpdb->query("DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations"); update_option('woocommerce_default_country', 'CA'); update_option('woocommerce_default_state', 'QC'); $tax_rate_1 = array('tax_rate_country' => 'CA', 'tax_rate_state' => '', 'tax_rate' => '5.0000', 'tax_rate_name' => 'GST', 'tax_rate_priority' => '1', 'tax_rate_compound' => '0', 'tax_rate_shipping' => '1', 'tax_rate_order' => '1', 'tax_rate_class' => ''); $tax_rate_2 = array('tax_rate_country' => 'CA', 'tax_rate_state' => 'QC', 'tax_rate' => '8.5000', 'tax_rate_name' => 'PST', 'tax_rate_priority' => '2', 'tax_rate_compound' => '1', 'tax_rate_shipping' => '1', 'tax_rate_order' => '2', 'tax_rate_class' => ''); $tax_rate_1_id = WC_Tax::_insert_tax_rate($tax_rate_1); $tax_rate_2_id = WC_Tax::_insert_tax_rate($tax_rate_2); $tax_rates = WC_Tax::find_rates(array('country' => 'CA', 'state' => 'QC', 'postcode' => '12345', 'city' => '', 'tax_class' => '')); // prices exclusive of tax $calced_tax = WC_Tax::calc_tax('100', $tax_rates, false, false); $this->assertEquals($calced_tax, array($tax_rate_1_id => '5.0000', $tax_rate_2_id => '8.925')); // prices inclusive of tax $calced_tax = WC_Tax::calc_tax('100', $tax_rates, true, false); /** * 100 is inclusive of all taxes. * * Compound would be 100 - ( 100 / 1.085 ) = 7.8341. * Next tax would be calced on 100 - 7.8341 = 92.1659. * 92.1659 - ( 92.1659 / 1.05 ) = 4.38885. */ $this->assertEquals($calced_tax, array($tax_rate_1_id => 4.3889, $tax_rate_2_id => 7.8341)); WC_Tax::_delete_tax_rate($tax_rate_1_id); WC_Tax::_delete_tax_rate($tax_rate_2_id); update_option('woocommerce_default_country', 'GB'); update_option('woocommerce_default_state', ''); }
/** * Calculate totals for the items in the cart. * * @access public */ public function calculate_totals() { global $woocommerce; $this->reset(); do_action('woocommerce_before_calculate_totals', $this); // Get count of all items + weights + subtotal (we may need this for discounts) if (sizeof($this->cart_contents) > 0) { foreach ($this->cart_contents as $cart_item_key => $values) { $_product = $values['data']; $this->cart_contents_weight = $this->cart_contents_weight + $_product->get_weight() * $values['quantity']; $this->cart_contents_count = $this->cart_contents_count + $values['quantity']; // Base Price (inclusive of tax for now) $row_base_price = $_product->get_price() * $values['quantity']; $base_tax_rates = $this->tax->get_shop_base_rate($_product->tax_class); $tax_amount = 0; if ($this->prices_include_tax) { if ($_product->is_taxable()) { $tax_rates = $this->tax->get_rates($_product->get_tax_class()); // ADJUST BASE if tax rate is different (different region or modified tax class) if ($tax_rates !== $base_tax_rates) { $base_taxes = $this->tax->calc_tax($row_base_price, $base_tax_rates, true, true); $modded_taxes = $this->tax->calc_tax($row_base_price - array_sum($base_taxes), $tax_rates, false); $row_base_price = $row_base_price - array_sum($base_taxes) + array_sum($modded_taxes); } $taxes = $this->tax->calc_tax($row_base_price, $tax_rates, true); $tax_amount = get_option('woocommerce_tax_round_at_subtotal') == 'no' ? $this->tax->get_tax_total($taxes) : array_sum($taxes); } // Sub total is based on base prices (without discounts) $this->subtotal = $this->subtotal + $row_base_price; $this->subtotal_ex_tax = $this->subtotal_ex_tax + ($row_base_price - $tax_amount); } else { if ($_product->is_taxable()) { $tax_rates = $this->tax->get_rates($_product->get_tax_class()); $taxes = $this->tax->calc_tax($row_base_price, $tax_rates, false); $tax_amount = get_option('woocommerce_tax_round_at_subtotal') == 'no' ? $this->tax->get_tax_total($taxes) : array_sum($taxes); } // Sub total is based on base prices (without discounts) $this->subtotal = $this->subtotal + $row_base_price + $tax_amount; $this->subtotal_ex_tax = $this->subtotal_ex_tax + $row_base_price; } } } // Now calc the main totals, including discounts if ($this->prices_include_tax) { /** * Calculate totals for items */ if (sizeof($this->cart_contents) > 0) { foreach ($this->cart_contents as $cart_item_key => $values) { /** * 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 * * Used this excellent article for reference: * http://developer.practicalecommerce.com/articles/1473-Coding-for-Tax-Calculations-Everything-You-Never-Wanted-to-Know-Part-2 */ $_product = $values['data']; // Base Price (inclusive of tax for now) $base_price = $_product->get_price(); // Base Price Adjustment if ($_product->is_taxable()) { // Get rates $tax_rates = $this->tax->get_rates($_product->get_tax_class()); /** * ADJUST TAX - Calculations when customer is OUTSIDE the shop base country/state and prices INCLUDE tax * OR * ADJUST TAX - Calculations when a tax class is modified */ if ($woocommerce->customer->is_customer_outside_base() && (defined('WOOCOMMERCE_CHECKOUT') || $woocommerce->customer->has_calculated_shipping()) || $_product->get_tax_class() !== $_product->tax_class) { // Get tax rate for the store base, ensuring we use the unmodified tax_class for the product $base_tax_rates = $this->tax->get_shop_base_rate($_product->tax_class); // Work out new price based on region $row_base_price = $base_price * $values['quantity']; $base_taxes = $this->tax->calc_tax($row_base_price, $base_tax_rates, true, true); // Unrounded $taxes = $this->tax->calc_tax($row_base_price - array_sum($base_taxes), $tax_rates, false); // Tax amount $tax_amount = array_sum($taxes); // Line subtotal + tax $line_subtotal_tax = get_option('woocommerce_tax_round_at_subtotal') == 'no' ? $this->tax->round($tax_amount) : $tax_amount; $line_subtotal = $row_base_price - array_sum($base_taxes); // Adjusted price $adjusted_price = ($row_base_price - array_sum($base_taxes) + array_sum($taxes)) / $values['quantity']; // Apply discounts $discounted_price = $this->get_discounted_price($values, $adjusted_price, true); $discounted_taxes = $this->tax->calc_tax($discounted_price * $values['quantity'], $tax_rates, true); $discounted_tax_amount = array_sum($discounted_taxes); // Sum taxes /** * Regular tax calculation (customer inside base and the tax class is unmodified */ } else { // Base tax for line before discount - we will store this in the order data $tax_amount = array_sum($this->tax->calc_tax($base_price * $values['quantity'], $tax_rates, true)); // Line subtotal + tax $line_subtotal_tax = get_option('woocommerce_tax_round_at_subtotal') == 'no' ? $this->tax->round($tax_amount) : $tax_amount; $line_subtotal = $base_price * $values['quantity'] - $this->tax->round($line_subtotal_tax); // Calc prices and tax (discounted) $discounted_price = $this->get_discounted_price($values, $base_price, true); $discounted_taxes = $this->tax->calc_tax($discounted_price * $values['quantity'], $tax_rates, true); $discounted_tax_amount = array_sum($discounted_taxes); // Sum taxes } // 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); } } else { // Discounted Price (price with any pre-tax discounts applied) $discounted_price = $this->get_discounted_price($values, $base_price, true); $discounted_tax_amount = 0; $tax_amount = 0; $line_subtotal_tax = 0; $line_subtotal = $base_price * $values['quantity']; } // Line prices $line_tax = get_option('woocommerce_tax_round_at_subtotal') == 'no' ? $this->tax->round($discounted_tax_amount) : $discounted_tax_amount; $line_total = $discounted_price * $values['quantity'] - $this->tax->round($line_tax); // Add any product discounts (after tax) $this->apply_product_discounts_after_tax($values, $line_total + $discounted_tax_amount); // Cart contents total is based on discounted prices and is used for the final total calculation $this->cart_contents_total = $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; } } } else { if (sizeof($this->cart_contents) > 0) { foreach ($this->cart_contents as $cart_item_key => $values) { /** * Prices exclude tax * * This calculation is simpler - work with the base, untaxed price. */ $_product = $values['data']; // Base Price (i.e. no tax, regardless of region) $base_price = $_product->get_price(); // Discounted Price (base price with any pre-tax discounts applied $discounted_price = $this->get_discounted_price($values, $base_price, true); // Tax Amount (For the line, based on discounted, ex.tax price) if ($_product->is_taxable()) { // Get tax rates $tax_rates = $this->tax->get_rates($_product->get_tax_class()); // Base tax for line before discount - we will store this in the order data $tax_amount = array_sum($this->tax->calc_tax($base_price * $values['quantity'], $tax_rates, false)); // Now calc product rates $discounted_taxes = $this->tax->calc_tax($discounted_price * $values['quantity'], $tax_rates, false); $discounted_tax_amount = array_sum($discounted_taxes); // 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); } } else { $discounted_tax_amount = 0; $tax_amount = 0; } // Line prices $line_subtotal_tax = $tax_amount; $line_tax = $discounted_tax_amount; $line_subtotal = $base_price * $values['quantity']; $line_total = $discounted_price * $values['quantity']; // Add any product discounts (after tax) $this->apply_product_discounts_after_tax($values, $line_total + $line_tax); // Cart contents total is based on discounted prices and is used for the final total calculation $this->cart_contents_total = $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; } } } // Add fees foreach ($this->get_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); // Store $fee->tax = array_sum($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); } } } // Only calculate the grand total + shipping if on the cart/checkout if (is_checkout() || is_cart() || defined('WOOCOMMERCE_CHECKOUT') || defined('WOOCOMMERCE_CART')) { // Total up/round taxes if (get_option('woocommerce_tax_round_at_subtotal') == 'no') { $this->tax_total = $this->tax->get_tax_total($this->taxes); $this->taxes = array_map(array($this->tax, 'round'), $this->taxes); } else { $this->tax_total = array_sum($this->taxes); } // Cart Shipping $this->calculate_shipping(); // Total up/round taxes for shipping if (get_option('woocommerce_tax_round_at_subtotal') == 'no') { $this->shipping_tax_total = $this->tax->get_tax_total($this->shipping_taxes); $this->shipping_taxes = array_map(array($this->tax, 'round'), $this->shipping_taxes); } else { $this->shipping_tax_total = array_sum($this->shipping_taxes); } // VAT exemption done at this point - so all totals are correct before exemption if ($woocommerce->customer->is_vat_exempt()) { $this->remove_taxes(); } // Cart Discounts (after tax) $this->apply_cart_discounts_after_tax(); // 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, and any discounts to be added after tax (e.g. store credit) $this->total = max(0, apply_filters('woocommerce_calculated_total', number_format($this->cart_contents_total + $this->tax_total + $this->shipping_tax_total + $this->shipping_total - $this->discount_total + $this->fee_total, $this->dp, '.', ''), $this)); } else { // Set tax total to sum of all tax rows $this->tax_total = $this->tax->get_tax_total($this->taxes); // VAT exemption done at this point - so all totals are correct before exemption if ($woocommerce->customer->is_vat_exempt()) { $this->remove_taxes(); } // Cart Discounts (after tax) $this->apply_cart_discounts_after_tax(); } $this->set_session(); }
/** * Calculate taxes for all line items and shipping, and store the totals and tax rows. * * Will use the base country unless customer addresses are set. * * @return bool success or fail. */ public function calculate_taxes() { $tax_total = 0; $shipping_tax_total = 0; $taxes = array(); $shipping_taxes = array(); $tax_based_on = get_option('woocommerce_tax_based_on'); // If is_vat_exempt is 'yes', or wc_tax_enabled is false, return and do nothing. if ('yes' === $this->is_vat_exempt || !wc_tax_enabled()) { return false; } if ('billing' === $tax_based_on) { $country = $this->billing_country; $state = $this->billing_state; $postcode = $this->billing_postcode; $city = $this->billing_city; } elseif ('shipping' === $tax_based_on) { $country = $this->shipping_country; $state = $this->shipping_state; $postcode = $this->shipping_postcode; $city = $this->shipping_city; } // Default to base if ('base' === $tax_based_on || empty($country)) { $default = wc_get_base_location(); $country = $default['country']; $state = $default['state']; $postcode = ''; $city = ''; } // Get items foreach ($this->get_items(array('line_item', 'fee')) as $item_id => $item) { $product = $this->get_product_from_item($item); $line_total = isset($item['line_total']) ? $item['line_total'] : 0; $line_subtotal = isset($item['line_subtotal']) ? $item['line_subtotal'] : 0; $tax_class = $item['tax_class']; $item_tax_status = $product ? $product->get_tax_status() : 'taxable'; if ('0' !== $tax_class && 'taxable' === $item_tax_status) { $tax_rates = WC_Tax::find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $tax_class)); $line_subtotal_taxes = WC_Tax::calc_tax($line_subtotal, $tax_rates, false); $line_taxes = WC_Tax::calc_tax($line_total, $tax_rates, false); $line_subtotal_tax = max(0, array_sum($line_subtotal_taxes)); $line_tax = max(0, array_sum($line_taxes)); $tax_total += $line_tax; wc_update_order_item_meta($item_id, '_line_subtotal_tax', wc_format_decimal($line_subtotal_tax)); wc_update_order_item_meta($item_id, '_line_tax', wc_format_decimal($line_tax)); wc_update_order_item_meta($item_id, '_line_tax_data', array('total' => $line_taxes, 'subtotal' => $line_subtotal_taxes)); // Sum the item taxes foreach (array_keys($taxes + $line_taxes) as $key) { $taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($taxes[$key]) ? $taxes[$key] : 0); } } } // Calc taxes for shipping foreach ($this->get_shipping_methods() as $item_id => $item) { $shipping_tax_class = get_option('woocommerce_shipping_tax_class'); // Inherit tax class from items if ('' === $shipping_tax_class) { $tax_classes = WC_Tax::get_tax_classes(); foreach ($tax_classes as $tax_class) { $tax_class = sanitize_title($tax_class); if (in_array($tax_class, $found_tax_classes)) { $tax_rates = WC_Tax::find_shipping_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $tax_class)); break; } } } else { $tax_rates = WC_Tax::find_shipping_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => 'standard' === $shipping_tax_class ? '' : $shipping_tax_class)); } $line_taxes = WC_Tax::calc_tax($item['cost'], $tax_rates, false); $line_tax = max(0, array_sum($line_taxes)); $shipping_tax_total += $line_tax; wc_update_order_item_meta($item_id, '_line_tax', wc_format_decimal($line_tax)); wc_update_order_item_meta($item_id, '_line_tax_data', array('total' => $line_taxes)); // Sum the item taxes foreach (array_keys($shipping_taxes + $line_taxes) as $key) { $shipping_taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($shipping_taxes[$key]) ? $shipping_taxes[$key] : 0); } } // Save tax totals $this->set_total($shipping_tax_total, 'shipping_tax'); $this->set_total($tax_total, 'tax'); // Tax rows $this->remove_order_items('tax'); // Now merge to keep tax rows foreach (array_keys($taxes + $shipping_taxes) as $tax_rate_id) { $this->add_tax($tax_rate_id, isset($taxes[$tax_rate_id]) ? $taxes[$tax_rate_id] : 0, isset($shipping_taxes[$tax_rate_id]) ? $shipping_taxes[$tax_rate_id] : 0); } return true; }
/** * 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); }
function vtmin_load_vtmin_cart_for_processing() { global $wpdb, $woocommerce, $vtmin_cart, $vtmin_cart_item, $vtmin_info; // from Woocommerce/templates/cart/mini-cart.php and Woocommerce/templates/checkout/review-order.php if (sizeof($woocommerce->cart->get_cart()) > 0) { $woocommerce->cart->calculate_totals(); //v1.09.3 calculation includes generating line subtotals, used below $vtmin_cart = new VTMIN_Cart(); foreach ($woocommerce->cart->get_cart() as $cart_item_key => $cart_item) { $_product = $cart_item['data']; if ($_product->exists() && $cart_item['quantity'] > 0) { $vtmin_cart_item = new VTMIN_Cart_Item(); //the product id does not change in woo if variation purchased. // Load expected variation id, if there, along with constructed product title. $varLabels = ' '; if ($cart_item['variation_id'] > ' ') { // get parent title $parent_post = get_post($cart_item['product_id']); // get variation names to string onto parent title foreach ($cart_item['variation'] as $key => $value) { $varLabels .= $value . ' '; } $vtmin_cart_item->product_id = $cart_item['variation_id']; $vtmin_cart_item->product_name = $parent_post->post_title . ' ' . $varLabels; } else { $vtmin_cart_item->product_id = $cart_item['product_id']; $vtmin_cart_item->product_name = $_product->get_title() . $woocommerce->cart->get_item_data($cart_item); } $vtmin_cart_item->quantity = $cart_item['quantity']; //v1.09.3 commented unit price //$vtmin_cart_item->unit_price = get_option( 'woocommerce_display_cart_prices_excluding_tax' ) == 'yes' || $woocommerce->customer->is_vat_exempt() ? $_product->get_price_excluding_tax() : $_product->get_price(); /* $quantity = 1; //v1.08 vat fix $vtmin_cart_item->unit_price = get_option( 'woocommerce_display_cart_prices_excluding_tax' ) == 'yes' || $woocommerce->customer->is_vat_exempt() ? $_product->get_price_excluding_tax() : $_product->get_price_including_tax( $quantity ); //$_product->get_price(); //v1.08 vat fix */ //v1.09.3 begin // pick up unit price from line subtotal only - // will include all taxation and price adjustments from other plugins //$vtmin_cart_item->total_price = $vtmin_cart_item->quantity * $vtmin_cart_item->unit_price; // $vtmin_cart_item->total_price = $cart_item['line_subtotal']; // $vtmin_cart_item->unit_price = $cart_item['line_subtotal'] / $cart_item['quantity']; if (get_option('woocommerce_calc_taxes') == 'no' || get_option('woocommerce_prices_include_tax') == 'no') { //NO VAT included in price $vtmin_cart_item->unit_price = $cart_item['line_subtotal'] / $cart_item['quantity']; $vtmin_cart_item->total_price = $cart_item['line_subtotal']; } else { //v1.0.7.4 begin //TAX included in price in DB, and Woo $cart_item pricing **has already subtracted out the TAX **, so restore the TAX // this price reflects the tax situation of the ORIGINAL price - so if the price was originally entered with tax, this will reflect tax $price = $cart_item['line_subtotal'] / $cart_item['quantity']; $qty = 1; $_tax = new WC_Tax(); // $product = get_product( $product_id ); $product = get_product($vtmin_cart_item->product_id); $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); $vtmin_cart_item->unit_price = round($price * $qty + $tax_amount, absint(get_option('woocommerce_price_num_decimals'))); $vtmin_cart_item->total_price = $vtmin_cart_item->unit_price * $cart_item['quantity']; } //v1.09.3 end /* ********************************* *** JUST the cat *ids* please... ************************************ */ $vtmin_cart_item->prod_cat_list = wp_get_object_terms($cart_item['product_id'], $vtmin_info['parent_plugin_taxonomy'], $args = array('fields' => 'ids')); $vtmin_cart_item->rule_cat_list = wp_get_object_terms($cart_item['product_id'], $vtmin_info['rulecat_taxonomy'], $args = array('fields' => 'ids')); //add cart_item to cart array $vtmin_cart->cart_items[] = $vtmin_cart_item; } } // endforeach; } }
public static function add_line_taxes($order_id) { global $wpdb; check_ajax_referer('calc-totals', 'security'); if (!current_user_can('edit_shop_orders')) { die(-1); } $tax = new WC_Tax(); $items = array(); $country = strtoupper(esc_attr($_POST['country'])); $state = strtoupper(esc_attr($_POST['state'])); $postcode = strtoupper(esc_attr($_POST['postcode'])); $city = wc_clean(esc_attr($_POST['city'])); $order = wc_get_order(absint($order_id)); $taxes = array(); $shipping_taxes = array(); // Parse the jQuery serialized items parse_str($_POST['items'], $items); // Prevent undefined warnings if (!isset($items['line_tax'])) { $items['line_tax'] = array(); } if (!isset($items['line_subtotal_tax'])) { $items['line_subtotal_tax'] = array(); } $items['order_taxes'] = array(); // Action $items = apply_filters('woocommerce_ajax_calc_line_taxes', $items, $order_id, $country, $_POST); // Get items and fees taxes if (isset($items['order_item_id'])) { $line_total = $line_subtotal = $order_item_tax_class = array(); foreach ($items['order_item_id'] as $parent_item_id) { $parent_item_id = absint($parent_item_id); $item_id = self::get_child_item_id($parent_item_id); if (empty($item_id)) { //no current suborder items continue; } $line_total[$item_id] = isset($items['line_total'][$parent_item_id]) ? wc_format_decimal($items['line_total'][$parent_item_id]) : 0; $line_subtotal[$item_id] = isset($items['line_subtotal'][$parent_item_id]) ? wc_format_decimal($items['line_subtotal'][$parent_item_id]) : $line_total[$parent_item_id]; $order_item_tax_class[$item_id] = isset($items['order_item_tax_class'][$parent_item_id]) ? sanitize_text_field($items['order_item_tax_class'][$parent_item_id]) : ''; $product_id = $order->get_item_meta($item_id, '_product_id', true); $vendor = yith_get_vendor($product_id, 'product'); if (!$vendor->is_valid()) { // no vnedor products continue; } $vendor_order_ids = $vendor->get_orders('suborder'); if (!in_array($order_id, $vendor_order_ids)) { // the current product isn't in the current suborder continue; } // Get product details if (get_post_type($product_id) == 'product') { $_product = wc_get_product($product_id); $item_tax_status = $_product->get_tax_status(); } else { $item_tax_status = 'taxable'; } if ('0' !== $order_item_tax_class[$item_id] && 'taxable' === $item_tax_status) { $tax_rates = WC_Tax::find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $order_item_tax_class[$item_id])); $line_taxes = WC_Tax::calc_tax($line_total[$item_id], $tax_rates, false); $line_subtotal_taxes = WC_Tax::calc_tax($line_subtotal[$item_id], $tax_rates, false); // Set the new line_tax foreach ($line_taxes as $_tax_id => $_tax_value) { $items['line_tax'][$item_id][$_tax_id] = $_tax_value; } // Set the new line_subtotal_tax foreach ($line_subtotal_taxes as $_tax_id => $_tax_value) { $items['line_subtotal_tax'][$item_id][$_tax_id] = $_tax_value; } // Sum the item taxes foreach (array_keys($taxes + $line_taxes) as $key) { $taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($taxes[$key]) ? $taxes[$key] : 0); } } } } // Get shipping taxes if (isset($items['shipping_method_id'])) { $matched_tax_rates = array(); $tax_rates = WC_Tax::find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => '')); if ($tax_rates) { foreach ($tax_rates as $key => $rate) { if (isset($rate['shipping']) && 'yes' == $rate['shipping']) { $matched_tax_rates[$key] = $rate; } } } $shipping_cost = $shipping_taxes = array(); foreach ($items['shipping_method_id'] as $item_id) { $item_id = absint($item_id); $shipping_cost[$item_id] = isset($items['shipping_cost'][$parent_item_id]) ? wc_format_decimal($items['shipping_cost'][$parent_item_id]) : 0; $_shipping_taxes = WC_Tax::calc_shipping_tax($shipping_cost[$item_id], $matched_tax_rates); // Set the new shipping_taxes foreach ($_shipping_taxes as $_tax_id => $_tax_value) { $items['shipping_taxes'][$item_id][$_tax_id] = $_tax_value; $shipping_taxes[$_tax_id] = isset($shipping_taxes[$_tax_id]) ? $shipping_taxes[$_tax_id] + $_tax_value : $_tax_value; } } } // Remove old tax rows $order->remove_order_items('tax'); // Add tax rows foreach (array_keys($taxes + $shipping_taxes) as $tax_rate_id) { $order->add_tax($tax_rate_id, isset($taxes[$tax_rate_id]) ? $taxes[$tax_rate_id] : 0, isset($shipping_taxes[$tax_rate_id]) ? $shipping_taxes[$tax_rate_id] : 0); } // Create the new order_taxes foreach ($order->get_taxes() as $tax_id => $tax_item) { $items['order_taxes'][$tax_id] = absint($tax_item['rate_id']); } foreach ($items as $meta_key => $meta_values) { if (is_array($meta_values)) { foreach ($meta_values as $key => $meta_value) { if ('order_taxes' == $meta_key) { continue; } else { if ('order_item_id' == $meta_key) { $child_item_id = self::get_child_item_id($meta_value); if ($child_item_id) { $items[$meta_key][$key] = $child_item_id; } else { unset($items[$meta_key][$key]); } } else { if ('meta_key' == $meta_key || 'meta_value' == $meta_key) { unset($items[$meta_key][$key]); } else { if ('line_tax' == $meta_key || 'line_subtotal_tax' == $meta_key || 'refund_line_tax' == $meta_key) { $line_tax_ids = $items[$meta_key]; $child_item_ids = array_keys($order->get_items()); foreach ($line_tax_ids as $line_tax_id => $line_tax_value) { if (!in_array($line_tax_id, $child_item_ids)) { unset($items[$meta_key][$line_tax_id]); } } } else { $child_item_id = self::get_child_item_id($meta_value); if ($child_item_id) { $items[$meta_key][$child_item_id] = $items[$meta_key][$key]; unset($items[$meta_key][$key]); } } } } } } } else { if ('_order_total' == $meta_key) { $items['_order_total'] = $order->get_total(); } } } if (!empty($items['order_item_id'])) { wc_save_order_items($order_id, $items); } }
/** * Calculate recurring line taxes when a store manager clicks the "Calc Line Tax" button on the "Edit Order" page. * * Based on the @see woocommerce_calc_line_taxes() function. * @since 1.2.4 * @return void */ public static function calculate_recurring_line_taxes() { global $woocommerce, $wpdb; check_ajax_referer('woocommerce-subscriptions', 'security'); $tax = new WC_Tax(); $taxes = $tax_rows = $item_taxes = $shipping_taxes = $return = array(); $item_tax = 0; $order_id = absint($_POST['order_id']); $country = strtoupper(esc_attr($_POST['country'])); $state = strtoupper(esc_attr($_POST['state'])); $postcode = strtoupper(esc_attr($_POST['postcode'])); $tax_class = esc_attr($_POST['tax_class']); if (isset($_POST['city'])) { $city = sanitize_title(esc_attr($_POST['city'])); } $shipping = $_POST['shipping']; $line_subtotal = isset($_POST['line_subtotal']) ? esc_attr($_POST['line_subtotal']) : 0; $line_total = isset($_POST['line_total']) ? esc_attr($_POST['line_total']) : 0; $product_id = ''; if (isset($_POST['order_item_id'])) { $product_id = woocommerce_get_order_item_meta($_POST['order_item_id'], '_product_id'); } elseif (isset($_POST['product_id'])) { $product_id = esc_attr($_POST['product_id']); } if (!empty($product_id) && WC_Subscriptions_Product::is_subscription($product_id)) { // Get product details $product = WC_Subscriptions::get_product($product_id); $item_tax_status = $product->get_tax_status(); if ($item_tax_status == 'taxable') { $tax_rates = $tax->find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $tax_class)); $line_subtotal_taxes = $tax->calc_tax($line_subtotal, $tax_rates, false); $line_taxes = $tax->calc_tax($line_total, $tax_rates, false); $line_subtotal_tax = $tax->round(array_sum($line_subtotal_taxes)); $line_tax = $tax->round(array_sum($line_taxes)); if ($line_subtotal_tax < 0) { $line_subtotal_tax = 0; } if ($line_tax < 0) { $line_tax = 0; } $return = array('recurring_line_subtotal_tax' => $line_subtotal_tax, 'recurring_line_tax' => $line_tax); // Sum the item taxes foreach (array_keys($taxes + $line_taxes) as $key) { $taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($taxes[$key]) ? $taxes[$key] : 0); } } // Now calculate shipping tax $matched_tax_rates = array(); $tax_rates = $tax->find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => '')); if ($tax_rates) { foreach ($tax_rates as $key => $rate) { if (isset($rate['shipping']) && $rate['shipping'] == 'yes') { $matched_tax_rates[$key] = $rate; } } } $shipping_taxes = $tax->calc_shipping_tax($shipping, $matched_tax_rates); $shipping_tax = $tax->round(array_sum($shipping_taxes)); $return['recurring_shipping_tax'] = $shipping_tax; // Remove old tax rows $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'recurring_tax' )", $order_id)); $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'recurring_tax'", $order_id)); // Get tax rates $rates = $wpdb->get_results("SELECT tax_rate_id, tax_rate_country, tax_rate_state, tax_rate_name, tax_rate_priority FROM {$wpdb->prefix}woocommerce_tax_rates ORDER BY tax_rate_name"); $tax_codes = array(); foreach ($rates as $rate) { $code = array(); $code[] = $rate->tax_rate_country; $code[] = $rate->tax_rate_state; $code[] = $rate->tax_rate_name ? sanitize_title($rate->tax_rate_name) : 'TAX'; $code[] = absint($rate->tax_rate_priority); $tax_codes[$rate->tax_rate_id] = strtoupper(implode('-', array_filter($code))); } // Now merge to keep tax rows ob_start(); foreach (array_keys($taxes + $shipping_taxes) as $key) { $item = array(); $item['rate_id'] = $key; $item['name'] = $tax_codes[$key]; $item['label'] = $tax->get_rate_label($key); $item['compound'] = $tax->is_compound($key) ? 1 : 0; $item['tax_amount'] = $tax->round(isset($taxes[$key]) ? $taxes[$key] : 0); $item['shipping_tax_amount'] = $tax->round(isset($shipping_taxes[$key]) ? $shipping_taxes[$key] : 0); if (!$item['label']) { $item['label'] = $woocommerce->countries->tax_or_vat(); } // Add line item $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $item['name'], 'order_item_type' => 'recurring_tax')); // Add line item meta if ($item_id) { woocommerce_add_order_item_meta($item_id, 'rate_id', $item['rate_id']); woocommerce_add_order_item_meta($item_id, 'label', $item['label']); woocommerce_add_order_item_meta($item_id, 'compound', $item['compound']); woocommerce_add_order_item_meta($item_id, 'tax_amount', $item['tax_amount']); woocommerce_add_order_item_meta($item_id, 'shipping_tax_amount', $item['shipping_tax_amount']); } include plugin_dir_path(WC_Subscriptions::$plugin_file) . 'templates/admin/post-types/writepanels/order-tax-html.php'; } $return['tax_row_html'] = ob_get_clean(); echo json_encode($return); } die; }
function woocommerce_calc_line_taxes() { global $woocommerce; check_ajax_referer('calc-totals', 'security'); $tax = new WC_Tax(); $base_tax_amount = 0; $line_tax_amount = 0; $country = strtoupper(esc_attr($_POST['country'])); $state = strtoupper(esc_attr($_POST['state'])); $postcode = strtoupper(esc_attr($_POST['postcode'])); $line_subtotal = esc_attr($_POST['line_subtotal']); $line_total = esc_attr($_POST['line_total']); $item_id = esc_attr($_POST['item_id']); $tax_class = esc_attr($_POST['tax_class']); // Get product details $_product = new WC_Product($item_id); $item_tax_status = $_product->get_tax_status(); if ($item_tax_status == 'taxable') { $tax_rates = $tax->find_rates($country, $state, $postcode, $tax_class); $line_subtotal_tax_amount = rtrim(rtrim(number_format(array_sum($tax->calc_tax($line_subtotal, $tax_rates, false)), 4, '.', ''), '0'), '.'); $line_tax_amount = rtrim(rtrim(number_format(array_sum($tax->calc_tax($line_total, $tax_rates, false)), 4, '.', ''), '0'), '.'); } if ($line_subtotal_tax_amount < 0) { $line_subtotal_tax_amount = 0; } if ($line_tax_amount < 0) { $line_tax_amount = 0; } echo json_encode(array('line_subtotal_tax' => $line_subtotal_tax_amount, 'line_tax' => $line_tax_amount)); // Quit out die; }
/** * 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); }
/** * Calc line tax * * @access public * @return void */ function woocommerce_calc_line_taxes() { global $woocommerce, $wpdb; check_ajax_referer('calc-totals', 'security'); header('Content-Type: application/json; charset=utf-8'); $tax = new WC_Tax(); $taxes = $tax_rows = $item_taxes = $shipping_taxes = array(); $order_id = absint($_POST['order_id']); $country = strtoupper(esc_attr($_POST['country'])); $state = strtoupper(esc_attr($_POST['state'])); $postcode = strtoupper(esc_attr($_POST['postcode'])); $city = sanitize_title(esc_attr($_POST['city'])); $items = $_POST['items']; $shipping = $_POST['shipping']; $item_tax = 0; // Calculate sales tax first if (sizeof($items) > 0) { foreach ($items as $item_id => $item) { $item_id = absint($item_id); $line_subtotal = isset($item['line_subtotal']) ? esc_attr($item['line_subtotal']) : ''; $line_total = esc_attr($item['line_total']); $tax_class = esc_attr($item['tax_class']); if (!$item_id || $tax_class == '0') { continue; } // Get product details if (get_post_type($item_id) == 'product') { $_product = get_product($item_id); $item_tax_status = $_product->get_tax_status(); } else { $item_tax_status = 'taxable'; } // Only calc if taxable if ($item_tax_status == 'taxable') { $tax_rates = $tax->find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $tax_class)); $line_subtotal_taxes = $tax->calc_tax($line_subtotal, $tax_rates, false); $line_taxes = $tax->calc_tax($line_total, $tax_rates, false); $line_subtotal_tax = $tax->round(array_sum($line_subtotal_taxes)); $line_tax = $tax->round(array_sum($line_taxes)); //$line_subtotal_tax = rtrim( rtrim( number_format( array_sum( $line_subtotal_taxes ), 4, '.', '' ), '0' ), '.' ); //$line_tax = rtrim( rtrim( number_format( array_sum( $line_taxes ), 4, '.', '' ), '0' ), '.' ); if ($line_subtotal_tax < 0) { $line_subtotal_tax = 0; } if ($line_tax < 0) { $line_tax = 0; } $item_taxes[$item_id] = array('line_subtotal_tax' => $line_subtotal_tax, 'line_tax' => $line_tax); $item_tax += $line_tax; // Sum the item taxes foreach (array_keys($taxes + $line_taxes) as $key) { $taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($taxes[$key]) ? $taxes[$key] : 0); } } } } // Now calculate shipping tax $matched_tax_rates = array(); $tax_rates = $tax->find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => '')); if ($tax_rates) { foreach ($tax_rates as $key => $rate) { if (isset($rate['shipping']) && $rate['shipping'] == 'yes') { $matched_tax_rates[$key] = $rate; } } } $shipping_taxes = $tax->calc_shipping_tax($shipping, $matched_tax_rates); //$shipping_tax = rtrim( rtrim( number_format( array_sum( $shipping_taxes ), 2, '.', '' ), '0' ), '.' ); $shipping_tax = $tax->round(array_sum($shipping_taxes)); // Remove old tax rows $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'tax' )", $order_id)); $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'tax'", $order_id)); // Get tax rates $rates = $wpdb->get_results("SELECT tax_rate_id, tax_rate_country, tax_rate_state, tax_rate_name, tax_rate_priority FROM {$wpdb->prefix}woocommerce_tax_rates ORDER BY tax_rate_name"); $tax_codes = array(); foreach ($rates as $rate) { $code = array(); $code[] = $rate->tax_rate_country; $code[] = $rate->tax_rate_state; $code[] = $rate->tax_rate_name ? sanitize_title($rate->tax_rate_name) : 'TAX'; $code[] = absint($rate->tax_rate_priority); $tax_codes[$rate->tax_rate_id] = strtoupper(implode('-', array_filter($code))); } // Now merge to keep tax rows ob_start(); foreach (array_keys($taxes + $shipping_taxes) as $key) { $item = array(); $item['rate_id'] = $key; $item['name'] = $tax_codes[$key]; $item['label'] = $tax->get_rate_label($key); $item['compound'] = $tax->is_compound($key) ? 1 : 0; $item['tax_amount'] = $tax->round(isset($taxes[$key]) ? $taxes[$key] : 0); $item['shipping_tax_amount'] = $tax->round(isset($shipping_taxes[$key]) ? $shipping_taxes[$key] : 0); if (!$item['label']) { $item['label'] = $woocommerce->countries->tax_or_vat(); } // Add line item $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $item['name'], 'order_item_type' => 'tax')); // Add line item meta if ($item_id) { woocommerce_add_order_item_meta($item_id, 'rate_id', $item['rate_id']); woocommerce_add_order_item_meta($item_id, 'label', $item['label']); woocommerce_add_order_item_meta($item_id, 'compound', $item['compound']); woocommerce_add_order_item_meta($item_id, 'tax_amount', $item['tax_amount']); woocommerce_add_order_item_meta($item_id, 'shipping_tax_amount', $item['shipping_tax_amount']); } include 'admin/post-types/writepanels/order-tax-html.php'; } $tax_row_html = ob_get_clean(); // Return echo json_encode(array('item_tax' => $item_tax, 'item_taxes' => $item_taxes, 'shipping_tax' => $shipping_tax, 'tax_row_html' => $tax_row_html)); // Quit out die; }
/** * * Gets product taxes for custom price * * @param int $total - custom price excluding taxes * @param str $item_tax_class * @return array $taxes * **/ public function easy_booking_get_product_taxes($total, $item_tax_class) { $tax_rates = WC_Tax::get_rates($item_tax_class); $taxes = WC_Tax::calc_tax($total, $tax_rates, false); return $taxes; }
/** * Calculate taxes for all line items and shipping, and store the totals and tax rows. * * Will use the base country unless customer addresses are set. * @param $args array Added in 2.7.0 to pass things like location. */ public function calculate_taxes($args = array()) { $tax_based_on = get_option('woocommerce_tax_based_on'); $args = wp_parse_args($args, array('country' => 'billing' === $tax_based_on ? $this->get_billing_country() : $this->get_shipping_country(), 'state' => 'billing' === $tax_based_on ? $this->get_billing_state() : $this->get_shipping_state(), 'postcode' => 'billing' === $tax_based_on ? $this->get_billing_postcode() : $this->get_shipping_postcode(), 'city' => 'billing' === $tax_based_on ? $this->get_billing_city() : $this->get_shipping_city())); // Default to base if ('base' === $tax_based_on || empty($args['country'])) { $default = wc_get_base_location(); $args['country'] = $default['country']; $args['state'] = $default['state']; $args['postcode'] = ''; $args['city'] = ''; } // Calc taxes for line items foreach ($this->get_items(array('line_item', 'fee')) as $item_id => $item) { $tax_class = $item->get_tax_class(); $tax_status = $item->get_tax_status(); if ('0' !== $tax_class && 'taxable' === $tax_status) { $tax_rates = WC_Tax::find_rates(array('country' => $args['country'], 'state' => $args['state'], 'postcode' => $args['postcode'], 'city' => $args['city'], 'tax_class' => $tax_class)); $total = $item->get_total(); $taxes = WC_Tax::calc_tax($total, $tax_rates, false); if ($item->is_type('line_item')) { $subtotal = $item->get_subtotal(); $subtotal_taxes = WC_Tax::calc_tax($subtotal, $tax_rates, false); $item->set_taxes(array('total' => $taxes, 'subtotal' => $subtotal_taxes)); } else { $item->set_taxes(array('total' => $taxes)); } $item->save(); } } // Calc taxes for shipping foreach ($this->get_shipping_methods() as $item_id => $item) { $shipping_tax_class = get_option('woocommerce_shipping_tax_class'); // Inherit tax class from items if ('' === $shipping_tax_class) { $tax_rates = array(); $tax_classes = array_merge(array(''), WC_Tax::get_tax_classes()); $found_tax_classes = $this->get_items_tax_classes(); foreach ($tax_classes as $tax_class) { $tax_class = sanitize_title($tax_class); if (in_array($tax_class, $found_tax_classes)) { $tax_rates = WC_Tax::find_shipping_rates(array('country' => $args['country'], 'state' => $args['state'], 'postcode' => $args['postcode'], 'city' => $args['city'], 'tax_class' => $tax_class)); break; } } } else { $tax_rates = WC_Tax::find_shipping_rates(array('country' => $args['country'], 'state' => $args['state'], 'postcode' => $args['postcode'], 'city' => $args['city'], 'tax_class' => 'standard' === $shipping_tax_class ? '' : $shipping_tax_class)); } $item->set_taxes(array('total' => WC_Tax::calc_tax($item->get_total(), $tax_rates, false))); $item->save(); } $this->update_taxes(); }
/** * 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(); }
/** * 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); }
/** * 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; }