So I have almost achieved my goal - to create a new discounted price on a specific product, should another specific product be in the customer's cart.
I am using ACF to select the core product and the discounted one, which is all being pulled fine. The issue lies in the order in which the products are added to the cart.
If I add the discounted product BEFORE the core product, the discounted product correctly adjusts to $9.99 (the ACF determined new price). However, if I add the core product first, and THEN add the product that should be discounted, the price remains the same - with no discount applied.
I used this code for reference: https://stackoverflow.com/a/47500323/16291715
My code:
add_action( 'woocommerce_before_calculate_totals', 'boga_discount', 20, 1 );
function boga_discount( $cart ) {
if (is_admin() && !defined('DOING_AJAX'))
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// First loop to check if CORE product is in cart
foreach ( $cart->get_cart() as $cart_item ){
$is_in_cart = $cart_item['product_id'] == 1249 ? true : false;
}
// Second loop change prices
foreach ( $cart->get_cart() as $cart_item ) {
// Get an instance of the WC_Product object (or the variation product object)
$product = $cart_item['data'];
// Here we target DISCOUNT ID 17
if( $product->get_id() == 1361 ) {
// GET THE NEW PRICE
$new_price = 9.99; // <== Add your code HERE
// When product CORE product is in cart
if( $is_in_cart ){
$product->set_price( $new_price );
}
}
}
}
I can't understand why this would happen for the life of me, but I am sure there is some minor detail I am missing.
In the first loop you have
$is_in_cart = $cart_item['product_id'] == 813 ? true : false;
Suppose there are 2 products in the cart 813 & 815.
While going through the cart items (first loop), suppose product 813 is first found, so $is_in_cart is true. Then in the same loop, however, $cart_item['product_id'] is now equal to 815. The condition says again, it is equal to 813? this does not match, so $is_in_cart will be false. That's the issue
So you don't need to use 2 foreach loops, this should suffice:
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Settings
$core_product = 813;
$discounted_product = 815;
$new_price = 9.99;
// Initialize
$flag_core_product = false;
$flag_discounted_product = false;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Compare
if ( $cart_item['product_id'] == $core_product ) {
$flag_core_product = true;
} elseif ( $cart_item['product_id'] == $discounted_product ) {
$flag_discounted_product = true;
// Store data (discounted product)
$cart_item_data = $cart_item['data'];
}
}
// Both are true
if ( $flag_core_product && $flag_discounted_product ) {
// Set new price
$cart_item_data->set_price( $new_price );
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );
Related
I'm trying to figure out how to add a specific item to cart, if this variation ID is added to cart. For example I have like 4 variations of a configurable product.
11264
11265
11266
11267
If product variation 11264 and 11265 is added to cart, then product with product ID 111 should also be added to cart. If 11266 and 11267 is added to cart, then product with product ID 222 should also be added to cart.
I found this code online that can add an item to cart based on the product category:
function aaptc_add_product_to_cart( $item_key, $product_id ) {
$product_category_id = 123; // cricket bat category id
$product_cats_ids = wc_get_product_term_ids( $product_id, 'product_cat' );
if ( ! is_admin() && in_array( $product_category_id, $product_cats_ids ) ) {
$free_product_id = 1522; // Product Id of the free product which will get added to cart
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->get_id() == $free_product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $free_product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $free_product_id );
}
}
}
add_action( 'woocommerce_add_to_cart', 'aaptc_add_product_to_cart', 10, 2 );
That code works as intended. But now I want to make it conditional. So I modified this line:
if ( ! $found )
WC()->cart->add_to_cart( $free_product_id );
And changed it to:
if ($found && WC()->cart->(11264,11265))
WC()->cart->add_to_cart(111);
So that if variation ID 11264 or 11265 exists in cart, then add product ID 111. I think my syntax is wrong, but I'm not sure what's the next step. Tried googling it for hours but I cannot find any decent examples.
Slight Modification in foreach loop to get & compare variations might resolve the issue
foreach( WC()->cart->get_cart() as $cart_item_key => $values ) {
$variation_ids = array(11264,11265);
//check if variation exist in cart & if found add product in cart with ID 111
if( in_array( $values['variation_id'], $variation_ids ) ) {
WC()->cart->add_to_cart(111);
}
else {
//Do something
}
}
I am trying to pass the total amount of the cart ($GetTotalPrice) from function cart_prices_GetPrice() to function cart_prices_ApplyPrice(), using a woocommerce_after_calculate_totals and woocommerce_before_calculate_totals hooks, but I get an empty value.
//Trying to get cart amount
add_action('woocommerce_after_calculate_totals', 'cart_prices_GetPrice');
function cart_prices_GetPrice() {
//Getting the cart amount
$GetTotalPrice = WC()->cart->get_cart_total();
return $GetTotalPrice;
}
//Applying custom price
add_action('woocommerce_before_calculate_totals', 'cart_prices_ApplyPrice');
function cart_prices_ApplyPrice( $cart_object ) {
//Getting the cart amount from first function
$totalprice = cart_prices_GetPrice(); // doesn't work and returns 0 :(
//price change to cost2
if( $totalprice != 0 && $totalprice >= 2000 ) {
foreach ( $cart_object->get_cart() as $cart_id => $cart_item ) {
// get products id
$product_id = $cart_item['product_id'];
if( $cart_item['product_id'] == $product_id ) {
// price change to cost2
$new_price1 = 0.20;
$cart_item['data']->set_price( $new_price1 );
}
}
}
}
At the same time, each of the functions separately works perfectly.
What am I doing wrong? Is it possible to somehow link two hook data so that the first one doesn't return an empty value?
Update:
I will not be able to refuse the hook woocommerce_before_calculate_totals, because I need to apply a separate price reduction for each product in the cart.
You can simply not use cart total in woocommerce_before_calculate_totals when you want to alter cart item price for many reasons…
Instead you will get cart item subtotal inside woocommerce_before_calculate_totals hook. On the code below I use the discounted cart item subtotal including taxes:
add_action('woocommerce_before_calculate_totals', 'customize_cart_item_prices');
function customize_cart_item_prices( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Avoiding the hook repetition for price calculations
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
$threshold_amount = 1000; // Min subtotal
$discount_rate = 0.2; // price discount rate (20%)
$cart_items = $cart->get_cart();
// Getting non discounted cart items subtotal
$subtotal_excl_tax = array_sum( wp_list_pluck( $cart_items, 'line_subtotal' ) );
$subtotal_tax = array_sum( wp_list_pluck( $cart_items, 'line_subtotal_tax' ) );
// Getting discounted cart items subtotal
$total_excl_tax = array_sum( wp_list_pluck( $cart_items, 'line_total' ) );
$total_tax = array_sum( wp_list_pluck( $cart_items, 'line_tax' ) );
if( ( $total_excl_tax + $total_tax ) >= $threshold_amount ) {
// Loop through cart items
foreach ( $cart_items as $item ) {
$price = $item['data']->get_price(); // Get price
$item['data']->set_price( $price * ( 1 - $discount_rate ) ); // Set new price
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
See on Change cart item prices in Woocommerce 3 answer code, to see how to handle minicart displayed custom cart item price.
Inspired by this answer code, we are currently using some custom code that adds a $2.50 dollar fee when the Quantity equals 6.
However, we want it to add a $2.50 fee when two products in the same category have each a quantity of 6.
It almost works, but when there are two products in the same category and one of them has a quantity of 12 then the code snippet instead of keeping the $fee_amout to $2.50, changes it to $7.50.
So we need to find a way to better target the individual products and their respective quantity or use or equation and -5 from it whenever it finds an instance of the product having a quantity of 12.
function custom_pcat_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Set HERE your categories (can be term IDs, slugs or names) in a coma separated array
$categories = array('649');
$fee_amount = 0;
$cat_count = 0;
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ) {
if( has_term( $categories, 'product_cat', $cart_item['product_id']))
$quantity = $cart_item['quantity'];
$cat_count += $cart_item['quantity'];
}
if ($quantity == 6){
$fee_amount = (2.5 * ($cat_count/6));
;}
// Adding the fee
if ( $fee_amount > 0 ){
// Last argument is related to enable tax (true or false)
WC()->cart->add_fee( __( "Find-it Mixed Case", "woocommerce" ), $fee_amount, false );
}
}
Updated
If I have well understood, you want to add a fixed cart fee, when there is 2 items from a specific product category in cart that have each one a quantity greater or equal to 6.
Try the following code:
add_action( 'woocommerce_cart_calculate_fees', 'custom_cart_fee', 20, 1 );
function custom_cart_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Set HERE your categories (can be term IDs, slugs or names) in a coma separated array
$categories = array('649');
// Initializing
$count = 0;
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ) {
if( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ) {
if( $cart_item['quantity'] >= 6 ){
$count++;
}
}
}
if ( $count >= 1 ) {
$fee_amount = 2.50 * $count;
$cart->add_fee( __( "Shipping fee", "woocommerce" ), $fee_amount, false );
// Last argument is related to enable tax (true or false)
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
This is the continuation of : Set product sale price programmatically in WooCommerce 3
The answer works, however once a user adds the product to cart, the old price still shows up on checkout.
How to get the correct sale price on cart and checkout pages for cart items?
Any help is appreciated.
The missing part to get it work for for cart and checkout pages (and also Orders and email notifications too) is a very simple trick:
add_action( 'woocommerce_before_calculate_totals', 'set_cart_item_sale_price', 20, 1 );
function set_cart_item_sale_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterate through each cart item
foreach( $cart->get_cart() as $cart_item ) {
$price = $cart_item['data']->get_sale_price(); // get sale price
$cart_item['data']->set_price( $price ); // Set the sale price
}
}
Code goes in function.php file of your active child theme (active theme).
Tested and works.
So the code just set the product sale price as the product price in cart items and it works.
Hope this code is helpful for you
add_filter( 'woocommerce_get_price_html', 'bbloomer_alter_price_display', 9999, 2 );
function bbloomer_alter_price_display( $price_html, $product ) {
// ONLY ON FRONTEND
if ( is_admin() ) return $price_html;
// ONLY IF PRICE NOT NULL
if ( '' === $product->get_price() ) return $price_html;
// IF CUSTOMER LOGGED IN, APPLY 20% DISCOUNT
if ( wc_current_user_has_role( 'customer' ) ) {
$orig_price = wc_get_price_to_display( $product );
$price_html = wc_price( $orig_price * 0.80 );
}
return $price_html;
}
add_action( 'woocommerce_before_calculate_totals', 'bbloomer_alter_price_cart', 9999 );
function bbloomer_alter_price_cart( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;
// IF CUSTOMER NOT LOGGED IN, DONT APPLY DISCOUNT
if ( ! wc_current_user_has_role( 'customer' ) ) return;
// LOOP THROUGH CART ITEMS & APPLY 20% DISCOUNT
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
$price = $product->get_price();
$cart_item['data']->set_price( $price * 0.80 );
}
}
#LoicTheAztec answers works very nicely, but is not required.
You need to filter at least woocommerce_product_get_price and woocommerce_product_variation_get_price with the dynamic_sales_price_function.
To make it work really smoothly, you need some more filters as well.
The accepted answer did not work for me.
Here is what worked:
function get_active_price($price, $product) {
if ($product->is_on_sale()) {
return $product->get_sale_price();
}
return $product->get_regular_price();
}
add_filter('woocommerce_product_get_price', 'get_active_price'));
This worked with custom sale and regular prices.
My WordPresss related task is simple, but I can't find the solution. I have 2 products in my woocommerce shop, and I would like to show both of them on the cart-page in the woocommerce table, let the customer set the quantity of them. If the customer don't want to buy something, just leave it on 0.
The problem is the cart table is not shown if it is empty and I can only see the items that I put in there.
you can add product like that and add condition according you i set for admin and product id. change product id with your product id
/*
* goes in theme functions.php or a custom plugin
**/
// add item to cart on visit
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
$product_id = 64;
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->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 )
WC()->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $product_id );
}
}
}