Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing WooCommerce cart price after applied coupon code

I have created a product on WooCommerce, and added two options on product detail page using the hook woocommerce_before_add_to_cart_button. Now when customers add product to cart from product detail page they have two options their. They can choose one option from these two options.

Then I have stored the user selected value in cart meta using the woocommerce hook woocommerce_add_cart_item_data.

I am using the code from this answer: Save product custom field radio button value in cart and display it on Cart page

This is my code:

// single Product Page options  
add_action("woocommerce_before_add_to_cart_button", "options_on_single_product");
function options_on_single_product(){
    $dp_product_id = get_the_ID(); 
    $product_url = get_permalink($dp_product_id);

    ?>
        <input type="radio" name="custom_options" checked="checked" value="option1"> option1<br />
        <input type="radio" name="custom_options" value="option2"> option2
    <?php
}


//Store the custom field
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_data_with_add_to_cart', 10, 2 );
function save_custom_data_with_add_to_cart( $cart_item_meta, $product_id ) {
    global $woocommerce;
    $cart_item_meta['custom_options'] = $_POST['custom_options'];
    return $cart_item_meta; 
}

And this is what I have tried:

add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 10, 1);
function add_custom_price( $cart_obj ) {

    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    foreach ( $cart_obj->get_cart() as $key => $value ) {
        $product_id = $value['product_id'];
        $custom_options = $value['custom_options'];
        $coupon_code = $value['coupon_code'];
        if($custom_options == 'option2')
        {
            if($coupon_code !='')
            {
                global $woocommerce;
                if ( WC()->cart->has_discount( $coupon_code ) ) return;
                (WC()->cart->add_discount( $coupon_code ))

            //code for second discount
            }
            else{
                $percentage = get_post_meta( $product_id , 'percentage', true );
                //print_r($value);
                $old_price = $value['data']->regular_price;
                $new_price = ($percentage / 100) * $old_price;
                $value['data']->set_price( $new_price );
            }
        } 
    }
}

Now what I am trying to get with that last snippet is:

  • If Option1 is selected by the customer then woocommerce regular process is run.
  • If Option2 is selected then firstly coupon code applied to cart (if code entered by the customer) and then the price is divide by some percentage (stored in product meta) is applied afterward.

But it’s not working as expected because the changed product price is maid before and coupon discount is applied after on this changed price.

What I would like is that the coupon discount will be applied first on the product regular price and then after change this price with my custom product discount.

Is this possible? How can I achieve that?

Thanks.

like image 979
Archana Avatar asked Apr 19 '17 09:04

Archana


People also ask

How do I edit coupons in WooCommerce?

Step 1: Navigate to the Coupons Manager. Step 2: Click the name of the coupon you would like to edit. Step 3: Make the desired changes. Step 4: Save your changes by clicking Update.

What is fixed cart discount in WooCommerce?

General section. By default, WooCommerce comes with three discount types: Percentage discounts apply a percentage discount to the entire cart. Fixed cart discounts apply a fixed amount discount to the entire cart. Fixed product discounts apply a fixed amount discount to selected products only.

How do I move the WooCommerce coupon field in checkout?

Next, create an instance of jQuery Model Dialog that contains all the content of the coupon code. Position the model dialog so that it anchors to an HTML element inside the checkout form. Use the existing WooCommerce filters (or actions) to position the HTML element at your desired location within the checkout form.

How do I manage coupons in WooCommerce?

Go to WooCommerce -> Woo Discount Rule -> Add Rule. This will navigate to the rule page where you can create the WooCommerce discount rule and coupon code. You can create the WooCommerce discount code as a percentage discount, fixed price discount, free shipping, and more using the WooCommerce coupon generator plugin.


2 Answers

This is not really possible … Why? … Because (the logic):

  1. You have the product price
  2. Then the coupon discount is applied to that price (afterwards)
    ==> if you change the product price, the coupon is will be applied to that changed price

What you can do instead:

  1. You don't change product price
  2. if entered the coupon is applied and …
  3. If "option2" product is added to cart:
  4. Apply a custom discount (a negative fee) based on the product price added after using WC_cart add_fee() method…

For this last case you will have to fine tune your additional discount.
If the coupon has not been applied or it's removed there is no additional discount.

Your custom function will be hooked in woocommerce_cart_calculate_fees action hook instead:

add_action( 'woocommerce_cart_calculate_fees', 'option2_additional_discount', 10, 1 );
function option2_additional_discount( $cart_obj ) {

    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    $discount = 0;
    $applied_coupons = $cart_obj->get_applied_coupons();

    foreach ( $cart_obj->get_cart() as $item_values ) {
        if( 'option2' == $item_values['custom_options'] && !empty($applied_coupons) ){
            $product_id = $item_values['product_id'];
            $percentage = get_post_meta( $product_id , 'percentage', true );
            $quantity = $item_values['quantity'];
            $product_reg_price = $item_values['data']->regular_price;
            $line_total = $item_values['line_total'];
            $line_subtotal = $item_values['line_subtotal'];
            $percentage = 90;

            ## ----- CALCULATIONS (To Fine tune) ----- ##

            $item_discounted_price = ($percentage / 100) *  $product_reg_price * $item_values['quantity'];
            // Or Besed on line item subtotal
            $discounted_price = ($percentage / 100) * $line_subtotal;

            $discount += $product_reg_price - $item_discounted_price;
        }
    }
    if($discount != 0)
        $cart_obj->add_fee( __( 'Option2 discount', 'woocommerce' ) , - $discount );
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested and works.

like image 145
LoicTheAztec Avatar answered Sep 18 '22 18:09

LoicTheAztec


Adding a negative fee using the WC_Cart->add_fee() method wasn't working for me. When i check the WC Cart class, it even states you are not allowed to use a negative ammount.

See the docs.

I did the following:

  • create a placeholder coupon with a 'secure' code, e.g. custom_discount_fjgndfl28. Set a discount ammount of 0, so when somebody (somehow) uses this coupon outside your program the discount is still 0.
  • Use filter woocommerce_get_shop_coupon_data, and set all the coupon data you want for that coupon/session.
  • Hook into woocommerce_before_calculate_totals and set your custom coupon to the cart.
  • At this point the Cart should calculate everything correctly. And when it becomes an order, it also has the correct discount ammount.
  • Note: the coupon code is also used as a label in some templates. Use filter woocommerce_cart_totals_coupon_label to change it.

Example functions:

/**
 * NOTE: All the hooks and filters below have to be called from your own
 * does_it_need_custom_discount() function. I used the 'wp' hook for mine.
 * Do not copy/paste this to your functions.php.
**/

add_filter('woocommerce_get_shop_coupon_data', 'addVirtualCoupon', 10, 2);
function addVirtualCoupon($unknown_param, $curr_coupon_code) {

    if($curr_coupon_code == 'custom_discount_fjgndfl28') {

      // possible types are: 'fixed_cart', 'percent', 'fixed_product' or 'percent_product.
      $discount_type = 'fixed_cart'; 

      // how you calculate the ammount and where you get the data from is totally up to you.
      $amount = $get_or_calculate_the_coupon_ammount;

      if(!$discount_type || !$amount) return false;

        $coupon = array(
            'id' => 9999999999 . rand(2,9),
            'amount' => $amount,
            'individual_use' => false,
            'product_ids' => array(),
            'exclude_product_ids' => array(),
            'usage_limit' => '',
            'usage_limit_per_user' => '',
            'limit_usage_to_x_items' => '',
            'usage_count' => '',
            'expiry_date' => '',
            'apply_before_tax' => 'yes',
            'free_shipping' => false,
            'product_categories' => array(),
            'exclude_product_categories' => array(),
            'exclude_sale_items' => false,
            'minimum_amount' => '',
            'maximum_amount' => '',
            'customer_email' => '',
            'discount_type' => $discount_type,
        );

        return $coupon;
    }
}

add_action('woocommerce_before_calculate_totals', 'applyFakeCoupons');
function applyFakeCoupons() {
  global $woocommerce;
  // $woocommerce->cart->remove_coupons(); remove existing coupons if needed.
  $woocommerce->cart->applied_coupons[] = $this->coupon_code; 
}

add_filter( 'woocommerce_cart_totals_coupon_label', 'cart_totals_coupon_label', 100, 2 );
function cart_totals_coupon_label($label, $coupon) {

    if($coupon) {
      $code = $coupon->get_code();
      if($code == 'custom_discount_fjgndfl28') {
        return 'Your custom coupon label';
      }
    }

    return $label;
}

Please Note: i copied these functions out of a class that handles much more, it's only to help you get going.

like image 44
Bjorn Avatar answered Sep 20 '22 18:09

Bjorn