I'm trying to create a shortcode to place on my single product pages so to display the stock quantity for admins only. For normal customers I just show whether the product is in stock or not. This is the snippet I'm trying to create:
function Product_stock() {
if ( current_user_can( 'administrator' ) ) {
echo '<div class="sales-admin"><b>Available Stock (admin):</b><ol>';
global $product;
echo wc_get_stock_html( $product );
}
}
add_shortcode( 'Product_stock', 'Product_stock' );
However the above code displays the stock in the format I have it set for my woocommerce store, so it just displays again in this format Availability: In stock or Availability: Out of stock.
What do I need to write to add to get it to print the actual stock value?
One solution is to hook the product availability text.
You can try something like that :
/**
* WC : add product quantity on front-end only to admins and shop managers
*/
add_filter( 'woocommerce_get_availability_text', 'a3w_woocommerce_get_availability_text', 10, 2);
function a3w_woocommerce_get_availability_text( $availability, $product ) {
if ( current_user_can( 'manage_woocommerce' ) ) { // replace 'manage_woocommerce' by 'administrator' for admin only OR any other condition
$availability .= sprintf( ' ( %d )', $product->get_stock_quantity() );
}
return $availability;
}, 20, 2 );
This will display something like that to admins :
Availability: In stock ( 18 )
And this to customers :
Availability: In stock
Tested and working.
Place this code in the functions.php in your child theme.
Related
Right now, my shop only displays only 2 badges on my shop page. Special Offer and out of stock. I would also want to include stock quantity below 10 and Allow for back order. I have looked at multiple plugins as well as php code, but nothing seems to work. What I want to do is display "Only {QTY} Is Available" is the stock drops below 10 and if it is 0 then "available on back order". Website is built in WooCommerce and WordPress
I would like these messages to replace the special offer badge if these conditions are met. I have attached a screenshot below:-
Any assistance is greatly appriciated
There are in fact two parts to your question: how to change the stock availability text and how to remove the sale badge, when some criteria on the product stock level is met.
Changing the stock availability text should be easily achieved by hooking in the woocommerce_get_availability filter. Get the product stock quantity and amend the text accordingly. E.g.:
add_filter( 'woocommerce_get_availability', 'custom_override_get_availability', 10, 2 );
function custom_override_get_availability( $availability, $_product ) {
$stock_quantity = $_product->get_stock_quantity();
if ( 0 === $stock_quantity ) {
$availability['availability'] = 'Available on backorder';
} elseif ( $stock_quantity < 10 ) {
$availability['availability'] = sprintf( 'Only %s available', $stock_quantity );
} else {
$availability['availability'] = sprintf( '%s left in stock', $stock_quantity );
}
return $availability;
}
As for removing the sale badge, I think the plugin you're using provides templates that can be overridden. Have a look in the Views folder, you should find the sale-flash.php templates. You can then override those templates in your theme and apply a similar logic, as you have access to the product. E.g. this shows the sale badge only when the product is on sale and the stock quantity is greater than or equal to 10:
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
global $post, $product;
?>
<?php if ( $product->is_on_sale() && $product->get_stock_quantity() >= 10 ) : ?>
<?php echo apply_filters( 'woocommerce_sale_flash', '<span class="onsale wdr-onsale">' . esc_html__( 'Sale!', 'woocommerce' ) . '</span>', $post, $product ); ?>
<?php endif;
Although I'm not really sure if you want to remove the sale badge when the product stock quantity is less than 10.
I am developing a wordpress and woocommerce-based website where information on cooking-related training is provided and various kitchen materials are sold.
Those who want to participate in the trainings apply by filling out a form. Kitchen supplies are also sold through woocommerce.
Trainings are added to the website with a type of content called training.
Some trainings are requested to be sold over the woocommerce structure. However, these "Trainings" that want to be sold are wanted to remain in the form of educational content. In addition, it is requested not to be added or moved as a product.
First of all, I created a virtual product called Education. I hid the product in the store.
Then I added a custom field for Tutorials called price. The price of each training to be sold will be entered here.
I have a button "Register for Training" on the training detail page, I changed it to "Buy" for the trainings wanted to sell and the link
?add-to-cart=340&custom_price=600&quantity=1
I gave in the form.
Here 340 is the id of the virtual product I created.
When the Buy button is clicked, the virtual product called Education is added to the basket. But I want to update the name and price of this training according to which training detail page is printed.
The codes I added to functions.php.
add_action( 'woocommerce_before_calculate_totals', 'before_calculate_totals' );
function before_calculate_totals( $_cart ){
// loop through the cart_contents
foreach ( $_cart->cart_contents as $cart_item_key => &$item ) {
// you will need to determine the product id you want to modify, only when the "donation_amount" is passed
if ( $item['product_id'] == 340 && isset( $_GET['custom_price'] ) ){
// custom price from POST
$custom_price = $_GET['custom_price'] > 0 ? $_GET['custom_price'] : 0;
// save to the cart data
//$item['data']->price = $custom_price;
// new versions of WooCommerce may require (instead of line above)...
$item['data']->set_price($custom_price);
}
}
}
function ipe_product_custom_price( $cart_item_data, $product_id ) {
if( isset( $_POST['custom_price'] ) && !empty($_POST['custom_price'])) {
$cart_item_data[ "custom_price" ] = $_POST['custom_price'];
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'ipe_product_custom_price', 99, 2 );
I wanted to update the price with these codes, but it didn't work.
How do I dynamically update the information of the virtual product? Or what different method would you suggest?
There are some mistakes in your code and some missing things to make it work. Try the following:
// Set custom data as custom cart data in the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_price_as_custom_cart_item_data', 30, 3 );
function add_custom_price_as_custom_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
if( ! isset($_GET['custom_price']) ) {
$cart_item_data['custom_price'] = (float) esc_attr( $_GET['custom_price'] );
$cart_item_data['unique_key'] = md5( microtime().rand() ); // Make each item unique
}
return $cart_item_data;
}
// Change cart item price
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price' );
function change_cart_item_price( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// loop through the cart_contents
foreach ( $cart->get_cart() as $cart_item ) {
// you will need to determine the product id you want to modify, only when the "donation_amount" is passed
if ( isset($cart_item['custom_price']) ) {.
$item['data']->set_price($cart_item['custom_price']);
}
}
}
// Display the custom price on minicart too
add_filter( 'woocommerce_cart_item_price', 'change_minicart_item_price', 10, 3 );
function change_minicart_item_price( $price_html, $cart_item, $cart_item_key ) {
if( ! is_cart() && isset( $cart_item['custom_price'] ) ) {
$args = array( 'price' => floatval($cart_item['custom_price']) );
if ( WC()->cart->display_prices_including_tax() ) {
$product_price = wc_get_price_including_tax( $cart_item['data'], $args );
} else {
$product_price = wc_get_price_excluding_tax( $cart_item['data'], $args );
}
return wc_price( $product_price );
}
return $price_html;
}
Code goes in functions.php file of your active child theme (or active theme). It should works.
I am using Woocommerce and I have integrated some custom fields to allow the user to specify new values that I will later append to the product title.
I am using update_post_meta/get_post_meta to save the new information. This part works fine.
Then I used the filter woocommerce_product_title to update the title. This filter is working fine when there is the use of $product->get_title() but will do nothing when using $product->get_name()which isn't an issue because in some places I don't want to append the new information.
I also used the filter the_title for the product page.
Basically my code looks like below where return_custom() will be the function than will build the new information based on the product ID.
function update_title($title, $id = null ) {
$prod=get_post($id);
if (empty($prod->ID) || strcmp($prod->post_type,'product')!=0 ) {
return $title;
}
return $title.return_custom($id);
}
function update_product_title($title, $product) {
$id = $product->get_id();
return $title.return_custom($id);
}
add_filter( 'woocommerce_product_title', 'update_product_title', 9999, 2);
add_filter( 'the_title', 'update_title', 10, 2 );
The issue arise when adding the product to the cart. The name used is the default one so my below code isn't enough to update the product name used in the cart. Same thing for the notification mails. I suppose this is logical since the email will use the information of the cart.
I am pretty sure that everything is happening inside add_to_cart() but I am not able to find any filter/hook related to the product name.
How to make sure the name used in the cart is good? What filter/hook shoud I consider in addition to the ones I am already using in order to append my new information to the product title within the cart?
I want to make sure that the new title will be seen during all the shopping process. From the product page until the notification mail.
The following will allow you to customize the product name in cart, checkout, orders and email notifications, just with one hooked function:
// Just used for testing
function return_custom( $id ) {
return ' - (' . $id . ')';
}
// Customizing cart item name in cart, checkout, orders and email notifications
add_action( 'woocommerce_before_calculate_totals', 'set_custom_cart_item_name', 10, 1 );
function set_custom_cart_item_name( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Required since Woocommerce version 3.2 for cart items properties changes
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Get the product name and the product ID
$product_name = $cart_item['data']->get_name();
$product_id = $cart_item['data']->get_id();
// Set the new product name
$cart_item['data']->set_name( $product_name . return_custom($product_id) );
}
}
Code goes on function.php file of your active child theme (or active theme). Tested and works.
Try using woocommerce_cart_item_name filter.
[woocommerce_cart] shortcode uses cart/cart.php template, and the code displaying the title is this:
if ( ! $product_permalink ) {
echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . ' ' );
} else {
echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '%s', esc_url( $product_permalink ), $_product->get_name() ), $cart_item, $cart_item_key ) );
}
I would like to alter a specific product in Woocommerce, adding programmatically to its original an amount of $10, EXCEPT in those cases:
On specific product pages that are bookable products for hire (Woocommerce Bookings) that belongs to a certain Category "C".
If any cart item belongs to that category "C".
I was using this answer code to one of my questions until now. But I should need to display this specific product altered price everywhere except on that product pages that are bookable products, in front end.
I have also this code that displays the Force Sells price beside the titles. This should still show the specific product original price on that product pages that are bookable products in the Force Sells section:
function wc_forcesells_product_price( $name, $product ) {
// Only in single product pages
if( ! is_product() ) return $name;
// The product name + the product formatted price
return $name . ' (' . wc_price( wc_get_price_to_display( $product ) ) . ')';
}
add_filter( 'woocommerce_product_title', 'wc_forcesells_product_price', 20, 2 );
Reference for above: Add price beside Woocommerce Force Sells.
Updated to handle multiple product Ids if necessary
Try the following that will display for a specific product ID a custom price everywhere except:
On booking products to hire pages
When a product category is in cart
The code will also change this custom product price in cart items except when a specific product category is in cart items.
You will have to set in those functions:
This specific product ID
The booking products IDs to hire
The Additional price amount
The product category (Id slug or name)
The code:
// Change product ID 87 price everywhere on front end EXCEPT:
// - On booking products to hire pages
// - When a product category is in cart
add_filter( 'woocommerce_get_price_html', 'product_id_87_displayed_price', 10, 2 );
function product_id_87_displayed_price( $price_html, $product ) {
// Only for product ID 87
if( in_array( $product->get_id(), array( 87, 2799 ) ) ){
return $price_html; // Exit
// HERE set booking products IDs to hire in the array
$product_ids_to_hire = array( 53, 738 );
// HERE set your product category term ID, slug or name
$product_category = 'hoodies';
// HERE set the price additional amount
$addp = 10;
// EXCEPT on booking products to hire pages
if( is_single($product_ids_to_hire) ) return $price_html; // Exit
// Checking for the product category in cart items loop
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( has_term( $product_category, 'product_cat', $cart_item['product_id'] ) )
return $price_html; // Product category found ==> Exit
}
if ( '' !== $product->get_price() && ! $product->is_on_sale() ) {
$price_html = wc_price( wc_get_price_to_display( $product ) + $addp ) . $product->get_price_suffix();
}
return $price_html;
}
// Add custom calculated price to cart item data for product ID 87
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_simple_product_custom_price', 20, 2 );
function add_cart_simple_product_custom_price( $cart_item_data, $product_id ){
// Only for product ID 87
if( ! in_array( $product->get_id(), array( 87 ) ) )
return $cart_item_data; // Exit
// HERE set the price additional amount
$addp = 10;
$product = wc_get_product($product_id); // The WC_Product Object
$price = (float) $product->get_price(); // The product price
// Set the custom amount in cart object
$cart_item_data['new_price'] = $price + $addp;
return $cart_item_data;
}
// Set custom calculated price to cart for product ID 87
add_action( 'woocommerce_before_calculate_totals', 'set_cart_simple_product_custom_price', 20, 1 );
function set_cart_simple_product_custom_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// HERE set your product category term ID, slug or name
$product_category = 'hoodies';
// 1st cart items Loop: checking for the product category
foreach ( $cart->get_cart() as $cart_item ) {
if ( has_term( $product_category, 'product_cat', $cart_item['product_id'] ) )
return; // Product category found ==> We EXIT from this function
}
// 2nd Loop: Changing cart item prices (Product category not found)
foreach ( $cart->get_cart() as $cart_item ) {
if( isset($cart_item['new_price']))
$cart_item['data']->set_price( $cart_item['new_price'] );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
It should also work with your wc_forcesells_product_price() function.
I amusing WooCommerce and with WooCommerce Bookings plugin. In their documentation there's this solution for booking multiple items by using the "persons" quantity.
So what the Bookings plugins does, it ads meta data to the variations, it also includes "persons" what we're currently using as an indicator for the amount of items being booked.
What we want to next is retrieve the meta data [Persons] from the product variations and display this as the item quantity.
What I've done so far is edit the woocommerce cart template and copied it to our child theme. (we can't use hooks since there's no hook available to use in the shop table).
</td>
<!-- Use [_persons] as product quantity -->
<td class="product-quantity--booking">
<?php
if ( ! empty( $cart_item['booking'] ) ) {
echo WC()->cart->get_item_data( $cart_item );
}
?>
</td>
What I need to figure out is, how do i filter these variations and only show the meta data I need? I would only need to show meta data "persons".
The next thing would be to filter only the start date and end date to display on the other meta data column.
[EDIT 18/02]
So I've found out that I can't use "get_item_date" since it does not take any arguments to show a specific value.
I tried to use "wc_display_item_meta", but I don't know how to select the item I need.
<!-- Use ['Persons'] as product quantity -->
<td class="product-quantity--booking">
<?php
if ( ! empty( $cart_item['booking'] ) ) {
$item_meta = wc_display_item_meta( $cart_item['booking']['Persons'])
echo $item_meta;
}
?>
</td>
By using the vardump I found the key I need is "['booking']['Persons']"
Maybe someone could help me to point out how to filter the array for needed items?
The wc_display_item_meta() function is not to be used in Cart or with cart items. It's explicitly for WC_Order_Item where $item argument is an Order Item.
You will see in the source code:
/**
* Display item meta data.
*
* #since 3.0.0
* #param WC_Order_Item $item Order Item.
* #param array $args Arguments.
* #return string|void
*/
function wc_display_item_meta( $item, $args = array() ) { /* … … */ }
So this function will not work in your case.
How to do it (2ways):
1) You can use two custom hooked functions, that will remove all booking data except 'Persons' and that will remove the quantity for your bookable products only.
// Display only 'Persons' for bookable products
add_filter( 'woocommerce_get_item_data', 'display_only_persons_for_bookings', 20, 2 );
function display_only_persons_for_bookings( $cart_data, $cart_item ){
// Check that is a bookable product and that is Cart page
if( ! isset($cart_item['booking']) || ! is_cart() ) return $cart_data; // Exit
// Loop through this cart item data
foreach($cart_data as $key =>$values){
// Checking that there is some data and that is Cart page
if( ! isset($values['name']) || ! is_cart() ) return $cart_data; // Exit
// Removing ALL displayed data except "Persons"
if( $values['name'] != __('Persons','woocommerce') ){
unset($cart_data[$key]);
}
}
return $cart_data;
}
// Remove cart quantity for bookable products
add_filter( 'woocommerce_cart_item_quantity', 'remove_cart_quantity_for_bookings', 20, 3 );
function remove_cart_quantity_for_bookings( $product_quantity, $cart_item_key, $cart_item ){
// Check that is a bookable product
if( isset($cart_item['booking']) )
$product_quantity = '';
return $product_quantity;
}
This code goes on function.php file of your active child theme (or theme).
Tested and works.
2) Or you can remove all booking displayed meta data and display the "Persons" in the quantity field:
// Remove displayed cart item meta data for bookable products
add_filter( 'woocommerce_get_item_data', 'remove_cart_data_for_bookings', 20, 2 );
function remove_cart_data_for_bookings( $cart_data, $cart_item ){
// Check that is a bookable product and that is Cart page
if( ! isset($cart_item['booking']) || ! is_cart() ) return $cart_data; // Exit
// Loop through this cart item data
foreach($cart_data as $key =>$values){
// Removing ALL displayed data
unset($cart_data[$key]);
}
return $cart_data;
}
// Add "Persons" to replace cart quantity for bookable products
add_filter( 'woocommerce_cart_item_quantity', 'replace_cart_quantity_for_bookings', 20, 3 );
function replace_cart_quantity_for_bookings( $product_quantity, $cart_item_key, $cart_item ){
// Check that is a bookable product
if( isset($cart_item['booking']) ){
$product_quantity = '<span style="text-align: center; display:inline-block;">'.$cart_item['booking']['Persons'].'<br>
<small>(' . __('persons','woocommerce') . ')</small><span>';
}
return $product_quantity;
}
This code goes on function.php file of your active child theme (or theme).
Tested and works
If you manage only booking products, you could rename "Quantity" column label to "Persons" in the template cart/cart.php (overriding it via your active theme).