On a single product page I'd like to know if there's a conditional for detecting whether the current product has been marked as 'Featured'?
I've tried variations on:
if( has_term( 1, 'featured' ) ) {
etc ...
and
if( has_term( TRUE, 'featured' ) ) {
etc ...
No luck so far. Any ideas?
$current_product = wc_get_product( get_the_ID() );
$current_product_featured_query = $current_product->featured;
if( $current_product_featured_query == "yes" ) {...
*... this product is indeed, featured!*
does the job.
Related
I need to add 'per metre' to the price on most of my online catalogue, I tried the code on this thread in my finctions.php but I cannot get it to omit/include particular categories- it seems to be all or nothing. What am I doing wrong?
I have edited the code as such:
/*add 'per metre' after selected items*/
add_filter( 'woocommerce_get_price_html', 'conditional_price_suffix', 20, 2 );
function conditional_price_suffix( $price, $product ) {
// HERE define your product categories (can be IDs, slugs or names)
$product_categories = array('fabric','haberdashery', 'lining',);
if( ! has_term( $product_categories, 'fasteners', 'patches', 'remnnants', $product->get_id() ) )
$price .= ' ' . __('per metre');
return $price;
}
I want 'fabrics', 'haberdashery', 'lining' to show per metre, and 'fasteners', 'patches', 'remnants' to NOT show the suffix.
I have tried variations of the code -my exclusions in the top bit and the inclusions in the second part, and with/without the "( ! has term" section, but whichever I do takes all the suffix messages away, or applies to all categories.
It would be amazing if I could get this to work as have previously been using a very bloated plug-in. I'm only basically capable in this stuff so please feel free to talk me through it as if I am an idiot.
There is a little mistake in your code in the has_term() function.
To handle parent product categories, we will use a custom conditional function instead of has_tem().
I have also added some code to handle the product variation selected price of variable products, So try this instead:
// Custom conditional function that handle parent product categories too
function has_product_categories( $categories, $product_id = 0 ) {
$parent_term_ids = $categories_ids = array(); // Initializing
$taxonomy = 'product_cat';
$product_id = $product_id == 0 ? get_the_id() : $product_id;
if( is_string( $categories ) ) {
$categories = (array) $categories; // Convert string to array
}
// Convert categories term names and slugs to categories term ids
foreach ( $categories as $category ){
$result = (array) term_exists( $category, $taxonomy );
if ( ! empty( $result ) ) {
$categories_ids[] = reset($result);
}
}
// Loop through the current product category terms to get only parent main category term
foreach( get_the_terms( $product_id, $taxonomy ) as $term ){
if( $term->parent > 0 ){
$parent_term_ids[] = $term->parent; // Set the parent product category
$parent_term_ids[] = $term->term_id; // (and the child)
} else {
$parent_term_ids[] = $term->term_id; // It is the Main category term and we set it.
}
}
return array_intersect( $categories_ids, array_unique($parent_term_ids) ) ? true : false;
}
add_filter( 'woocommerce_get_price_html', 'conditional_price_suffix', 10, 2 );
function conditional_price_suffix( $price, $product ) {
// Handling product variations
$product_id = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();
// HERE define your product categories (can be IDs, slugs or names)
$product_categories = array('fabric','haberdashery', 'lining');
if( has_product_categories( $product_categories, $product_id ) )
$price .= ' ' . __('per metre');
return $price;
}
Code goes in function.php file of your active child theme (or active theme). tested and works.
I have configured WooCommerce composite product's component in the following manner ( see below 2 screenshots ).
On the front-end side of product single page , I am selecting the same product ( eg : Form D ) from those 2 components ( see below screenshot ).
Now , when I click on Add to cart button , it is showing a Notice like : only 1 product may allow adding to cart , but also it is adding the same product to the cart ( see below screenshot ).
Now , to prevent adding same products in the cart , I write following code :
add_filter( 'woocommerce_add_to_cart_validation', 'filter__woocommerce_add_to_cart_validation', 10, 3 );
function filter__woocommerce_add_to_cart_validation ( $passed, $product_id, $quantity ){
$all_pro_id = array();
if ( isset( $_POST['wccp_component_selection'] ) && ! empty( $_POST['wccp_component_selection'] ) ) {
foreach ( $_POST['wccp_component_selection'] as $key => $value ) {
if ( ! empty( $value ) ) {
$all_pro_id[] = $value;
}
}
//echo "<pre>"; print_r($all_pro_id); die;
if ( count( $all_pro_id ) > count( array_unique( $all_pro_id ) ) ) {
//echo "yes";
$passed = false;
}
}
return $passed;
}
Still , the code is not working and it is adding the same product to the cart ( see below screenshot ).
I struggle to find a way to update the stock of the similar variation of a product.
All my product have, say, a main variation, "Black" (30$) or "white" (250$) but sometimes they have another variation which is a "date start", so date_start:"12th june", date_start: "30th july", etc. I need to update the stock of the "date" variation when it's present (if there is no date, woocommerce update the main stock, no problem).
If someone choose "Black"+"12th June" I need the stock of "12 June" to be decreased also for "white"...
Before someone ask, "black" and "white" have different price per product... And "date" change also per product, that's why I need to use variation (and not addon attribute with a plugin).
Maybe someone have a better idea for organising this, but I try many other solution, always a caveat. This one seems the simpler, just have to find the good "hook" and "code"
Here is some pseudo code I made for this:
if(Product is sold):
VarSoldVariation = getSoldVariation(product->ID);
OtherVariationWithSameDate = getVariations (VarSoldVariation->pa_dates);
foreach(OtherVariationWithSameDate)updateStockVariation();
endif;
OK, it seems a little weird for not using metadata/attributes for this case, instead of variations. However, I've done more unusual stuff with variations than this one, so without judging your decision:
At first, you have to find a suitable action hook which fires after an order takes place. Some of them are:
woocommerce_order_status_$STATUS_TRANSITION[from]_to_$STATUS_TRANSITION[to]
woocommerce_order_status_$STATUS_TRANSITION[to] (e.g. woocommerce_order_status_completed)
woocommerce_order_status_changed
woocommerce_payment_complete
woocommerce_thankyou
Update 2:
I rewrite the code with some improvements:
In my initial answer, I used a WordPress get_posts function (which uses WP_Query) with a meta_query parameter, which you should definitely change to tax_query in this case, due to performance considerations. And we also know that it's better practice to use wc_get_products and WC_Product_Query where it's possible. However in this case it's not even needed to do a direct post query on db and it's possible to get the variations from get_available_variations method of WC_Product_Variable
Now it checks for quantity on order item and uses it on other date variations stock update.
Now it uses WC classes and functions wherever is possible, instead of direct updating of metadata, stock quantity, stock status, etc.
The Updated Code:
add_action('woocommerce_order_status_processing', 'sync_variations_stock');
/**
* Update same date variations on customer order place
* #param $order_id
* #return void
*/
function sync_variations_stock($order_id)
{
if (is_admin()) return; // make sure it's a user order and we aren't on admin dashboard
$order = wc_get_order( $order_id );
foreach( $order->get_items() as $item ) {
if ($item->get_type() !== 'line_item') continue; //if $item is not a product or variation
$order_variation_count = $item->get_quantity();
$order_product_id = $item->get_product_id();
$order_variation_id = $item->get_variation_id();
if ( ! $order_variation_id ) continue; // if the item isn't a variation
$order_variation = wc_get_product($order_variation_id);
$order_variation_attribs = $order_variation->get_variation_attributes();
if ( isset($order_variation_attribs['attribute_pa_date']) ) {
$current_date_attrib = $order_variation_attribs['attribute_pa_date'];
} else {
continue; // stop if the variation in the order doesn't have 'pa_date' attrib
}
$product = wc_get_product( $order_product_id );
$variations = $product->get_available_variations();
foreach ( $variations as $variation ) {
if ( $variation['variation_id'] == $order_variation_id ) {
continue; //if this variation is the one we have in our order
}
if ( ! isset( $variation['attributes']['attribute_pa_color'] ) || !isset( $variation['attributes']['attribute_pa_date'] ) ) {
continue; //if this variation does not have the color or date attrib
}
if ( $variation['attributes']['attribute_pa_date'] == $current_date_attrib ) {
/*
* wc_update_product_stock function sets the stock quantity if the variation stock management is enabled
* NOTE: This function may cause a negative stock even if the variation backorder is set to false
*/
wc_update_product_stock( $variation['variation_id'], $order_variation_count, 'decrease' );
wc_delete_product_transients($variation['variation_id']); // Clear/refresh the variation cache (optionally if needed)
}
}
}
}
Tested and It's Working!
My First Answer:
For this example, I will use the last one. However you should be careful about this hook, since it fires on every page load of the 'WC Thank You page'. It would be a good idea to use one of these hooks instead:
woocommerce_order_status_processing
woocommerce_order_status_completed
woocommerce_payment_complete
Final code would be something like this:
add_action('woocommerce_thankyou', 'sync_variations_stock');
function sync_variations_stock($order_id)
{
$order = wc_get_order( $order_id );
foreach( $order->get_items() as $item ){
$product_id = $item->get_product_id();
$product_variation_id = $item->get_variation_id();
if (!$product_variation_id) return; // if the item isn't a variation
$date_variation = get_post_meta( $product_variation_id, 'attribute_pa_date', true);
$color_variation = get_post_meta( $product_variation_id, 'attribute_pa_color', true);
if ( ! $date_variation && ! $color_variation ) return; //if the variation doesn't have date and color attributes
$args = array(
'post_parent' => $product_id,
'post_type' => 'product_variation',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'attribute_pa_date',
'value' => $date_variation,
'compare' => '='
),
array(
'key' => 'attribute_pa_color',
'value' => $color_variation,
'compare' => '!='
)
),
);
$other_date_variations = get_posts($args);
if( is_array($other_date_variations) && !empty($other_date_variations) ){
foreach ($other_date_variations as $date_variation) {
// do your stock updating proccess here. (updateStockVariation() as you write in your code)
$variation_id = $date_variation->ID;
$date_variation_stock = (int) get_post_meta( $variation_id, '_stock', true);
if ($date_variation_stock > 0) { //to prevent backorders
$date_variation_stock = $date_variation_stock - 1;
update_post_meta($variation_id, '_stock', $date_variation_stock);
// if the variation is now out-of-stock, set it as so
if ($date_variation_stock === 0) {
update_post_meta($variation_id, '_stock_status', 'outofstock');
wp_set_post_terms( $variation_id, 'outofstock', 'product_visibility', true );
}
}
}
}
}
}
Note: You have to replace attribute_pa_date & attribute_pa_color to match your attribute slugs.
Update 1
There are other consideration in this topic. WC Variation stock quantities may be changed in other senarios and circumstances, such as order edit on dashboard, order refunds, direct product edit, etc. Before going live, you have to think about these too.
Whoever as I said, there may be other ways to do what you are trying to. But I couldn't understand your setup and the relation between your variations and the dates. I think it's better to ask a approach related question for this, on WB.SE
I also just made a small change. In your code, if people refresh the page, the stock of the other variation are decreased... As woocommerce will always decrease the stock of the bought variation first, I go get this stock variation number and update other one with it. So I'm sure everything stays the same. :)
Here is the code updated:
function sync_variations_stock($order_id)
{
if (is_admin()) return; // make sure it's a user order and we aren't on admin dashboard
$order = wc_get_order( $order_id );
foreach( $order->get_items() as $item ) {
if ($item->get_type() !== 'line_item') continue; //if $item is not a product or variation
$order_variation_count = $item->get_quantity();
$order_product_id = $item->get_product_id();
$order_variation_id = $item->get_variation_id();
if ( ! $order_variation_id ) continue; // if the item isn't a variation
$order_variation = wc_get_product($order_variation_id);
$order_variation_attribs = $order_variation->get_variation_attributes();
if ( isset($order_variation_attribs['attribute_pa_dates']) ) {
$current_date_attrib = $order_variation_attribs['attribute_pa_dates'];
//Get the stock of the current variation for updating others.
$new_stock = $order_variation->get_stock_quantity();
} else {
continue; // stop if the variation in the order doesn't have 'pa_dates' attrib
}
$product = wc_get_product( $order_product_id );
$variations = $product->get_available_variations();
foreach ( $variations as $variation ) {
if ( $variation['variation_id'] == $order_variation_id ) {
continue; //if this variation is the one we have in our order
}
if ( ! isset( $variation['attributes']['attribute_pa_admissible-emploi-quebec'] ) || !isset( $variation['attributes']['attribute_pa_dates'] ) ) {
continue; //if this variation does not have the color or date attrib
}
if ( $variation['attributes']['attribute_pa_dates'] == $current_date_attrib ) {
/*
* wc_update_product_stock function sets the stock quantity if the variation stock management is enabled
* NOTE: This function may cause a negative stock even if the variation backorder is set to false
*/
//wc_update_product_stock( $variation['variation_id'], $order_variation_count, 'decrease' );
//Update stock of other variation with the stock number of the one just bought
wc_update_product_stock( $variation['variation_id'], $new_stock, 'set' );
wc_delete_product_transients($variation['variation_id']); // Clear/refresh the variation cache (optionally if needed)
}
}
}
}
I am using WooCommerce with WPML plugin. I want to implement a feature on checkout when a customer under certain conditions can have an upgrade of his product but keeping the old product price.
The products are variable with many variable attributes.
So, more specifically, what I want is if a customer has selected a specific product variation with x price on checkout (under a certain condition) I could change his cart item with another product's variation but keep the x price.
What I tried first is to change only the name of the product using woocommerce_order_item_name hook but the change doesn't follow on the order. This is important because some order data are then sent to an API.
Afterwards I used "Changing WooCommerce cart item names" answer code, which worked perfectly for my purpose until I installed WPML. For some reason the WC_Cart method set_name() doesn't work with WPML. I opened a support thread but they still can't find a solution.
Can anyone suggest any other solution?
Update
I have tried an approach where I remove the product item on cart and then I add the one I need. After I use set_price() to change the price of the newly added item. The removal/addition seems to be working but the price is not changed on one language and it is not applied on both languages after placing order.
This is the code I use:
function berrytaxiplon_change_product_name( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Get an instance of the WC_Product object
$product = $cart_item['data'];
// Get the product name (Added Woocommerce 3+ compatibility)
$product_id = method_exists( $product, 'get_parent_id' ) ? $product->get_parent_id() : $product->post->post_parent;
if ( ICL_LANGUAGE_CODE == 'en') {
if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 12) {
$new_product = wc_get_product( 82 );
$atrributes = $product->get_attributes('view');
foreach ($atrributes as $atrribute_key => $atrribute_value) {
$new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
}
$new_variation_id = find_matching_product_variation_id(82, $new_attributes);
$cart->remove_cart_item( $cart_item_key );
$cart->add_to_cart( 82, 1, $new_variation_id, $new_attributes, $cart_item );
foreach ( WC()->cart->get_cart() as $new_item ) {
$new_item['data']->set_price( $cart_item['s-fare'] );
}
}
} else {
if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 282) {
$new_product = wc_get_product( 303 );
$atrributes = $product->get_attributes('view');
foreach ($atrributes as $atrribute_key => $atrribute_value) {
$new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
}
$new_variation_id = find_matching_product_variation_id(303, $new_attributes);
$cart->remove_cart_item( $cart_item_key );
$cart->add_to_cart( 303, 1, $new_variation_id, $new_attributes, $cart_item );
foreach ( WC()->cart->get_cart() as $new_item ) {
$new_item['data']->set_price( $cart_item['s-fare']);
}
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'berrytaxiplon_change_product_name', 10, 1 );
Any idea why the set_price() method is not applied?
Update 2
WPMl uses 'woocommerce_before_calculate_totals' and overrides the action added on functions.php
WPML support provided a solution using 3 filters:
https://wpml.org/forums/topic/cant-use-set_name-method-for-the-product-object-on-checkout/#post-3977153
So this is a code that I am using in one of my projects to add a product variation to cart based off of some filters and the selected product:
$product = new WC_Product($product_id); //The main product whose variation has to be added
$product_name = $product->get_name(); //Name of the main product
$quantity = sanitize_text_field($cData['quantity']); //You can set this to 1
$variation_id = sanitize_text_field($cData['variation_id']); //I had the variation ID from filters
$variation = array(
'pa_duration' => sanitize_text_field($cData['duration']) //The variation slug was also available for me.
);
$cart_item_data = array('custom_price' => sanitize_text_field($custom_price));
$cart = WC()->cart->add_to_cart( (int)$product_id, (int)$quantity, (int)$variation_id, $variation, $cart_item_data ); //This will add products to cart but with the actual price of the variation being added and meta data holding the custom price.
WC()->cart->calculate_totals();
WC()->cart->set_session();
WC()->cart->maybe_set_cart_cookies();
Then you need to do a check on before cart totals are calculated and set the price to custom price like this:
function woocommerce_custom_price_to_cart_item( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value["custom_price"] ) ) {
$value['data']->set_price($value["custom_price"]);
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'woocommerce_custom_price_to_cart_item', 99 );
The code provided from Faham is very helpful but the page-template that leads to checkout is already over-complicated so I focused to use his logic on the 'woocommerce_before_calculate_totals' hook I am trying all along.
So instead of trying to change the name I remove the item and add the new one. Then calling a new loop I set the price to be of the item that was removed.
function berrytaxiplon_change_product_name( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Get an instance of the WC_Product object
$product = $cart_item['data'];
// Get the product name (Added Woocommerce 3+ compatibility)
$product_id = method_exists( $product, 'get_parent_id' ) ? $product->get_parent_id() : $product->post->post_parent;
if ( ICL_LANGUAGE_CODE == 'en') {
if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 12) {
// SET THE NEW NAME
$new_product = wc_get_product( 82 );
$atrributes = $product->get_attributes('view');
foreach ($atrributes as $atrribute_key => $atrribute_value) {
$new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
}
$new_variation_id = find_matching_product_variation_id(82, $new_attributes);
$cart->remove_cart_item( $cart_item_key );
$cart->add_to_cart( 82, 1, $new_variation_id, $new_attributes, $cart_item );
foreach ( WC()->cart->get_cart() as $new_item ) {
$new_item['data']->set_price( get_post_meta( $cart_item['variation_id'], '_price', true ) );
}
}
} else {
if (isset($cart_item['s-member-level']) && $cart_item['s-member-level'] == 3 && $product_id == 282) {
// SET THE NEW NAME
$new_product = wc_get_product( 303 );
$atrributes = $product->get_attributes('view');
foreach ($atrributes as $atrribute_key => $atrribute_value) {
$new_attributes['attribute_' . $atrribute_key] = strtolower($atrribute_value);
}
$new_variation_id = find_matching_product_variation_id(303, $new_attributes);
$cart->remove_cart_item( $cart_item_key );
$cart->add_to_cart( 303, 1, $new_variation_id, $new_attributes, $cart_item );
foreach ( WC()->cart->get_cart() as $new_item ) {
$new_item['data']->set_price( get_post_meta( $cart_item['variation_id'], '_price', true ) );
}
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'berrytaxiplon_change_product_name', 10, 1 );
I use the function below to match the attributes taken from the question WooCommerce: Get Product Variation ID from Matching Attributes
function find_matching_product_variation_id($product_id, $attributes)
{
return (new \WC_Product_Data_Store_CPT())->find_matching_product_variation(
new \WC_Product($product_id),
$attributes
);
}
I am a little skeptical using add_to_cart() and a second foreach() inside the $cart_item. But I tested and it seems to work without errors.
Update
Actually there is an issue with this code (or with WPML again). It seems that set_price() is not applied on the secondary language. Yet if I reload the checkout page an send the data again the new price is applied.
what I am trying to do is creating custom coupon type with custom programmatically discount in woocommerce using hooks. This what my code looks like
//add new coupon type called "custom_discount"
function custom_discount_type( $discount_types ) {
$discount_types['custom_discount'] =__( 'custom discount', 'woocommerce' );
return $discount_types;
}
// add the hooks
add_filter( 'woocommerce_coupon_discount_types', 'custom_discount_type',10, 1);
//function to get coupon amount for "custom_discount"
function woocommerce_coupon_get_discount_amount($discount, $discounting_amount, $cart_item, $single, $coupon) {
if ($coupon->type == 'custom_discount'){ //if $coupon->type == 'fixed_cart' or 'percent' or 'fixed_product' or 'percent_product' The code Works
$discount = 85;
return $discount;
} else {
return $discount;
}
}
//add hook to coupon amount hook
add_filter('woocommerce_coupon_get_discount_amount', 'woocommerce_coupon_get_discount_amount', 10, 5);
the coupon always return with 0(Zero) discount amount. Am I missing something.
Thanks for helping =)
Ok so this topic lead me on the road to making my own coupon type so I thought I should answer it to help others.
I tried out your code and was wondering why any normal coupon showed "-£xx" before the remove button, but the custom coupon type just was lank, no minus, no pound sign jut empty. Turns out it's an issue with how WooCommerce validates the coupon on cart products. It will become active if the coupon is valid regardless if the item in the cart are.
Your coupon needs to be either valid and valid for a product, or valid for the entire cart. However when Woocommerce checks to see if your product is valid for the product it ONLY checks if the coupon type is fixed_product or 'percent_product'. If it's anything else it runs the woocommerce_coupon_is_valid_for_product hook with the existing data but defaulting to false, so your woocommerce_coupon_get_discount_amount hook will not fire.
So you need to hook into woocommerce_coupon_is_valid_for_product and validate the product to make sure it's valid. In my case I just copied the default valid function and changes $this to $coupon.
// Check if coupon is valid for product
add_filter('woocommerce_coupon_is_valid_for_product', 'woocommerce_coupon_is_valid_for_product', 10, 4);
function woocommerce_coupon_is_valid_for_product($valid, $product, $coupon, $values){
if ( ! $coupon->is_type( array( 'bogof' ) ) ) {
return $valid;
}
$product_cats = wp_get_post_terms( $product->id, 'product_cat', array( "fields" => "ids" ) );
var_dump( $coupon->product_ids );
// Specific products get the discount
if ( sizeof( $coupon->product_ids ) > 0 ) {
if ( in_array( $product->id, $coupon->product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $coupon->product_ids ) ) || in_array( $product->get_parent(), $coupon->product_ids ) ) {
$valid = true;
}
}
// Category discounts
if ( sizeof( $coupon->product_categories ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $coupon->product_categories ) ) > 0 ) {
$valid = true;
}
}
if ( ! sizeof( $coupon->product_ids ) && ! sizeof( $coupon->product_categories ) ) {
// No product ids - all items discounted
$valid = true;
}
// Specific product ID's excluded from the discount
if ( sizeof( $coupon->exclude_product_ids ) > 0 ) {
if ( in_array( $product->id, $coupon->exclude_product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $coupon->exclude_product_ids ) ) || in_array( $product->get_parent(), $coupon->exclude_product_ids ) ) {
$valid = false;
}
}
// Specific categories excluded from the discount
if ( sizeof( $coupon->exclude_product_categories ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $coupon->exclude_product_categories ) ) > 0 ) {
$valid = false;
}
}
// Sale Items excluded from discount
if ( $coupon->exclude_sale_items == 'yes' ) {
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( isset( $product->variation_id ) ) {
if ( in_array( $product->variation_id, $product_ids_on_sale, true ) ) {
$valid = false;
}
} elseif ( in_array( $product->id, $product_ids_on_sale, true ) ) {
$valid = false;
}
}
return $valid;
}
This question may be a little old, but there are two main coupon types, and you should add another filter based on the coupon you're adding. The filter for woocommerce_cart_coupon_types is for cart discounts, while woocommerce_product_coupon_types is for product discounts.
What you want to use is the second one woocommerce_product_coupon_types when you want WooCommerce to automatically validate the product/category_in and product/category_exclude fields etc.
You can still use the answer submitted by Chris for any manual validation, without using the above filters as required, however, please note that there are two validation functions to use/filter: $coupon->is_valid_for_cart() and $coupon->is_valid_for_product().