Woocommerce Coupon to Waive Custom Fee - wordpress

I have the following code that adds a Handling fee to orders with subtotal under $100. I'm trying to make a coupon that will remove the handling fee (or make it $0) that I can give to specific customers, even if their order is under $100. I've made a coupon in Woocommerce and it takes off the $25 from the cart subtotal without changing the Handling. If I don't make a coupon in Woocommerce, I get the "this coupon doesn't exist" message when I try to apply the coupon. Help?
function woo_add_cart_fee() {
global $woocommerce;
$subt = $woocommerce->cart->subtotal;
if ($subt < 100 && $coupon_code == "NOHANDLING" ) {
$handling = 0;
$woocommerce->cart->add_fee( __('Handling', 'woocommerce'), $handling );
}
elseif ($subt < 100 ) {
$handling = 25;
$woocommerce->cart->add_fee( __('Handling', 'woocommerce'), $handling );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'woo_add_cart_fee' );
Thanks.

Related

Edit WooCommerce Prices Programmatically for ALL products [duplicate]

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.
This is not really possible … Why? … Because (the logic):
You have the product price
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:
You don't change product price
if entered the coupon is applied and …
If "option2" product is added to cart:
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.
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.

Update cart fee on shipping method change in WooCommerce

I need a way to offer shipping discount up to a maximum of $25. I'm using the Fee API to do this since there are no coupon codes for shipping. Here's my code:
add_action( 'woocommerce_cart_calculate_fees', 'conditional_shipping_discount' );
function conditional_shipping_discount() {
$max_shipping_discount = 25;
$cart_subtotal = WC()->cart->subtotal;
$current_shipping_method = WC()->session->get( 'chosen_shipping_methods' );
$current_shipping_method_cost = WC()->session->get('cart_totals')['shipping_total'];
// If our cart is > $99 AND shipping is not already free
if ( ($cart_subtotal >= 99) && ($current_shipping_method_cost > 0) ) {
// Calculate 25% of cart subtotal
$calculated_shipping_discount = $cart_subtotal * 0.25;
// $shipping_discount = lowest value
$shipping_discount = min( $current_shipping_method_cost, $calculated_shipping_discount, $max_shipping_discount );
WC()->cart->add_fee( 'Shipping Discount', -1 * abs($shipping_discount) );
}
}
This works great for the most part, except when the customer selects a different shipping method. The fee only shows the previously selected shipping discount. This GIF shows what I mean:
The fee is corrected when the page is reloaded, or when the update_checkout event is triggered by changing item quantities or updating the checkout fields.
How can I have the cart fee (shipping discount) to update immediately when the shipping method is selected? Thank you in advance.
As I am checking your function, we always got previous shipping costs if I switch/change shipping methods. there is only one issue with $current_shipping_method_cost.
So if you change 1 line
$current_shipping_method_cost = WC()->session->get('cart_totals')['shipping_total'];
to
$current_shipping_method_cost = WC()->cart->get_shipping_total();
You always getting current/active shipping costs.
full code is :
add_action( 'woocommerce_cart_calculate_fees', 'conditional_shipping_discount' );
function conditional_shipping_discount() {
$max_shipping_discount = 25;
$cart_subtotal = WC()->cart->subtotal;
//$current_shipping_method = WC()->session->get( 'chosen_shipping_methods' );
$current_shipping_method_cost = WC()->cart->get_shipping_total();
// If our cart is > $99 AND shipping is not already free
if ( ($cart_subtotal >= 99) && ($current_shipping_method_cost > 0) )
{
// Calculate 25% of cart subtotal
$calculated_shipping_discount = $cart_subtotal * 0.25;
// $shipping_discount = lowest value
$shipping_discount = min( $current_shipping_method_cost, $calculated_shipping_discount, $max_shipping_discount );
WC()->cart->add_fee( 'Shipping Discount', -1 * abs($shipping_discount) );
}
}

How do I dynamically change the coupon amount and display it in the cart totals in WooCommerce

I have created a Woocommerce coupon with a discount type set to fixed cart discount and an initial coupon amount.
I want the coupon to function in such a way that when the customer enters the coupon code the total discount is computed and is set as the coupon amount. I'm using the woocommerce_applied_coupon hook in the theme's function.php.
Here's how I coded:
add_action( 'woocommerce_applied_coupon', 'action_woocommerce_applied_coupon', 10, 3 );
function action_woocommerce_applied_coupon( $array, $int, $int ){
$total_discount = 0;
$wc = wc();//use the WC class
foreach($wc->cart->get_cart() as $cart_item){
//loop through each cart line item
$total_discount += ...; //this is where the total discount is computed
}
//use the WC_COUPON class
$wc_coupon = new WC_Coupon("coupon-code");// create an instance of the class using the coupon code
$wc_coupon->set_amount($total_discount);//set coupon amount to the computed discounted price
var_dump($wc_coupon->get_amount());//check if the coupon amount did update
}
The var_dump displayed the $total_discount. But when I checked on the Cart Totals, I still see the initial coupon amount as the discount.
How do I get to update the coupon amount and apply this as the discount to the cart totals?
Try this
add_action( 'woocommerce_before_calculate_totals', 'auto_add_coupons_total_based', 10, 1 ); function auto_add_coupons_total_based( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// HERE define your coupon code
$coupon_percent = 'xyz20'; # <=== <=== <=== <=== <=== <===
$coupon_fixed = 'fixedamount'; # <=== <=== <=== <=== <=== <=== <===
// Get cart subtotal
$subtotal = 0;
foreach($cart->get_cart() as $cart_item ){
$subtotal += $cart_item['line_subtotal'];
$subtotal += $cart_item['line_subtotal_tax']; // with taxes
}
//Set HERE the limit amount
$limit = 40; //without Tax
// Coupon type "percent" (less than 200)
if( $subtotal < 200 && ! $cart->has_discount( $coupon_percent ) ){
// If coupon "fixed amount" type is in cart we remove it
if( $cart->has_discount( $coupon_fixed ) )
$cart->remove_coupon( $coupon_fixed );
}
// Coupon type "fixed amount" (Up to 200)
if( $subtotal >= 200 && $cart->has_discount( $coupon_percent ) ) {
// If coupon "percent" type is in cart we remove it
if( $cart->has_discount( $coupon_percent ) )
$cart->remove_coupon( $coupon_percent );
// Apply the "fixed amount" type coupon code
$cart->add_discount( $coupon_fixed );
// Displaying a custom message
$message = __( "The total discount limit of $$limit has been reached", "woocommerce" );
wc_add_notice( $message, 'notice' );
}
}

Remove Calculate Fee Action Before Order Process

I have a custom function which i use to add/remove a custom fee to the Cart Totals. The fee works fine during the Cart Ajax Calculations, but for some reason the fee still gets charged to the order after checkout. How can I remove this before the order is processed? Here is what i currently have to calculate the fee:
function woo_add_cart_fee() {
global $woocommerce;
if ( ! $_POST || ( is_admin() && ! is_ajax() ) ) {
return;
}
$checkout = WC()->checkout()->checkout_fields;
parse_str( $_POST['post_data'], $post_data );
// Add Fee if no VAT Number is Provided
if($post_data['vat_number'] == '' OR strlen($post_data['vat_number']) < 1 OR empty($post_data['vat_number'])){
$vat_total = 25; // $25.00 fee
$woocommerce->cart->add_fee( __('VAT Fee', 'woocommerce'), $vat_total );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'woo_add_cart_fee' );
The problem is that once the user checks out, the fee is always added, even if they provide a VAT number (my custom field).
So I tried adding this snippet to remove the action completely before the order is processed, but this does not seem to work either:
function action_woocommerce_before_checkout_process( $array ) {
if($_POST['vat_number'] == '' OR strlen($_POST['vat_number']) < 1 OR empty($_POST['vat_number'])){
remove_action( 'woocommerce_cart_calculate_fees', 'woo_add_cart_fee', 1 );
}
}
// add the action
add_action( 'woocommerce_before_checkout_process', 'action_woocommerce_before_checkout_process');
I believe I may be using the wrong hook woocommerce_before_checkout_process because it doesn't seem to be firing.
Any Idea what could be happening? Thanks!
I was able to fix this by adding some control flow for $woocommerce->cart->add_fee It turns out that the calculate_totals function is run again before the thank you page
if(isset($_POST['vat_number'])){
if($_POST['vat_number'] == '' OR empty($_POST['vat_number'])){
$woocommerce->cart->add_fee( __($vat_label, 'woocommerce'), $vat_total );
}
} else {
$woocommerce->cart->add_fee( __($vat_label, 'woocommerce'), $vat_total );
}

Woocommerce - Extra fee based on product quantity

I found this answer in a earlier post here (below), but I want to know of there is any way to add an extra fee based on quantity if the product quantity changes?
Lets say there is 100 items in one package. (the problem is also that there is not they same amount of item in all packages, some can be 100, some can be 150, 200, or 500)
Example:
1-99 = 1$.
100 = no fee.
101 - 199 1$
200 = no fee
201 - 299 = 1$ and so on..
Total will always be 1$ per product but the total can be more if they order several products that have these breaks. The total can be 4$ if there is 4 products with break-cost.
(Also, not sure where to put the code)
Thank you!
The code I found here: Add additional costs based on quantity in Woocommerce
// Hook before adding fees
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees');
/**
* Add custom fee on article specifics
* #param WC_Cart $cart
*/
function add_custom_fees( WC_Cart $cart ){
$fees = 0;
foreach( $cart->get_cart() as $item ){
// Check if odds and if it's the right item
if( $item[ 'quantity' ] % 2 == 1 && get_post_meta( $item[ 'product_id' ], 'custom_fee_for_supplier_name', true) ){
// You can also put a custom price in each produt with get_post_meta
$fees += 10;
}
}
if( $fees != 0 ){
// You can customize the descriptions here
$cart->add_fee( 'Custom fee (odds paquets)', $fees);
}
}
The woocommerce_after_cart_item_quantity_update fires right after a quantity is updated. If you modify your function a little bit (to use WC()->cart to access the cart object) you can run the same function on both hooks. I thought it might keep adding additional fees, but in my testing it seems to just recalculate the right cost for the same fee.
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees');
add_action( 'woocommerce_after_cart_item_quantity_update', 'add_custom_fees' );
/**
* Add custom fee on article specifics
* #param WC_Cart $cart
*/
function add_custom_fees(){
$fees = 0;
foreach( WC()->cart->get_cart() as $item ){
// Check if odds and if it's the right item
if( $item[ 'quantity' ] % 2 == 1 && get_post_meta( $item[ 'product_id' ], '_custom_fee_for_odds', true ) ){
// You can also put a custom price in each produt with get_post_meta
$fees += 10;
}
}
if( $fees > 0 ){
// You can customize the descriptions here
WC()->cart->add_fee( 'Custom fee (odds paquets)', $fees);
}
}

Resources