This question already has answers here:
How to add variation stock status to Woocommerce product variation dropdown
(4 answers)
Closed 4 years ago.
I Would like next to variation to show "Out of Stock" when product variation is out of stock. Below, I am using the code from this answer:
add_filter( 'woocommerce_variation_option_name',
'customizing_variations_terms_name', 10, 1 );
function customizing_variations_terms_name( $term_name ){
if(is_admin())
return $term_name;
global $product;
$second_loop_stoped = false;
// Get available product variations
$product_variations = $product->get_available_variations();
// Iterating through each available product variation
foreach($product_variations as $variation){
$variation_id = $variation['variation_id'];
$variation_obj = new WC_Product_Variation( $variation_id );
## WOOCOMMERCE RETRO COMPATIBILITY ##
if ( version_compare( WC_VERSION, '3.0', '<' ) ) # BEFORE Version 3 (older)
{
$stock_status = $variation_obj->stock_status;
$stock_qty = intval($variation_obj->stock);
// The attributes WC slug key and slug value for this variation
$attributes_arr = $variation_obj->get_variation_attributes();
}
else # For newest verions: 3.0+ (and Up)
{
$stock_status = $variation_obj->get_stock_status();
$stock_qty = $variation_obj->get_stock_quantity();
// The attributes taxonomy key and slug value for this variation
$attributes_arr = $variation_obj->get_attributes();
}
if(count($attributes_arr) != 1) // Works only for 1 attribute set in the product
return $term_name;
// Get the terms for this attribute
foreach( $attributes_arr as $attr_key => $term_slug){
// Get the attribute taxonomy
$term_key = str_replace('attribute_', '', $attr_key );
// get the corresponding term object
$term_obj = get_term_by( 'slug', $term_slug, $term_key );
if( $term_obj->name == $term_name ){ // If the term name matches we stop the loops
$second_loop_stoped = true;
break;
}
}
if($second_loop_stoped)
break;
}
if( $stock_qty>0 )
return $term_name .= ' - ' . $stock_status . ' ('.$stock_qty.')';
else
return $term_name .= ' - ' . $stock_status;
}
but when test on my site, it show "Out of Stock" for all variations i have on selected product. For example on this product it show "Out of Stock" for all variations i have, but in fact, only variation "50 ליטר" is in real "Out Of Stock". other variations are In Stock ! I using Woocommerce 3.3.5 version. Can someone to tell me what i need to do to show out of stock next to variations if product is out of stock for real, because i m using radio buttons as replacement for standard dropdown button from Woocommerce.
Seems over complicated, I managed to do it this way. Seems to work, displays out of stock for the correct variations when I tested it.
add_filter( 'woocommerce_variation_option_name', 'customizing_variations_terms_name', 10, 1 );
function customizing_variations_terms_name( $term_name ) {
global $product;
// Get available product variations
$product_variations = $product->get_available_variations();
// product_variation terms are normalized to lowercase with
// spaces replaced by dashes.
// if your term name contains capital letters or spaces i.e. 'SIZE 6'
// the product variation will be lowercase with dashes, i.e. 'size-6'
/*
$term_name = str_replace(' ', '-', strtolower($term_name));
*/
foreach ( $product_variations as $product_variation ) {
if( isset( $product_variation['attributes'] ) ) {
$key = array_search($term_name, $product_variation['attributes']);
if( $key !== false && ! $product_variation['is_in_stock'] ) {
return $term_name . ' - Out of Stock';
}
}
}
return $term_name;
}
In addition to this you can grey out the product so they can't select it if that's something that are after.
add_filter( 'woocommerce_variation_is_active', 'grey_out_variations_when_out_of_stock', 10, 2 );
function grey_out_variations_when_out_of_stock( $grey_out, $variation ) {
if ( ! $variation->is_in_stock() )
return false;
return true;
}
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.
Please pardon me if there's a better way to do this as I am not very familiar with this code. I would like to display only the link to the homepage and the current product on the breadcrumb.
Desire result:
Currently:
I found the code for the breadcrumb, is there a way to only display the first and last crumb regardless of the hierarchy?
foreach ( $breadcrumb as $key => $crumb ) {
echo $before;
if ( ! empty( $crumb[1] ) && sizeof( $breadcrumb ) !== $key ) {
echo '' . esc_html( $crumb[0] ) . '';
} else if(!is_product() && !flatsome_option('wc_category_page_title')) {
echo esc_html( $crumb[0] );
}
echo $after;
// Single product or Active title
if(is_product() || flatsome_option('wc_category_page_title')){
$key = $key+1;
if ( sizeof( $breadcrumb ) > $key) {
echo ' <span class="divider">'.$delimiter.'</span> ';
}
} else{
// Category pages
if ( sizeof( $breadcrumb ) !== $key + 1 ) {
echo ' <span class="divider">'.$delimiter.'</span> ';
}
}
}
The reason why I am doing this is that some of the products have multiple categories and by default, it will only show the breadcrumb for the primary category. I would rather make a truncated version as suggested by the owner.
I was also wondering if I can simply dynamically retrieve the product title and link + static homepage link, make it into a shortcode so that I can paste it in the product page.
Hi – the first example in the answer above, also removes the shop from woocommerce breadcrumb. Here is a working example, that only removes the category:
// remove only the category from woocommerce breadcrumbs
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
//print the array and look for the key with the category
//echo '<pre>';
//print_r($crumbs);
//echo '</pre>';
//unset($crumbs[2]); in my case it is key 2
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
} else {
unset($crumbs[2]); // this isn't enough, it would leave a trailing delimiter
$newBreadC = array_values($crumbs); //therefore create new array
return $newBreadC; //return the new array
}
}
If you want to remove categories and subcategories from product breadcrumbs on the product page you can use the woocommerce_get_breadcrumb hook.
// change the breadcrumb on the product page
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
}
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
return $new_crumbs;
}
The code has been tested and works. Add it to your active theme's functions.php.
A good alternative is to set the primary product category for each product. You can do this by installing the Yoast SEO plugin.
You can use the _yoast_wpseo_primary_product_cat product meta to set the id of the primary product category.
After setting the primary category id by editing the product in the
backend or importing a .csv file you will only need to change the
permalink and breadcrumbs based on the primary product category.
To update the product permalink:
// update the product permalink based on the primary product category
add_filter( 'wc_product_post_type_link_product_cat', 'change_product_permalink_by_cat', 10, 3 );
function change_product_permalink_by_cat( $term, $terms, $post ) {
// get the primary term as saved by Yoast
$primary_cat_id = get_post_meta( $post->ID, '_yoast_wpseo_primary_product_cat', true );
// if there is a primary and it's not currently chosen as primary
if ( $primary_cat_id && $term->term_id != $primary_cat_id ) {
// find the primary term in the term list
foreach ( $terms as $term_key => $term_object ) {
if ( $term_object->term_id == $primary_cat_id ) {
// return this as the primary term
$term = $terms[ $term_key ];
break;
}
}
}
return $term;
}
To update the product breadcrumbs on the product page:
// change the breadcrumb on the product page
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
}
global $product;
$new_crumbs = array();
if ( $product->get_meta( '_yoast_wpseo_primary_product_cat', true ) ) {
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the id of the primary product category
$primary_cat_id = $product->get_meta( '_yoast_wpseo_primary_product_cat', true );
// create an array with all parent categories (based on the id of the primary product category)
$parent_categories = get_ancestors( $primary_cat_id, 'product_cat' );
$parent_categories = array_reverse($parent_categories);
$parent_categories[] = $primary_cat_id;
// for each product category it gets the name and the permalink
foreach ( $parent_categories as $cat_id ) {
$term = get_term_by( 'id', $cat_id, 'product_cat' );
$new_crumbs[] = array(
0 => $term->name,
1 => esc_url( get_term_link( $term, 'product_cat' ) )
);
}
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
} else {
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
}
return $new_crumbs;
}
The code has been tested and works. Add it to your active theme's functions.php.
From here :
How to add custom stock status to products in WooCommerce 4+
Woocommerce - Check if product was created less than 60 days ago
Target > Check the product's age, if it has reached x days make the stock 'expired' (costume).
Here the code to make stock custom
//PART1
// Add new stock status options
function filter_woocommerce_product_stock_status_options( $status ) {
// Add new statuses
$status['pre_order'] = __( 'Pre order', 'woocommerce' );
$status['expired'] = __( 'Expired', 'woocommerce' );
return $status;
}
add_filter( 'woocommerce_product_stock_status_options', 'filter_woocommerce_product_stock_status_options', 10, 1 );
// Availability text
function filter_woocommerce_get_availability_text( $availability, $product ) {
// Get stock status
//i try change this to : switch ($product->get_stock_status){
//also deleted
switch( $product->get_stock_status() ) {
case 'pre_order':
$availability = __( 'Pre order', 'woocommerce' );
break;
case 'expired':
$availability = __( 'Expired', 'woocommerce' );
break;
}
return $availability;
}
add_filter( 'woocommerce_get_availability_text', 'filter_woocommerce_get_availability_text', 10, 2 );
// Availability CSS class
function filter_woocommerce_get_availability_class( $class, $product ) {
// Get stock status
//i try change this to : switch ($product->get_stock_status){
//also deleted
switch( $product->get_stock_status() ) {
case 'pre_order':
$class = 'pre-order';
break;
case 'expired':
$class = 'expired';
break;
}
return $class;
}
add_filter( 'woocommerce_get_availability_class', 'filter_woocommerce_get_availability_class', 10, 2 );
//PART2
// Admin stock html
function filter_woocommerce_admin_stock_html( $stock_html, $product ) {
// Simple
if ( $product->is_type( 'simple' ) ) {
// Get stock status
//i try change this to : $product->get_stock_status;
//also deleted
$product_stock_status = $product->get_stock_status();
// Variable
} elseif ( $product->is_type( 'variable' ) ) {
foreach( $product->get_visible_children() as $variation_id ) {
// Get product
$variation = wc_get_product( $variation_id );
// Get stock status
//i try change this to : $product->get_stock_status;
//also deleted
$product_stock_status = $variation->get_stock_status();
/*
Currently the status of the last variant in the loop will be displayed.
So from here you need to add your own logic, depending on what you expect from your custom stock status.
By default, for the existing statuses. The status displayed on the admin products list table for variable products is determined as:
- Product should be in stock if a child is in stock.
- Product should be out of stock if all children are out of stock.
- Product should be on backorder if all children are on backorder.
- Product should be on backorder if at least one child is on backorder and the rest are out of stock.
*/
}
}
// Stock status
switch( $product_stock_status ) {
case 'pre_order':
$stock_html = '<mark class="pre-order" style="background:transparent none;color:#33ccff;font-weight:700;line-height:1;">' . __( 'Pre order', 'woocommerce' ) . '</mark>';
break;
case 'expired':
$stock_html = '<mark class="expired" style="background:transparent none;color:#cc33ff;font-weight:700;line-height:1;">' . __( 'Contact us', 'woocommerce' ) . '</mark>';
break;
}
return $stock_html;
}
add_filter( 'woocommerce_admin_stock_html', 'filter_woocommerce_admin_stock_html', 10, 2 );
So, i has edited the code and here's the complete code.
add_filter( 'woocommerce_product_get_stock_status', 'conditional_product_status', 10, 2 );
add_filter( 'woocommerce_product_variation_get_stock_status', 'conditional_product_status', 10, 2 );
function conditional_product_status( $product_stock_status, $product ){
global $product;
$package = $product->get_attribute( 'pa_package' );
switch($package){
case 'Silver':
$var = 14*24*60*60;
break;
case 'Gold':
$var = 90*24*60*60;
break;
case 'Platinum':
$var = 180*24*60*60;
break;
default:
$var = 1*24*60*60;
break;
}
// Get the date for the product published and current date
$datetime_created = $product->get_date_created(); // Get product created datetime
$timestamp_created = $datetime_created->getTimestamp(); // product created timestamp
$datetime_now = new WC_DateTime(); // Get now datetime (from Woocommerce datetime object)
$timestamp_now = $datetime_now->getTimestamp(); // Get now timestamp
$time_delta = $timestamp_now - $timestamp_created; // Difference in seconds
if($product->is_type('simple')&& $time_delta > $var ){
$product_stock_status = 'expired';
}elseif($product->is_type('variable')&& $time_delta > $var ){
$product_stock_status = 'expired';
}
return $product_stock_status;
}
On the back end, it has changed the stock state and like the way I want it to.
I enabled wp_debug and didn't see any error messages.
The problem is that every time I editing a product (example: change the price, or change the attribute value), and then i update it, that to be always loading and never finishes.
I'm stuck here and don't know what to do.
Can someone direct me to the right path?
Thank you
I am using some custom logics to show or hide variations in variable products using this hook woocommerce_get_children https://stackoverflow.com/a/67804835/6829420 and its working fine as per my expectations.
However, I am having wrong 'Sign Up Fee' in the variable product page because its keep getting the values from one of my hidden variations. Here below is my try and what I have done so far.
add_filter( 'woocommerce_subscriptions_product_price_string', 'subscr_product_price_string', 10, 3 );
function subscr_product_price_string( $subscription_string, $product, $include ){
if (is_admin() || (! $product->is_type( 'variable-subscription' ))) return $subscription_string;
if ( $include['sign_up_fee'] && $product->get_sign_up_fee( $product ) > 0 ) {
$available_variations = $product->get_available_variations();
echo count($available_variations);
echo $product->get_sign_up_fee();
exit;
//echo $prices = get_price_sign_up_fee($product);
exit;
}
Say for example, I have 3 variations "Blue, Yellow and Green" and I have hide "Blue" and go to variation product page.
Then If I do this,
$available_variations = $product->get_available_variations();
echo count($available_variations);
Then its showing 2 but my echo $product->get_sign_up_fee(); keep getting value from "Blue" which has the lowest value but this is wrong value.
Can someone guide me how can I get sign up values based on my available variations only.
I have already updated default From value using below code with woocommerce_variable_price_html hook and its working fine as its getting value based on available variations.
function updateFromDefaultValue($price, $product) {
$prefix = sprintf('%s: ', __('From', 'iconic'));
$min_price_regular = $product->get_variation_regular_price('min', true);
$min_price_sale = $product->get_variation_sale_price('min', true);
$max_price = $product->get_variation_price('max', true);
$min_price = $product->get_variation_price('min', true);
$price = ($min_price_sale == $min_price_regular) ?
wc_price($min_price_regular) :
'<del>' . wc_price($min_price_regular) . '</del>' . '<ins>' . wc_price($min_price_sale) . '</ins>';
return ($min_price == $max_price) ?
$price :
sprintf('%s%s', $prefix, $price);
}
add_filter('woocommerce_variable_price_html', 'updateFromDefaultValue', 10, 2);
Thanks
UPDATE
I have found a hook woocommerce_subscriptions_product_price_string_inclusions through which I am getting an updated sign_up_fee value based on my "Available Subscription". Below is my code.
add_filter('woocommerce_subscriptions_product_price_string_inclusions', 'updateSubscriptionPeriod', 10, 2);
function updateSubscriptionPeriod( $include, $product ) {
if (is_admin() || (! $product->is_type( 'variable-subscription' ))) return $include;
$min_max_variation_data = $product->get_min_and_max_variation_data( array_keys( $prices ) );
if(empty($min_max_variation_data) || empty($min_max_variation_data['subscription'])) return $include;
$include['sign_up_fee'] = $min_max_variation_data['subscription']['signup-fee'];
$include['trial_length'] = 5;
$include['subscription_period'] = 50;
$include['subscription_length'] = $min_max_variation_data['subscription']['interval'];
$include['subscription_price'] = $min_max_variation_data['max']['price'];
return $include;
}
Now I am able to get value 20, please see screenshot below.
But as you can see the difference in my screenshot, I am still not getting the updated values here every 3 years with a 2-week free trial a as it should be said, very 4 years with a 2-month free trial because this is what my values from my available variations.
You can also see I am trying to put some static values as well as dynamic values like this.
$include['trial_length'] = 5;
$include['subscription_period'] = 50;
$include['subscription_length'] = $min_max_variation_data['subscription']['interval'];
$include['subscription_price'] = $min_max_variation_data['max']['price'];
But its just not updating in my product page.
Can someone guide me please how can I update these due values.
Thanks in advance.
UPDATE 2
I have searched across and found woocommerce_subscriptions_product_price_string hook and here is my attempt.
add_filter( 'woocommerce_subscriptions_product_price_string', 'subscriptions_custom_price_string', 20, 3 );
function subscriptions_custom_price_string( $price_string, $product, $args ) {
// Get the trial length to check if it's enabled
$trial_length = $args['trial_length'];
$speriod = $args['subscription_period'];
$sfee = $args['sign_up_fee'];
$sfee = wc_price($sfee);
$sign_up_fee = isset($args['sign_up_fee']) ? __(" and a $sfee sign-up fee", "woocommerce") : '';
if( $trial_length > 0 )
$price_string = $args['price'] . ' / ' . $speriod . $sign_up_fee;
return $price_string;
}
And its showing like this below.
So now basically its updating but its not correct as I lost a format and some other important values.
Can someone guide how can I properly overwrite this filter ?
Thanks
woocommerce_variable_subscription_price_html is the correct hook that you should be using, and here's an example that might work for you:
add_filter( 'woocommerce_variable_subscription_price_html', 'variable_subscription_custom_price_html', 10, 2 );
function variable_subscription_custom_price_html( $price, $product ) {
// Unhook to avoid infinite loop.
remove_filter( 'woocommerce_variable_subscription_price_html', __FUNCTION__, 10, 2 );
// List of product IDs for variations you're hiding.
$hidden_products = array( 4171 );
// Check if the product has any variations that you manually hid.
$hidden = array_intersect( $hidden_products, get_children( array(
'post_parent' => $product->get_id(),
'post_type' => 'product_variation',
'fields' => 'ids',
) ) );
if ( ! empty( $hidden ) ) {
$prices = $product->get_variation_prices( true )['price'];
asort( $prices ); // sort from lowest to highest price
$variation = wc_get_product( array_keys( $prices )[0] );
// It's a variation, so we manually add the 'From: text'.
$price = 'From: ' . $variation->get_price_html();
}
// Re-hook this function.
add_filter( 'woocommerce_variable_subscription_price_html', __FUNCTION__, 10, 2 );
return $price;
}
In Woocommerce, I want to prevent users from adding to cart if a specific attribute is selected.
The attribute name is "Production Time" and the specific value we want to disable from purchasing is "Urgent".
The code below is from this link - Hide Add to Cart button in Woocommerce product variations for a specific attribute value
However, it only works with numbers. When the attribute is a text, it doesn't work.
add_filter( 'woocommerce_variation_is_purchasable', 'conditional_variation_is_purchasable', 20, 2 );
function conditional_variation_is_purchasable( $purchasable, $product ) {
## ---- Your settings ---- ##
$taxonomy = 'production-time';
$term_name = 'urgent';
## ---- The active code ---- ##
$found = false;
// Loop through all product attributes in the variation
foreach ( $product->get_variation_attributes() as $variation_attribute => $term_slug ){
$attribute_taxonomy = str_replace('attribute_', '', $variation_attribute); // The taxonomy
$term = get_term_by( 'slug', $term_slug, $taxonomy ); // The WP_Term object
// Searching for attribute 'pa_size' with value 'XL'
if($attribute_taxonomy == $taxonomy && $term->name == $term_name ){
$found = true;
break;
}
}
if( $found )
$purchasable = false;
return $purchasable;
}