In WooCommerce, I have products with a regular price of X and a sales price of Y. I would like to add a coupon with a code for a $45.00 discount to be taken from the regular price X.
I would like the coupon to disregard the sale price so I get X-$45 NOT Y-$45. But when the coupon is not applied price Y is used.
I found the following which works for percentage discounts, but I can't seem to make it work for a fixed product discount price.
add_filter('woocommerce_coupon_get_discount_amount', 'woocommerce_coupon_get_discount_amount', 10, 5 );
function woocommerce_coupon_get_discount_amount( $discount, $discounting_amount, $cart_item, $single, $coupon ) {
if ($coupon->type == 'percent_product' || $coupon->type == 'percent') {
global $woocommerce;
$cart_total = 0;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $cart_item ) {
$variable_product1= new WC_Product_Variation( $cart_item["variation_id"] );
$cart_total += $variable_product1 ->regular_price * $cart_item['quantity'];
}
$discount = round( ( $cart_total / 100 ) * $coupon->amount, $woocommerce->cart->dp );
return $discount;
}
return $discount;
}
This seems to check for a coupon and then change the cart price to regular price rather than discounted price (obtained from woocommerce dynamic pricing in this application) prior to applying the coupon. placed in functions.php
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 10, 1);
function add_custom_price( $cart_object) {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$coupon = False;
if ($coupons = WC()->cart->get_applied_coupons() == False )
$coupon = False;
else {
foreach ( WC()->cart->get_applied_coupons() as $code ) {
$coupons1 = new WC_Coupon( $code );
if ($coupons1->type == 'percent_product' || $coupons1->type == 'percent')
$coupon = True;
}
}
if ($coupon == True)
foreach ( $cart_object->get_cart() as $cart_item )
{
$price = $cart_item['data']->regular_price;
$cart_item['data']->set_price( $price );
}
}
Here's a more complete answer based on JSE's which checks if each product qualifies before reverting the price to the non-sale one. So if the coupon is restricted to particular products or cats, or is excluded from any, it will act accordingly. For coupons with no restrictions, it'll just go through the entire cart, as expected.
function regular_price_for_coupon_items($cart_object){
if (!function_exists('WC') || !isset(WC()->cart)){
return;
}
if (is_admin() && !defined('DOING_AJAX')){
return;
}
// Check if cart has coupon.
if (!WC()->cart->has_discount()){
return;
}
// Get coupons.
$coupons = WC()->cart->get_applied_coupons();
// Loop through coupons.
foreach ($coupons as $coupon){
// Create coupon object.
$coupon = new WC_Coupon($coupon);
foreach ($cart_object->get_cart() as $cart_item){
// Check if products are not resticted from this coupon
$included_products = true;
$included_cats = true;
if (count($product_ids = $coupon->get_product_ids()) > 0){
if (!in_array($cart_item['product_id'], $product_ids, false)){
$included_products = false;
}
}
if (count($exluded_product_ids = $coupon->get_excluded_product_ids()) > 0){
if (in_array($cart_item['product_id'], $exluded_product_ids, false)){
$included_products = false;
}
}
if (count($product_cats = $coupon->get_product_categories()) > 0){
if (!has_term($product_cats, 'product_cat', $cart_item['product_id'])){
$included_cats = false;
}
}
if (count($excluded_product_cats = $coupon->get_excluded_product_categories()) > 0){
if (has_term($excluded_product_cats, 'product_cat', $cart_item['product_id'])){
$included_cats = false;
}
}
if ($included_products && $included_cats){
$price = $cart_item['data']->get_regular_price();
$cart_item['data']->set_price($price);
}
}
}
}
add_action('woocommerce_before_calculate_totals', 'regular_price_for_coupon_items', PHP_INT_MAX, 1);
If you wish to exclude a particular type of coupon, you can use $coupon->get_discount_type() in an if statement in the coupon foreach loop, right after the coupon object is instantiated, and "continue" to iterate to the next if it's not the type of coupon you wish to act on.
WooCommerce should cover the basis that this function does not. Such as having it set to a Fixed Cart Discount where there is at least one item that doesn't qualify among other scenarios based on each coupon's restrictions. If I've missed anything, please let me know!
Related
I have the following code, it loops trougth woocommerce cart and selects the most expensive item and deducts it from the total of the cart updating the total price in the process.
add_filter( 'woocommerce_cart_total', 'wc_modify_cart_price' );
add_filter( 'woocommerce_order_amount_total', 'wc_modify_cart_price' );
function wc_modify_cart_price( $price ) {
$cart_ct = WC()->cart->get_cart_contents_count();
$tax = WC()->cart->get_taxes();
if($cart_ct > 1 ){
$product_prices = [];
$fee = 0;
$prices = array();
// Loop Through cart items - Collect product prices
foreach ( WC()->cart->get_cart() as $cart_item ) {
$_product = wc_get_product( $cart_item['product_id'] );
$terms = get_the_terms( $cart_item['product_id'], 'product_cat' );
#var_dump($terms[0]->name);
if($terms[0]->name == "pizza"){
$prices[] = $cart_item['data']->get_price();
}
}
$sort_price = max($prices);
$max_price = $sort_price;
$cart_total = WC()->cart->cart_contents_total;
$addition = $cart_total - $max_price;
WC()->cart->total = $addition;
$addition = $cart_total - $max_price + $tax[2];
}else{
echo $tax;
$addition = WC()->cart->cart_contents_total + $tax[2];
}
return $addition;
}
add_filter( 'woocommerce_calculated_total', 'wc_modify_cart_price', 10, 2 );
My issue is when i continue and go to checkout the total price order reverts back and i can't find out why, maybe im just calculating and not actually setting said price? how can i not only calculate but actually set that price as the order price?
I would use a different approach here because the numbers won't add up. If you just change the final amount you'll have something like this:
Pizza 1 $50
Pizza 2 $30
subtotal: $80
Total: $30
There are $50 missing without any explanation.
You can use the hook woocommerce_before_calculate_totals to change the cart items.
For example, this code will change the price of the most expensive item to 0. But there is one problem with this. The customer can increase the quantity of these items and you will give away more than you wanted.
add_action( 'woocommerce_before_calculate_totals', 'set_most_expensive_product_to_zero' );
function set_most_expensive_product_to_zero( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
// Set the initial maximum price to 0
$max_price = 0;
// Set the initial product ID to 0
$max_price_product_id = 0;
// Iterate through each item in the cart
foreach ( $cart->get_cart() as $cart_item ) {
// Get the product price
$product_price = $cart_item['data']->get_price();
// Check if the product price is greater than the current maximum price
if ( $product_price > $max_price ) {
// Update the maximum price
$max_price = $product_price;
// Update the product ID of the most expensive product
$max_price_product_id = $cart_item['product_id'];
}
}
// Iterate through each item in the cart again
foreach ( $cart->get_cart() as $cart_item ) {
// Check if the product ID matches the ID of the most expensive product
if ( $cart_item['product_id'] == $max_price_product_id ) {
// Set the price of the most expensive product to 0
$cart_item['data']->set_price( 0 );
}
}
}
Another approach, the one I would use, is creating a one time coupon code with the amount of the free pizza.
Let's say a customer buys 2 pizzas for $20 each and one $16 pizza. You create a $20 coupon and you are done. All the numbers will match and customers will see their discount on the cart/checkout pages.
Here is the code to do just that:
add_action( 'woocommerce_before_calculate_totals', 'generate_coupon_for_most_expensive_product' );
function generate_coupon_for_most_expensive_product( $cart ) {
if(WC()->cart->get_cart_contents_count() < 2){
remove_coupon_from_cart('pizza_');
return;
}
if ( has_coupon_code_with_prefix( 'pizza_' ) ) {
return;
}
// Set the initial maximum price to 0
$max_price = 0;
// Set the initial product ID to 0
$max_price_product_id = 0;
// Iterate through each item in the cart
foreach ( $cart->get_cart() as $cart_item ) {
// Get the product price
$product_price = $cart_item['data']->get_price();
// Check if the product price is greater than the current maximum price
if ( $product_price > $max_price ) {
// Update the maximum price
$max_price = $product_price;
// Update the product ID of the most expensive product
$max_price_product_id = $cart_item['product_id'];
}
}
// Check if the maximum price is greater than 0
if ( $max_price > 0 ) {
// Generate a coupon code
$coupon_code = uniqid( 'pizza_' );
$coupon = new WC_Coupon();
$coupon->set_code($coupon_code);
$coupon->set_amount( $max_price );
$coupon->set_individual_use(true);
$coupon->set_usage_limit(1);
$coupon->set_product_ids(array($max_price_product_id));
$coupon->save();
// Apply the coupon to the cart
$cart->add_discount( $coupon_code );
}
}
function has_coupon_code_with_prefix( $prefix ) {
// Get the applied coupons
$applied_coupons = WC()->cart->get_applied_coupons();
// Check if there are any applied coupons
if ( ! empty( $applied_coupons ) ) {
// Iterate through the applied coupons
foreach ( $applied_coupons as $coupon_code ) {
// Check if the coupon code starts with the specified prefix
if ( strpos( $coupon_code, $prefix ) === 0 ) {
// There is a coupon code applied that starts with the specified prefix
return true;
}
}
}
// No coupon code was found that starts with the specified prefix
return false;
}
function remove_coupon_from_cart($prefix) {
global $woocommerce;
// Get the cart object
$cart = $woocommerce->cart;
// Get the coupon codes applied to the cart
$coupon_codes = $cart->get_coupons();
// Loop through the coupon codes and remove any that start with the specified prefix
foreach ($coupon_codes as $code => $coupon) {
if (strpos($code, $prefix) === 0) {
$cart->remove_coupon($code);
// Save the updated cart
$cart->calculate_totals();
}
}
}
I am trying to implement a rule to make discounts on products depending on the quantity of products and with a coupon code, taking into account the following points:
With the application of a code the second item has a discount of 40%, the discount must be applied to the item of lesser value, if 4 products are purchased, the discount must be applied to the 2 items of lesser value and so on.
Try this code but it only applies to the second item and doesn't take into account if there are more than 4 items
add_filter( 'woocommerce_coupon_get_discount_amount',
'filter_wc_coupon_get_discount_amount', 10, 5 );
function filter_wc_coupon_get_discount_amount( $discount_amount,
$discounting_amount, $cart_item, $single, $coupon ) {
// Define below your existing coupon code
$coupon_code = 'coupon_code';
// Only for a defined coupon code
if( strtolower( $coupon_code ) !== $coupon->get_code() )
return $discount_amount;
$items_prices = [];
$items_count = 0;
// Loop through cart items
foreach( WC()->cart->get_cart() as $key => $item ){
// Get the cart item price (the product price)
if ( wc_prices_include_tax() ) {
$price = wc_get_price_including_tax( $item['data'] );
} else {
$price = wc_get_price_excluding_tax( $item['data'] );
}
if ( $price > 0 ){
$items_prices[$key] = $price;
$items_count += $item['quantity'];
}
}
// Only when there is more than one item in cart
if ( $items_count > 1 ) {
asort($items_prices); // Sorting prices from lowest to highest
$item_keys = array_keys($items_prices);
$item_key = reset($item_keys); // Get current cart item key
// Targeting only the current cart item that has the lowest price
if ( $cart_item['key'] == $item_key ) {
return $discount_amount; // return discount
}
} else {
return 0;
}
}
I'm trying to create code that automatically adds an item to the customer's cart once they reach a particular price point in the cart. AND I'm trying to exclude that from happening if they are only ordering virtual products, as the "free gift" is only meant for products that are being shipped out. The code I'm using is adding the free gift at the right dollar amount, but it's not excluding any virtual product. Can anyone id what I'm doing wrong?
Here's the code:
/**
* Add another product depending on the cart total
*/
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
global $woocommerce;
$product_id = 85942; //replace with your product id
$found = false;
$cart_total = 15; //replace with your cart total needed to add above item
if( $woocommerce->cart->total >= $cart_total ) {
//check if product already in cart
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
$isVirtualOnly = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values) {
$_product = $values[‘data’];
if ($_product != null)
if ($_product->get_type() != $_virtual)
$isVirtualOnly = false;
}
if ($isVirtualOnly != true) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->get_id() == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
$woocommerce->cart->add_to_cart( $product_id );
}
} else {
// if no products in cart, add it
$woocommerce->cart->add_to_cart( $product_id );
}
}
}
}
/**
* END Add another product depending on the cart total
*/
Updated: The following will auto add to cart a free product:
if there is at least a shippable item in cart,
if the free product is not in cart yet,
and if the cart subtotal is not below a specific amount.
Or will remove the fee product from cart:
if the cart subtotal is not below a specific amount,
and if there is only virtual products.
The code:
add_action( 'woocommerce_before_calculate_totals', 'add_free_product_to_cart' );
function add_free_product_to_cart( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$free_product_id = 38; // <= Set the free product id to add
$min_subtotal = 80; // <= Set the minimum cart subtotal required
$has_shippable = $free_key = false; // Initializing
$cart_subtotal = 0;
// Loop through cart items (first loop)
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ){
// Check if free product is in cart
if ( $free_product_id == $cart_item['product_id'] ) {
$free_key = $cart_item_key;
$free_qty = $cart_item['quantity'];
$cart_item['data']->set_price(0); // Optional: Set free product price to zero
}
// Check for non virtual products
if ( $cart_item['data']->is_virtual() !== true ) {
$has_shippable = true;
}
// Calculate items subtotal: Add discounted Line total with taxes
$cart_subtotal += $cart_item['line_total'] + $cart_item['line_tax'];
}
// Add Free product
if ( $cart_subtotal >= $min_subtotal && $has_shippable && $free_key === false ) {
$cart->add_to_cart( $free_product_id, 1 );
}
// Remove free product
elseif ( ( $cart_subtotal < $min_subtotal ) && $free_key !== false ) {
$cart->remove_cart_item( $free_key );
}
// Adjust free product quantity to 1
elseif ( $free_key !== false && $free_qty > 1 ) {
$cart->set_quantity( $free_key, 1 );
}
}
// Optional: Display free product price to zero on minicart
add_filter( 'woocommerce_cart_item_price', 'change_minicart_free_gifted_item_price', 10, 3 );
function change_minicart_free_gifted_item_price( $price_html, $cart_item, $cart_item_key ) {
$free_product_id = 38;
if( $cart_item['product_id'] == $free_product_id ) {
return wc_price( 0 );
}
return $price_html;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Related:
Free Give-Away product for specific cart subtotal in WooCommerce
Add free gifted product for a minimal cart amount in WooCommerce
I am looking for the right hook in WooCommerce because I need to add a promotional product to the cart when a certain cart amount of is reached, such as 100 conventional units.
I have also used the hook 'init' but I do not think it's right.
Here is my code:
function add_free_product_to_cart(){
global $woocommerce;
$product_id = 2006;
$found = false;
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 )
{
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values )
{
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
if(!$found)
{
$maximum = 100;
$current = WC()->cart->subtotal;
if($current > $maximum){
$woocommerce->cart->add_to_cart( $product_id );
}
}
}
}
add_action( 'woocommerce_add_to_cart', 'add_free_product_to_cart' );
which hook I should use for that purpose?
Or could you give me a related link to to some similar problem?
Thanks
As you are targeting a certain cart amount to add a promotional product in the cart, you could use woocommerce_before_calculate_totals hook to achieve this with a custom built function.
You have also to remove that promo item if customer update the cart (which is embed in that custom function too).
Here is the code:
add_action( 'woocommerce_before_calculate_totals', 'adding_promotional_product', 10, 1 );
function adding_promotional_product( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
$promo_id = 99; // <=== <=== <=== Set HERE the ID of your promotional product
$targeted_cart_subtotal = 100; // <=== Set HERE the target cart subtotal
$has_promo = false;
$subtotal = 0;
if ( ! $cart->is_empty() ){
// Iterating through each item in cart
foreach ($cart->get_cart() as $item_key => $cart_item ){
$product_id = version_compare( WC_VERSION, '3.0', '<' ) ? $cart_item['data']->id : $cart_item['data']->get_id();
// If Promo product is in cart
if( $product_id == $promo_id ) {
$has_promo = true;
$promo_key= $item_key;
} else {
// Adding subtotal item to global subtotal
$subtotal += $cart_item['line_subtotal'];
}
}
// If Promo product is NOT in cart and target subtotal reached, we add it.
if( ! $has_promo && $subtotal >= $targeted_cart_subtotal ) {
$cart->add_to_cart( $promo_id );
// echo 'add';
// If Promo product is in cart and target subtotal is not reached, we remove it.
} elseif( $has_promo && $subtotal < $targeted_cart_subtotal ) {
$cart->remove_cart_item( $promo_key );
}
}
}
This code goes on function.php file of your active child theme (or theme) or in any plugin file.
This code its tested and works.
Related thread: WooCommerce - Auto add or auto remove a freebie product from cart
Code updated on (2018-10-01)
I'm using dynamic pricing to discount an item to free one another is present, i want to avoid having the client add the free product as a separate step and just let the system add it.
I started off with the snippet but I can not get this to work when the item is present
this is what i got so far:
<?php
function add_product_to_cart() {
if ( ! is_admin() ) {
global $woocommerce;
$product_id_gift = 2287;
$found = false;
$product_id = 30;
$incart_free = false;
foreach($woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id ){
$incart_free = true;
}
return $incart_free;
}
if( $incart_free == true ) {
//check if product already in cart
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id_gift )
$found = true;
}
// if product not found, add it
if ( $found != true )
$woocommerce->cart->add_to_cart( $product_id_gift );
} else {
// if no products in cart, add it
$woocommerce->cart->add_to_cart( $product_id_gift );
}
}
}
}
add_action( 'init', 'add_product_to_cart' );
?>
Thank you!
If I understood your question correctly you are looking to make an item free if certain items are in the shopping cart. Here is my solution:
1. Create a coupon for WooCommerce in Wordpress. Make the coupon amount 100% and the discount type 'Product% Discount'. Go to Usage Restriction->Products and specify the specific product you want to be free, this will make the coupon only apply to that specific product.
2. Create a function that firsts checks if there are specific items present in the cart and if so, then adds and discounts the item you want to be free to the cart. The following code will do the trick (I have tested it and it worked fine, although it is not the cleanest solution):
add_action( 'init', 'product_discount' );
function product_discount(){
//variable declerations.
global $woocommerce;
$product_id = 1; // product to add
$products= array('2', '3', '4'); //specific product(s) to be present in the cart
$coupon_code = 'abc'; // coupon code from wp
//get the cart contents.
$cart_items = $woocommerce->cart->get_cart();
//check if the cart is not empty.
if(sizeof($cart_items) > 0){
//loop through the cart items looking for the specific products.
foreach ($cart_items as $key => $item){
//check if the cart items match to any of those in the array and check if the desired product is in the cart.
if(in_array($item['product_id'], $products) && $item['product_id'] != $product_id){
//add course.
$woocommerce->cart->add_to_cart($product_id);
//discount course.
$woocommerce->cart->add_discount(sanitize_text_field($coupon_code));
}else{
break; //to prevent the product from being added again for the next loop.
}
}
}
}
Hope this helps!
Your logic is completely wrong here because if ( $_product->id == $product_id_gift ) will never gonna be true as both product_id is different.
So logic should be :
1. Check all the products that are added in to the cart
2. Check that if any product in the cart having free products or not
3. If yes, then simply add the free product.
So code would be something like this :
add_action( 'init', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
global $woocommerce;
$product_id = 30; //Your product ID here
$free_product_id = 2287; //Free product ID here
$found = false;
//check if product already in cart
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id ){
$found = true;
}
}
// if product not found, add it
if ( $found )
$woocommerce->cart->add_to_cart( $free_product_id );
}
}
}
NOTE: Untested.So let me know the output as well.