/** * 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/woothemes/woocommerce/issues/7669 and https://github.com/woothemes/woocommerce/issues/8074. */ if (wc_prices_include_tax()) { $discount_percent = $cart_item['data']->get_price_including_tax() * $cart_item_qty / WC()->cart->subtotal; } else { $discount_percent = $cart_item['data']->get_price_excluding_tax() * $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); }
/** * 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(); $cart = $this->get_cart(); /** * Calculate subtotals for items. This is done first so that discount logic can use the values. */ foreach ($cart as $cart_item_key => $values) { $_product = $values['data']; $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. * * 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 = $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; } // Order cart items by price so coupon logic is 'fair' for customers and not based on order added to cart. uasort($cart, array($this, 'sort_by_subtotal')); /** * Calculate totals for items. */ 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 = $this->get_discounted_price($values, $base_price, true); $line_subtotal_tax = 0; $line_subtotal = $line_price; $line_tax = 0; $line_total = round($discounted_price * $values['quantity'], wc_get_rounding_precision()); /** * 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_get_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 and get the discounted price FOR A SINGLE ITEM $discounted_price = $this->get_discounted_price($values, $adjusted_price, true); // Convert back to line price $discounted_line_price = $discounted_price * $values['quantity']; // Now use rounded line price to get taxes. $discounted_taxes = WC_Tax::calc_tax($discounted_line_price, $item_tax_rates, true); $line_tax = array_sum($discounted_taxes); $line_total = $discounted_line_price - $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); // Convert back to line price $discounted_line_price = $discounted_price * $values['quantity']; // Now use rounded line price to get taxes. $discounted_taxes = WC_Tax::calc_tax($discounted_line_price, $item_tax_rates, true); $line_tax = array_sum($discounted_taxes); $line_total = $discounted_line_price - $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. For tax inclusive prices, we do some extra rounding logic so the stored * values "add up" when viewing the order in admin. This does have the disadvatage of not being able to * recalculate the tax total/subtotal accurately in the future, but it does ensure the data looks correct. * * Tax exclusive prices are not affected. */ if (!$_product->is_taxable() || $this->prices_include_tax) { $this->cart_contents[$cart_item_key]['line_total'] = round($line_total + $line_tax - wc_round_tax_total($line_tax), $this->dp); $this->cart_contents[$cart_item_key]['line_subtotal'] = round($line_subtotal + $line_subtotal_tax - wc_round_tax_total($line_subtotal_tax), $this->dp); $this->cart_contents[$cart_item_key]['line_tax'] = wc_round_tax_total($line_tax); $this->cart_contents[$cart_item_key]['line_subtotal_tax'] = wc_round_tax_total($line_subtotal_tax); $this->cart_contents[$cart_item_key]['line_tax_data'] = array('total' => array_map('wc_round_tax_total', $discounted_taxes), 'subtotal' => array_map('wc_round_tax_total', $taxes)); } else { $this->cart_contents[$cart_item_key]['line_total'] = $line_total; $this->cart_contents[$cart_item_key]['line_subtotal'] = $line_subtotal; $this->cart_contents[$cart_item_key]['line_tax'] = $line_tax; $this->cart_contents[$cart_item_key]['line_subtotal_tax'] = $line_subtotal_tax; $this->cart_contents[$cart_item_key]['line_tax_data'] = array('total' => $discounted_taxes, 'subtotal' => $taxes); } } // 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->get_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->get_is_vat_exempt()) { $this->remove_taxes(); } } do_action('woocommerce_after_calculate_totals', $this); $this->set_session(); }
/** * Load options. * * @access public */ public static function init() { self::$precision = wc_get_rounding_precision(); self::$round_at_subtotal = 'yes' === get_option('woocommerce_tax_round_at_subtotal'); add_action('update_option_woocommerce_tax_classes', array(__CLASS__, 'maybe_remove_tax_class_rates'), 10, 2); }
/** * Enqueue scripts. */ public function admin_scripts() { global $wp_query, $post; $screen = get_current_screen(); $screen_id = $screen ? $screen->id : ''; $wc_screen_id = sanitize_title(__('WooCommerce', 'woocommerce')); $suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min'; // Register scripts wp_register_script('woocommerce_admin', WC()->plugin_url() . '/assets/js/admin/woocommerce_admin' . $suffix . '.js', array('jquery', 'jquery-blockui', 'jquery-ui-sortable', 'jquery-ui-widget', 'jquery-ui-core', 'jquery-tiptip'), WC_VERSION); wp_register_script('jquery-blockui', WC()->plugin_url() . '/assets/js/jquery-blockui/jquery.blockUI' . $suffix . '.js', array('jquery'), '2.70', true); wp_register_script('jquery-tiptip', WC()->plugin_url() . '/assets/js/jquery-tiptip/jquery.tipTip' . $suffix . '.js', array('jquery'), WC_VERSION, true); wp_register_script('accounting', WC()->plugin_url() . '/assets/js/accounting/accounting' . $suffix . '.js', array('jquery'), '0.4.2'); wp_register_script('round', WC()->plugin_url() . '/assets/js/round/round' . $suffix . '.js', array('jquery'), WC_VERSION); wp_register_script('wc-admin-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes' . $suffix . '.js', array('jquery', 'jquery-ui-datepicker', 'jquery-ui-sortable', 'accounting', 'round', 'wc-enhanced-select', 'plupload-all', 'stupidtable', 'jquery-tiptip'), WC_VERSION); wp_register_script('zeroclipboard', WC()->plugin_url() . '/assets/js/zeroclipboard/jquery.zeroclipboard' . $suffix . '.js', array('jquery'), WC_VERSION); wp_register_script('qrcode', WC()->plugin_url() . '/assets/js/jquery-qrcode/jquery.qrcode' . $suffix . '.js', array('jquery'), WC_VERSION); wp_register_script('stupidtable', WC()->plugin_url() . '/assets/js/stupidtable/stupidtable' . $suffix . '.js', array('jquery'), WC_VERSION); wp_register_script('serializejson', WC()->plugin_url() . '/assets/js/jquery-serializejson/jquery.serializejson' . $suffix . '.js', array('jquery'), '2.6.1'); wp_register_script('flot', WC()->plugin_url() . '/assets/js/jquery-flot/jquery.flot' . $suffix . '.js', array('jquery'), WC_VERSION); wp_register_script('flot-resize', WC()->plugin_url() . '/assets/js/jquery-flot/jquery.flot.resize' . $suffix . '.js', array('jquery', 'flot'), WC_VERSION); wp_register_script('flot-time', WC()->plugin_url() . '/assets/js/jquery-flot/jquery.flot.time' . $suffix . '.js', array('jquery', 'flot'), WC_VERSION); wp_register_script('flot-pie', WC()->plugin_url() . '/assets/js/jquery-flot/jquery.flot.pie' . $suffix . '.js', array('jquery', 'flot'), WC_VERSION); wp_register_script('flot-stack', WC()->plugin_url() . '/assets/js/jquery-flot/jquery.flot.stack' . $suffix . '.js', array('jquery', 'flot'), WC_VERSION); wp_register_script('wc-settings-tax', WC()->plugin_url() . '/assets/js/admin/settings-views-html-settings-tax' . $suffix . '.js', array('jquery', 'wp-util', 'underscore', 'backbone', 'jquery-blockui'), WC_VERSION); wp_register_script('wc-backbone-modal', WC()->plugin_url() . '/assets/js/admin/backbone-modal' . $suffix . '.js', array('underscore', 'backbone', 'wp-util'), WC_VERSION); wp_register_script('wc-shipping-zones', WC()->plugin_url() . '/assets/js/admin/wc-shipping-zones' . $suffix . '.js', array('jquery', 'wp-util', 'underscore', 'backbone', 'jquery-ui-sortable', 'wc-enhanced-select', 'wc-backbone-modal'), WC_VERSION); wp_register_script('wc-shipping-zone-methods', WC()->plugin_url() . '/assets/js/admin/wc-shipping-zone-methods' . $suffix . '.js', array('jquery', 'wp-util', 'underscore', 'backbone', 'jquery-ui-sortable', 'wc-backbone-modal'), WC_VERSION); wp_register_script('wc-shipping-classes', WC()->plugin_url() . '/assets/js/admin/wc-shipping-classes' . $suffix . '.js', array('jquery', 'wp-util', 'underscore', 'backbone'), WC_VERSION); wp_register_script('select2', WC()->plugin_url() . '/assets/js/select2/select2' . $suffix . '.js', array('jquery'), '3.5.4'); wp_register_script('wc-enhanced-select', WC()->plugin_url() . '/assets/js/admin/wc-enhanced-select' . $suffix . '.js', array('jquery', 'select2'), WC_VERSION); wp_localize_script('wc-enhanced-select', 'wc_enhanced_select_params', array('i18n_matches_1' => _x('One result is available, press enter to select it.', 'enhanced select', 'woocommerce'), 'i18n_matches_n' => _x('%qty% results are available, use up and down arrow keys to navigate.', 'enhanced select', 'woocommerce'), 'i18n_no_matches' => _x('No matches found', 'enhanced select', 'woocommerce'), 'i18n_ajax_error' => _x('Loading failed', 'enhanced select', 'woocommerce'), 'i18n_input_too_short_1' => _x('Please enter 1 or more characters', 'enhanced select', 'woocommerce'), 'i18n_input_too_short_n' => _x('Please enter %qty% or more characters', 'enhanced select', 'woocommerce'), 'i18n_input_too_long_1' => _x('Please delete 1 character', 'enhanced select', 'woocommerce'), 'i18n_input_too_long_n' => _x('Please delete %qty% characters', 'enhanced select', 'woocommerce'), 'i18n_selection_too_long_1' => _x('You can only select 1 item', 'enhanced select', 'woocommerce'), 'i18n_selection_too_long_n' => _x('You can only select %qty% items', 'enhanced select', 'woocommerce'), 'i18n_load_more' => _x('Loading more results…', 'enhanced select', 'woocommerce'), 'i18n_searching' => _x('Searching…', 'enhanced select', 'woocommerce'), 'ajax_url' => admin_url('admin-ajax.php'), 'search_products_nonce' => wp_create_nonce('search-products'), 'search_customers_nonce' => wp_create_nonce('search-customers'))); // Accounting wp_localize_script('accounting', 'accounting_params', array('mon_decimal_point' => wc_get_price_decimal_separator())); // WooCommerce admin pages if (in_array($screen_id, wc_get_screen_ids())) { wp_enqueue_script('iris'); wp_enqueue_script('woocommerce_admin'); wp_enqueue_script('wc-enhanced-select'); wp_enqueue_script('jquery-ui-sortable'); wp_enqueue_script('jquery-ui-autocomplete'); $locale = localeconv(); $decimal = isset($locale['decimal_point']) ? $locale['decimal_point'] : '.'; $params = array('i18n_decimal_error' => sprintf(__('Please enter in decimal (%s) format without thousand separators.', 'woocommerce'), $decimal), 'i18n_mon_decimal_error' => sprintf(__('Please enter in monetary decimal (%s) format without thousand separators and currency symbols.', 'woocommerce'), wc_get_price_decimal_separator()), 'i18n_country_iso_error' => __('Please enter in country code with two capital letters.', 'woocommerce'), 'i18_sale_less_than_regular_error' => __('Please enter in a value less than the regular price.', 'woocommerce'), 'decimal_point' => $decimal, 'mon_decimal_point' => wc_get_price_decimal_separator()); wp_localize_script('woocommerce_admin', 'woocommerce_admin', $params); } // Edit product category pages if (in_array($screen_id, array('edit-product_cat'))) { wp_enqueue_media(); } // Products if (in_array($screen_id, array('edit-product'))) { wp_register_script('woocommerce_quick-edit', WC()->plugin_url() . '/assets/js/admin/quick-edit' . $suffix . '.js', array('jquery', 'woocommerce_admin'), WC_VERSION); wp_enqueue_script('woocommerce_quick-edit'); } // Meta boxes if (in_array($screen_id, array('product', 'edit-product'))) { wp_enqueue_media(); wp_register_script('wc-admin-product-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes-product' . $suffix . '.js', array('wc-admin-meta-boxes', 'media-models'), WC_VERSION); wp_register_script('wc-admin-variation-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes-product-variation' . $suffix . '.js', array('wc-admin-meta-boxes', 'serializejson', 'media-models'), WC_VERSION); wp_enqueue_script('wc-admin-product-meta-boxes'); wp_enqueue_script('wc-admin-variation-meta-boxes'); $params = array('post_id' => isset($post->ID) ? $post->ID : '', 'plugin_url' => WC()->plugin_url(), 'ajax_url' => admin_url('admin-ajax.php'), 'woocommerce_placeholder_img_src' => wc_placeholder_img_src(), 'add_variation_nonce' => wp_create_nonce('add-variation'), 'link_variation_nonce' => wp_create_nonce('link-variations'), 'delete_variations_nonce' => wp_create_nonce('delete-variations'), 'load_variations_nonce' => wp_create_nonce('load-variations'), 'save_variations_nonce' => wp_create_nonce('save-variations'), 'bulk_edit_variations_nonce' => wp_create_nonce('bulk-edit-variations'), 'i18n_link_all_variations' => esc_js(sprintf(__('Are you sure you want to link all variations? This will create a new variation for each and every possible combination of variation attributes (max %d per run).', 'woocommerce'), defined('WC_MAX_LINKED_VARIATIONS') ? WC_MAX_LINKED_VARIATIONS : 50)), 'i18n_enter_a_value' => esc_js(__('Enter a value', 'woocommerce')), 'i18n_enter_menu_order' => esc_js(__('Variation menu order (determines position in the list of variations)', 'woocommerce')), 'i18n_enter_a_value_fixed_or_percent' => esc_js(__('Enter a value (fixed or %)', 'woocommerce')), 'i18n_delete_all_variations' => esc_js(__('Are you sure you want to delete all variations? This cannot be undone.', 'woocommerce')), 'i18n_last_warning' => esc_js(__('Last warning, are you sure?', 'woocommerce')), 'i18n_choose_image' => esc_js(__('Choose an image', 'woocommerce')), 'i18n_set_image' => esc_js(__('Set variation image', 'woocommerce')), 'i18n_variation_added' => esc_js(__("variation added", 'woocommerce')), 'i18n_variations_added' => esc_js(__("variations added", 'woocommerce')), 'i18n_no_variations_added' => esc_js(__("No variations added", 'woocommerce')), 'i18n_remove_variation' => esc_js(__('Are you sure you want to remove this variation?', 'woocommerce')), 'i18n_scheduled_sale_start' => esc_js(__('Sale start date (YYYY-MM-DD format or leave blank)', 'woocommerce')), 'i18n_scheduled_sale_end' => esc_js(__('Sale end date (YYYY-MM-DD format or leave blank)', 'woocommerce')), 'i18n_edited_variations' => esc_js(__('Save changes before changing page?', 'woocommerce')), 'i18n_variation_count_single' => esc_js(__('%qty% variation', 'woocommerce')), 'i18n_variation_count_plural' => esc_js(__('%qty% variations', 'woocommerce')), 'variations_per_page' => absint(apply_filters('woocommerce_admin_meta_boxes_variations_per_page', 15))); wp_localize_script('wc-admin-variation-meta-boxes', 'woocommerce_admin_meta_boxes_variations', $params); } if (in_array(str_replace('edit-', '', $screen_id), wc_get_order_types('order-meta-boxes'))) { wp_register_script('wc-admin-order-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes-order' . $suffix . '.js', array('wc-admin-meta-boxes', 'wc-backbone-modal'), WC_VERSION); wp_enqueue_script('wc-admin-order-meta-boxes'); $params = array('countries' => json_encode(array_merge(WC()->countries->get_allowed_country_states(), WC()->countries->get_shipping_country_states())), 'i18n_select_state_text' => esc_attr__('Select an option…', 'woocommerce')); wp_localize_script('wc-admin-order-meta-boxes', 'woocommerce_admin_meta_boxes_order', $params); } if (in_array($screen_id, array('shop_coupon', 'edit-shop_coupon'))) { wp_register_script('wc-admin-coupon-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes-coupon' . $suffix . '.js', array('wc-admin-meta-boxes'), WC_VERSION); wp_enqueue_script('wc-admin-coupon-meta-boxes'); } if (in_array(str_replace('edit-', '', $screen_id), array_merge(array('shop_coupon', 'product'), wc_get_order_types('order-meta-boxes')))) { $post_id = isset($post->ID) ? $post->ID : ''; $currency = ''; if ($post_id && in_array(get_post_type($post_id), wc_get_order_types('order-meta-boxes'))) { $order = wc_get_order($post_id); $currency = $order->get_order_currency(); } $params = array('remove_item_notice' => __('Are you sure you want to remove the selected items? If you have previously reduced this item\'s stock, or this order was submitted by a customer, you will need to manually restore the item\'s stock.', 'woocommerce'), 'i18n_select_items' => __('Please select some items.', 'woocommerce'), 'i18n_do_refund' => __('Are you sure you wish to process this refund? This action cannot be undone.', 'woocommerce'), 'i18n_delete_refund' => __('Are you sure you wish to delete this refund? This action cannot be undone.', 'woocommerce'), 'i18n_delete_tax' => __('Are you sure you wish to delete this tax column? This action cannot be undone.', 'woocommerce'), 'remove_item_meta' => __('Remove this item meta?', 'woocommerce'), 'remove_attribute' => __('Remove this attribute?', 'woocommerce'), 'name_label' => __('Name', 'woocommerce'), 'remove_label' => __('Remove', 'woocommerce'), 'click_to_toggle' => __('Click to toggle', 'woocommerce'), 'values_label' => __('Value(s)', 'woocommerce'), 'text_attribute_tip' => __('Enter some text, or some attributes by pipe (|) separating values.', 'woocommerce'), 'visible_label' => __('Visible on the product page', 'woocommerce'), 'used_for_variations_label' => __('Used for variations', 'woocommerce'), 'new_attribute_prompt' => __('Enter a name for the new attribute term:', 'woocommerce'), 'calc_totals' => __('Calculate totals based on order items, discounts, and shipping?', 'woocommerce'), 'calc_line_taxes' => __('Calculate line taxes? This will calculate taxes based on the customers country. If no billing/shipping is set it will use the store base country.', 'woocommerce'), 'copy_billing' => __('Copy billing information to shipping information? This will remove any currently entered shipping information.', 'woocommerce'), 'load_billing' => __('Load the customer\'s billing information? This will remove any currently entered billing information.', 'woocommerce'), 'load_shipping' => __('Load the customer\'s shipping information? This will remove any currently entered shipping information.', 'woocommerce'), 'featured_label' => __('Featured', 'woocommerce'), 'prices_include_tax' => esc_attr(get_option('woocommerce_prices_include_tax')), 'tax_based_on' => esc_attr(get_option('woocommerce_tax_based_on')), 'round_at_subtotal' => esc_attr(get_option('woocommerce_tax_round_at_subtotal')), 'no_customer_selected' => __('No customer selected', 'woocommerce'), 'plugin_url' => WC()->plugin_url(), 'ajax_url' => admin_url('admin-ajax.php'), 'order_item_nonce' => wp_create_nonce('order-item'), 'add_attribute_nonce' => wp_create_nonce('add-attribute'), 'save_attributes_nonce' => wp_create_nonce('save-attributes'), 'calc_totals_nonce' => wp_create_nonce('calc-totals'), 'get_customer_details_nonce' => wp_create_nonce('get-customer-details'), 'search_products_nonce' => wp_create_nonce('search-products'), 'grant_access_nonce' => wp_create_nonce('grant-access'), 'revoke_access_nonce' => wp_create_nonce('revoke-access'), 'add_order_note_nonce' => wp_create_nonce('add-order-note'), 'delete_order_note_nonce' => wp_create_nonce('delete-order-note'), 'calendar_image' => WC()->plugin_url() . '/assets/images/calendar.png', 'post_id' => isset($post->ID) ? $post->ID : '', 'base_country' => WC()->countries->get_base_country(), 'currency_format_num_decimals' => wc_get_price_decimals(), 'currency_format_symbol' => get_woocommerce_currency_symbol($currency), 'currency_format_decimal_sep' => esc_attr(wc_get_price_decimal_separator()), 'currency_format_thousand_sep' => esc_attr(wc_get_price_thousand_separator()), 'currency_format' => esc_attr(str_replace(array('%1$s', '%2$s'), array('%s', '%v'), get_woocommerce_price_format())), 'rounding_precision' => wc_get_rounding_precision(), 'tax_rounding_mode' => WC_TAX_ROUNDING_MODE, 'product_types' => array_unique(array_merge(array('simple', 'grouped', 'variable', 'external'), array_keys(wc_get_product_types()))), 'i18n_download_permission_fail' => __('Could not grant access - the user may already have permission for this file or billing email is not set. Ensure the billing email is set, and the order has been saved.', 'woocommerce'), 'i18n_permission_revoke' => __('Are you sure you want to revoke access to this download?', 'woocommerce'), 'i18n_tax_rate_already_exists' => __('You cannot add the same tax rate twice!', 'woocommerce'), 'i18n_product_type_alert' => __('Your product has variations! Before changing the product type, it is a good idea to delete the variations to avoid errors in the stock reports.', 'woocommerce'), 'i18n_delete_note' => __('Are you sure you wish to delete this note? This action cannot be undone.', 'woocommerce')); wp_localize_script('wc-admin-meta-boxes', 'woocommerce_admin_meta_boxes', $params); } // Term ordering - only when sorting by term_order if ((strstr($screen_id, 'edit-pa_') || !empty($_GET['taxonomy']) && in_array($_GET['taxonomy'], apply_filters('woocommerce_sortable_taxonomies', array('product_cat')))) && !isset($_GET['orderby'])) { wp_register_script('woocommerce_term_ordering', WC()->plugin_url() . '/assets/js/admin/term-ordering' . $suffix . '.js', array('jquery-ui-sortable'), WC_VERSION); wp_enqueue_script('woocommerce_term_ordering'); $taxonomy = isset($_GET['taxonomy']) ? wc_clean($_GET['taxonomy']) : ''; $woocommerce_term_order_params = array('taxonomy' => $taxonomy); wp_localize_script('woocommerce_term_ordering', 'woocommerce_term_ordering_params', $woocommerce_term_order_params); } // Product sorting - only when sorting by menu order on the products page if (current_user_can('edit_others_pages') && $screen_id == 'edit-product' && isset($wp_query->query['orderby']) && $wp_query->query['orderby'] == 'menu_order title') { wp_register_script('woocommerce_product_ordering', WC()->plugin_url() . '/assets/js/admin/product-ordering' . $suffix . '.js', array('jquery-ui-sortable'), WC_VERSION, true); wp_enqueue_script('woocommerce_product_ordering'); } // Reports Pages if (in_array($screen_id, apply_filters('woocommerce_reports_screen_ids', array($wc_screen_id . '_page_wc-reports', 'toplevel_page_wc-reports', 'dashboard')))) { wp_register_script('wc-reports', WC()->plugin_url() . '/assets/js/admin/reports' . $suffix . '.js', array('jquery', 'jquery-ui-datepicker'), WC_VERSION); wp_enqueue_script('wc-reports'); wp_enqueue_script('flot'); wp_enqueue_script('flot-resize'); wp_enqueue_script('flot-time'); wp_enqueue_script('flot-pie'); wp_enqueue_script('flot-stack'); } // API settings if ($wc_screen_id . '_page_wc-settings' === $screen_id && isset($_GET['section']) && 'keys' == $_GET['section']) { wp_register_script('wc-api-keys', WC()->plugin_url() . '/assets/js/admin/api-keys' . $suffix . '.js', array('jquery', 'woocommerce_admin', 'underscore', 'backbone', 'wp-util', 'qrcode', 'zeroclipboard'), WC_VERSION, true); wp_enqueue_script('wc-api-keys'); wp_localize_script('wc-api-keys', 'woocommerce_admin_api_keys', array('ajax_url' => admin_url('admin-ajax.php'), 'update_api_nonce' => wp_create_nonce('update-api-key'), 'clipboard_failed' => esc_html__('Copying to clipboard failed. Please press Ctrl/Cmd+C to copy.', 'woocommerce'))); } // System status if ($wc_screen_id . '_page_wc-status' === $screen_id) { wp_enqueue_script('zeroclipboard'); } if (in_array($screen_id, array('user-edit', 'profile'))) { wp_register_script('wc-users', WC()->plugin_url() . '/assets/js/admin/users' . $suffix . '.js', array('jquery', 'wc-enhanced-select'), WC_VERSION, true); wp_enqueue_script('wc-users'); wp_localize_script('wc-users', 'wc_users_params', array('countries' => json_encode(array_merge(WC()->countries->get_allowed_country_states(), WC()->countries->get_shipping_country_states())), 'i18n_select_state_text' => esc_attr__('Select an option…', 'woocommerce'))); } }
/** * Load options. * * @access public */ public static function init() { self::$precision = wc_get_rounding_precision(); self::$round_at_subtotal = 'yes' === get_option('woocommerce_tax_round_at_subtotal'); }
/** * Gets the total discount amount. * @param bool $ex_tax Show discount excl any tax. * @return float */ public function get_total_discount($ex_tax = true) { if (!$this->order_version || version_compare($this->order_version, '2.3.7', '<')) { // Backwards compatible total calculation - totals were not stored consistently in old versions. if ($ex_tax) { if ($this->prices_include_tax) { $total_discount = (double) $this->cart_discount - (double) $this->cart_discount_tax; } else { $total_discount = (double) $this->cart_discount; } } else { if ($this->prices_include_tax) { $total_discount = (double) $this->cart_discount; } else { $total_discount = (double) $this->cart_discount + (double) $this->cart_discount_tax; } } // New logic - totals are always stored exclusive of tax, tax total is stored in cart_discount_tax } else { if ($ex_tax) { $total_discount = (double) $this->cart_discount; } else { $total_discount = (double) $this->cart_discount + (double) $this->cart_discount_tax; } } return apply_filters('woocommerce_order_amount_total_discount', round($total_discount, wc_get_rounding_precision()), $this); }
/** * Helper function to get around WooCommerce version 2.6.3 which removed the constant WC_ROUNDING_PRECISION and * introduced the function wc_get_rounding_precision. Every version 2.6.2 and earlier has the constant. Every version * 2.6.4 and later (hopefully) will also have the constant AND the wc_get_rounding_precision function. 2.6.3 only has * the function however. * * @see https://github.com/Prospress/woocommerce-subscriptions/issues/1545 * * @return int rounding precision */ function wcs_get_rounding_precision() { if (function_exists('wc_get_rounding_precision')) { $precision = wc_get_rounding_precision(); } elseif (defined('WC_ROUNDING_PRECISION')) { $precision = WC_ROUNDING_PRECISION; } else { $precision = wc_get_price_decimals() + 2; } return $precision; }