cart = $cart; $this->wcCustomerConverter = Factory::get( "WC_WcCustomerConverter", $cart->getContext()->getGlobalContext() ); } /** * @param bool $inclTax * * @return float */ protected function calculateItemsSubtotalsWithoutImmutable($inclTax = true) { /** @see \WC_Cart_Totals::calculate_item_subtotals */ $cart = $this->cart; $cartContext = $cart->getContext(); $context = $cartContext->getGlobalContext(); $adjust_non_base_location_prices = apply_filters('woocommerce_adjust_non_base_location_prices', true); $is_customer_vat_exempt = $cart->getContext()->getCustomer()->isVatExempt(); $calculate_tax = $context->getIsTaxEnabled() && ! $is_customer_vat_exempt; $itemsSubtotals = floatval(0); foreach ($cart->getItems() as $item) { if ($item->hasAttr(CartItemAttributeEnum::IMMUTABLE())) { continue; } $product = $item->getWcItem()->getProduct(); $priceIncludesTax = $context->getIsPricesIncludeTax(); $taxable = $context->getIsTaxEnabled() && 'taxable' === $product->get_tax_status(); if ($item->isPriceChanged()) { $price = $item->getTotalPrice(); if($product->is_on_sale('edit') && 'compare_discounted_and_sale' === $context->getOption('discount_for_onsale')) { $salePrice = (float)$product->get_sale_price('edit') * $item->getQty(); $price = min([$price, $salePrice]); } } else { $price = $product->is_on_sale('edit') ? (float)$product->get_sale_price('edit') : $item->getPrice(); // $facade = $item->getWcItem(); // if($facade->getNewPrice() !== null) { // $price = $facade->getNewPrice(); // } else { // $price = $product->is_on_sale('edit') ? (float)$product->get_sale_price('edit') : $item->getPrice(); // } $price *= $item->getQty(); } $wcCustomer = $this->wcCustomerConverter->convertToWcCustomer($cartContext->getCustomer()); if ($context->getIsTaxEnabled() && WC()->session !== null) { // Some contexts ( e.g. REST API ) does not have initiated session. Do not calculate taxes in that case. if (WC()->session === null) { $tax_rates = array(); } else { $tax_rates = \WC_Tax::get_rates($product->get_tax_class(), $wcCustomer); } } else { $tax_rates = array(); } if ($priceIncludesTax) { if ($is_customer_vat_exempt) { /** @see \WC_Cart_Totals::remove_item_base_taxes */ if ($priceIncludesTax && $taxable) { if (apply_filters('woocommerce_adjust_non_base_location_prices', true)) { $base_tax_rates = \WC_Tax::get_base_tax_rates($product->get_tax_class('unfiltered')); } else { $base_tax_rates = $tax_rates; } // Work out a new base price without the shop's base tax. $taxes = \WC_Tax::calc_tax($price, $base_tax_rates, true); // Now we have a new item price (excluding TAX). $price = round($price - array_sum($taxes)); $priceIncludesTax = false; } } elseif ($adjust_non_base_location_prices) { /** @see \WC_Cart_Totals::adjust_non_base_location_price */ if ($priceIncludesTax && $taxable) { $base_tax_rates = \WC_Tax::get_base_tax_rates($product->get_tax_class('unfiltered')); if ($tax_rates !== $base_tax_rates) { // Work out a new base price without the shop's base tax. $taxes = \WC_Tax::calc_tax($price, $base_tax_rates, true); $new_taxes = \WC_Tax::calc_tax($price - array_sum($taxes), $tax_rates, false); // Now we have a new item price. $price = $price - array_sum($taxes) + array_sum($new_taxes); } } } } $subtotal = $price; $subtotal_tax = floatval(0); if ($calculate_tax && $taxable) { $subtotal_taxes = \WC_Tax::calc_tax($subtotal, $tax_rates, $priceIncludesTax); $subtotal_tax = array_sum(array_map(array($this, 'roundLineTax'), $subtotal_taxes)); if ($priceIncludesTax) { // Use unrounded taxes so we can re-calculate from the orders screen accurately later. $subtotal = $subtotal - array_sum($subtotal_taxes); } } $itemsSubtotals += wc_round_tax_total($subtotal); if ($inclTax) { $itemsSubtotals += wc_round_tax_total($subtotal_tax); } } return $itemsSubtotals; } /** * @param bool $inclTax * * @return float */ protected function calculateItemsSubtotals($inclTax = true) { /** @see \WC_Cart_Totals::calculate_item_subtotals */ $cart = $this->cart; $cartContext = $cart->getContext(); $context = $cartContext->getGlobalContext(); $adjust_non_base_location_prices = apply_filters('woocommerce_adjust_non_base_location_prices', true); $is_customer_vat_exempt = $cart->getContext()->getCustomer()->isVatExempt(); $calculate_tax = $context->getIsTaxEnabled() && ! $is_customer_vat_exempt; $itemsSubtotals = floatval(0); foreach ($cart->getItems() as $item) { $product = $item->getWcItem()->getProduct(); $priceIncludesTax = $context->getIsPricesIncludeTax(); $taxable = $context->getIsTaxEnabled() && 'taxable' === $product->get_tax_status(); if ($item->isPriceChanged()) { $price = $item->getTotalPrice(); } else { $facade = $item->getWcItem(); if($facade->getNewPrice() !== null) { $price = $facade->getNewPrice(); } else { $price = $product->is_on_sale('edit') ? (float)$product->get_sale_price('edit') : $item->getPrice(); } $price *= $item->getQty(); } $wcCustomer = $this->wcCustomerConverter->convertToWcCustomer($cartContext->getCustomer()); if ($context->getIsTaxEnabled()) { $tax_rates = \WC_Tax::get_rates($product->get_tax_class(), $wcCustomer); } else { $tax_rates = array(); } if ($priceIncludesTax) { if ($is_customer_vat_exempt) { /** @see \WC_Cart_Totals::remove_item_base_taxes */ if ($priceIncludesTax && $taxable) { if (apply_filters('woocommerce_adjust_non_base_location_prices', true)) { $base_tax_rates = \WC_Tax::get_base_tax_rates($product->get_tax_class('unfiltered')); } else { $base_tax_rates = $tax_rates; } // Work out a new base price without the shop's base tax. $taxes = \WC_Tax::calc_tax($price, $base_tax_rates, true); // Now we have a new item price (excluding TAX). $price = round($price - array_sum($taxes)); $priceIncludesTax = false; } } elseif ($adjust_non_base_location_prices) { /** @see \WC_Cart_Totals::adjust_non_base_location_price */ if ($priceIncludesTax && $taxable) { $base_tax_rates = \WC_Tax::get_base_tax_rates($product->get_tax_class('unfiltered')); if ($tax_rates !== $base_tax_rates) { // Work out a new base price without the shop's base tax. $taxes = \WC_Tax::calc_tax($price, $base_tax_rates, true); $new_taxes = \WC_Tax::calc_tax($price - array_sum($taxes), $tax_rates, false); // Now we have a new item price. $price = $price - array_sum($taxes) + array_sum($new_taxes); } } } } $subtotal = $price; $subtotal_tax = floatval(0); if ($calculate_tax && $taxable) { $subtotal_taxes = \WC_Tax::calc_tax($subtotal, $tax_rates, $priceIncludesTax); $subtotal_tax = array_sum(array_map(array($this, 'roundLineTax'), $subtotal_taxes)); if ($priceIncludesTax) { // Use unrounded taxes so we can re-calculate from the orders screen accurately later. $subtotal = $subtotal - array_sum($subtotal_taxes); } } $itemsSubtotals += wc_round_tax_total($subtotal); if ($inclTax) { $itemsSubtotals += wc_round_tax_total($subtotal_tax); } } return $itemsSubtotals; } protected static function roundLineTax($value, $in_cents = true) { if ( ! self::roundAtSubtotal()) { $value = wc_round_tax_total($value, $in_cents ? null : 0); } return $value; } protected static function roundAtSubtotal() { return 'yes' === get_option('woocommerce_tax_round_at_subtotal'); } /** * @param bool $inclTax * * @return float */ public function getSubtotal($inclTax = false) { return $this->calculateItemsSubtotals($inclTax); } /** * @param bool $inclTax * * @return float */ public function getSubtotalWithoutImmutable($inclTax = false) { return $this->calculateItemsSubtotalsWithoutImmutable($inclTax); } public function calculateTotalDiscounts($inclTax = false) { if (!(WC()->cart instanceof \WC_Cart)) { return 0.0; } $itemsSubtotals = $this->getSubtotalWithoutImmutable($inclTax); $items = array(); foreach ($this->cart->getItems() as $cartItem) { $cart_item_key = $cartItem->getWcItem()->getKey(); $cart_item = $cartItem->getWcItem()->getData(); $item = (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(), ); $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->getItemTaxRates($item); /** @see \WC_Cart_Totals::get_item_tax_rates() */ $items[$cart_item_key] = $item; } /** @see \WC_Cart_Totals::get_coupons_from_cart() */ $coupons = $this->getCouponsFromCart(); $discounts = new \WC_Discounts(WC()->cart); $discounts->set_items($items); add_filter("adp_get_price_html_is_mod_needed","__return_false"); foreach ($coupons as $coupon) { $discounts->apply_coupon($coupon); } remove_filter("adp_get_price_html_is_mod_needed","__return_false"); $couponDiscountAmountTotal = array_sum($discounts->get_discounts_by_coupon(false)); foreach ($this->cart->getCoupons() as $coupon) { if ($coupon instanceof CouponCart) { if ($coupon->isType($coupon::TYPE_FIXED_VALUE)) { $couponDiscountAmountTotal += $coupon->getValue(); } elseif ($coupon->isType($coupon::TYPE_PERCENTAGE)) { $couponDiscountAmountTotal += $itemsSubtotals * $coupon->getValue() / 100; } } elseif ($coupon instanceof CouponCartItem) { $couponDiscountAmountTotal += $coupon->getValue() * $coupon->getAffectedCartItemQty(); } } return $couponDiscountAmountTotal; } protected function getItemTaxRates($item) { if (!wc_tax_enabled()) { return array(); } $tax_class = $item->product->get_tax_class(); $item_tax_rates = array(); if (isset($this->item_tax_rates)) { $item_tax_rates = $this->item_tax_rates[$tax_class] ?? $this->item_tax_rates[$tax_class] = \WC_Tax::get_rates( $item->product->get_tax_class(), WC()->cart->get_customer() ); } // Allow plugins to filter item tax rates. // return apply_filters( 'woocommerce_cart_totals_get_item_tax_rates', $item_tax_rates, $item, $this->cart ); return $item_tax_rates; } protected function getCouponsFromCart() { $coupons = WC()->cart->get_coupons(); foreach ($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; } // Allow plugins to override the default order. $coupon->sort = apply_filters('woocommerce_coupon_sort', $coupon->sort, $coupon); } uasort($coupons, array($this, 'sortCouponsCallback')); return $coupons; } /** * @see \WC_Cart_Totals::sort_coupons_callback() */ protected function sortCouponsCallback($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; } }