Product get out of stock after order on Woocommerce - wordpress

I have a really weird behavior on Woocommerce with the stock management. On a product with stock management enabled, a stock of 100 items and stock status "available", every time I do an order the product stock go to negative and get out of stock.
For example, if I do an order of 2 items of the product, the stock go to -2 right after the order, even if the stock was at 100 right before.
The product is a simple one, without any attribute. I use the following hooks to alter some label and stuffs, but none seems related to this issue:
add_filter('woocommerce_product_single_add_to_cart_text', array(&$this->wc, 'add_to_cart_text'), 11);
add_filter('woocommerce_add_to_cart', array(&$this->wc, 'add_to_cart'), 10, 1);
add_action('woocommerce_cart_item_removed', array(&$this->wc, 'cart_item_removed'), 10, 1);
add_action('woocommerce_order_status_completed', array(&$this->wc, 'order_status_completed'), 10, 1);
add_action('woocommerce_after_shop_loop_item', array(&$this->wc, 'replace_add_to_cart'));
In short, woocommerce_product_single_add_to_cart_text change the add to cart button label, woocommerce_add_to_cart place some vars in session, woocommerce_cart_item_removed remove those session var on item removal from cart, woocommerce_order_status_completed do some stuffs with the session vars (update a CPT from those session vars - I don't touch the order or the product at all) and woocommerce_after_shop_loop_item display a button on product listing. I tried to disable the woocommerce_order_status_completed hook, it didn't change anything.
I will paste any code of those function if any of you think some could be related to this stock issue.
I'm using latest version of Woocommerce and Wordpress.

I found out the culprit, as helgatheviking suggested I disabled all the plugins one by one and found out that the plugin Progression One Click Import provided by the theme and marked as "recommended for theme use" was doing this.
My guess is that it is related to this filter in the plugin code:
add_filter( 'add_post_metadata', array( $this, 'check_previous_meta' ), 10, 5 );
Which is doing this:
public function check_previous_meta( $continue, $post_id, $meta_key, $meta_value, $unique ) {
$old_value = get_metadata( 'post', $post_id, $meta_key );
if ( count( $old_value ) == 1 ) {
if ( $old_value[0] === $meta_value ) {
return false;
} elseif ( $old_value[0] !== $meta_value ) {
update_post_meta( $post_id, $meta_key, $meta_value );
return false;
}
}
}
The flaw in this is that it is inserting the stock meta value raw (-2) instead of decrementing the existing meta value, which Woocommerce seems to do with some filter on his end - a behavior that is overwrited by this filter.
I guess this could be fixed by changing the filter priority but just disabling the plugin was good for me as I don't need to import preview data.

Related

WooCommerce function that disables an admin user to reduce the product stock quantity

I would like to add a function that is triggered every time that the stock quantity of a product will be changed in the admin product page, such that this function will not allow any reduce of the stock value - but only increase.
This is to prevent an admin user to reduce the stock quantity of the products.
Of course, this function should not be triggered if a product will be in an order, since then of course I would like the stock quantity to be reduced.
I tried the following function in the functions.php but unfortunately did not work.
Since I'm new to woocommerce and php, any ideas that could provide a solid solution to the problem?
// get old and new product stock quantity
function get_old_and_new_product_quantity_stock( $sql, $product_id_with_stock, $new_stock, $operation ) {
$product = wc_get_product( $product_id_with_stock );
$old_stock_quantity = $product->get_stock_quantity();
$new_stock_quantity = $new_stock;
echo $old_stock_quantity, $new_stock_quantity;
if ($new_stock_quantity < $old_stock_quantity) {
$new_stock = $old_stock_quantity;
$new_stock_quantity = $old_stock_quantity;
}
return $sql;
}
add_filter( 'woocommerce_update_product_stock_query', 'get_old_and_new_product_quantity_stock', 10, 4 );
You can use the update_post_meta action hook to check if the new value is less than the previous value and display error message.
This will work for quick edit and for product edit page. But the wp_die on product page will look bad so use the javascript to prevent submitting on product edit page (there was another question about it yesterday)
Be sure to test this snippet and create some orders that will reduce the stock automatically. I added is_admin() check but please do a good test.
add_action( 'update_post_meta', 'prevent_reducing_stock_metadata', 10, 4 );
function prevent_reducing_stock_metadata( $meta_id, $post_id, $meta_key, $meta_value ) {
// Check if the meta key is _stock and the new value is less than the previous value
if ( '_stock' == $meta_key && $meta_value < get_post_meta( $post_id, '_stock', true ) ) {
// Check if this is an update from the WordPress admin area
if ( is_admin() ) {
wp_die( __( 'Error: You cannot reduce the stock level for this product.' ), 'error' );
}
}
}

Set Discounted Price for Related Product on Product Page

I am trying to set the price of all related products on the products page to 10% less = 0.9
The goal is to provide a discount of all related products on the product page but when viewed as a product, give the normal price.
Overall, the idea is to provide incentive that generates up-selling of related products.
I am asking for two things here. ONE: change the product price for related products on the product page (10% off) and TWO: carry that discounted price into the cart and checkout when the related discounted product is added to cart from the product page.
I almost got the first part down, but the code I'm trying to get working is giving me an error saying:
Warning: A non-numeric value encountered
My code so far:
add_filter( 'woocommerce_get_price_html', 'related_product_price_discount', 100, 2 );
function related_product_price_discount( $price, $product ) {
global $woocommerce_loop;
// make sure this is a related product on product page
if( is_product() && $woocommerce_loop['name'] == 'related' ){
$price = $price * 0.9;
}
// return related product price with discount
return apply_filters( 'woocommerce_get_price', $price );
}
The rule in StackOverFlow is one question at the time, so I will answer only your first question, which is related to your code issue...
Note that the hook woocommerce_get_price_html is related to the displayed formatted price.
To avoid the error "Warning: A non-numeric value encountered", you will use the following:
add_filter( 'woocommerce_get_price_html', 'related_product_price_discount', 100, 2 );
function related_product_price_discount( $price_html, $product ) {
global $woocommerce_loop;
// make sure this is a related product on product page
if( is_product() && $woocommerce_loop['name'] == 'related' ){
$price_html = wc_price( wc_get_price_to_display( $product ) * 0.9 );
}
// return related product displayed discounted formatted price
return $price_html;
}
Code goes in functions.php file of your active child theme (active theme). Tested and works.

How do I disable shipping in some of my woocommerce products

I have a WooCommerce online shop that offers shipping to most products. Some of the products are for local pickup. I've tried setting a class on shipping zones with cost equal to zero and assigning the class on the products. But so far, the checkout still displays the shipping cost. Is there any way where some products will not have a shipping cost?
If you are searching for plugin solution, try WooCommerce Conditional Shipping and Payments. By using this plugin, you could add restrictions on certain product or product categories.
You've might want to look into the woocommerce_package_rates filter, which allows you to filter the set of shipping options that are available to the customer. An example would be something like this:
<?php
// add this snippet to functions.php:
add_filter( 'woocommerce_package_rates', function ( $rates, $package ) {
// examine $package for products. this could be a whitelist of specific
// products that you wish to be treated in a special manner...
$special_ids = array( 1, 2, 3, 4, 5 );
$special_product_present = false;
foreach ( $package['contents'] as $line_item ) {
if ( in_array( $line_item['product_id'], $special_ids ) ) {
$special_product_present = true;
}
}
$rates = array_filter( $rates, function ( $r ) use ( $special_product_present ) {
// do some logic here to return true (for rates that you wish to be displayed), or false.
// example: only allow shipping methods that start with "local"
if ( $special_product_present ) {
return preg_match( '/^local/', strtolower( $r->label ) );
} else {
return true;
}
} );
return $rates;
}, 10, 2 );
This blog post here shows some variations on that idea using this hook, including how to customize the available rates based on shopping cart value, customer's country, number of items in the cart, etc. And here's the source code: https://github.com/woocommerce/woocommerce/blob/v2.2.3/includes/class-wc-shipping.php#L366

Adding extra info to order

I am programmatically adding a product to the cart. Besides this, somehow, I want to store some extra info (an array) to the order. When the client finishes the order, I want to access that info through some WordPress actions. I'll have to do this immediately after adding the product to the cart, because the info may change after that, if the user doesn't finish the order right away. Is there any way I can do it, without putting the database to work?
You should probably use the WooCommerce Cart Item Meta API and the WooCommerce Order Item Meta API.
You use them like this:
// Add to cart item
// This is triggered on add to cart
add_filter('woocommerce_add_cart_item_data', 'my_add_cart_item_data', 10, 2);
function my_add_cart_item_data( $cart_item_meta, $product_id ) {
//Here we can easily filter what values should be added to what products using the $product_id
$cart_item_meta['my_meta_key'] = 'meta value';
return $cart_item_meta;
}
// Add to order item when the cart is converted to an order
// This is triggered when the order is created
add_action('woocommerce_add_order_item_meta', 'my_order_item_meta'), 10, 2);
function my_order_item_meta( $item_id, $values, $cart_item_key ) {
// The value stored in cart above is accessable in $values here
woocommerce_add_order_item_meta( $item_id, 'meta_key', $values['my_meta_key'] );
//Or add what ever you want
$meta_value = 'value';
woocommerce_add_order_item_meta( $item_id, 'meta_key', $meta_value );
}
I hope that helps.

Prevent WooCommerce brand from being sellable

Hi there we have an online store running on WooCommerce and using the WooCommerce brands plugin (http://docs.woothemes.com/document/wc-brands/) but there is one brand that we are allowed show online with price but not allowed to actually sell.
Is there a function I can add for this particular brand to functions.php that will change the add to cart button in category or widget layout to "more info" and link to the product and then on the product page instead of the add to cart section just have a text message saying to call the store.
You can filter WooCommerce's is_purchasable method. Any item that returns false will not be able to be purchased.
function so_26378581_purchasable( $purchasable, $product ){
if ( has_term( 'your-brand', 'product_brand', $product->id ) ){
$purchasable = false;
}
return $purchasable;
}
add_action( 'woocommerce_is_purchasable', 'so_26378581_purchasable', 10, 2 );
This uses conditional logic to test whether the $product in question has the your-brand term in the product_brand taxonomy... via the has_term() function.
By the way, this is not the type of functionality that belongs in functions.php. Your theme should only be concerned with display/appearances. I would recommend that you make this its own plugin, or add to to a site specific snippets plugin.
to me it made no error message, but it did not work, as applied in my test subject and stand continues with two products of different brands.
I put this code:
function so_26378581_purchasable( $purchasable, $product ){
if ( has_term( 'product_brand', 'product_brand', $product->id ) ){
$purchasable = false;
}
return $purchasable;
}
add_action( 'woocommerce_is_purchasable', 'so_26378581_purchasable', 10, 2 );

Resources