File "class-wc-cart-totals.php"
Full path: /home/kosmetik/public_html/wp-content/plugins/woocommerce/includes/class-wc-cart-totals.php
File
size: 21.36 B
MIME-type: text/x-php
Charset: utf-8
Download Open Edit Advanced Editor Back
<?php
use Automattic\WooCommerce\Utilities\NumberUtil;
if (!defined('ABSPATH')) {
exit;
}
final class WC_Cart_Totals
{
use WC_Item_Totals;
protected $cart;
protected $customer;
protected $items = array();
protected $fees = array();
protected $shipping = array();
protected $coupons = array();
protected $coupon_discount_totals = array();
protected $coupon_discount_tax_totals = array();
protected $calculate_tax = true;
protected $totals = array('fees_total' => 0, 'fees_total_tax' => 0, 'items_subtotal' => 0, 'items_subtotal_tax' => 0, 'items_total' => 0, 'items_total_tax' => 0, 'total' => 0, 'shipping_total' => 0, 'shipping_tax_total' => 0, 'discounts_total' => 0);
public function __construct(&$cart = null)
{
if (!is_a($cart, 'WC_Cart')) {
throw new Exception('A valid WC_Cart object is required');
}
$this->cart = $cart;
$this->calculate_tax = wc_tax_enabled() && !$cart->get_customer()->get_is_vat_exempt();
$this->calculate();
}
protected function calculate()
{
$this->calculate_item_totals();
$this->calculate_shipping_totals();
$this->calculate_fee_totals();
$this->calculate_totals();
}
protected function get_default_item_props()
{
return (object) array('object' => null, 'tax_class' => '', 'taxable' => false, 'quantity' => 0, 'product' => false, 'price_includes_tax' => false, 'subtotal' => 0, 'subtotal_tax' => 0, 'subtotal_taxes' => array(), 'total' => 0, 'total_tax' => 0, 'taxes' => array());
}
protected function get_default_fee_props()
{
return (object) array('object' => null, 'tax_class' => '', 'taxable' => false, 'total_tax' => 0, 'taxes' => array());
}
protected function get_default_shipping_props()
{
return (object) array('object' => null, 'tax_class' => '', 'taxable' => false, 'total' => 0, 'total_tax' => 0, 'taxes' => array());
}
protected function get_items_from_cart()
{
$this->items = array();
foreach ($this->cart->get_cart() as $cart_item_key => $cart_item) {
$item = $this->get_default_item_props();
$item->key = $cart_item_key;
$item->object = $cart_item;
$item->tax_class = $cart_item['data']->get_tax_class();
$item->taxable = 'taxable' === $cart_item['data']->get_tax_status();
$item->price_includes_tax = wc_prices_include_tax();
$item->quantity = $cart_item['quantity'];
$item->price = wc_add_number_precision_deep((float) $cart_item['data']->get_price() * (float) $cart_item['quantity']);
$item->product = $cart_item['data'];
$item->tax_rates = $this->get_item_tax_rates($item);
$this->items[$cart_item_key] = $item;
}
}
protected function get_tax_class_costs()
{
$item_tax_classes = wp_list_pluck($this->items, 'tax_class');
$shipping_tax_classes = wp_list_pluck($this->shipping, 'tax_class');
$fee_tax_classes = wp_list_pluck($this->fees, 'tax_class');
$costs = array_fill_keys($item_tax_classes + $shipping_tax_classes + $fee_tax_classes, 0);
$costs['non-taxable'] = 0;
foreach ($this->items + $this->fees + $this->shipping as $item) {
if (0 > $item->total) {
continue;
}
if (!$item->taxable) {
$costs['non-taxable'] += $item->total;
} elseif ('inherit' === $item->tax_class) {
$costs[reset($item_tax_classes)] += $item->total;
} else {
$costs[$item->tax_class] += $item->total;
}
}
return array_filter($costs);
}
protected function get_fees_from_cart()
{
$this->fees = array();
$this->cart->calculate_fees();
$fee_running_total = 0;
foreach ($this->cart->get_fees() as $fee_key => $fee_object) {
$fee = $this->get_default_fee_props();
$fee->object = $fee_object;
$fee->tax_class = $fee->object->tax_class;
$fee->taxable = $fee->object->taxable;
$fee->total = wc_add_number_precision_deep($fee->object->amount);
if (0 > $fee->total) {
$max_discount = NumberUtil::round($this->get_total('items_total', true) + $fee_running_total + $this->get_total('shipping_total', true)) * -1;
if ($fee->total < $max_discount) {
$fee->total = $max_discount;
}
}
$fee_running_total += $fee->total;
if ($this->calculate_tax) {
if (0 > $fee->total) {
$tax_class_costs = $this->get_tax_class_costs();
$total_cost = array_sum($tax_class_costs);
if ($total_cost) {
foreach ($tax_class_costs as $tax_class => $tax_class_cost) {
if ('non-taxable' === $tax_class) {
continue;
}
$proportion = $tax_class_cost / $total_cost;
$cart_discount_proportion = $fee->total * $proportion;
$fee->taxes = wc_array_merge_recursive_numeric($fee->taxes, WC_Tax::calc_tax($fee->total * $proportion, WC_Tax::get_rates($tax_class)));
}
}
} elseif ($fee->object->taxable) {
$fee->taxes = WC_Tax::calc_tax($fee->total, WC_Tax::get_rates($fee->tax_class, $this->cart->get_customer()), false);
}
}
$fee->taxes = apply_filters('woocommerce_cart_totals_get_fees_from_cart_taxes', $fee->taxes, $fee, $this);
$fee->total_tax = array_sum(array_map(array($this, 'round_line_tax'), $fee->taxes));
$fee->object->total = wc_remove_number_precision_deep($fee->total);
$fee->object->tax_data = wc_remove_number_precision_deep($fee->taxes);
$fee->object->tax = wc_remove_number_precision_deep($fee->total_tax);
$this->fees[$fee_key] = $fee;
}
}
protected function get_shipping_from_cart()
{
$this->shipping = array();
if (!$this->cart->show_shipping()) {
return;
}
foreach ($this->cart->calculate_shipping() as $key => $shipping_object) {
$shipping_line = $this->get_default_shipping_props();
$shipping_line->object = $shipping_object;
$shipping_line->tax_class = get_option('woocommerce_shipping_tax_class');
$shipping_line->taxable = true;
$shipping_line->total = wc_add_number_precision_deep($shipping_object->cost);
$shipping_line->taxes = wc_add_number_precision_deep($shipping_object->taxes, false);
$shipping_line->taxes = array_map(array($this, 'round_item_subtotal'), $shipping_line->taxes);
$shipping_line->total_tax = array_sum($shipping_line->taxes);
$this->shipping[$key] = $shipping_line;
}
}
protected function get_coupons_from_cart()
{
$this->coupons = $this->cart->get_coupons();
foreach ($this->coupons as $coupon) {
switch ($coupon->get_discount_type()) {
case 'fixed_product':
$coupon->sort = 1;
break;
case 'percent':
$coupon->sort = 2;
break;
case 'fixed_cart':
$coupon->sort = 3;
break;
default:
$coupon->sort = 0;
break;
}
$coupon->sort = apply_filters('woocommerce_coupon_sort', $coupon->sort, $coupon);
}
uasort($this->coupons, array($this, 'sort_coupons_callback'));
}
protected function sort_coupons_callback($a, $b)
{
if ($a->sort === $b->sort) {
if ($a->get_limit_usage_to_x_items() === $b->get_limit_usage_to_x_items()) {
if ($a->get_amount() === $b->get_amount()) {
return $b->get_id() - $a->get_id();
}
return $a->get_amount() < $b->get_amount() ? -1 : 1;
}
return $a->get_limit_usage_to_x_items() < $b->get_limit_usage_to_x_items() ? -1 : 1;
}
return $a->sort < $b->sort ? -1 : 1;
}
protected function remove_item_base_taxes($item)
{
if ($item->price_includes_tax && $item->taxable) {
if (apply_filters('woocommerce_adjust_non_base_location_prices', true)) {
$base_tax_rates = WC_Tax::get_base_tax_rates($item->product->get_tax_class('unfiltered'));
} else {
$base_tax_rates = $item->tax_rates;
}
$taxes = WC_Tax::calc_tax($item->price, $base_tax_rates, true);
$item->price = NumberUtil::round($item->price - array_sum($taxes));
$item->price_includes_tax = false;
}
return $item;
}
protected function adjust_non_base_location_price($item)
{
if ($item->price_includes_tax && $item->taxable) {
$base_tax_rates = WC_Tax::get_base_tax_rates($item->product->get_tax_class('unfiltered'));
if ($item->tax_rates !== $base_tax_rates) {
$taxes = WC_Tax::calc_tax($item->price, $base_tax_rates, true);
$new_taxes = WC_Tax::calc_tax($item->price - array_sum($taxes), $item->tax_rates, false);
$item->price = $item->price - array_sum($taxes) + array_sum($new_taxes);
}
}
return $item;
}
protected function get_discounted_price_in_cents($item_key)
{
$item = $this->items[$item_key];
$price = isset($this->coupon_discount_totals[$item_key]) ? $item->price - $this->coupon_discount_totals[$item_key] : $item->price;
return $price;
}
protected function get_item_tax_rates($item)
{
if (!wc_tax_enabled()) {
return array();
}
$tax_class = $item->product->get_tax_class();
$item_tax_rates = isset($this->item_tax_rates[$tax_class]) ? $this->item_tax_rates[$tax_class] : ($this->item_tax_rates[$tax_class] = WC_Tax::get_rates($item->product->get_tax_class(), $this->cart->get_customer()));
return apply_filters('woocommerce_cart_totals_get_item_tax_rates', $item_tax_rates, $item, $this->cart);
}
protected function get_item_costs_by_tax_class()
{
$tax_classes = array('non-taxable' => 0);
foreach ($this->items + $this->fees + $this->shipping as $item) {
if (!isset($tax_classes[$item->tax_class])) {
$tax_classes[$item->tax_class] = 0;
}
if ($item->taxable) {
$tax_classes[$item->tax_class] += $item->total;
} else {
$tax_classes['non-taxable'] += $item->total;
}
}
return $tax_classes;
}
public function get_total($key = 'total', $in_cents = false)
{
$totals = $this->get_totals($in_cents);
return isset($totals[$key]) ? $totals[$key] : 0;
}
protected function set_total($key, $total)
{
$this->totals[$key] = $total;
}
public function get_totals($in_cents = false)
{
return $in_cents ? $this->totals : wc_remove_number_precision_deep($this->totals);
}
protected function get_values_for_total($field)
{
return array_values(wp_list_pluck($this->items, $field));
}
protected function get_merged_taxes($in_cents = false, $types = array('items', 'fees', 'shipping'))
{
$items = array();
$taxes = array();
if (is_string($types)) {
$types = array($types);
}
foreach ($types as $type) {
if (isset($this->{$type})) {
$items = array_merge($items, $this->{$type});
}
}
foreach ($items as $item) {
foreach ($item->taxes as $rate_id => $rate) {
if (!isset($taxes[$rate_id])) {
$taxes[$rate_id] = 0;
}
$taxes[$rate_id] += $this->round_line_tax($rate);
}
}
return $in_cents ? $taxes : wc_remove_number_precision_deep($taxes);
}
protected function round_merged_taxes($taxes)
{
foreach ($taxes as $rate_id => $tax) {
$taxes[$rate_id] = $this->round_line_tax($tax);
}
return $taxes;
}
protected function combine_item_taxes($item_taxes)
{
$merged_taxes = array();
foreach ($item_taxes as $taxes) {
foreach ($taxes as $tax_id => $tax_amount) {
if (!isset($merged_taxes[$tax_id])) {
$merged_taxes[$tax_id] = 0;
}
$merged_taxes[$tax_id] += $tax_amount;
}
}
return $merged_taxes;
}
protected function calculate_item_totals()
{
$this->get_items_from_cart();
$this->calculate_item_subtotals();
$this->calculate_discounts();
foreach ($this->items as $item_key => $item) {
$item->total = $this->get_discounted_price_in_cents($item_key);
$item->total_tax = 0;
if (has_filter('woocommerce_get_discounted_price')) {
$item->total = wc_add_number_precision(apply_filters('woocommerce_get_discounted_price', wc_remove_number_precision($item->total), $item->object, $this->cart));
}
if ($this->calculate_tax && $item->product->is_taxable()) {
$total_taxes = apply_filters('woocommerce_calculate_item_totals_taxes', WC_Tax::calc_tax($item->total, $item->tax_rates, $item->price_includes_tax), $item, $this);
$item->taxes = $total_taxes;
$item->total_tax = array_sum(array_map(array($this, 'round_line_tax'), $item->taxes));
if ($item->price_includes_tax) {
$item->total = $item->total - array_sum($item->taxes);
}
}
$this->cart->cart_contents[$item_key]['line_tax_data']['total'] = wc_remove_number_precision_deep($item->taxes);
$this->cart->cart_contents[$item_key]['line_total'] = wc_remove_number_precision($item->total);
$this->cart->cart_contents[$item_key]['line_tax'] = wc_remove_number_precision($item->total_tax);
}
$items_total = $this->get_rounded_items_total($this->get_values_for_total('total'));
$this->set_total('items_total', $items_total);
$this->set_total('items_total_tax', array_sum(array_values(wp_list_pluck($this->items, 'total_tax'))));
$this->cart->set_cart_contents_total($this->get_total('items_total'));
$this->cart->set_cart_contents_tax(array_sum($this->get_merged_taxes(false, 'items')));
$this->cart->set_cart_contents_taxes($this->get_merged_taxes(false, 'items'));
}
protected function calculate_item_subtotals()
{
$merged_subtotal_taxes = array();
$adjust_non_base_location_prices = apply_filters('woocommerce_adjust_non_base_location_prices', true);
$is_customer_vat_exempt = $this->cart->get_customer()->get_is_vat_exempt();
foreach ($this->items as $item_key => $item) {
if ($item->price_includes_tax) {
if ($is_customer_vat_exempt) {
$item = $this->remove_item_base_taxes($item);
} elseif ($adjust_non_base_location_prices) {
$item = $this->adjust_non_base_location_price($item);
}
}
$item->subtotal = $item->price;
if ($this->calculate_tax && $item->product->is_taxable()) {
$item->subtotal_taxes = WC_Tax::calc_tax($item->subtotal, $item->tax_rates, $item->price_includes_tax);
$item->subtotal_tax = array_sum(array_map(array($this, 'round_line_tax'), $item->subtotal_taxes));
if ($item->price_includes_tax) {
$item->subtotal = $item->subtotal - array_sum($item->subtotal_taxes);
}
foreach ($item->subtotal_taxes as $rate_id => $rate) {
if (!isset($merged_subtotal_taxes[$rate_id])) {
$merged_subtotal_taxes[$rate_id] = 0;
}
$merged_subtotal_taxes[$rate_id] += $this->round_line_tax($rate);
}
}
$this->cart->cart_contents[$item_key]['line_tax_data'] = array('subtotal' => wc_remove_number_precision_deep($item->subtotal_taxes));
$this->cart->cart_contents[$item_key]['line_subtotal'] = wc_remove_number_precision($item->subtotal);
$this->cart->cart_contents[$item_key]['line_subtotal_tax'] = wc_remove_number_precision($item->subtotal_tax);
}
$items_subtotal = $this->get_rounded_items_total($this->get_values_for_total('subtotal'));
$this->set_total('items_subtotal', $items_subtotal);
$this->set_total('items_subtotal_tax', array_sum($merged_subtotal_taxes), 0);
$this->cart->set_subtotal($this->get_total('items_subtotal'));
$this->cart->set_subtotal_tax($this->get_total('items_subtotal_tax'));
}
protected function calculate_discounts()
{
$this->get_coupons_from_cart();
$discounts = new WC_Discounts($this->cart);
$discounts->set_items($this->items);
foreach ($this->coupons as $coupon) {
$discounts->apply_coupon($coupon);
}
$coupon_discount_amounts = $discounts->get_discounts_by_coupon(true);
$coupon_discount_tax_amounts = array();
if ($this->calculate_tax) {
foreach ($discounts->get_discounts(true) as $coupon_code => $coupon_discounts) {
$coupon_discount_tax_amounts[$coupon_code] = 0;
foreach ($coupon_discounts as $item_key => $coupon_discount) {
$item = $this->items[$item_key];
if ($item->product->is_taxable()) {
$item_tax = array_sum(WC_Tax::calc_tax($coupon_discount, $item->tax_rates, $item->price_includes_tax));
$coupon_discount_tax_amounts[$coupon_code] += $item_tax;
if ($item->price_includes_tax) {
$coupon_discount_amounts[$coupon_code] -= $item_tax;
}
}
}
}
}
$this->coupon_discount_totals = (array) $discounts->get_discounts_by_item(true);
$this->coupon_discount_tax_totals = $coupon_discount_tax_amounts;
if (wc_prices_include_tax()) {
$this->set_total('discounts_total', array_sum($this->coupon_discount_totals) - array_sum($this->coupon_discount_tax_totals));
$this->set_total('discounts_tax_total', array_sum($this->coupon_discount_tax_totals));
} else {
$this->set_total('discounts_total', array_sum($this->coupon_discount_totals));
$this->set_total('discounts_tax_total', array_sum($this->coupon_discount_tax_totals));
}
$this->cart->set_coupon_discount_totals(wc_remove_number_precision_deep($coupon_discount_amounts));
$this->cart->set_coupon_discount_tax_totals(wc_remove_number_precision_deep($coupon_discount_tax_amounts));
$this->cart->set_discount_total($this->get_total('discounts_total'));
$this->cart->set_discount_tax($this->get_total('discounts_tax_total'));
}
protected function calculate_fee_totals()
{
$this->get_fees_from_cart();
$this->set_total('fees_total', array_sum(wp_list_pluck($this->fees, 'total')));
$this->set_total('fees_total_tax', array_sum(wp_list_pluck($this->fees, 'total_tax')));
$this->cart->fees_api()->set_fees(wp_list_pluck($this->fees, 'object'));
$this->cart->set_fee_total(wc_remove_number_precision_deep(array_sum(wp_list_pluck($this->fees, 'total'))));
$this->cart->set_fee_tax(wc_remove_number_precision_deep(array_sum(wp_list_pluck($this->fees, 'total_tax'))));
$this->cart->set_fee_taxes(wc_remove_number_precision_deep($this->combine_item_taxes(wp_list_pluck($this->fees, 'taxes'))));
}
protected function calculate_shipping_totals()
{
$this->get_shipping_from_cart();
$this->set_total('shipping_total', array_sum(wp_list_pluck($this->shipping, 'total')));
$this->set_total('shipping_tax_total', array_sum(wp_list_pluck($this->shipping, 'total_tax')));
$this->cart->set_shipping_total($this->get_total('shipping_total'));
$this->cart->set_shipping_tax($this->get_total('shipping_tax_total'));
$this->cart->set_shipping_taxes(wc_remove_number_precision_deep($this->combine_item_taxes(wp_list_pluck($this->shipping, 'taxes'))));
}
protected function calculate_totals()
{
$this->set_total('total', NumberUtil::round($this->get_total('items_total', true) + $this->get_total('fees_total', true) + $this->get_total('shipping_total', true) + array_sum($this->get_merged_taxes(true)), 0));
$items_tax = array_sum($this->get_merged_taxes(false, array('items')));
$shipping_and_fee_taxes = NumberUtil::round(array_sum($this->get_merged_taxes(false, array('fees', 'shipping'))), wc_get_price_decimals());
$this->cart->set_total_tax($items_tax + $shipping_and_fee_taxes);
if (has_action('woocommerce_calculate_totals')) {
do_action('woocommerce_calculate_totals', $this->cart);
}
$this->cart->set_total(max(0, apply_filters('woocommerce_calculated_total', $this->get_total('total'), $this->cart)));
}
}