/**
  * Returns the price in html format.
  *
  * @access public
  * @param string $price (default: '')
  * @return string
  */
 public function get_price_html($price = '')
 {
     $tax_display_mode = get_option('woocommerce_tax_display_shop');
     $child_prices = array();
     foreach ($this->get_children() as $child_id) {
         $child = wc_get_product($child_id);
         if ('' !== $child->get_price()) {
             $child_prices[] = 'incl' === $tax_display_mode ? wc_get_price_including_tax($child) : wc_get_price_excluding_tax($child);
         }
     }
     if (!empty($child_prices)) {
         $min_price = min($child_prices);
         $max_price = max($child_prices);
     } else {
         $min_price = '';
         $max_price = '';
     }
     if ('' !== $min_price) {
         $price = $min_price !== $max_price ? sprintf(_x('%1$s–%2$s', 'Price range: from-to', 'woocommerce'), wc_price($min_price), wc_price($max_price)) : wc_price($min_price);
         $is_free = 0 == $min_price && 0 == $max_price;
         if ($is_free) {
             $price = apply_filters('woocommerce_grouped_free_price_html', __('Free!', 'woocommerce'), $this);
         } else {
             $price = apply_filters('woocommerce_grouped_price_html', $price . wc_get_price_suffix($this), $this, $child_prices);
         }
     } else {
         $price = apply_filters('woocommerce_grouped_empty_price_html', '', $this);
     }
     return apply_filters('woocommerce_get_price_html', $price, $this);
 }
 /**
  * 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.
  *
  * @param WC_Product $product
  * @param int $quantity
  * @return string formatted price
  */
 public function get_product_subtotal($product, $quantity)
 {
     $price = $product->get_price();
     $taxable = $product->is_taxable();
     // Taxable
     if ($taxable) {
         if ('excl' === $this->tax_display_cart) {
             $row_price = wc_get_price_excluding_tax($product, array('qty' => $quantity));
             $product_subtotal = wc_price($row_price);
             if ($this->prices_include_tax && $this->tax_total > 0) {
                 $product_subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
             }
         } else {
             $row_price = wc_get_price_including_tax($product, array('qty' => $quantity));
             $product_subtotal = wc_price($row_price);
             if (!$this->prices_include_tax && $this->tax_total > 0) {
                 $product_subtotal .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>';
             }
         }
         // Non-taxable
     } else {
         $row_price = $price * $quantity;
         $product_subtotal = wc_price($row_price);
     }
     return apply_filters('woocommerce_cart_product_subtotal', $product_subtotal, $product, $quantity, $this);
 }
 /**
  * Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale.
  *
  * Can be filtered by plugins which modify costs, but otherwise will include the raw meta costs unlike get_price() which runs costs through the woocommerce_get_price filter.
  * This is to ensure modified prices are not cached, unless intended.
  *
  * @since  2.7.0
  * @param  WC_Product
  * @param  bool $include_taxes If taxes should be calculated or not.
  */
 private function read_price_data(&$product, $include_taxes = false)
 {
     global $wp_filter;
     /**
      * Transient name for storing prices for this product (note: Max transient length is 45)
      * @since 2.5.0 a single transient is used per product for all prices, rather than many transients per product.
      */
     $transient_name = 'wc_var_prices_' . $product->get_id();
     /**
      * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters.
      * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique.
      * @var string
      */
     $price_hash = $include_taxes ? array(get_option('woocommerce_tax_display_shop', 'excl'), WC_Tax::get_rates()) : array(false);
     $filter_names = array('woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price');
     foreach ($filter_names as $filter_name) {
         if (!empty($wp_filter[$filter_name])) {
             $price_hash[$filter_name] = array();
             foreach ($wp_filter[$filter_name] as $priority => $callbacks) {
                 $price_hash[$filter_name][] = array_values(wp_list_pluck($callbacks, 'function'));
             }
         }
     }
     $price_hash[] = WC_Cache_Helper::get_transient_version('product');
     $price_hash = md5(json_encode(apply_filters('woocommerce_get_variation_prices_hash', $price_hash, $product, $include_taxes)));
     /**
      * $this->prices_array is an array of values which may have been modified from what is stored in transients - this may not match $transient_cached_prices_array.
      * If the value has already been generated, we don't need to grab the values again so just return them. They are already filtered.
      */
     if (!empty($this->prices_array[$price_hash])) {
         if ($include_taxes) {
             $product->set_variation_prices_including_taxes($this->prices_array[$price_hash]);
         } else {
             $product->set_variation_prices($this->prices_array[$price_hash]);
         }
         /**
          * No locally cached value? Get the data from the transient or generate it.
          */
     } else {
         // Get value of transient
         $transient_cached_prices_array = array_filter((array) json_decode(strval(get_transient($transient_name)), true));
         // If the product version has changed since the transient was last saved, reset the transient cache.
         if (empty($transient_cached_prices_array['version']) || WC_Cache_Helper::get_transient_version('product') !== $transient_cached_prices_array['version']) {
             $transient_cached_prices_array = array('version' => WC_Cache_Helper::get_transient_version('product'));
         }
         // If the prices are not stored for this hash, generate them and add to the transient.
         if (empty($transient_cached_prices_array[$price_hash])) {
             $prices = array();
             $regular_prices = array();
             $sale_prices = array();
             $variation_ids = $product->get_visible_children();
             foreach ($variation_ids as $variation_id) {
                 if ($variation = wc_get_product($variation_id)) {
                     $price = apply_filters('woocommerce_variation_prices_price', $variation->get_price('edit'), $variation, $product);
                     $regular_price = apply_filters('woocommerce_variation_prices_regular_price', $variation->get_regular_price('edit'), $variation, $product);
                     $sale_price = apply_filters('woocommerce_variation_prices_sale_price', $variation->get_sale_price('edit'), $variation, $product);
                     // Skip empty prices
                     if ('' === $price) {
                         continue;
                     }
                     // If sale price does not equal price, the product is not yet on sale
                     if ($sale_price === $regular_price || $sale_price !== $price) {
                         $sale_price = $regular_price;
                     }
                     // If we are getting prices for display, we need to account for taxes
                     if ($include_taxes) {
                         if ('incl' === get_option('woocommerce_tax_display_shop')) {
                             $price = '' === $price ? '' : wc_get_price_including_tax($variation, array('qty' => 1, 'price' => $price));
                             $regular_price = '' === $regular_price ? '' : wc_get_price_including_tax($variation, array('qty' => 1, 'price' => $regular_price));
                             $sale_price = '' === $sale_price ? '' : wc_get_price_including_tax($variation, array('qty' => 1, 'price' => $sale_price));
                         } else {
                             $price = '' === $price ? '' : wc_get_price_excluding_tax($variation, array('qty' => 1, 'price' => $price));
                             $regular_price = '' === $regular_price ? '' : wc_get_price_excluding_tax($variation, array('qty' => 1, 'price' => $regular_price));
                             $sale_price = '' === $sale_price ? '' : wc_get_price_excluding_tax($variation, array('qty' => 1, 'price' => $sale_price));
                         }
                     }
                     $prices[$variation_id] = wc_format_decimal($price, wc_get_price_decimals());
                     $regular_prices[$variation_id] = wc_format_decimal($regular_price, wc_get_price_decimals());
                     $sale_prices[$variation_id] = wc_format_decimal($sale_price . '.00', wc_get_price_decimals());
                 }
             }
             asort($prices);
             asort($regular_prices);
             asort($sale_prices);
             $transient_cached_prices_array[$price_hash] = array('price' => $prices, 'regular_price' => $regular_prices, 'sale_price' => $sale_prices);
             set_transient($transient_name, json_encode($transient_cached_prices_array), DAY_IN_SECONDS * 30);
         }
         /**
          * Give plugins one last chance to filter the variation prices array which has been generated and store locally to the class.
          * This value may differ from the transient cache. It is filtered once before storing locally.
          */
         $this->prices_array[$price_hash] = apply_filters('woocommerce_variation_prices', $transient_cached_prices_array[$price_hash], $product, $include_taxes);
         if ($include_taxes) {
             $product->set_variation_prices_including_taxes($this->prices_array[$price_hash]);
         } else {
             $product->set_variation_prices($this->prices_array[$price_hash]);
         }
     }
 }
/**
 * Returns the price including or excluding tax, based on the 'woocommerce_tax_display_shop' setting.
 * @since  2.7.0
 * @param  WC_Product $product
 * @param  array $args
 * @return float
 */
function wc_get_price_to_display($product, $args = array())
{
    $args = wp_parse_args($args, array('qty' => 1, 'price' => $product->get_price()));
    $price = $args['price'];
    $qty = $args['qty'];
    return 'incl' === get_option('woocommerce_tax_display_shop') ? wc_get_price_including_tax($product, array('qty' => $qty, 'price' => $price)) : wc_get_price_excluding_tax($product, array('qty' => $qty, 'price' => $price));
}
 /**
  * Returns the price (including tax). Uses customer tax rates. Can work for a specific $qty for more accurate taxes.
  *
  * @deprecated 2.7.0 Use wc_get_price_including_tax instead.
  * @param  int $qty
  * @param  string $price to calculate, left blank to just use get_price()
  * @return string
  */
 public function get_price_including_tax($qty = 1, $price = '')
 {
     wc_deprecated_function('WC_Product::get_price_including_tax', '2.7', 'wc_get_price_including_tax');
     return wc_get_price_including_tax($this, array('qty' => $qty, 'price' => $price));
 }
/**
 * Get the price suffix for a product if needed.
 * @since  2.7.0
 * @param  WC_Product  $product
 * @param  string  $price
 * @param  integer $qty
 * @return string
 */
function wc_get_price_suffix($product, $price = '', $qty = 1)
{
    if (($price_display_suffix = get_option('woocommerce_price_display_suffix')) && wc_tax_enabled()) {
        $price = '' === $price ? $product->get_price() : $price;
        $price_display_suffix = ' <small class="woocommerce-price-suffix">' . wp_kses_post($price_display_suffix) . '</small>';
        $find = array('{price_including_tax}', '{price_excluding_tax}');
        $replace = array(wc_price(wc_get_price_including_tax($product, array('qty' => $qty, 'price' => $price))), wc_price(wc_get_price_excluding_tax($product, array('qty' => $qty, 'price' => $price))));
        $price_display_suffix = str_replace($find, $replace, $price_display_suffix);
    } else {
        $price_display_suffix = '';
    }
    return apply_filters('woocommerce_get_price_suffix', $price_display_suffix, $product);
}
 /**
  * Get the suffix to display after prices > 0.
  *
  * @param  string  $price to calculate, left blank to just use get_price()
  * @param  integer $qty   passed on to get_price_including_tax() or get_price_excluding_tax()
  * @return string
  */
 public function get_price_suffix($price = '', $qty = 1)
 {
     $html = '';
     if (($suffix = get_option('woocommerce_price_display_suffix')) && wc_tax_enabled()) {
         if ('' === $price) {
             $price = $this->get_price();
         }
         $replacements = array('{price_including_tax}' => wc_price(wc_get_price_including_tax($this, array('qty' => $qty, 'price' => $price))), '{price_excluding_tax}' => wc_price(wc_get_price_excluding_tax($this, array('qty' => $qty, 'price' => $price))));
         $html = str_replace(array_keys($replacements), array_values($replacements), ' <small class ="woocommerce-price-suffix">' . wp_kses_post($suffix) . '</small>');
     }
     return apply_filters('woocommerce_get_price_suffix', $html, $this);
 }
 /**
  * Get discount amount for a cart item.
  *
  * @param  float $discounting_amount Amount the coupon is being applied to
  * @param  array|null $cart_item Cart item being discounted if applicable
  * @param  boolean $single True if discounting a single qty item, false if its the line
  * @return float Amount this coupon has discounted
  */
 public function get_discount_amount($discounting_amount, $cart_item = null, $single = false)
 {
     $discount = 0;
     $cart_item_qty = is_null($cart_item) ? 1 : $cart_item['quantity'];
     if ($this->is_type(array('percent_product', 'percent'))) {
         $discount = $this->get_amount() * ($discounting_amount / 100);
     } elseif ($this->is_type('fixed_cart') && !is_null($cart_item) && WC()->cart->subtotal_ex_tax) {
         /**
          * This is the most complex discount - we need to divide the discount between rows based on their price in.
          * proportion to the subtotal. This is so rows with different tax rates get a fair discount, and so rows.
          * with no price (free) don't get discounted.
          *
          * Get item discount by dividing item cost by subtotal to get a %.
          *
          * Uses price inc tax if prices include tax to work around https://github.com/woocommerce/woocommerce/issues/7669 and https://github.com/woocommerce/woocommerce/issues/8074.
          */
         if (wc_prices_include_tax()) {
             $discount_percent = wc_get_price_including_tax($cart_item['data']) * $cart_item_qty / WC()->cart->subtotal;
         } else {
             $discount_percent = wc_get_price_excluding_tax($cart_item['data']) * $cart_item_qty / WC()->cart->subtotal_ex_tax;
         }
         $discount = $this->get_amount() * $discount_percent / $cart_item_qty;
     } elseif ($this->is_type('fixed_product')) {
         $discount = min($this->get_amount(), $discounting_amount);
         $discount = $single ? $discount : $discount * $cart_item_qty;
     }
     $discount = min($discount, $discounting_amount);
     // Handle the limit_usage_to_x_items option
     if ($this->is_type(array('percent_product', 'fixed_product'))) {
         if ($discounting_amount) {
             if (!$this->get_limit_usage_to_x_items()) {
                 $limit_usage_qty = $cart_item_qty;
             } else {
                 $limit_usage_qty = min($this->get_limit_usage_to_x_items(), $cart_item_qty);
                 $this->set_limit_usage_to_x_items(max(0, $this->get_limit_usage_to_x_items() - $limit_usage_qty));
             }
             if ($single) {
                 $discount = $discount * $limit_usage_qty / $cart_item_qty;
             } else {
                 $discount = $discount / $cart_item_qty * $limit_usage_qty;
             }
         }
     }
     $discount = round($discount, wc_get_rounding_precision());
     return apply_filters('woocommerce_coupon_get_discount_amount', $discount, $discounting_amount, $cart_item, $single, $this);
 }