Updating product stock programmatically in Woocommerce 3
Update 2
Since woocommerce 3 "outofstock" product status is saved in 2 locations:
- As post meta data for
_stock_status
meta key (just as before).- As a post term name
outofstock
remaining toproduct_visibility
custom taxonomy
That means that you missed just a step (the step 3):
$out_of_stock_staus = 'outofstock';
// 1. Updating the stock quantity
update_post_meta($product_id, '_stock', 0);
// 2. Updating the stock quantity
update_post_meta( $product_id, '_stock_status', wc_clean( $out_of_stock_staus ) );
// 3. Updating post term relationship
wp_set_post_terms( $product_id, 'outofstock', 'product_visibility', true );
// And finally (optionally if needed)
wc_delete_product_transients( $product_id ); // Clear/refresh the variation cache
It hope that it will work with your cron job.
Original answer
Your code is a bit outdated since woocommerce 3 and there is no specifically a stock status setting for product variations...
There is a dedicated function in woocommerce to get the product Id from the sku that you could use:
wc_get_product_id_by_sku( $product_sku );
For the parent variable product, you should not need to enabled stock management as this is done in each of its product variations (so at the product variation level).
Since woocommerce 3, the "outofstock" stock status is also managed thought a custom taxonomy
product_visibility
which term name isoutofstock
. So updating post meta is not enough.
Also is better to use the new CRUD setters and getters methods introduced with woocommerce 3.
So try the following code instead:
// get the product ID from the SKU
$product_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $sku ) );
// Get an instance of the WC_Product object
$product = new WC_Product( $product_id );
// Get product stock quantity and stock status
$stock_quantity = $product->get_stock_quantity();
$stock_status = $product->get_stock_status();
// Display stock quantity and status
echo '<p>Product Stock quantity: ' . $stock_quantity . '</br>
Product Stock status: ' . $stock_status . '</p></br>';
// Set product stock quantity (zero) and stock status (out of stock)
$product->set_stock_quantity();
$product->set_stock_status('outofstock');
// Save the data and refresh caches
$product->save();
Tested and works in a normal context (but apparently not with a cron job)
Update product stock quantity and status in WooCommerce 3
The WC_Product Object is already included as 2nd argument for woocommerce_update_product
Hook… So you can change a little bit your code like:
But as this hook is located on WC_Product_Data_Store_CPT
Class, It doesn't really like the WC_Product
setter methods and especially save()
method.
So instead we will replace this hook with the following:
add_action( 'woocommerce_admin_process_product_object', 'set_no_stock_if_discontinued' );
function set_no_stock_if_discontinued( $product ) {
if( $product->get_meta('_is_discontinued') === 'yes'
|| ( isset($_POST['_is_discontinued'])
&& esc_attr($_POST['_is_discontinued']) === 'yes' ) ) {
// Using WC setters
$product->set_manage_stock(true);
$product->set_stock_quantity(0);
$product->set_stock_status('outofstock');
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Note: The
save()
method is not needed as it's triggered just after this hook.
Global note: You can also set "manage stock" to false.
Update product stock amount and stock status when WooCommerce order status is completed
Although your code works, you are missing some checks/points:
- Your update functions takes place for every product, while this actually only applies to variations that are outofstock
- wc_update_product_stock() - Update a product's stock amount
- wc_update_product_stock_status() - Update a product's stock status
So you get:
function action_woocommerce_order_status_completed( $order_id ) {
// The WC_Order instance Object
$order = wc_get_order( $order_id );
// Is a order
if ( is_a( $order, 'WC_Order' ) ) {
// Loop through order items
foreach ( $order->get_items() as $key => $item ) {
// The WC_Product instance Object
$product = $item->get_product();
// Product type
if ( $product->get_type() == 'variation' ) {
// NOT in stock
if ( ! $product->is_in_stock() ) {
// Update a product's stock amount
wc_update_product_stock( $product, 1, 'set', 'false' );
// Update a product's stock status
wc_update_product_stock_status( $product->get_id(), 'instock' );
}
}
}
}
}
add_action( 'woocommerce_order_status_completed', 'action_woocommerce_order_status_completed', 10, 1 );
About update product stock status function in WooCommerce 3
If you look to the source code in wc_update_product_stock_status()
function:
/**
* Update a product's stock status.
*
* @param int $product_id
* @param int $status
*/
function wc_update_product_stock_status( $product_id, $status ) {
$product = wc_get_product( $product_id );
if ( $product ) {
$product->set_stock_status( $status );
$product->save();
}
}
It uses the WC_Product
set_stock_status()
Woocommerce 3 CRUD method which uses strings But not integers values:
/**
* Set stock status.
*
* @param string $status New status.
*/
public function set_stock_status( $status = 'instock' ) {
$valid_statuses = wc_get_product_stock_status_options();
if ( isset( $valid_statuses[ $status ] ) ) {
$this->set_prop( 'stock_status', $status );
} else {
$this->set_prop( 'stock_status', 'instock' );
}
}
So it's an error in the comment usage in
wc_update_product_stock_status()
function.
It still uses: 'instock'
and 'outofstock'
status strings. the default value is 'instock'
…
The main difference is also that stock status is now handled as
outofstock
term for the custom taxonomyproduct_visibility
Before Woocommerce 3, stock status was handled as product meta data.
How to directly update WooCommerce product stock quantity by its sku
$wpdb
and JOIN
works too slow, so the fastest way is to use four separate prepared queries:
$conn = mysqli_init();
mysqli_real_connect( $conn, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME );
// Get product ID by SKU
$stmt1 = $conn->prepare( "SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_sku' AND meta_value = ?");
// Update stock level on postmeta table
$stmt2 = $conn->prepare( "UPDATE {$wpdb->prefix}postmeta SET meta_value = ? WHERE meta_key = '_stock' AND post_id = ?");
// Update stock data on wc_product_meta_lookup table
$stmt3 = $conn->prepare( "UPDATE {$wpdb->prefix}wc_product_meta_lookup SET stock_quantity = ?, stock_status = ? WHERE product_id = ?");
// Update stock status on postmeta table
$stmt4 = $conn->prepare( "UPDATE {$wpdb->prefix}postmeta SET meta_value = ? WHERE meta_key = '_stock_status' AND post_id = ?");
foreach ( $products as $product ) {
$qty = $product['ON_HAND'];
$sku = $product['PRODUCT_SKU'];
$status = $qty ? 'instock' : 'onbackorder';
// Get product ID by SKU
$stmt1->bind_param( "s", $sku );
$stmt1->execute();
$res = $stmt1->get_result();
while ( $row = $res->fetch_assoc() ) {
$id = $row['post_id'];
// Update stock level on postmeta table
$stmt2->bind_param( "dd", $qty, $id );
$stmt2->execute();
// Update stock data on wc_product_meta_lookup table
$stmt3->bind_param( "dsd", $qty, $status, $id );
$stmt3->execute();
// Update stock status on postmeta table
$stmt4->bind_param( "sd", $status, $id );
$stmt4->execute();
}
}
Increase/adjust product stock quantity for custom order status in WooCommerce
To add a custom order status I rewritten your code because:
- The
init
hook has been replaced withwoocommerce_register_shop_order_post_statuses
to register custom order statuses. wc_order_statuses
is added to show the order status in the dropdown @ single orderbulk_actions-edit-shop_order
is added to show order status in the dropdown @ bulk actions
// Register order status
function filter_woocommerce_register_shop_order_post_statuses( $order_statuses ) {
// Status must start with "wc-"
$order_statuses['wc-cancelled-by-us'] = array(
'label' => _x( 'Cancelled by admin', 'Order status', 'woocommerce' ),
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
/* translators: %s: number of orders */
'label_count' => _n_noop( 'Cancelled by admin <span class="count">(%s)</span>', 'Cancelled by admin <span class="count">(%s)</span>', 'woocommerce' ),
);
return $order_statuses;
}
add_filter( 'woocommerce_register_shop_order_post_statuses', 'filter_woocommerce_register_shop_order_post_statuses', 10, 1 );
// Show order status in the dropdown @ single order
function filter_wc_order_statuses( $order_statuses ) {
$new_order_statuses = array();
// Add new order status after cancel
foreach ( $order_statuses as $key => $status ) {
$new_order_statuses[ $key ] = $status;
if ( 'wc-cancelled' === $key ) {
// Status must start with "wc-"
$new_order_statuses['wc-cancelled-by-us'] = _x( 'Cancelled by admin', 'Order status', 'woocommerce' );
}
}
return $new_order_statuses;
}
add_filter( 'wc_order_statuses', 'filter_wc_order_statuses', 10, 1 );
// Show order status in the dropdown @ bulk actions
function filter_bulk_actions_edit_shop_order( $bulk_actions ) {
// Note: "mark_" must be there instead of "wc"
$bulk_actions['mark_cancelled-by-us'] = __( 'Cancelled by admin', 'woocommerce' );
return $bulk_actions;
}
add_filter( 'bulk_actions-edit-shop_order', 'filter_bulk_actions_edit_shop_order', 10, 1 );
To answer your question, it is best to look at which functionality is applied for the existing 'cancel'
order status and then reproduce it.
In wc-stock-functions.php we can see that the function wc_maybe_increase_stock_levels()
is called for both the 'cancel'
status and the 'pending'
status
To reproduce it we can use the woocommerce_order_status_' . $status_transition['to']
composite hook that can be used with any custom order status.
So you get:
// Maybe increase stock levels
add_action( 'woocommerce_order_status_cancelled-by-us', 'wc_maybe_increase_stock_levels' );
Related Topics
Send Data from Android to Server via JSON
Phpmailer - the Following Smtp Error: Data Not Accepted
PHP Unable to Load PHP_Curl.Dll Extension
PHP Short Circuit Lazy Evaluation, Where Is It in the PHP.Net Manual
Overwrite Line in File with PHP
PHP Sample Script for Pagination
PHP Ltrim Behavior with Character List
Url Rewriting-Code for Rewriting
Accessing Files Relative to Bundle in Symfony2
Why Should I Use Bitwise/Bitmask in PHP
Remove Trailing Slash from String PHP
Difference Between Mt_Rand() and Rand()
How to Get the Value of a Private Property with Reflection
Output Text File with Line Breaks in PHP
PHP Datetime Round Up to Nearest 10 Minutes
Enabling 'Strict_Types' Globally in PHP 7
Call to Undefined Function Odbc_Connect() Message While Connecting Sap Hana Database