0.0, 'label' => '', ); if ( ! klbq_tax_enabled() || ! $product instanceof WC_Product || 'taxable' !== $product->get_tax_status() ) { return $out; } foreach ( WC_Tax::get_base_tax_rates( $product->get_tax_class() ) as $rate ) { $out['rate'] += isset( $rate['rate'] ) ? (float) $rate['rate'] : 0; if ( '' === $out['label'] && ! empty( $rate['label'] ) ) { $out['label'] = (string) $rate['label']; } } return $out; } /** * Catalog unit price as quoted (with or without tax, see klbq_tax_mode()). * * @param WC_Product $product Product. * @return float */ function klbq_product_price( $product ) { $args = array( 'price' => (float) $product->get_price(), 'qty' => 1, ); $price = 'excl' === klbq_tax_mode() ? wc_get_price_excluding_tax( $product, $args ) : wc_get_price_including_tax( $product, $args ); /** * Filters the catalog price a quote line starts with. * * @param float $price Unit price, as quoted. * @param WC_Product $product Product or variation. */ return (float) apply_filters( 'klbq_product_price', (float) $price, $product ); } /** * Quote line built from a product. * * @param WC_Product $product Product or variation. * @param float $qty Quantity. * @param float|null $price Unit price as quoted, or null for the catalog price. * @return array */ function klbq_item_from_product( $product, $qty = 1, $price = null ) { $product_id = $product->get_parent_id() ? $product->get_parent_id() : $product->get_id(); $image_id = $product->get_image_id(); if ( ! $image_id && $product->get_parent_id() ) { $parent = wc_get_product( $product->get_parent_id() ); $image_id = $parent ? $parent->get_image_id() : 0; } $catalog = klbq_product_price( $product ); $tax = klbq_product_tax( $product ); return array( 'product_id' => $product_id, 'variation_id' => $product->get_parent_id() ? $product->get_id() : 0, 'name' => wp_strip_all_tags( $product->get_name() ), 'sku' => (string) $product->get_sku(), 'image' => $image_id ? (string) wp_get_attachment_image_url( $image_id, 'woocommerce_thumbnail' ) : '', 'qty' => (float) $qty, 'price' => null === $price ? $catalog : (float) $price, 'price_original' => $catalog, 'tax_rate' => $tax['rate'], 'tax_label' => $tax['label'], 'stock' => $product->managing_stock() ? (int) $product->get_stock_quantity() : null, ); } /** * Compute quote totals. Mirrors totals() in assets/js/doc.js. * * - incl: subtotal − discount = total; the tax is the part contained in it. * - excl: subtotal − discount = base; total = base + tax. * * @param array $data Quote data (items, discount, tax_mode). * @return array{subtotal: float, discount: float, base: float, tax: float, total: float, factor: float} */ function klbq_totals( $data ) { $decimals = wc_get_price_decimals(); $mode = isset( $data['tax_mode'] ) && 'excl' === $data['tax_mode'] ? 'excl' : 'incl'; $subtotal = 0; foreach ( $data['items'] as $item ) { $subtotal += $item['price'] * $item['qty']; } $value = isset( $data['discount']['value'] ) ? (float) $data['discount']['value'] : 0; if ( isset( $data['discount']['type'] ) && 'pct' === $data['discount']['type'] ) { $discount = $subtotal * min( max( $value, 0 ), 100 ) / 100; } else { $discount = min( max( $value, 0 ), $subtotal ); } $discount = round( $discount, $decimals ); $after = $subtotal - $discount; $factor = $subtotal > 0 ? $after / $subtotal : 1; $tax = 0; foreach ( $data['items'] as $item ) { $line = $item['price'] * $item['qty'] * $factor; $rate = isset( $item['tax_rate'] ) ? max( 0, (float) $item['tax_rate'] ) : 0; if ( $rate > 0 ) { $tax += 'excl' === $mode ? $line * $rate / 100 : $line - $line / ( 1 + $rate / 100 ); } } $tax = round( $tax, $decimals ); return array( 'subtotal' => $subtotal, 'discount' => $discount, 'base' => 'excl' === $mode ? $after : $after - $tax, 'tax' => $tax, 'total' => 'excl' === $mode ? $after + $tax : $after, 'factor' => $factor, ); } /** * Tax ID labels by store country: [company label, customer label]. Proper * names of each country's tax ID, so they are not translated. * * @param string $country ISO country code. * @return string[] */ function klbq_country_tax_labels( $country ) { $map = array( 'AR' => array( 'CUIT', 'CUIT / DNI' ), 'BO' => array( 'NIT', 'NIT / CI' ), 'BR' => array( 'CNPJ', 'CNPJ / CPF' ), 'CL' => array( 'RUT', 'RUT' ), 'CO' => array( 'NIT', 'NIT / CC' ), 'CR' => array( 'Cédula jurídica', 'Cédula' ), 'DO' => array( 'RNC', 'RNC / Cédula' ), 'EC' => array( 'RUC', 'RUC / CI' ), 'ES' => array( 'NIF', 'NIF / CIF' ), 'GT' => array( 'NIT', 'NIT / DPI' ), 'HN' => array( 'RTN', 'RTN' ), 'MX' => array( 'RFC', 'RFC' ), 'NI' => array( 'RUC', 'RUC / Cédula' ), 'PA' => array( 'RUC', 'RUC / Cédula' ), 'PE' => array( 'RUC', 'RUC / DNI' ), 'PY' => array( 'RUC', 'RUC / CI' ), 'SV' => array( 'NIT', 'NIT / DUI' ), 'UY' => array( 'RUT', 'RUT / CI' ), 'VE' => array( 'RIF', 'RIF / CI' ), ); if ( isset( $map[ $country ] ) ) { return $map[ $country ]; } return array( __( 'Tax ID', 'koalab-quote' ), __( 'Tax ID', 'koalab-quote' ) ); } /** * Store base country, or '' before WooCommerce loads. * * @return string */ function klbq_base_country() { return function_exists( 'WC' ) && WC()->countries ? (string) WC()->countries->get_base_country() : ''; } /** * Calling code of the store country without "+" (57 for Colombia), used to * turn local phone numbers into WhatsApp links. * * @return string */ function klbq_calling_code() { if ( ! function_exists( 'WC' ) || ! WC()->countries ) { return ''; } $code = WC()->countries->get_country_calling_code( klbq_base_country() ); $code = is_array( $code ) ? reset( $code ) : $code; return preg_replace( '/\D/', '', (string) $code ); } /** * Currency settings for the JavaScript money formatter. * * @return array */ function klbq_currency_config() { return array( 'symbol' => html_entity_decode( get_woocommerce_currency_symbol(), ENT_QUOTES, 'UTF-8' ), 'code' => get_woocommerce_currency(), 'decimals' => wc_get_price_decimals(), 'thousand' => wc_get_price_thousand_separator(), 'decimal' => wc_get_price_decimal_separator(), 'position' => get_option( 'woocommerce_currency_pos', 'left' ), ); } /** * BCP 47 tag of the user's locale for Intl date formatting (es-CO). * * @return string */ function klbq_js_locale() { return str_replace( '_', '-', determine_locale() ); }