I have stock items with variations all of which have stock management; The problem is when I do sell a stock variation item the parent item stock count does not reduce !
Example:- I have a parent code which is a T-SHIRT and 3 variations S/M/L these variations have a combined stock value of 30 (10 each size) then I want my parent code to also show a stock value of 30; currently when i sell a stock variation item it only reduces the stock for that item and not the Parent item(sku).
Is there a plugin or code which will allow me to do this ?
I tested the code and it worked, but please make some more tests before make it live. Code goes to functions.php of your theme or child theme.
add_action( 'woocommerce_reduce_order_stock', 'lets_reduce_parent' );
function lets_reduce_parent( $order_id ) {
$order = wc_get_order( $order_id );
foreach( $order->get_items() as $item_id => $item ){
$parent_id = $item->get_product_id();
$child_id = $item->get_variation_id();
$quantity = $item->get_quantity();
if($child_id == 0) return;
else
{
$parent_product = wc_get_product( $parent_id );
$stock = $parent_product->get_stock_quantity();
$new_stock = $stock - $quantity;
wc_update_product_stock( $parent_product, $quantity, 'decrease' );
$order->add_order_note( "Parent Product ID: $parent_id - Stock Reduced: $stock -> $new_stock");
}
}
}
Ref-1: Woocommerce custom stock update after successful order
Ref-2: Get Order items and WC_Order_Item_Product in WooCommerce 3
I hope this will help. Please inform me if you have any problems. Have a good day.
Related
i want see attributes in the back end on the all products page.
and can it shows the quanity vs in stock out of stock?
many plugins tried so far none suitable
I don't know about any plugin for these kind of functionality. But, I've created this functionality. Let me share the details with you.
Add following snippets of code to your active child-theme's functions.php file.
Below displayed snippets will add new columns to All Products page's Product Table. i.e. Stock Quantity and Color. Here Color is my product's attribute.
add_filter( 'manage_edit-product_columns', 'admin_products_column', 9999 );
function admin_products_column( $columns ){
$columns['stock_quantity'] = 'Stock Quantity'; // Stock
$columns['color'] = 'Color'; // Attribute
return $columns;
}
Now we need to bring the data in these columns, So for the Stock Quantity use below mentioned code snippets.
add_action( 'manage_product_posts_custom_column', 'admin_products_stock_column_content', 10, 2 );
function admin_products_stock_column_content( $column, $product_id ){
if ( $column == 'stock_quantity' ) { // condition to check stock qty. column.
$product = wc_get_product( $product_id );
echo $product->get_stock_quantity();
}
}
To get data in color attribute column use below mentioned code snippets.
add_action( 'manage_product_posts_custom_column', 'admin_products_attribute_color_column_content', 10, 2 );
function admin_products_attribute_color_column_content( $column, $product_id ){
if ( $column == 'color' ) { // condition to check color attribute column
$product = wc_get_product( $product_id );
echo $product->get_attribute('pa_color'); // 'pa_color' is Slug of Color Attribute.
}
}
Final result will look like this image.
I already found this code here and it works for like 80/90% for me (see below).
This code adds 60 euro to my cart when there is a product from category ID 349 in the cart. When I add a product from that category to my cart when the cart is empty it works fine. But when there is already a product in my cart from a different category and then I add the product with category 349 it doesn't add the 60 euro extra fee. How is this possible?
function woo_add_cart_fee() {
$category_ID = '349';
global $woocommerce;
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
// Get the terms, i.e. category list using the ID of the product
$terms = get_the_terms( $values['product_id'], 'product_cat' );
// Because a product can have multiple categories, we need to iterate through the list of the products category for a match
foreach ($terms as $term) {
// 349 is the ID of the category for which we want to remove the payment gateway
if($term->term_id == $category_ID){
$excost = 60;
}
}
$woocommerce->cart->add_fee('Extra bezorgkosten kunstgras', $excost, $taxable = false, $tax_class = '');
}
}
add_action( 'woocommerce_cart_calculate_fees', 'woo_add_cart_fee' );
The code you are using is a bit outdated and you should use has_term() Wordpress conditional function to target a product category this way:
add_action( 'woocommerce_cart_calculate_fees','custom_pcat_fee', 20, 1 );
function custom_pcat_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Set HERE your categories (can be term IDs, slugs or names) in a coma separated array
$categories = array('349');
$fee_amount = 0;
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ){
if( has_term( $categories, 'product_cat', $cart_item['product_id']) )
$fee_amount = 60;
}
// Adding the fee
if ( $fee_amount > 0 ){
// Last argument is related to enable tax (true or false)
WC()->cart->add_fee( __( "Extra bezorgkosten kunstgras", "woocommerce" ), $fee_amount, false );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
A fee of 60 will always be added if there is in cart an item from 349 product category ID.
I found a solution like the following. Note that I kept adding a fee, because I started from a previous snippet. You can apply an existing discount or calculate a different total according to your needs.
In this case, I apply a negative fee - like a discount - according to a >= 300 total value of the cart, whose values would be -25% only for local_pickup shipping method, calculating taxes in addition.
function discount_to_cat(){
// thanks to LoicTheAztec's snippet
$cat_in_cart = false;
// Loop through all products in the Cart
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( has_term( 'category_to_pick', 'product_cat', $cart_item['product_id'] ) ) {
$cat_in_cart = true;
$tot_category_price = $cart_item['data']->get_price();
$tot_category_qty = $cart_item['quantity'];
$tot_category = $tot_category_price * $tot_category_qty;
break;
}
} global $product;
$total = WC()->cart->subtotal;
$discount_label = "";
if($total >= 300){
$discount_label=15;
}
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_shipping = explode(':',$chosen_methods[0]);
if($chosen_shipping[0]=='local_pickup'){
$discount_label=25;
}
$discount_applied = ($total-$tot_category)*$discount_label/100;
if($discount_label!=""){
$discount_applied_net = ($discount_applied/1.1); //1.1 according taxes for shipping
WC()->cart->add_fee( "Discount applied: ($discount_label%)", -$discount_applied_net, false );
}
}
add_action( 'woocommerce_cart_calculate_fees','discount_to_cat' );
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 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 );
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.