Based on Make coupon field mandatory for a product category in WooCommerce answer, I am trying to implement a mandatory coupons for specific products in woocommerce.
Here is my code attempt:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupon_for_specific_items' );
function mandatory_coupon_for_specific_items() {
$targeted_ids = array(40, 41, 42, 43, 44); // The targeted product ids (in this array)
$coupon_code = 'summer1, summer2, summer3, summer4, summer5'; // The required coupon code
$coupon_applied = in_array( strtolower($coupon_code), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $cart_item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $cart_item['product_id'], $targeted_ids ) && ! $coupon_applied ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $cart_item['data']-
>get_name() ), 'error' );
break; // stop the loop
}
}
}
The code works perfectly to make a coupon mandatory but I need that ONE fo the products can be checkedout using only ONE of the coupons.
With the actual code if I add product ID 40 to the cart, is obligatory to add the 5 coupons, summer1, summer2, summer3, summer 4 and summer5, otherwise the checkout will be impossible. I would like the customer can make the checkout using any of the coupons for any of the products listed in the targeted_id array.
In other words, for all the products the use of coupon is mandatory but not necessarily a specific coupon, could be anyone of the listed in the code.
For example purposes I listed 5 products and 5 coupons, but in reality there are 100 products and 100 coupons
Thank you in advance
There are some mistakes in your code, use the following instead:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupon_for_specific_items' );
function mandatory_coupon_for_specific_items() {
$targeted_ids = array(40, 41, 42, 43, 44); // The targeted product ids (in this array)
$coupon_codes = array('summer1', 'summer2', 'summer3', 'summer4', 'summer5'); // Array of required coupon codes
$coupons_found = array_intersect( array_filter( array_map( 'sanitize_title', $coupon_codes) ), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $item['product_id'], $targeted_ids ) && empty($coupons_found) ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $item['data']->get_name() ), 'error' );
break; // stop the loop
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Related
I am trying to have mandatory Coupons when checking out for specific products in WooCommerce. I tried Allow specific products to be purchased only if a coupon is applied in Woocommerce answer code, which works perfectly.
However it allows to define only one coupon code at a time.
My client has 13 different Sales Agents that she'd like to assign coupons to.
Is there a way to define more coupons in an array (or something similar)?
For multiple coupon codes, use the following:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupons_for_specific_items' );
function mandatory_coupons_for_specific_items() {
$targeted_ids = array(37); // Your targeted product ids (in this array)
$coupon_codes = array('summer2', 'summer4'); // The required coupon codes (in this array)
$applied_coupons = array_intersect( array_filter( array_map( 'sanitize_title', $coupon_codes) ), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $cart_item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $cart_item['product_id'], $targeted_ids ) && empty( $applied_coupons ) ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $cart_item['data']->get_name() ), 'error' );
break; // stop the loop
}
}
}
Code goes in functions.php file of the active child theme (or active theme). It should work.
This question already has an answer here:
Woocomerce Remove a specific cart items when adding to cart another specific items
(1 answer)
Closed 2 years ago.
In Woocommerce, when I add a bundled product A I want to remove products B, C, D (single products that are already inside that bundle) and if product A (bundle) is in the cart, do not allow to add B, C or D products.
The answer Woocomerce Remove a specific cart items when adding to cart another specific items is very close of what I would like.
How can I do it backwards?
For the first condition you can indeed use the woocommerce_add_to_cart hook, for the 2nd condition you better use the woocommerce_add_to_cart_validation hook.
So we apply both conditions together and we get
function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
// Set ID's
$product_a = 30;
$product_b_c_d = array ( 813, 815, 817 );
// Get current product id
$product_id = $variation_id > 0 ? $variation_id : $product_id;
// Product A is added
if ( $product_a == $product_id ) {
// Loop trough cart items
foreach( WC()->cart->get_cart() as $key => $item ) {
// Product ID is in array
if ( in_array( $item['product_id'], $product_b_c_d ) ) {
// Remove cart item(s)
WC()->cart->remove_cart_item( $key );
}
}
// Optionaly displaying a notice
wc_add_notice( __( 'Product A is added, Product B, C or D has been removed from cart.', 'woocommperce' ), 'notice' );
}
// Product B, C or D is added
elseif ( in_array( $product_id, $product_b_c_d ) ) {
// Generate card id
$product_cart_id = WC()->cart->generate_cart_id( $product_a );
// Check if product A in the cart
$item_key = WC()->cart->find_product_in_cart( $product_cart_id );
// if item_key true
if ( $item_key ) {
// Optionaly displaying a notice
wc_add_notice( __( 'Product A is in cart, Product B, C or D are not allowed', 'woocommerce' ), 'error' );
$passed = false;
}
}
// Return
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );
For a specific bundled product, the code below will:
remove any individual child products when bundle product is added to cart
avoid individual child products to be added to cart when the bundle product is already in cart.
So one hooked function for each case, will be a lighter process:
// When the bunddle product is added to cart remove single components from cart
add_action( 'woocommerce_add_to_cart', 'on_bundled_product_added_to_cart', 10, 4 );
function on_bundled_product_added_to_cart( $cart_item_key, $product_id, $quantity, $variation_id ) {
// SETTINGS
$bundled_product_id = 83; // Set HERE your bundled product ID
$item_ids_to_remove = array(37, 53, 31); // Set HERE the product ID(s) to remove(s)
$removed_items = 0; // Initializing
// When the bundled product is added to cart
if( $bundled_product_id == $product_id ) {
// Loop through cart items
foreach( WC()->cart->get_cart() as $item_key => $cart_item ){
// Get the cart item keys of the items to be removed
if( array_intersect( array($cart_item['product_id'], $cart_item['variation_id']), $item_ids_to_remove ) ) {
WC()->cart->remove_cart_item($item_key);
$removed_items++;
}
}
}
// Optionaly displaying a notice for the removed items
if( ! empty($removed_item_names) ){
wc_add_notice( sprintf( __( 'Some products have been removed from cart as they are already bundled in your cart', 'woocommerce' ), $items_text ), 'notice' );
}
}
// Add to cart validation for bundled components
add_filter( 'woocommerce_add_to_cart_validation', 'check_cart_items_for_bundle_product', 9999, 4 );
function check_cart_items_for_bundle_product( $passed, $product_id, $quantity, $variation_id = 0 ) {
// SETTINGS
$bundled_product_id = 83; // Set HERE your bundled product ID
$bundled_items_ids = array(37, 53, 31); // Set HERE the bundled items ID(s) from the bundled product
$bundled_in_cart = false;
if( ! WC()->cart->is_empty() ) {
// Loop through cart items
foreach( WC()->cart->get_cart() as $cart_item ){
// check for bundled product
if( $bundled_product_id == $cart_item['product_id'] ) {
$bundled_in_cart = true;
break;
}
}
if( $bundled_in_cart && array_intersect(array($product_id, $variation_id), $bundled_items_ids) ) {
// Add a custom notice
wc_add_notice( __( 'This product is already a component of the bundled product in your cart', 'woocommerce' ), 'error' );
return false;
}
}
return $passed;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
The message error when trying to add to cart a component of a bundled product when it's in cart already:
The message when components of a bundled product are in cart and they are removed when adding the parent bundled product (in cart page):
I am working on a woocommerce website and I am trying to restrict a product to be purchased only if a coupon is applied for it, so it should not be processed without adding a coupon code. User must enter a coupon code to be able to order this specific product (not on all other products).
Any help is appreciated.
For defined products, the following code will not allow checkout if a coupon is not applied, displaying an error message:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupon_for_specific_items' );
function mandatory_coupon_for_specific_items() {
$targeted_ids = array(37); // The targeted product ids (in this array)
$coupon_code = 'summer2'; // The required coupon code
$coupon_applied = in_array( strtolower($coupon_code), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $cart_item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $cart_item['product_id'], $targeted_ids ) && ! $coupon_applied ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $cart_item['data']->get_name() ), 'error' );
break; // stop the loop
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
And in checkout:
I have a function on my Woocommerce website that enables customers to set a custom amount to pay for a specific product, based on a value I'm passing through the URL.
I'm using the woocommerce_before_calculate_totals hook, and up until I upgraded to WC 3.3.5, it was working fine. Now, when I run the code, the checkout initially shows the custom amount.
However, once the loader has finished updating, it resets the price to '0' (i.e. displaying £0.00 the checkout page's total fields).
Here's that code:
add_action( 'woocommerce_before_calculate_totals', 'pay_custom_amount', 99);
function pay_custom_amount() {
$payment_value = $_GET['amount'];
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if($cart_item['data']->id == 21 ){
$cart_item['data']->set_price($payment_value);
}
}
}
Well, colour me baffled. I've trawled Stack Overflow for solutions but can't see any similar problems. I see the hook runs multiple times but gather this is normal.
If anyone know what might be happening here, it would be great if you could share.
You can't get a price from an URL and set it in woocommerce_before_calculate_totals action hook. This needs to be done differently.
In the below code:
the first hooked function will get that "amount" from the URl and will set it (register it) in cart item object as custom data.
the 2nd hooked function will read that amount from custom cart item data and will set it as the new price.
Now your the target product ID in your code need to be the same ID that the added to cart product.
The code:
// Get the custom "amount" from URL and save it as custom data to the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_pack_data_to_cart_item_data', 20, 2 );
function add_pack_data_to_cart_item_data( $cart_item_data, $product_id ){
if( ! isset($_GET['amount']) )
return $cart_item_data;
$amount = esc_attr( $_GET['amount'] );
if( empty($amount) )
return $cart_item_data;
// Set the custom amount in cart object
$cart_item_data['custom_price'] = (float) $amount;
$cart_item_data['unique_key'] = md5( microtime() . rand() ); // Make each item unique
return $cart_item_data;
}
// Alter conditionally cart item price based on product ID and custom registered "amount"
add_action( 'woocommerce_before_calculate_totals', 'change_conditionally_cart_item_price', 30, 1 );
function change_conditionally_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// HERE set your targeted product ID
$targeted_product_id = 21;
foreach ( $cart->get_cart() as $cart_item ) {
// Checking for the targeted product ID and the registered "amount" cart item custom data to set the new price
if($cart_item['data']->get_id() == $targeted_product_id && isset($cart_item['custom_price']) )
$cart_item['data']->set_price($cart_item['custom_price']);
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I've searched extensively to see if other folks had this situation and received an answer with no luck.
Essentially, I have two items customers can add to their cart. I want to make it so they cannot checkout with either of those items if their subtotal is not $15 or more.
Having the ability to just drop their IDs into the code would be fine. Or, I can assign them to the same category and set this minimum by category.
So far all I have is the basic PHP that sets a universal minimum. I just need some help figuring out how to limit this to meet my needs.
I'm a designer not a dev, so any help is much appreciated.
// Set a minimum dollar amount per order
add_action( 'woocommerce_check_cart_items', 'spyr_set_min_total' );
function spyr_set_min_total() {
// Only run in the Cart or Checkout pages
if( is_cart() || is_checkout() ) {
global $woocommerce;
// Set minimum cart total
$minimum_cart_total = 10;
// Total we are going to be using for the Math
// This is before taxes and shipping charges
$total = WC()->cart->subtotal;
// Compare values and add an error is Cart's total
// happens to be less than the minimum required before checking out.
// Will display a message along the lines of
// A Minimum of 10 USD is required before checking out. (Cont. below)
// Current cart total: 6 USD
if( $total <= $minimum_cart_total ) {
// Display our error message
wc_add_notice( sprintf( '<strong>A Minimum of %s %s is required before checking out.</strong>'
.'<br />Current cart\'s total: %s %s',
$minimum_cart_total,
get_option( 'woocommerce_currency'),
$total,
get_option( 'woocommerce_currency') ),
'error' );
}
}
}
2020 Update
Setting minimum value for some products categories or products ID's in cart for Cart and Checkout pages only.
This untested snippet made of this and this from wooThemes, and this too:
add_action( 'woocommerce_checkout_process', 'wc_minimum_order_amount' );
add_action( 'woocommerce_before_cart' , 'wc_minimum_order_amount' );
function wc_minimum_order_amount() {
## SETTINGS ##
$minimum_amount = 50; // Define a minimum order amount
$category_ids = array( 17, 18 ); // Define your category ids in the array (or an empty array to disable)
$product_ids = array( 64, 67, 78 ); // Define your product ids in the array (or an empty array to disable)
// Only on cart or checkout pages
if( WC()->cart->is_empty() || ! ( is_cart() || is_checkout() ) )
return; // Exit
$total_amount = WC()->cart->subtotal; // Items subtotal including taxes
if ( $total_amount < $minimum_amount ) {
$needs_minimum_amount = false; // Initializing
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product_id = $cart_item['product_id'];
$variation_id = $cart_item['variation_id'];
// 1. Check for matching product categories
if( sizeof($category_ids) > 0 ) {
$taxonomy = 'product_cat';
if ( has_term( $category_ids, $taxonomy, $product_id ) ) {
$needs_minimum_amount = true;
break; // Stop the loop
}
}
// 2. Check for matching product Ids
if( sizeof($product_ids) > 0 ) {
if ( array_intersect( $product_ids, array($product_id, $variation_id) ) ) {
$needs_minimum_amount = true;
break; // Stop the loop
}
}
}
if( $needs_minimum_amount ) {
wc_print_notice( sprintf(
__("You must have an order with a minimum of %s to place your order. Your current order total is %s."),
wc_price( $minimum_amount ),
wc_price( $total_amount )
), 'error' );
}
}
}
Code goes in functions.php file of your active child theme (active theme). Tested and works.
You can set your categories ID's, your products ID's and the minimum order value amount.
— — … I m p o r t a n t … N o t e … — —
This hooks are working for messages. But to avoid users from clicking you need to add Javascript/jQuery and maybe with ajax too (client side detection).