From ad6c173757d59cc087e181914044eb2553b6b770 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 10 Apr 2015 10:28:30 +0100 Subject: [PATCH 1/4] WC_DISCOUNT_ROUNDING_MODE half down --- woocommerce.php | 1 + 1 file changed, 1 insertion(+) diff --git a/woocommerce.php b/woocommerce.php index cd4918c3d00..bcc39064b25 100644 --- a/woocommerce.php +++ b/woocommerce.php @@ -160,6 +160,7 @@ final class WooCommerce { $this->define( 'WC_VERSION', $this->version ); $this->define( 'WOOCOMMERCE_VERSION', $this->version ); $this->define( 'WC_ROUNDING_PRECISION', 4 ); + $this->define( 'WC_DISCOUNT_ROUNDING_MODE', 2 ); $this->define( 'WC_TAX_ROUNDING_MODE', 'yes' === get_option( 'woocommerce_prices_include_tax', 'no' ) ? 2 : 1 ); $this->define( 'WC_DELIMITER', '|' ); $this->define( 'WC_LOG_DIR', $upload_dir['basedir'] . '/wc-logs/' ); From 68194bf0eded7a03000d52b9e4f150c41dc22a9a Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 10 Apr 2015 10:28:46 +0100 Subject: [PATCH 2/4] Round discount totals --- includes/class-wc-cart.php | 35 +++++++++++------- includes/class-wc-coupon.php | 69 +++++++++++++++--------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index 39230e4a78c..e83265d9b70 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -1298,6 +1298,9 @@ class WC_Cart { $this->cart_contents[ $cart_item_key ]['line_tax_data'] = array( 'total' => $discounted_taxes, 'subtotal' => $taxes ); } + // Round cart contents + $this->cart_contents_total = round( $this->cart_contents_total, $this->dp ); + // Only calculate the grand total + shipping if on the cart/checkout if ( is_checkout() || is_cart() || defined('WOOCOMMERCE_CHECKOUT') || defined('WOOCOMMERCE_CART') ) { @@ -1326,6 +1329,14 @@ class WC_Cart { // Allow plugins to hook and alter totals before final total is calculated do_action( 'woocommerce_calculate_totals', $this ); + /*echo '
';
+				echo 'Cart Contents Total: ' . $this->cart_contents_total . '
'; + echo 'Tax Total: ' . $this->tax_total . '
'; + echo 'Shipping Tax Total: ' . $this->shipping_tax_total . '
'; + echo 'Shipping Total: ' . $this->shipping_total . '
'; + echo 'Fee Total: ' . $this->fee_total . '
'; + echo '
';*/ + // 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 ) ); @@ -1740,11 +1751,11 @@ class WC_Cart { public function get_coupon_discount_amount( $code, $ex_tax = true ) { $discount_amount = isset( $this->coupon_discount_amounts[ $code ] ) ? $this->coupon_discount_amounts[ $code ] : 0; - if ( $ex_tax ) { - return $discount_amount; - } else { - return $discount_amount + $this->get_coupon_discount_tax_amount( $code ); + if ( ! $ex_tax ) { + $discount_amount += $this->get_coupon_discount_tax_amount( $code ); } + + return round( $discount_amount, $this->dp, WC_DISCOUNT_ROUNDING_MODE ); } /** @@ -1754,7 +1765,7 @@ class WC_Cart { * @return float discount amount */ public function get_coupon_discount_tax_amount( $code ) { - return isset( $this->coupon_discount_tax_amounts[ $code ] ) ? $this->coupon_discount_tax_amounts[ $code ] : 0; + return round( isset( $this->coupon_discount_tax_amounts[ $code ] ) ? $this->coupon_discount_tax_amounts[ $code ] : 0, $this->dp, WC_DISCOUNT_ROUNDING_MODE ); } /** @@ -1809,7 +1820,7 @@ class WC_Cart { foreach ( $this->coupons as $code => $coupon ) { if ( $coupon->is_valid() && ( $coupon->is_valid_for_product( $product, $values ) || $coupon->is_valid_for_cart() ) ) { - $discount_amount = $coupon->get_discount_amount( ( 'yes' === get_option( 'woocommerce_calc_discounts_sequentially', 'no' ) ? $price : $undiscounted_price ), $values, $single = true ); + $discount_amount = $coupon->get_discount_amount( ( 'yes' === get_option( 'woocommerce_calc_discounts_sequentially', 'no' ) ? $price : $undiscounted_price ), $values, true ); $discount_amount = min( $price, $discount_amount ); $price = max( $price - $discount_amount, 0 ); @@ -2149,7 +2160,7 @@ class WC_Cart { * @return float */ public function get_cart_discount_total() { - return $this->discount_cart; + return round( $this->discount_cart, $this->dp, WC_DISCOUNT_ROUNDING_MODE ); } /** @@ -2158,7 +2169,7 @@ class WC_Cart { * @return float */ public function get_cart_discount_tax_total() { - return $this->discount_cart_tax; + return round( $this->discount_cart_tax, $this->dp, WC_DISCOUNT_ROUNDING_MODE ); } /** @@ -2167,8 +2178,8 @@ class WC_Cart { * @return mixed formatted price or false if there are none */ public function get_total_discount() { - if ( $this->discount_cart ) { - $total_discount = wc_price( $this->discount_cart ); + if ( $this->get_cart_discount_total() ) { + $total_discount = wc_price( $this->get_cart_discount_total() ); } else { $total_discount = false; } @@ -2183,8 +2194,8 @@ class WC_Cart { */ public function get_discounts_before_tax() { _deprecated_function( 'get_discounts_before_tax', '2.3', 'get_total_discount' ); - if ( $this->discount_cart ) { - $discounts_before_tax = wc_price( $this->discount_cart ); + if ( $this->get_cart_discount_total() ) { + $discounts_before_tax = wc_price( $this->get_cart_discount_total() ); } else { $discounts_before_tax = false; } diff --git a/includes/class-wc-coupon.php b/includes/class-wc-coupon.php index 9af846fc2cb..0699543937c 100644 --- a/includes/class-wc-coupon.php +++ b/includes/class-wc-coupon.php @@ -600,60 +600,49 @@ class WC_Coupon { * @return float Amount this coupon has discounted */ public function get_discount_amount( $discounting_amount, $cart_item = null, $single = false ) { - $discount = 0; + $discount = 0; + $cart_item_qty = is_null( $cart_item ) ? 1 : $cart_item['quantity']; - if ( $this->is_type( 'fixed_product' ) ) { + if ( $this->is_type( array( 'percent_product', 'percent' ) ) ) { + $discount = $this->coupon_amount * ( $discounting_amount / 100 ); - $discount = $discounting_amount < $this->coupon_amount ? $discounting_amount : $this->coupon_amount; + } 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 + */ + $discount_percent = ( $cart_item['data']->get_price() * $cart_item_qty ) / ( wc_prices_include_tax() ? WC()->cart->subtotal : WC()->cart->subtotal_ex_tax ); + $discount = ( $this->coupon_amount * $discount_percent ) / $cart_item_qty; - // If dealing with a line and not a single item, we need to multiple fixed discount by cart item qty. - if ( ! $single && ! is_null( $cart_item ) ) { - // Discount for the line. - $discount = $discount * $cart_item['quantity']; - } - - } elseif ( $this->is_type( array( 'percent_product', 'percent' ) ) ) { - - $discount = round( ( $discounting_amount / 100 ) * $this->coupon_amount, WC()->cart->dp ); - - } elseif ( $this->is_type( 'fixed_cart' ) ) { - if ( ! is_null( $cart_item ) ) { - /** - * 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 % - */ - $discount_percent = 0; - - if ( WC()->cart->subtotal_ex_tax ) { - // Uses price inc tax if prices include tax to work around https://github.com/woothemes/woocommerce/issues/7669 - $discount_percent = ( $cart_item['data']->get_price() * $cart_item['quantity'] ) / ( wc_prices_include_tax() ? WC()->cart->subtotal : WC()->cart->subtotal_ex_tax ); - } - - $discount = min( ( $this->coupon_amount * $discount_percent ) / $cart_item['quantity'], $discounting_amount ); - } else { - $discount = min( $this->coupon_amount, $discounting_amount ); - } + } elseif ( $this->is_type( 'fixed_product' ) ) { + $discount = min( $discount, $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' ) ) && ! is_null( $cart_item ) ) { + if ( $this->is_type( array( 'percent_product', 'fixed_product' ) ) ) { if ( '' === $this->limit_usage_to_x_items ) { - $qty = $cart_item['quantity']; + $limit_usage_qty = $cart_item_qty; } else { - $qty = min( $this->limit_usage_to_x_items, $cart_item['quantity'] ); - $this->limit_usage_to_x_items = max( 0, $this->limit_usage_to_x_items - $qty ); + $limit_usage_qty = min( $this->limit_usage_to_x_items, $cart_item_qty ); + $this->limit_usage_to_x_items = max( 0, $this->limit_usage_to_x_items - $limit_usage_qty ); } - if ( $single ) { - $discount = ( $discount * $qty ) / $cart_item['quantity']; + $discount = ( $discount * $limit_usage_qty ) / $cart_item_qty; } else { - $discount = ( $discount / $cart_item['quantity'] ) * $qty; + $discount = ( $discount / $cart_item_qty ) * $limit_usage_qty; } } + $discount = round( $discount, WC_ROUNDING_PRECISION ); + return apply_filters( 'woocommerce_coupon_get_discount_amount', $discount, $discounting_amount, $cart_item, $single, $this ); } From 5b1ea0dea1b3b67cb297399a2b71dc354cec49e7 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 10 Apr 2015 10:44:13 +0100 Subject: [PATCH 3/4] Remove comments --- includes/class-wc-cart.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index e83265d9b70..031596d01ca 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -1329,14 +1329,6 @@ class WC_Cart { // Allow plugins to hook and alter totals before final total is calculated do_action( 'woocommerce_calculate_totals', $this ); - /*echo '
';
-				echo 'Cart Contents Total: ' . $this->cart_contents_total . '
'; - echo 'Tax Total: ' . $this->tax_total . '
'; - echo 'Shipping Tax Total: ' . $this->shipping_tax_total . '
'; - echo 'Shipping Total: ' . $this->shipping_total . '
'; - echo 'Fee Total: ' . $this->fee_total . '
'; - echo '
';*/ - // 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 ) ); From 2ddd07b252af6b978320a40b92486cfeb8991796 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 10 Apr 2015 10:55:40 +0100 Subject: [PATCH 4/4] FIx fixed product discount --- includes/class-wc-coupon.php | 2 +- tests/unit-tests/coupon/coupon.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-coupon.php b/includes/class-wc-coupon.php index 0699543937c..17acc8d143b 100644 --- a/includes/class-wc-coupon.php +++ b/includes/class-wc-coupon.php @@ -620,7 +620,7 @@ class WC_Coupon { $discount = ( $this->coupon_amount * $discount_percent ) / $cart_item_qty; } elseif ( $this->is_type( 'fixed_product' ) ) { - $discount = min( $discount, $discounting_amount ); + $discount = min( $this->coupon_amount, $discounting_amount ); $discount = $single ? $discount : $discount * $cart_item_qty; } diff --git a/tests/unit-tests/coupon/coupon.php b/tests/unit-tests/coupon/coupon.php index 79e81d0290b..fd6cce400bc 100644 --- a/tests/unit-tests/coupon/coupon.php +++ b/tests/unit-tests/coupon/coupon.php @@ -199,7 +199,7 @@ class Coupon extends \WC_Unit_Test_Case { update_post_meta( $coupon->id, 'discount_type', 'fixed_product' ); update_post_meta( $coupon->id, 'coupon_amount', '5' ); - // Create a flat rate method + // Create a flat rate method - $10 \WC_Helper_Shipping::create_simple_flat_rate(); // We need this to have the calculate_totals() method calculate totals @@ -207,7 +207,7 @@ class Coupon extends \WC_Unit_Test_Case { define( 'WOOCOMMERCE_CHECKOUT', true ); } - // Add fee + // Add fee - $10 \WC_Helper_Fee::add_cart_fee(); // Add product to cart