Updating Product Stock Programmatically in Woocommerce 3

Updating product stock programmatically in Woocommerce 3

Update 2

Since woocommerce 3 "outofstock" product status is saved in 2 locations:

  1. As post meta data for _stock_status meta key (just as before).
  2. As a post term name outofstock remaining to product_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 is outofstock. 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 taxonomy product_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 with woocommerce_register_shop_order_post_statuses to register custom order statuses.
  • wc_order_statuses is added to show the order status in the dropdown @ single order
  • bulk_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



Leave a reply



Submit