What I'm doing:
I am creating a coupon based promo. The coupon will discount 10% of the most expensive item in the cart that belongs to a specific product category.
The problem:
The code below successfully checks if the cart contains products from the specific category, and obtains 10% of the price of the most expensive product from that array to use for the discount. However, I can't get the coupon discount amount to change in the cart.
The code:
function change_coupon_price_for_promo( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ){
return;
}
// coupon code
$coupon_name = 'MYCOUPONNAME';
// if coupon is applied to cart...
if( $cart->has_discount( $coupon_name ) ) {
// Form array of products from the cart that belong to the specified category
$specProduct = array(); // array of specific products in cart
foreach ( $cart->get_cart() as $key => $cart_item ) {
$cart_item_id = $cart_item['product_id'];
$terms = wp_get_post_terms( $cart_item_id, 'product_cat' );
foreach ( $terms as $term ){
// gets product cat id
$product_cat_id = $term->term_id;
// gets an array of all parent category levels
$product_parent_categories_all_hierachy = get_ancestors( $product_cat_id, 'product_cat' );
// This cuts the array and extracts the last set in the array
$last_parent_cat = array_slice($product_parent_categories_all_hierachy, -1, 1, true);
if(in_array(3831, $last_parent_cat)){
$specProduct[$key] = $cart_item_id;
break;
}
}
}
//if there are items from the specific category in the cart
if(!empty($specProduct)){
$specPriceArray = array(); // array of specific product prices in cart
foreach ($specProduct as $key => $specItem) {
$thisProd = wc_get_product($specItem);
$thisPrice = $thisProd->get_price();
$specPriceArray[$key] = $thisPrice;
}
// most expensive specific product price
$highestSpec = max($specPriceArray);
// discount of most expensive item
$finalDiscount = $highestSpec * .1;
// print_r($specProduct); GOOD
// print_r($specPriceArray); GOOD
// echo $highestSpec; GOOD
// echo $finalDiscount; GOOD
/***** APPLY COUPON CHANGE HERE<-------HOW??? *****/
$cart->discount_total = $finalDiscount;
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'change_coupon_price_for_promo', 10, 1 );
What I'm asking:
The last line of the code before the closing brackets...
$cart->discount_total = $finalDiscount;
is what I tried using to change the coupon's discount in the cart. It doesn't work. How can I accomplish this? TY.
EDIT:
I should note that the coupon being added is set up as a "Fixed cart discount" with a rate of 0 so it is added as a line item to the order. This way the user sees the discount for the coupon code on its own line in the cart/checkout.
you can use function set_price.
you can call it in your code like:
$cart_item['data']->set_price('New price here')
use this inside the foreach that loops your cart items
Related
I am working on this woocommerce search function that uses AJAX to fetch products on every keystroke and that gives the possibility to add or remove units of a certain product to cart right from the instant search results modal.
In each search result there is this group of two buttons that allow the client to add 1 or remove 1 product from cart at a time. I managed to make the add to cart button work but can't seem to find a good solution to remove just one single unit from cart instead of all of them at once.
I have tried this:
function remove_product_from_cart_programmatically() {
if ( is_admin() ) return;
$product_id = 282;
$product_cart_id = WC()->cart->generate_cart_id( $product_id );
$cart_item_key = WC()->cart->find_product_in_cart( $product_cart_id );
if ( $cart_item_key ) WC()->cart->remove_cart_item( $cart_item_key );
}
but it removes all products with that ID at once.
I want to be able to remove unit by unit in case there are items in cart with that ID.
This is the code I have right now for the remove from cart button:
add_action('wp_ajax_search_remove_from_cart', 'search_remove_from_cart');
add_action('wp_ajax_nopriv_search_remove_from_cart', 'search_remove_from_cart');
// handle the ajax request
function search_remove_from_cart()
{
$product_id = $_REQUEST['product_id'];
WC()->cart->remove_cart_item($product_id);
$products_in_cart = WC()->cart->get_cart_item_quantities($product_id);
$updated_qty = $products_in_cart[$product_id];
// in the end, returns success json data
wp_send_json_success(["Product removed from cart Successfuly!", $updated_qty]);
// or, on error, return error json data
wp_send_json_error(["Could not remove the product from cart"]);
}
Is there any function to remove only one unit of a certain product from cart?
Thank you all !
You have to change the quantity instead of removing the product. Number of product units in the cart = quantity.
Try something like this:
function remove_from_cart(){
$product_id = 47;
$cart = WC()->cart;
$product_cart_id = $cart->generate_cart_id( $product_id );
$cart_item_key = $cart->find_product_in_cart( $product_cart_id );
if ( $cart_item_key ){
$quantity = $cart->get_cart_item($cart_item_key)['quantity'] - 1;
$cart->set_quantity($cart_item_key, $quantity);
}
}
I would like to know if it's possible to disable checkout when there isn't a certain product in the cart above a certain cart total in WooCommerce.
I found a code which does the exact opposite I think:
function sv_wc_prevent_checkout_for_category() {
// set the slug of the category for which we disallow checkout
$category = 'clothing';
// get the product category
$product_cat = get_term_by( 'slug', $category, 'product_cat' );
// sanity check to prevent fatals if the term doesn't exist
if ( is_wp_error( $product_cat ) ) {
return;
}
$category_name = '' . $product_cat->name . '';
// check if this category is the only thing in the cart
if ( sv_wc_is_category_alone_in_cart( $category ) ) {
// render a notice to explain why checkout is blocked
wc_add_notice( sprintf( 'Hi there! Looks like your cart only contains products from the %1$s category – you must purchase a product from another category to check out.', $category_name ), 'error' );
}
}
add_action( 'woocommerce_check_cart_items', 'sv_wc_prevent_checkout_for_category' );
/**
* Checks if a cart contains exclusively products in a given category
*
* #param string $category the slug of the product category
* #return bool - true if the cart only contains the given category
*/
function sv_wc_is_category_alone_in_cart( $category ) {
// check each cart item for our category
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// if a product is not in our category, bail out since we know the category is not alone
if ( ! has_term( $category, 'product_cat', $cart_item['data']->id ) ) {
return false;
}
}
// if we're here, all items in the cart are in our category
return true;
}
As mentioned in the comments, we need to see what you've tried in order to help you.
You stated,
...possible to disable checkout when there isn't a certain product in
the cart above a certain cart total in WooCommerce.
So you're looking at the cart_items array, for a specific item and its count in the cart session:
foreach(WC()->cart->get_cart() as $item) {
echo $item['product_id'];
echo $item['quantity'];
}
Now follow what you have above, and you should be able to make a function that prevents checkout if a certain item with a certain count don't exist in the cart.
A specific WooCommerce product can only be by itself in the cart.
So how do I clear the cart while adding this specific product to the cart? and how can I remove this specific product from the cart when adding any other product?
I've figured out how to empty the cart when adding a specific product but I don't know how to remove this specific product from the cart when adding any other product.
The following will remove conditionally cart items based on a specific product:
When the specific product is added to cart, all other items are removed.
When any other product is added to cart, it removes the specific product (if it's in cart)
Here is the code:
// Remove conditionally cart items based on a specific product (item)
add_action( 'woocommerce_before_calculate_totals', 'remove_cart_items_conditionally', 10, 1 );
function remove_cart_items_conditionally( $cart ) {
// HERE define your specific product ID
$specific_product_id = 37;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$cart_items = $cart->get_cart(); // Cart items array
$items_count = count($cart_items); // Different cart items count
// Continue if cart has at least 2 different cart items
if ( $items_count < 2 )
return;
$last_item = end($cart_items); // Last cart item data array
$is_last_item = false; // Initializing
// Check if the specific product is the last added item
if ( in_array($specific_product_id, array( $last_item['product_id'], $last_item['variation_id'] ) ) ) {
$is_last_item = true;
}
// Loop through cart items
foreach ( $cart_items as $cart_item_key => $cart_item ) {
// Remove all others cart items when specific product ID is the last added to cart
if ( ! in_array($specific_product_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) && $is_last_item ) {
$cart->remove_cart_item( $cart_item_key );
}
// Remove the specific item when its is not the last added to cart
elseif ( in_array($specific_product_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) && ! $is_last_item ) {
$cart->remove_cart_item( $cart_item_key );
}
}
}
Code goes on function.php file of your active child theme (or active theme). Tested and works.
The above works perfectly! You can also add a notice to the cart once items have been removed to help with the user experience. Add this to the // Loop through cart items section in the first if statement:
wc_add_notice( __( 'Product XYZ has been removed from your cart because...', 'theme_domain' ), 'notice' );
I am a beginner trying to learn Wordpress and have a variable product with prices:
cod Rs 250
online Rs 200
I want to use the minimum price in my themes functions.php.
How will I be able to fetch or get the minimum value?
I want to use in the below code :
function filter_gateways($gateways){
$payment_NAME = 'cod'; //
$min_variation_price = ''; <--------------- minimum variation price
global $woocommerce;
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
// Get the terms, i.e. category list using the ID of the product
$terms = get_the_terms( $values['product_id'], 'product_cat' );
// Because a product can have multiple categories, we need to iterate through the list of the products category for a match
foreach ($terms as $term) {
// 20 is the ID of the category for which we want to remove the payment gateway
if($term->term_id == $min_variation_price){
unset($gateways[$payment_NAME]);
// If you want to remove another payment gateway, add it here i.e. unset($gateways['cod']);
break;
}
break;
}
}
return $gateways;
}
To get the minimum variation active price in WooCommerce from a WC_Product_Variable object:
$variation_min_price = $product->get_variation_price();
It seems that you are trying here to unset 'cod' payment gateway when a cart item min variation price is matching with his product category ID.
Using the WordPress conditional function has_term() will really simplify your code (it accepts term Ids, term slugs or term names for any taxonomy (as 'product_cat' here for product categories).
I suppose that your function is hooked in woocommerce_available_payment_gateways filter hook…
I made some changes in your code as it's a bit outdated and with some errors:
add_filter('woocommerce_available_payment_gateways', 'conditional_payment_gateways', 10, 1);
function conditional_payment_gateways( $available_gateways ) {
// Not in backend (admin)
if( is_admin() )
return $available_gateways;
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Get the WC_Product object
$product = wc_get_product($cart_item['product_id']);
// Only for variable products
if($product->is_type('variable')){
// Get the Variation "min" price
$variation_min_price = $product->get_variation_price();
// If the product category match with the variation 'min" price
if( has_term( $variation_min_price, 'product_cat', $cart_item['product_id'] ) ){
// Cash on delivery (cod) payment gateway
unset($available_gateways['cod']); // unset 'cod'
break; // stop the loop
}
}
}
return $available_gateways;
}
Code goes in functions.php file of your active child theme (or theme) or also in any plugin file.
This should work (but I can't really test it as it's very specific regarding the product categories ID and the min variation prices)…
All related price methods for variable product type:
// All prices: multidimensional array with all variation prices (including active prices, regular prices and sale prices).
$prices = $product->get_variation_prices();
$prices_for_display = $product->get_variation_prices( true ); // For display
// The min or max active price.
$min_price = $product->get_variation_price(); // Min active price
$min_price_for_display = $product->get_variation_price('min', true); // Min active price for display
$max_price = $product->get_variation_price('max'); // Max active price
$max_price_for_display = $product->get_variation_price('max', true); // Max active price for display
// The min or max regular price.
$min_price = $product->get_variation_regular_price(); // Min regular price
$min_price_for_display = $product->get_variation_regular_price('min', true); // Min regular price for display
$max_price = $product->get_variation_regular_price('max'); // Max regular price
$max_price_for_display = $product->get_variation_regular_price('max', true); // Max regular price for display
// The min or max sale price.
$min_price = $product->get_variation_sale_price(); // Min sale price
$min_price_for_display = $product->get_variation_sale_price('min', true); // Min sale price for display
$max_price = $product->get_variation_sale_price('max'); // Max sale price
$max_price_for_display = $product->get_variation_sale_price('max', true); // Max sale price for display
Related answers:
Disable WooCommerce Payment methods if cart item quantity limit is reached
I think you are looking for that :
add_filter( 'woocommerce_variable_sale_price_html', 'get_min_variation_price_format', 10, 2 );
add_filter( 'woocommerce_variable_price_html', 'get_min_variation_price_format', 10, 2 );
function get_min_variation_price_format( $price, $product ) {
$min_variation_price = $product->get_variation_regular_price( 'min');
return wc_price($min_variation_price);
}
I have a coupon automatically adding to my cart on checkout. Basically; if category A and B are present apply the coupon. If the coupon is applied and both categories are no longer present (customer removed item from cart) then remove coupon.
This works great but I am having trouble setting up what I actually want which goes a step further:
if Both products A and B are present in cart apply the discount for EACH Of product A in cart. so if you have 5 Product A apply the discount 5 times. if the customer then removes one product A remove the extra coupon that was applied.
How would I achieve this? here is the current code for matching and applying the discount:
global $woocommerce;
//create empty category array for push
$category_array = [];
// coupon code created in wooCommerce to apply
$discount = 'A-bought-with-B';
// start of the loop that fetches the cart items
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$terms = get_the_terms( $_product->id, 'product_cat' );
// find product categories and push them to the array
foreach ($terms as $term) {
$_categoryid = $term -> name;
$category_name = strtolower( $_categoryid );
// push term name to category array
$category_array[] = $category_name;
}
}
// 01
// A Discount when purchased with B
if ( in_array("A", $category_array) && in_array("B", $category_array) ) {
$woocommerce->cart->add_discount( sanitize_text_field( $discount ) );
} // if categories are not in cart, but the coupon is applied remove it
else if ( !in_array("A", $category_array) || !in_array("B", $category_array) && $woocommerce->cart->has_discount( $sticker_coupon_code ) ) {
$woocommerce->cart->remove_coupon( $discount );
}