Filter example: to return rounding to .5 cents you'd use:
function euro_5cent_rounding( $in ) {
return round( $in / 5, 2 ) * 5;
}
add_filter( 'woocommerce_tax_round', 'euro_5cent_rounding' );
public static round ( $in ) : double | ||
Résultat | double |
/** * Gets order total - formatted for display. * * @return string */ public function get_formatted_order_total($tax_display = '', $display_refunded = true) { $formatted_total = wc_price($this->get_total(), array('currency' => $this->get_order_currency())); $order_total = $this->get_total(); $total_refunded = $this->get_total_refunded(); $tax_string = ''; // Tax for inclusive prices if (wc_tax_enabled() && 'incl' == $tax_display) { $tax_string_array = array(); if ('itemized' == get_option('woocommerce_tax_total_display')) { foreach ($this->get_tax_totals() as $code => $tax) { $tax_amount = $total_refunded && $display_refunded ? wc_price(WC_Tax::round($tax->amount - $this->get_total_tax_refunded_by_rate_id($tax->rate_id)), array('currency' => $this->get_order_currency())) : $tax->formatted_amount; $tax_string_array[] = sprintf('%s %s', $tax_amount, $tax->label); } } else { $tax_amount = $total_refunded && $display_refunded ? $this->get_total_tax() - $this->get_total_tax_refunded() : $this->get_total_tax(); $tax_string_array[] = sprintf('%s %s', wc_price($tax_amount, array('currency' => $this->get_order_currency())), WC()->countries->tax_or_vat()); } if (!empty($tax_string_array)) { $tax_string = ' ' . sprintf(__('(Includes %s)', 'woocommerce'), implode(', ', $tax_string_array)); } } if ($total_refunded && $display_refunded) { $formatted_total = '<del>' . strip_tags($formatted_total) . '</del> <ins>' . wc_price($order_total - $total_refunded, array('currency' => $this->get_order_currency())) . $tax_string . '</ins>'; } else { $formatted_total .= $tax_string; } return apply_filters('woocommerce_get_formatted_order_total', $formatted_total, $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; }
/** * 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; }
public static function calculate_shipping_tax($shipping_amount, $order) { $tax_based_on = get_option('woocommerce_tax_based_on'); $wc_tax_enabled = get_option('woocommerce_calc_taxes'); // if taxes aren't enabled don't calculate them if ('no' === $wc_tax_enabled) { return 0; } if ('base' === $tax_based_on) { $default = wc_get_base_location(); $country = $default['country']; $state = $default['state']; $postcode = ''; $city = ''; } elseif ('billing' === $tax_based_on) { $country = $order->billing_country; $state = $order->billing_state; $postcode = $order->billing_postcode; $city = $order->billing_city; } else { $country = $order->shipping_country; $state = $order->shipping_state; $postcode = $order->shipping_postcode; $city = $order->shipping_city; } // 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 ($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($shipping_amount, $matched_tax_rates); $shipping_tax_total = WC_Tax::round(array_sum($shipping_taxes)); return $shipping_tax_total; }
/** * Get totals for display on pages and in emails. * * @return array */ public function get_order_item_totals($tax_display = '') { if (!$tax_display) { $tax_display = $this->tax_display_cart; } $total_rows = array(); if ($subtotal = $this->get_subtotal_to_display(false, $tax_display)) { $total_rows['cart_subtotal'] = array('label' => __('Subtotal:', 'woocommerce'), 'value' => $subtotal); } if ($this->get_total_discount() > 0) { $total_rows['discount'] = array('label' => __('Discount:', 'woocommerce'), 'value' => '-' . $this->get_discount_to_display()); } if ($this->get_shipping_method()) { $total_rows['shipping'] = array('label' => __('Shipping:', 'woocommerce'), 'value' => $this->get_shipping_to_display()); } if ($fees = $this->get_fees()) { foreach ($fees as $id => $fee) { if (apply_filters('woocommerce_get_order_item_totals_excl_free_fees', $fee['line_total'] + $fee['line_tax'] == 0, $id)) { continue; } if ('excl' == $tax_display) { $total_rows['fee_' . $id] = array('label' => $fee['name'] . ':', 'value' => wc_price($fee['line_total'], array('currency' => $this->get_order_currency()))); } else { $total_rows['fee_' . $id] = array('label' => $fee['name'] . ':', 'value' => wc_price($fee['line_total'] + $fee['line_tax'], array('currency' => $this->get_order_currency()))); } } } // Tax for tax exclusive prices if ('excl' == $tax_display) { if (get_option('woocommerce_tax_total_display') == 'itemized') { foreach ($this->get_tax_totals() as $code => $tax) { $total_rows[sanitize_title($code)] = array('label' => $tax->label . ':', 'value' => $tax->formatted_amount); } } else { $total_rows['tax'] = array('label' => WC()->countries->tax_or_vat() . ':', 'value' => wc_price($this->get_total_tax(), array('currency' => $this->get_order_currency()))); } } if ($this->get_total() > 0 && $this->payment_method_title) { $total_rows['payment_method'] = array('label' => __('Payment Method:', 'woocommerce'), 'value' => $this->payment_method_title); } // Order total display $order_total = $this->get_total(); $total_refunded = $this->get_total_refunded(); $total_string = ''; $tax_string = ''; // Tax for inclusive prices if (wc_tax_enabled() && 'incl' == $tax_display) { $tax_string_array = array(); if ('itemized' == get_option('woocommerce_tax_total_display')) { foreach ($this->get_tax_totals() as $code => $tax) { $tax_amount = $total_refunded ? wc_price(WC_Tax::round($tax->amount - $this->get_total_tax_refunded_by_rate_id($tax->rate_id)), array('currency' => $this->get_order_currency())) : $tax->formatted_amount; $tax_string_array[] = sprintf('%s %s', $tax_amount, $tax->label); } } else { $tax_string_array[] = sprintf('%s %s', wc_price($this->get_total_tax() - $this->get_total_tax_refunded(), array('currency' => $this->get_order_currency())), WC()->countries->tax_or_vat()); } if (!empty($tax_string_array)) { $tax_string = ' ' . sprintf(__('(Includes %s)', 'woocommerce'), implode(', ', $tax_string_array)); } } if ($total_refunded) { $total_string = '<del>' . strip_tags($this->get_formatted_order_total()) . '</del> <ins>'; $total_string .= wc_price($order_total - $total_refunded, array('currency' => $this->get_order_currency())); $total_string .= $tax_string; $total_string .= '</ins>'; } else { $total_string = $this->get_formatted_order_total() . $tax_string; } $total_rows['order_total'] = array('label' => __('Total:', 'woocommerce'), 'value' => $total_string); return apply_filters('woocommerce_get_order_item_totals', $total_rows, $this); }
?> <?php if (wc_tax_enabled()) { ?> <?php foreach ($order->get_tax_totals() as $code => $tax) { ?> <tr> <td class="label"><?php echo $tax->label; ?> :</td> <td class="total"><?php if (($refunded = $order->get_total_tax_refunded_by_rate_id($tax->rate_id)) > 0) { echo '<del>' . strip_tags($tax->formatted_amount) . '</del> <ins>' . wc_price(WC_Tax::round($tax->amount, wc_get_price_decimals()) - WC_Tax::round($refunded, wc_get_price_decimals()), array('currency' => $order->get_order_currency())) . '</ins>'; } else { echo $tax->formatted_amount; } ?> </td> <td width="1%"></td> </tr> <?php } ?> <?php } ?> <?php
/** * * @param object $_this * @return number */ function this_get_tax($_this) { $cart = $_this->get_cart(); $tax = $tax_rates = array(); foreach ($cart as $cart_item_key => $values) { $_product = $values['data']; // Prices $base_price = $_product->get_price(); $line_price = $_product->get_price() * $values['quantity']; // Tax data $taxes = array(); $discounted_taxes = array(); /** * No tax to calculate */ if (!$_product->is_taxable()) { // Discounted Price (price with any pre-tax discounts applied) $discounted_price = 0; $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 * * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing with out of base locations. * e.g. If a product costs 10 including tax, all users will pay 10 regardless of location and taxes. * This feature is experimental @since 2.4.7 and may change in the future. Use at your risk. */ if ($item_tax_rates !== $base_tax_rates && apply_filters('woocommerce_adjust_non_base_location_prices', true)) { // Work out a new base price without the shop's base tax $taxes = WC_Tax::calc_tax($line_price, $base_tax_rates, true, true); // Now we have a new item price (excluding TAX) $line_subtotal = round($line_price - array_sum($taxes), WC_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 = 0; $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) { $tax[$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 = 0; $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) { $tax[$key] = (isset($discounted_taxes[$key]) ? $discounted_taxes[$key] : 0) + (isset($_this->taxes[$key]) ? $_this->taxes[$key] : 0); } } $tax = array_map(array('WC_Tax', 'round'), $tax); $tax = array_sum($tax); $shipping_total = WC()->shipping->shipping_total; $shipping_taxes = WC()->shipping->shipping_taxes; if ($_this->round_at_subtotal) { $shipping_tax_total = WC_Tax::get_tax_total($shipping_taxes); $shipping_taxes = array_map(array('WC_Tax', 'round'), $shipping_taxes); } else { $shipping_tax_total = array_sum($shipping_taxes); } $array = array('tax' => $tax, 'shipping' => $shipping_total, 'shipping_tax' => $shipping_tax_total); } return $array; }
/** * Update tax lines at order level by looking at the line item taxes themselves. * * @return bool success or fail */ public function update_taxes() { $order_taxes = array(); $order_shipping_taxes = array(); foreach ($this->get_items(array('line_item', 'fee')) as $item_id => $item) { $line_tax_data = maybe_unserialize($item['line_tax_data']); if (isset($line_tax_data['total'])) { foreach ($line_tax_data['total'] as $tax_rate_id => $tax) { if (!isset($order_taxes[$tax_rate_id])) { $order_taxes[$tax_rate_id] = 0; } $order_taxes[$tax_rate_id] += $tax; } } } foreach ($this->get_items(array('shipping')) as $item_id => $item) { $line_tax_data = maybe_unserialize($item['taxes']); if (isset($line_tax_data)) { foreach ($line_tax_data as $tax_rate_id => $tax) { if (!isset($order_shipping_taxes[$tax_rate_id])) { $order_shipping_taxes[$tax_rate_id] = 0; } $order_shipping_taxes[$tax_rate_id] += $tax; } } } // Remove old existing tax rows $this->remove_order_items('tax'); // Now merge to keep tax rows foreach (array_keys($order_taxes + $order_shipping_taxes) as $tax_rate_id) { $this->add_tax($tax_rate_id, isset($order_taxes[$tax_rate_id]) ? $order_taxes[$tax_rate_id] : 0, isset($order_shipping_taxes[$tax_rate_id]) ? $order_shipping_taxes[$tax_rate_id] : 0); } // Save tax totals $this->set_total(WC_Tax::round(array_sum($order_shipping_taxes)), 'shipping_tax'); $this->set_total(WC_Tax::round(array_sum($order_taxes)), 'tax'); return true; }
/** * 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()); $base_tax_rates = $this->tax->get_shop_base_rate($_product->tax_class); /** * ADJUST TAX - Calculations when base tax is not equal to the item tax */ if ($tax_rates !== $base_tax_rates) { $row_base_price = $base_price * $values['quantity']; // Work out a new base price without the shop's base tax $line_tax = array_sum($this->tax->calc_tax($row_base_price, $base_tax_rates, true)); // Now we have a new item price (excluding TAX) $line_subtotal = $this->tax->round($row_base_price - $line_tax); // Now add taxes for the users location $line_subtotal_tax = array_sum($this->tax->calc_tax($line_subtotal, $tax_rates, false)); $line_subtotal_tax = get_option('woocommerce_tax_round_at_subtotal') == 'yes' ? $line_subtotal_tax : $this->tax->round($line_subtotal_tax); // 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 = $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; } } } // 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 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); } // 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(); }
/** * 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); }
/** * 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 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; }
/** * Test the rounding method. */ public function test_round() { $this->assertEquals(WC_Tax::round('2.1234567'), '2.1235'); }
protected function _import_line_items(&$order, $order_id, $index) { $is_product_founded = false; switch ($this->import->options['pmwi_order']['products_source']) { // Get data from existing products case 'existing': foreach ($this->data['pmwi_order']['products'][$index] as $productIndex => $productItem) { if (empty($productItem['sku'])) { continue; } $args = array('post_type' => 'product', 'meta_key' => '_sku', 'meta_value' => $productItem['sku'], 'meta_compare' => '='); $product = false; $query = new WP_Query($args); while ($query->have_posts()) { $query->the_post(); $product = WC()->product_factory->get_product($query->post->ID); break; } wp_reset_postdata(); if (empty($product)) { $args['post_type'] = 'product_variation'; $query = new WP_Query($args); while ($query->have_posts()) { $query->the_post(); $product = WC()->product_factory->get_product($query->post->ID); break; } wp_reset_postdata(); } if ($product) { $is_product_founded = true; $item_price = $product->get_price(); $item_qty = empty($productItem['qty']) ? 1 : $productItem['qty']; $item_subtotal = $item_price * $item_qty; $item_subtotal_tax = 0; $line_taxes = array(); foreach ($productItem['tax_rates'] as $key => $tax_rate) { if (empty($tax_rate['code'])) { continue; } $tax_rate_codes = explode("|", $tax_rate['code']); $percentage_value = explode("|", $tax_rate['percentage_value']); $amount_per_unit = explode("|", $tax_rate['amount_per_unit']); foreach ($tax_rate_codes as $rate_key => $tax_rate_code) { if ($tax_rate_code == 'standard') { $tax_rate_code = ''; } $line_tax = 0; switch ($tax_rate['calculate_logic']) { case 'percentage': if (!empty($percentage_value[$rate_key]) and is_numeric($percentage_value[$rate_key])) { $line_tax = WC_Tax::round($item_subtotal / 100 * $percentage_value[$rate_key]); $item_subtotal_tax += $line_tax; } if (!empty($this->tax_rates)) { foreach ($this->tax_rates as $rate_id => $rate) { if ($rate->tax_rate_name == $tax_rate_code) { $line_taxes[$rate->tax_rate_id] = $line_tax; break; } } } break; case 'per_unit': if (!empty($amount_per_unit[$rate_key]) and is_numeric($amount_per_unit[$rate_key])) { $line_tax = WC_Tax::round($amount_per_unit[$rate_key] * $item_qty); $item_subtotal_tax += $line_tax; } if (!empty($this->tax_rates)) { foreach ($this->tax_rates as $rate_id => $rate) { if ($rate->tax_rate_name == $tax_rate_code) { $line_taxes[$rate->tax_rate_id] = $line_tax; break; } } } break; // Look up tax rate code // Look up tax rate code default: $found_rates = WC_Tax::get_rates_for_tax_class($tax_rate_code); if (!empty($found_rates)) { $found_priority = array(); foreach ($found_rates as $found_rate) { $matched_tax_rates = array(); if (in_array($found_rate->tax_rate_priority, $found_priority)) { continue; } $matched_tax_rates[$found_rate->tax_rate_id] = array('rate' => $found_rate->tax_rate, 'label' => $found_rate->tax_rate_name, 'shipping' => $found_rate->tax_rate_shipping ? 'yes' : 'no', 'compound' => $found_rate->tax_rate_compound ? 'yes' : 'no'); $line_tax = array_sum(WC_Tax::calc_tax($item_subtotal, $matched_tax_rates, $this->prices_include_tax)); $item_subtotal_tax += $line_tax; $line_taxes[$found_rate->tax_rate_id] = $line_tax; $found_priority[] = $found_rate->tax_rate_priority; } } break; } } } $variation = array(); $variation_str = ''; if ($product instanceof WC_Product_Variation) { $variation = $product->get_variation_attributes(); if (!empty($variation)) { foreach ($variation as $key => $value) { $variation_str .= $key . '-' . $value; } } } $product_item = new PMXI_Post_Record(); $product_item->getBy(array('import_id' => $this->import->id, 'post_id' => $order_id, 'unique_key' => 'line-item-' . $product->id . '-' . $variation_str)); if ($product_item->isEmpty()) { $item_id = false; // in case when this is new order just add new line items if (!$item_id) { $item_id = $order->add_product($product, $item_qty, array('variation' => $variation, 'totals' => array('subtotal' => $item_subtotal, 'subtotal_tax' => $item_subtotal_tax, 'total' => $item_subtotal, 'tax' => $item_subtotal_tax, 'tax_data' => array('total' => $line_taxes, 'subtotal' => array())))); } if (!$item_id) { $this->logger and call_user_func($this->logger, __('- <b>WARNING</b> Unable to create order line product.', 'wp_all_import_plugin')); } else { $product_item->set(array('import_id' => $this->import->id, 'post_id' => $order_id, 'unique_key' => 'line-item-' . $product->id . '-' . $variation_str, 'product_key' => 'line-item-' . $item_id, 'iteration' => $this->import->iteration))->save(); } } else { $item_id = str_replace('line-item-', '', $product_item->product_key); $is_updated = $order->update_product($item_id, $product, array('qty' => $item_qty, 'tax_class' => $product->get_tax_class(), 'totals' => array('subtotal' => $item_subtotal, 'subtotal_tax' => $item_subtotal_tax, 'total' => $item_subtotal, 'tax' => $item_subtotal_tax, 'tax_data' => array('total' => $line_taxes, 'subtotal' => array())), 'variation' => $variation)); if ($is_updated) { $product_item->set(array('iteration' => $this->import->iteration))->save(); } } } } break; // Manually import product order data and do not try to match to existing products // Manually import product order data and do not try to match to existing products default: $is_product_founded = true; foreach ($this->data['pmwi_order']['manual_products'][$index] as $productIndex => $productItem) { $item_price = $productItem['price_per_unit']; $item_qty = empty($productItem['qty']) ? 1 : $productItem['qty']; $item_subtotal = $item_price * $item_qty; $item_subtotal_tax = 0; $line_taxes = array(); foreach ($productItem['tax_rates'] as $key => $tax_rate) { if (empty($tax_rate['code'])) { continue; } $line_tax = 0; switch ($tax_rate['calculate_logic']) { case 'percentage': if (!empty($tax_rate['percentage_value']) and is_numeric($tax_rate['percentage_value'])) { $line_tax = WC_Tax::round($item_subtotal / 100 * $tax_rate['percentage_value']); $item_subtotal_tax += $line_tax; } break; case 'per_unit': if (!empty($tax_rate['amount_per_unit']) and is_numeric($tax_rate['amount_per_unit'])) { $line_tax = WC_Tax::round($tax_rate['amount_per_unit'] * $item_qty); $item_subtotal_tax += $line_tax; } break; // Look up tax rate code // Look up tax rate code default: $found_rates = WC_Tax::get_rates_for_tax_class($tax_rate['code']); if (!empty($found_rates)) { $matched_tax_rates = array(); $found_priority = array(); foreach ($found_rates as $found_rate) { if (in_array($found_rate->tax_rate_priority, $found_priority)) { continue; } $matched_tax_rates[$found_rate->tax_rate_id] = array('rate' => $found_rate->tax_rate, 'label' => $found_rate->tax_rate_name, 'shipping' => $found_rate->tax_rate_shipping ? 'yes' : 'no', 'compound' => $found_rate->tax_rate_compound ? 'yes' : 'no'); $found_priority[] = $found_rate->tax_rate_priority; } $line_tax = array_sum(WC_Tax::calc_tax($item_subtotal, $matched_tax_rates, true)); $item_subtotal_tax += $line_tax; } break; } if (!empty($this->tax_rates)) { foreach ($this->tax_rates as $rate_id => $rate) { $line_taxes[$rate->tax_rate_id] = $line_tax; break; } } } $variation = array(); $product_item = new PMXI_Post_Record(); $product_item->getBy(array('import_id' => $this->import->id, 'post_id' => $order_id, 'unique_key' => 'manual-line-item-' . $productIndex . '-' . $productItem['sku'])); if ($product_item->isEmpty()) { $item_id = wc_add_order_item($order_id, array('order_item_name' => $productItem['sku'], 'order_item_type' => 'line_item')); if (!$item_id) { $this->logger and call_user_func($this->logger, __('- <b>WARNING</b> Unable to create order line product.', 'wp_all_import_plugin')); } else { wc_add_order_item_meta($item_id, '_qty', wc_stock_amount($item_qty)); wc_add_order_item_meta($item_id, '_tax_class', ''); wc_add_order_item_meta($item_id, '_line_subtotal', wc_format_decimal($item_subtotal)); wc_add_order_item_meta($item_id, '_line_total', wc_format_decimal($item_subtotal)); wc_add_order_item_meta($item_id, '_line_subtotal_tax', wc_format_decimal($item_subtotal_tax)); wc_add_order_item_meta($item_id, '_line_tax', wc_format_decimal($item_subtotal_tax)); wc_add_order_item_meta($item_id, '_line_tax_data', array('total' => $line_taxes, 'subtotal' => array())); if (!empty($productItem['meta_name'])) { foreach ($productItem['meta_name'] as $key => $meta_name) { wc_add_order_item_meta($item_id, $meta_name, isset($productItem['meta_value'][$key]) ? $productItem['meta_value'][$key] : ''); } } $product_item->set(array('import_id' => $this->import->id, 'post_id' => $order_id, 'unique_key' => 'manual-line-item-' . $productIndex . '-' . $productItem['sku'], 'product_key' => 'manual-line-item-' . $item_id, 'iteration' => $this->import->iteration))->save(); } } else { $item_id = str_replace('manual-line-item-', '', $product_item->product_key); if (is_numeric($item_id)) { wc_update_order_item($item_id, array('order_item_name' => $productItem['sku'], 'order_item_type' => 'line_item')); wc_update_order_item_meta($item_id, '_qty', wc_stock_amount($item_qty)); wc_update_order_item_meta($item_id, '_tax_class', ''); wc_update_order_item_meta($item_id, '_line_subtotal', wc_format_decimal($item_subtotal)); wc_update_order_item_meta($item_id, '_line_total', wc_format_decimal($item_subtotal)); wc_update_order_item_meta($item_id, '_line_subtotal_tax', wc_format_decimal($item_subtotal_tax)); wc_update_order_item_meta($item_id, '_line_tax', wc_format_decimal($item_subtotal_tax)); wc_update_order_item_meta($item_id, '_line_tax_data', array('total' => $line_taxes, 'subtotal' => array())); if (!empty($productItem['meta_name'])) { foreach ($productItem['meta_name'] as $key => $meta_name) { wc_update_order_item_meta($item_id, $meta_name, isset($productItem['meta_value'][$key]) ? $productItem['meta_value'][$key] : ''); } } $product_item->set(array('iteration' => $this->import->iteration))->save(); } } } break; } return $is_product_founded; }
/** * Calculate totals for the items in the cart. */ public function calculate_totals() { $this->reset(); $this->coupons = $this->get_coupons(); do_action('woocommerce_before_calculate_totals', $this); if ($this->is_empty()) { $this->set_session(); return; } $tax_rates = array(); $shop_tax_rates = array(); /** * Calculate subtotals for items. This is done first so that discount logic can use the values. */ foreach ($this->get_cart() as $cart_item_key => $values) { $_product = $values['data']; // Count items + weight $this->cart_contents_weight += $_product->get_weight() * $values['quantity']; $this->cart_contents_count += $values['quantity']; // Prices $line_price = $_product->get_price() * $values['quantity']; $line_subtotal = 0; $line_subtotal_tax = 0; /** * No tax to calculate */ if (!$_product->is_taxable()) { // Subtotal is the undiscounted price $this->subtotal += $line_price; $this->subtotal_ex_tax += $line_price; /** * Prices include tax * * To prevent rounding issues we need to work with the inclusive price where possible * otherwise we'll see errors such as when working with a 9.99 inc price, 20% VAT which would * be 8.325 leading to totals being 1p off * * Pre tax coupons come off the price the customer thinks they are paying - tax is calculated * afterwards. * * e.g. $100 bike with $10 coupon = customer pays $90 and tax worked backwards from that */ } elseif ($this->prices_include_tax) { // Get base tax rates if (empty($shop_tax_rates[$_product->tax_class])) { $shop_tax_rates[$_product->tax_class] = WC_Tax::get_base_tax_rates($_product->tax_class); } // Get item tax rates if (empty($tax_rates[$_product->get_tax_class()])) { $tax_rates[$_product->get_tax_class()] = WC_Tax::get_rates($_product->get_tax_class()); } $base_tax_rates = $shop_tax_rates[$_product->tax_class]; $item_tax_rates = $tax_rates[$_product->get_tax_class()]; /** * ADJUST TAX - Calculations when base tax is not equal to the item tax */ if ($item_tax_rates !== $base_tax_rates) { // Work out a new base price without the shop's base tax $taxes = WC_Tax::calc_tax($line_price, $base_tax_rates, true, true); // Now we have a new item price (excluding TAX) $line_subtotal = $line_price - array_sum($taxes); // Now add modified taxes $tax_result = WC_Tax::calc_tax($line_subtotal, $item_tax_rates); $line_subtotal_tax = array_sum($tax_result); /** * Regular tax calculation (customer inside base and the tax class is unmodified */ } else { // Calc tax normally $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates, true); $line_subtotal_tax = array_sum($taxes); $line_subtotal = $line_price - array_sum($taxes); } /** * Prices exclude tax * * This calculation is simpler - work with the base, untaxed price. */ } else { // Get item tax rates if (empty($tax_rates[$_product->get_tax_class()])) { $tax_rates[$_product->get_tax_class()] = WC_Tax::get_rates($_product->get_tax_class()); } $item_tax_rates = $tax_rates[$_product->get_tax_class()]; // Base tax for line before discount - we will store this in the order data $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates); $line_subtotal_tax = array_sum($taxes); $line_subtotal = $line_price; } // Add to main subtotal $this->subtotal += $line_subtotal + $line_subtotal_tax; $this->subtotal_ex_tax += $line_subtotal; } /** * Calculate totals for items */ foreach ($this->get_cart() as $cart_item_key => $values) { $_product = $values['data']; // Prices $base_price = $_product->get_price(); $line_price = $_product->get_price() * $values['quantity']; // Tax data $taxes = array(); $discounted_taxes = array(); /** * No tax to calculate */ if (!$_product->is_taxable()) { // Discounted Price (price with any pre-tax discounts applied) $discounted_price = $this->get_discounted_price($values, $base_price, true); $line_subtotal_tax = 0; $line_subtotal = $line_price; $line_tax = 0; $line_total = WC_Tax::round($discounted_price * $values['quantity']); /** * Prices include tax */ } elseif ($this->prices_include_tax) { $base_tax_rates = $shop_tax_rates[$_product->tax_class]; $item_tax_rates = $tax_rates[$_product->get_tax_class()]; /** * ADJUST TAX - Calculations when base tax is not equal to the item tax */ if ($item_tax_rates !== $base_tax_rates) { // Work out a new base price without the shop's base tax $taxes = WC_Tax::calc_tax($line_price, $base_tax_rates, true, true); // Now we have a new item price (excluding TAX) $line_subtotal = round($line_price - array_sum($taxes), WC_ROUNDING_PRECISION); $taxes = WC_Tax::calc_tax($line_subtotal, $item_tax_rates); $line_subtotal_tax = array_sum($taxes); // Adjusted price (this is the price including the new tax rate) $adjusted_price = ($line_subtotal + $line_subtotal_tax) / $values['quantity']; // Apply discounts $discounted_price = $this->get_discounted_price($values, $adjusted_price, true); $discounted_taxes = WC_Tax::calc_tax($discounted_price * $values['quantity'], $item_tax_rates, true); $line_tax = array_sum($discounted_taxes); $line_total = $discounted_price * $values['quantity'] - $line_tax; /** * Regular tax calculation (customer inside base and the tax class is unmodified */ } else { // Work out a new base price without the item tax $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates, true); // Now we have a new item price (excluding TAX) $line_subtotal = $line_price - array_sum($taxes); $line_subtotal_tax = array_sum($taxes); // Calc prices and tax (discounted) $discounted_price = $this->get_discounted_price($values, $base_price, true); $discounted_taxes = WC_Tax::calc_tax($discounted_price * $values['quantity'], $item_tax_rates, true); $line_tax = array_sum($discounted_taxes); $line_total = $discounted_price * $values['quantity'] - $line_tax; } // Tax rows - merge the totals we just got foreach (array_keys($this->taxes + $discounted_taxes) as $key) { $this->taxes[$key] = (isset($discounted_taxes[$key]) ? $discounted_taxes[$key] : 0) + (isset($this->taxes[$key]) ? $this->taxes[$key] : 0); } /** * Prices exclude tax */ } else { $item_tax_rates = $tax_rates[$_product->get_tax_class()]; // Work out a new base price without the shop's base tax $taxes = WC_Tax::calc_tax($line_price, $item_tax_rates); // Now we have the item price (excluding TAX) $line_subtotal = $line_price; $line_subtotal_tax = array_sum($taxes); // Now calc product rates $discounted_price = $this->get_discounted_price($values, $base_price, true); $discounted_taxes = WC_Tax::calc_tax($discounted_price * $values['quantity'], $item_tax_rates); $discounted_tax_amount = array_sum($discounted_taxes); $line_tax = $discounted_tax_amount; $line_total = $discounted_price * $values['quantity']; // Tax rows - merge the totals we just got foreach (array_keys($this->taxes + $discounted_taxes) as $key) { $this->taxes[$key] = (isset($discounted_taxes[$key]) ? $discounted_taxes[$key] : 0) + (isset($this->taxes[$key]) ? $this->taxes[$key] : 0); } } // Cart contents total is based on discounted prices and is used for the final total calculation $this->cart_contents_total += $line_total; // Store costs + taxes for lines $this->cart_contents[$cart_item_key]['line_total'] = $line_total; $this->cart_contents[$cart_item_key]['line_tax'] = $line_tax; $this->cart_contents[$cart_item_key]['line_subtotal'] = $line_subtotal; $this->cart_contents[$cart_item_key]['line_subtotal_tax'] = $line_subtotal_tax; // Store rates ID and costs - Since 2.2 $this->cart_contents[$cart_item_key]['line_tax_data'] = array('total' => $discounted_taxes, 'subtotal' => $taxes); } // Round cart contents $this->cart_contents_total = round($this->cart_contents_total, $this->dp); // Only calculate the grand total + shipping if on the cart/checkout if (is_checkout() || is_cart() || defined('WOOCOMMERCE_CHECKOUT') || defined('WOOCOMMERCE_CART')) { // Calculate the Shipping $this->calculate_shipping(); // Trigger the fees API where developers can add fees to the cart $this->calculate_fees(); // Total up/round taxes and shipping taxes if ($this->round_at_subtotal) { $this->tax_total = WC_Tax::get_tax_total($this->taxes); $this->shipping_tax_total = WC_Tax::get_tax_total($this->shipping_taxes); $this->taxes = array_map(array('WC_Tax', 'round'), $this->taxes); $this->shipping_taxes = array_map(array('WC_Tax', 'round'), $this->shipping_taxes); } else { $this->tax_total = array_sum($this->taxes); $this->shipping_tax_total = array_sum($this->shipping_taxes); } // VAT exemption done at this point - so all totals are correct before exemption if (WC()->customer->is_vat_exempt()) { $this->remove_taxes(); } // Allow plugins to hook and alter totals before final total is calculated do_action('woocommerce_calculate_totals', $this); // Grand Total - Discounted product prices, discounted tax, shipping cost + tax $this->total = max(0, apply_filters('woocommerce_calculated_total', round($this->cart_contents_total + $this->tax_total + $this->shipping_tax_total + $this->shipping_total + $this->fee_total, $this->dp), $this)); } else { // Set tax total to sum of all tax rows $this->tax_total = WC_Tax::get_tax_total($this->taxes); // VAT exemption done at this point - so all totals are correct before exemption if (WC()->customer->is_vat_exempt()) { $this->remove_taxes(); } } do_action('woocommerce_after_calculate_totals', $this); $this->set_session(); }
/** * Update tax lines for the order based on the line item taxes themselves. */ public function update_taxes() { $cart_taxes = array(); $shipping_taxes = array(); foreach ($this->get_items(array('line_item', 'fee')) as $item_id => $item) { $taxes = $item->get_taxes(); foreach ($taxes['total'] as $tax_rate_id => $tax) { $cart_taxes[$tax_rate_id] = isset($cart_taxes[$tax_rate_id]) ? $cart_taxes[$tax_rate_id] + $tax : $tax; } } foreach ($this->get_shipping_methods() as $item_id => $item) { $taxes = $item->get_taxes(); foreach ($taxes['total'] as $tax_rate_id => $tax) { $shipping_taxes[$tax_rate_id] = isset($shipping_taxes[$tax_rate_id]) ? $shipping_taxes[$tax_rate_id] + $tax : $tax; } } // Remove old existing tax rows. $this->remove_order_items('tax'); // Now merge to keep tax rows. foreach (array_keys($cart_taxes + $shipping_taxes) as $tax_rate_id) { $item = new WC_Order_Item_Tax(); $item->set_rate($tax_rate_id); $item->set_tax_total(isset($cart_taxes[$tax_rate_id]) ? $cart_taxes[$tax_rate_id] : 0); $item->set_shipping_tax_total(isset($shipping_taxes[$tax_rate_id]) ? $shipping_taxes[$tax_rate_id] : 0); $this->add_item($item); } // Save tax totals $this->set_shipping_tax(WC_Tax::round(array_sum($shipping_taxes))); $this->set_cart_tax(WC_Tax::round(array_sum($cart_taxes))); $this->save(); }
/** * Calculate wcmp vendor shipping tax * * @param double $shipping_amount * @param object $order */ public function calculate_shipping_tax($shipping_amount, $order) { global $WCMp, $woocommerce; $wc_tax_enabled = get_option('woocommerce_calc_taxes'); if ('no' === $wc_tax_enabled) { return 0; } $tax_based_on = get_option('woocommerce_tax_based_on'); $WC_Tax = new WC_Tax(); if ('base' === $tax_based_on) { $default = wc_get_base_location(); $country = $default['country']; $state = $default['state']; $postcode = ''; $city = ''; } elseif ('billing' === $tax_based_on) { $country = $order->billing_country; $state = $order->billing_state; $postcode = $order->billing_postcode; $city = $order->billing_city; } else { $country = $order->shipping_country; $state = $order->shipping_state; $postcode = $order->shipping_postcode; $city = $order->shipping_city; } $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; } } } $vendor_shipping_taxes = $WC_Tax->calc_shipping_tax($shipping_amount, $matched_tax_rates); $vendor_shipping_tax_total = $WC_Tax->round(array_sum($vendor_shipping_taxes)); return $vendor_shipping_tax_total; }
/** * 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); }
public static function woocommerce_get_variation_price($price, $product, $min_or_max, $display) { global $post, $woocommerce; $baseprice = $price; $result = $baseprice; if ($post == null || !is_admin()) { $variation_id = get_post_meta($product->id, '_' . $min_or_max . '_price_variation_id', true); if (!$variation_id) { return false; } $price = get_post_meta($variation_id, '_price', true); $result = $price; if ($display) { $variation = $product->get_child($variation_id); $commission = self::get_commission($product, $variation_id); if ($commission) { $baseprice = $variation->get_regular_price(); if ($variation->get_sale_price() != $variation->get_regular_price() && $variation->get_sale_price() == $variation->price) { if (get_option("wrp-baseprice", "regular") == "sale") { $baseprice = $variation->get_sale_price(); } } $product_price = $baseprice; $type = get_option("wrp-method", "rate"); $result = 0; if ($type == "rate") { // if rate and price includes taxes if ($variation->is_taxable() && get_option('woocommerce_prices_include_tax') == 'yes') { $_tax = new WC_Tax(); $tax_rates = $_tax->get_shop_base_rate($variation->tax_class); $taxes = $_tax->calc_tax($baseprice, $tax_rates, true); $product_price = $_tax->round($baseprice - array_sum($taxes)); } $result = WooRolePricing::bcmul($product_price, $commission, WOO_ROLE_PRICING_DECIMALS); if ($variation->is_taxable() && get_option('woocommerce_prices_include_tax') == 'yes') { $_tax = new WC_Tax(); $tax_rates = $_tax->get_shop_base_rate($variation->tax_class); $taxes = $_tax->calc_tax($result, $tax_rates, false); // important false $result = $_tax->round($result + array_sum($taxes)); } } else { $result = WooRolePricing::bcsub($product_price, $commission, WOO_ROLE_PRICING_DECIMALS); } } } } return $result; }