Search code examples
phpwordpresswoocommercecronproduct

Create a cron job to update Woocommerce products stock status based on custom fields


I'm trying to create a cron job that will compare a date set as product metadata to the current date, then update the product stock if the date set as product metadata has passed.

Here's what I have so far, but it doesn't work:

// Custom CRON Function
function update_pre_order_stock_status($post_id){

        $current_date = date( 'YYYY-mm-dd' );
        $expiryDate = $product->get_meta('woo_expiry_date');
        $woo_expiry_action = $product->get_meta('woo_expiry_action');
        $product_category = array( 'comic-book-pre-orders', 'dc-comics-pre-orders', 'image-comics-pre-orders', 'manga-pre-orders', 'marvel-comics-pre-orders', 'other-publisher-pre-orders');
    
        if(( is_product_category( $product_category )) && ( $expiryDate <= $current_date ) && ( $woo_expiry_action == 'out' )) {
            
            // 1. Updating the stock quantity
            update_post_meta( $post_id, '_stock', 0);

            // 2. Updating the stock quantity
            update_post_meta( $post_id, '_stock_status', wc_clean( 'outofstock' ) );

            // 3. Updating post term relationship
            wp_set_post_terms( $post_id, 'outofstock', 'product_visibility', true );
        }
}

if ( ! wp_next_scheduled( 'my_pre_order_stock_update' ) ) {
    wp_schedule_event( time(), 'hourly', 'my_pre_order_stock_update' );
}

add_action( 'my_pre_order_stock_update', 'update_pre_order_stock_status' );

The Cron event 'my_pre_order_stock_update' shows up in the cron list, but running it isn't doing anything.

Any recommendations would be very much appreciated!


Solution

  • There are many mistakes in your code:

    • The variable $post_id is not defined as you are not passing any post ID argument to wp_next_scheduled and wp_schedule_event functions.
    • The variable $product is not defined either (and can't be defined actually).

    I assume that you are trying to update some specific products based on:

    • their assigned product category based on a list of term slugs
    • a date custom metadata in woo_expiry_date
    • a value custom metadata in woo_expiry_action

    So you need to get those products IDs via a WP_Query which will be the simplest way.

    Now as WooCommerce is migrating to custom tables since version 3, and as some product data is cached, you should not use WordPress post meta functions anymore.

    Try the following instead (untested):

    // Custom Cron hooked Function called by wp_schedule_event
    add_action( 'jape_pre_order_stock_update', 'update_pre_order_stock_status' );
    function update_pre_order_stock_status(){
        // Get all related product IDs to update via a WP_Query
        $products_ids = get_posts( [
            'post_type'   => 'product',
            'post_status' => 'publish',
            'numberposts' => -1, // <= the number of products to process (-1 is all)
            'fields'      => 'ids',
            'tax_query' => array( array(
                'taxonomy' => 'product_cat',
                'field'    => 'slug',
                'terms'    => array(
                    'comic-book-pre-orders',
                    'dc-comics-pre-orders',
                    'image-comics-pre-orders',
                    'manga-pre-orders',
                    'marvel-comics-pre-orders',
                    'other-publisher-pre-orders',
                ),
            ) ),
            'meta_query' => array( 
                'relation' => 'AND',
                array(
                    'key'     => '_stock_status',
                    'value'   => 'instock',
                    'compare' => '=',
                ),
                array(
                    'key'     => 'woo_expiry_date',
                    'value'   => date_i18n('Y-m-d'),
                    'compare' => '<=',
                    'type'    => 'DATE',
                ),
                array(
                    'key'     => 'woo_expiry_action',
                    'value'   => 'out',
                    'compare' => '=',
                ),
            ),
        ] );
    
        foreach( $products_ids as $product_id ) {
            $product = wc_get_product($product_id);
    
            $product->set_stock_quantity(0); // Set stock quantity
            $product->set_stock_status('outofstock'); // Set stock status
            $product->save(); // Save updated product data
        }
    }
    
    // Cron schedule event function
    function cron_starter_hourly_products_update() {
        if ( ! wp_next_scheduled( 'jape_pre_order_stock_update' ) ) {
            wp_schedule_event( time(), 'hourly', 'jape_pre_order_stock_update' );
        }
    }
    
    cron_starter_hourly_products_update(); // Initiate cron schedule event
    

    Code goes in functions.php file of your child theme (or in a plugin). It could work.

    Caution: The number of product to process should be fine-tuned in 'numberposts' argument, as if there are too many products to process, it may crash the scheduled event or even the website.

    Related: