I set up a minimum quantity for a product category in WooCommerce. In my case I set the minimum quantity to 5 items. The code I use works fine but I would like to add two error messages for the customer:
1) If the customer tries to change the the quantity to less than the minimum by clicking the "-" symbol I would like to have something like: "The minimum quantity of this product is 5, please add at least 5 items to the basket"
2) If the customer clicks the "add to the basket" button I would like to have something like this: "There is a minimum quantity of 5 items for this product. Please check your basket"
Is there some code I can add to my actual code?
add_filter( 'woocommerce_quantity_input_args', 'min_qty_filter_callback', 20, 2 );
function min_qty_filter_callback( $args, $product ) {
$category = 'Noten'; // The targeted product category
$min_qty = 5; // The minimum product quantity
$product_id = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();
if( has_term( $category, 'product_cat', $product_id ) ){
$args['min_value'] = $min_qty;
}
return $args;
}
For add to cart validation add the follows code snippet to validate your item quantity -
function add_to_cart_validation( $flag, $product_id, $qunatity ) {
if( $qunatity < 5 ) { // if quantity less than 5
wc_add_notice( __( 'There is a minimum quantity of 5 items for this product. Please check your basket', 'text-domain' ), 'error' );
$flag = false;
}
return $flag;
}
add_filter( 'woocommerce_add_to_cart_validation', 'add_to_cart_validation', 10, 3 );
Related
everybody !
I'm looking for a way to no decrement when an order is completed.
Exemple: I got 1 T-shirt with 20 quantity. When i order, i want the number to be still 20, and not 19.)
here my hook:
add_action( 'woocommerce_order_status_completed', 'action_on_order_completed' , 10, 1 );
function action_on_order_completed( $order_id )
{
// Get an instance of the order object
$order = wc_get_order( $order_id );
// Iterating though each order items
foreach ( $order->get_items() as $item_id => $item_values ) {
// Item quantity
$item_qty = $item_values['qty'];
// getting the product ID (Simple and variable products)
$product_id = $item_values['variation_id'];
if( $product_id == 0 || empty($product_id) ) $product_id = $item_values['product_id'];
// Get an instance of the product object
$product = wc_get_product( $product_id );
// Get the stock quantity of the product
$product_stock = $product->get_stock_quantity();
// Increase back the stock quantity
wc_update_product_stock( $product, $item_qty, 'increase' );
}
}
But when the order is completed, the quantity still decrease. Any idea ?
That is because your hooked function is triggered after stock quantity is decreased.
You are trying to get previous stock quantity with
$product_stock = $product->get_stock_quantity();
But in that case this method returns already decreased value.
So, try this:
wc_update_product_stock( $product, $product_stock + $item_qty, 'increase' );
I'm building a website where i'll sell some fine art prints and i would like to set an automated discount when a customer buy the first print of a serie. Each of my prints are limited to 15 pieces and i would like to set a 30% discount for the first sell (#1/15) of each series. Then, for the next prints (#2 to #15), price goes back to normal.
Edit :
I did progress on my problem ! I did some research and tried a lot of different things and i found how to set the custom price i wanted on my product page (regular price + discount price) with the rule to only apply it if the related item available stock is 15.
regular price + discount price
And here's the code is used :
// Generating the product "regular price"
add_filter( 'woocommerce_product_get_regular_price', 'dynamic_regular_price', 10, 2 );
add_filter( 'woocommerce_product_variation_get_regular_price', 'dynamic_regular_price', 10, 2 );
function dynamic_regular_price( $regular_price, $product ) {
$stock_qte = $product->get_stock_quantity();
if( empty($regular_price) || $regular_price == 0 )
return $product->get_price();
else
return $regular_price;
}
// Generating the product "sale price"
add_filter( 'woocommerce_product_get_sale_price', 'dynamic_sale_price', 10, 2 );
add_filter( 'woocommerce_product_variation_get_sale_price', 'dynamic_sale_price', 10, 2 );
function dynamic_sale_price( $sale_price, $product ) {
$stock_qte = $product->get_stock_quantity();
if( $stock_qte == '15')
return $product-> get_regular_price() * 0.7;
else
//return $product->get_regular_price();
return $regular_price;
};
// Displayed formatted regular price + sale price
add_filter( 'woocommerce_get_price_html', 'dynamic_sale_price_html', 20, 2 );
function dynamic_sale_price_html( $price_html, $product ) {
$stock_qte = $product->get_stock_quantity();
if( $stock_qte == '15')
$price_html = wc_format_sale_price( wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) ), wc_get_price_to_display( $product, array( 'price' => $product->get_sale_price() ) ) ) . $product->get_price_suffix();
else
$price_html = wc_price(wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) ). $product->get_price_suffix());
return $price_html;
}
It's almost working but the code related to the cart values updates is not fully working. The cart sub-total is good and take count of the discount but the individual price of each product is not updated and shown as without discount. It's the same on the cart page.
cart (from mini cart)
cart (cart page)
add_action( 'woocommerce_before_calculate_totals', 'alter_price_cart', 9999 );
function alter_price_cart( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;
// IF CUSTOMER NOT LOGGED IN, DONT APPLY DISCOUNT
//if ( ! wc_current_user_has_role( 'customer' ) ) return;
// LOOP THROUGH CART ITEMS & APPLY 30% DISCOUNT
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
$price = $product->get_price();
$cart_item['data']->set_price( $price * 0.7 );
}
}
I'll also have to add the stock availability logic for the mini-cart / cart / checkout page but i'll do that in a second time.
I must have made some mistakes in the loop throught the cart but i cannot catch the issue :( Any idea about how to hook the price of each product and change the displayed value ?
Have a nice day,
Quentin
I want to split cart items of same product in individual lines. When I increase quantity on single product page and add to cart, it shows as separate cart items.
Im using WooCommerce - Treat cart items separate if quantity is more than 1 answer code.
I want when quantity is updated on cart page and clicked on 'Update cart', then items must split on individual lines.
How can I do that?
The code below will split items on individual lines when quantity is updated on the cart page.
Comment with explanation added to the code.
function on_action_cart_updated( $cart_updated ) {
if ( $cart_updated ) {
// Get cart
$cart_items = WC()->cart->get_cart();
foreach ( $cart_items as $cart_item_key => $cart_item ) {
$quantity = $cart_item['quantity'];
// If product has more than 1 quantity
if ( $quantity > 1 ) {
// Keep the product but set its quantity to 1
WC()->cart->set_quantity( $cart_item_key, 1 );
// Run a loop 1 less than the total quantity
for ( $j = 1; $j <= $quantity -1; $j++ ) {
// Set a unique key.
$cart_item['unique_key'] = md5( microtime() . rand() . "Hi Mom!" );
// Get vars
$product_id = $cart_item['product_id'];
$variation_id = $cart_item['variation_id'];
$variation = $cart_item['variation'];
// Add the product as a new line item with the same variations that were passed
WC()->cart->add_to_cart( $product_id, 1, $variation_id, $variation, $cart_item );
}
}
}
}
}
add_action( 'woocommerce_update_cart_action_cart_updated', 'on_action_cart_updated', 20, 1 );
How can I set the maximum weight per product (not per order)?
The customer can buy any number (quantity) of products until the max weight is reached. If he buys 5 products, The weight of total unit of each product cant reach max weight. There can be many products in an order but Total units(quantity) per product has Max weight.
For example, In a single order:
3 units of product A (weighing total 2kg)
5 units of product B (weighing total 1.4kg)
3 units of product C (weighing total 2kg)
In this example, the hooked function is triggered on add to cart action and you can perform all kind of checks to validate or not the action (and display a custom error message).
So you will see that you can target the total item weight individually…
add_filter( 'woocommerce_add_to_cart_validation', 'custom_add_to_cart_validation', 20, 5 );
function custom_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = '', $variations = '' ) {
// HERE define the weight limit per item
$weight_limit = 2; // 2kg
$total_item_weight = 0;
// Check cart items
foreach( WC()->cart->get_cart() as $cart_item ) {
$item_product_id = empty($variation_id) ? $product_id : $variation_id;
// If the product is already in cart
if( $item_product_id == $cart_item['data']->get_id() ){
// Get total cart item weight
$total_item_weight += $cart_item['data']->get_weight() * $cart_item['quantity'];
}
}
// Get an instance of the WC_Product object
$product = empty($variation_id) ? wc_get_product($product_id) : wc_get_product($variation_id);
// Get total item weight
$total_item_weight += $product->get_weight() * $quantity;
if( $total_item_weight > $weight_limit ){
$passed = false ;
$message = __( "Custom warning message for weight exceed", "woocommerce" );
wc_add_notice( $message, 'error' );
}
return $passed;
}
You will need also an additional hooked function that will be triggered on cart item quantity change:
add_filter( 'woocommerce_after_cart_item_quantity_update', 'limit_cart_item_quantity', 20, 4 );
function limit_cart_item_quantity( $cart_item_key, $new_quantity, $old_quantity, $cart ){
// HERE define the weight limit per item
$weight_limit = 2; // 2kg
// Get an instance of the WC_Product object
$product = $cart->cart_contents[ $cart_item_key ]['data'];
$product_weight = $product->get_weight(); // The product weight
// Calculate the limit allowed max quantity from allowed weight limit
$max_quantity = floor( $weight_limit / $product_weight );
// If the new quantity exceed the weight limit
if( ( $new_quantity * $product_weight ) > $weight_limit ){
// Change the quantity to the limit allowed max quantity
$cart->cart_contents[ $cart_item_key ]['quantity'] = $max_quantity;
// Add a custom notice
$message = __( "Custom warning message for weight exceed", "woocommerce" );
wc_add_notice( $message, 'notice' );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I would like to create a product in WooCommerce that is sold in units of gram.
The customer would enter the number of grams they want (in an input field) on the product page, and the price would be computed on the fly and added to the cart.
My question is: is this possible, and if so, can someone give me just a "big picture" idea of how I would implement it?
I don't need line-by-line code, just hoping someone with more knowledge of the structure of Woo can guide me on how to best attack the problem.
I already have parts of it worked out:
I can decide that the price entered for the product is the price per
100 grams, so that is how the seller will enter the price.
Then I can
write a little bit of Javascript to compute the price on the fly and
display it on the page as the user types the amount they want. No
problem.
But... I think every discrete product in Woo needs to have its own price.. So for example, if a customer wants 123g of a product, it seems like I might have to create a variation on the fly for that specific price/amount, and then add that to the cart. Which (judging by this) looks non-trivial and a little hacky. Is there a better way to do this?
WooCommerce has an option to show the weights as grams.
The following code will display the KG weights as grams on the WooCommerce templates :
// Convert the product weight
function ag_woocommerce_product_get_weight( $weight ) {
// Only convert if we have a weight
if ($weight) {
// The weight is in KGS, and we want grams, to multiple by 1000
$weight = $weight * 1000;
}
return $weight;
};
// add the filter
add_filter( 'woocommerce_product_get_weight', 'ag_woocommerce_product_get_weight', 10, 1 );
Hope this might help. Cheers!
There is a free plugin for WooCommerce that allows you to input a unit of measure (UOM) for each product:
https://wordpress.org/plugins/woocommerce-unit-of-measure/
I found this plugin that does pretty much exactly what I need-- https://woocommerce.com/products/measurement-price-calculator/
It's easier and quicker to give you that real example, than explain step by step… You will see which hooks are used for all steps or tasks.
You dont need variable products or generate a variation on the fly.
You just need to set on each simple product the price for one gram (or any other base). Now in this code, you can target those products with:
an array of product Ids
or by product categories (or even product tags).
Your concern is about the way to pass the data in the cart, to update the final price for each product and display the chosen grams amount in cart, checkout and in the order.
So in each product you will only set the price by gram… (or you can also make changes in the code and set the product price for 100 grs or even any other base).
The code:
// Add a product custom field "grams_quantity" that will update the displayed price
add_action('woocommerce_before_add_to_cart_button', 'special_product_by_grams', 25);
function special_product_by_grams(){
global $product;
// HERE Define the special product IDs sold by grams
$targeted_product_ids = array(37);
// or HERE Define a product categories (ids, slugs or names)
$categories = array('sold-by-gram');
// Only for products sold by gram
$product_id = $product->get_id();
if ( ! ( in_array( $product_id, $targeted_product_ids ) || has_term( $categories, 'product_cat', $product_id ) ) ) return;
?>
<div class="grams-field">
<label for="grams_quantity"><?php _e('Grams: ','woocoomerce'); ?><span></span><br>
<input type="number" step="1" name="grams_quantity" class="grams_quantity" id="grams_quantity" value="1">
</label>
</div><br>
<script type="text/javascript">
(function($){
// variables initialization
var priceByGram = <?php echo wc_get_price_to_display( $product ); ?>,
currencySymbol = $(".woocommerce-Price-currencySymbol").html(),
updatedPrice;
// On live event: imput number fields
$('input#grams_quantity').on( "click blur", function(){
updatedPrice = ($(this).val() * priceByGram).toFixed(2);
$(".woocommerce-Price-amount.amount").html('<span class="woocommerce-Price-amount amount">'+updatedPrice+' '+currencySymbol+'</span>');
console.log("event"); // <== To be removed
});
})(jQuery);
</script>
<?php
}
// Save the "grams_quantity" custom product field data in Cart item
add_filter( 'woocommerce_add_cart_item_data', 'save_in_cart_the_custom_product_field', 10, 2 );
function save_in_cart_the_custom_product_field( $cart_item_data, $product_id ) {
if( isset( $_POST['grams_quantity'] ) ) {
$cart_item_data[ 'grams_quantity' ] = $_POST['grams_quantity'];
// When add to cart action make an unique line item
$cart_item_data['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $_POST['grams_quantity'] );
}
return $cart_item_data;
}
// Update product price by grams in cart and checkout
add_filter( 'woocommerce_before_calculate_totals', 'update_prices_by_gram', 10, 1 );
function update_prices_by_gram( $cart_object ) {
// HERE Define the special product IDs sold by grams
$targeted_product_ids = array(37);
// or HERE Define a product categories (ids, slugs or names)
$categories = array('sold-by-gram');
foreach ( $cart_object->get_cart() as $cart_item ) {
// Only for products sold by gram
$product_id = $cart_item['product_id'];
if ( in_array( $product_id, $targeted_product_ids ) || has_term( $categories, 'product_cat', $product_id ) ){
// Get an instance of the WC_Product object and the
$product = $cart_item['data'];
$grams = $cart_item['grams_quantity'];
// Method is_on_sale() manage everything (dates…)
$product->set_price( $product->get_price() * $grams);
}
}
}
// Render "grams_quantity" the custom product field in cart and checkout
add_filter( 'woocommerce_get_item_data', 'render_product_custom_field_meta_on_cart_and_checkout', 10, 2 );
function render_product_custom_field_meta_on_cart_and_checkout( $cart_data, $cart_item ) {
$custom_items = array();
if( !empty( $cart_data ) )
$custom_items = $cart_data;
if( isset( $cart_item['grams_quantity'] ) )
$custom_items[] = array(
'name' => __( 'Grams', 'woocommerce' ),
'value' => sanitize_text_field( $cart_item['grams_quantity'] ),
'display' => sanitize_text_field( $cart_item['grams_quantity'] ),
);
return $custom_items;
}
// Save "grams_quantity" to the order items meta data
add_action('woocommerce_add_order_item_meta','add_product_custom_fiel_to_order_item_meta', 1, 3 );
function add_product_custom_fiel_to_order_item_meta( $item_id, $item_values, $item_key ) {
if( isset( $item_values['grams_quantity'] ) )
wc_update_order_item_meta( $item_id, 'Grams', sanitize_text_field( $item_values['grams_quantity'] ) );
}
Code goes in function.php file of your active child theme (or active theme) or in any plugin file.
Tested and works.