Temporary global sale price discount for all products in WooCommerce

642 Views Asked by At

What I need:

We are planning to start a campaign. We want to give a temporary 10% discount on every product we sell on our site for the next 30 days. We want to display both the regular price and the discounted price on all shop's pages.

  • We have approximately 200,000 products.
  • Our products don't have variations.

Requirements:

  • All products should get 10% discount.
  • The discount should be visible and active for all visitors.
  • The discount should be applied automatically without any user actions.
  • Both the regular price and the discounted price should be visible to all visitors of the site.
  • It should be easy to remove the discount after 30 days.
  • The solution should not relay on any additional plugins, except WooCommerce. We are trying to keep our third-party plugin usage to minimum.
  • The modifications to the front-end should be minimal to none.

Environment:

  • Our shop is built on WooCommerce 8.1.
  • Using W3 Total Cache with Redis.
  • Hosted on dedicated server with Ubuntu Server 22.04. Running Apache 2.4 with PHP 8.1 and MySQL 8.

What have I tried and didn't work:

  • Using a plugin. All plugins I've tried have problems (or require too much manual clicking) handling 200,000 products.
  • Creating a coupon and applying it programmatically on Cart page. This actually did the discount part. However, it required some modifications to the theme to show the new discounted price to the users.

Question:

How to do this?

2

There are 2 best solutions below

0
LoicTheAztec On BEST ANSWER

Updated (allowing products with zero price and resetting existing sale prices)

Instead of overwriting products prices as you suggest in your comment, you could use the following to add a temporary global discount, that keeps the original product price with a general sale price with a 10% discount:

// Below define the discount rate
function discount_rate(){
    return 0.9; // 10% discount
}

add_filter('woocommerce_product_get_price', 'custom_price_discount', 20, 2);
add_filter('woocommerce_product_variation_get_price', 'custom_price_discount', 20, 2);
function custom_price_discount( $price, $product ) {
    if ( $price > 0 ) {
        return floatval($product->get_regular_price()) * discount_rate();
    }
    return $price;
}

add_filter('woocommerce_product_get_sale_price', 'custom_sale_price_discount', 20, 2);
add_filter('woocommerce_product_variation_get_sale_price', 'custom_sale_price_discount', 20, 2); 
function custom_sale_price_discount( $price, $product ) {
    return $product->get_price();
}
  
add_filter('woocommerce_product_is_on_sale', 'custom_product_is_on_sale', 20, 2);
function custom_product_is_on_sale( $is_on_sale, $product ) {
    return $product->get_price() > 0 ? true : false;
}

function remove_zero_prices( $prices ) {
    foreach( $prices as $key => $price ) {
        if ( $price > 0 ) {
            continue;
        }
        unset($prices[$key]);
    }
    return $prices;
}

add_filter( 'woocommerce_get_price_html', 'custom_dynamic_sale_price_html', 20, 2 );
function custom_dynamic_sale_price_html( $price_html, $product ) {
    if( $product->is_type('variable') ) {
        $prices     = $product->get_variation_prices( true );
        $reg_prices = remove_zero_prices( $prices['regular_price'] );

        if ( count($reg_prices) > 0 ) {
            return wc_format_sale_price( wc_price( end($reg_prices) ), wc_price( reset($reg_prices) * discount_rate() ) ) . $product->get_price_suffix();
        } 
    }
    return $price_html;
}

Code goes in functions.php file of your child theme (or in a plugin). Tested and works. It could work with your very large catalog.

0
sotirov On

How I have done it:

  • Putting all products on sale by updating their _sale_price and _price with the data from _regular_price multiplied by 0.9 (10%).
  • When the promotion ends, manually removing _sale_price and updating _price with _regular_price by executing SQL queries.

Warning: This solutions don't work with variations!

Add the discount to all products

If the products aren't on sale, they don't have _sale_price meta_key in the database table wp_postmeta. To add these, we can loop trough all products and use update_post_meta. When working with bigger databases, I suggest splitting the update into smaller batches. I used 1000 per batch in the code below. If you want the query for all your products at once, replace 'posts_per_page' => 1000 with 'posts_per_page' => -1.

Warning: This code will not work properly if any of your products already have a _sale_price!

// Get the products that don't have '_sale_price'
$args = array(
    'post_type'      => 'product',
    'posts_per_page' => 1000,
    'meta_query'     => array(
        'relation' => 'AND',
        array(
            'key'     => '_sale_price',
            'compare' => 'NOT EXISTS',
        ),
        array(
            'key'     => '_regular_price',
            'value'   => '',
            'compare' => '!='
        ),
    ),
);
$products = query_posts( $args );

// Loop through queried products
foreach ($products as $post) {
    $product = wc_get_product( $post->ID );

    // Discount by 10% and round to 2 decimal places
    $discount = 10;
    $newprice = round($product->get_regular_price() * ((100-$discount) / 100), 2);

    // Update product's prices
    update_post_meta( $product->get_id(), '_sale_price', $newprice );
    update_post_meta( $product->get_id(), '_price', $newprice );
}

Remove the discount

I am using SQL queries, because it's fast and easy. If you are looking for an alternative way to do it, check this post: Remove Sale Price from all products in WooCommerce admin

Warning: These SQL queries will delete all your existing sale prices!

Delete all products' _sale_price:

DELETE FROM `wp_postmeta` WHERE meta_key = '_sale_price';

Update products' _price with the values of _regular_price:

UPDATE wp_postmeta as price 
    INNER JOIN wp_postmeta as regular_price ON 
        price.post_id = regular_price.post_id AND 
        regular_price.meta_key = "_regular_price" AND
        price.meta_key = "_price"
    SET price.meta_value = regular_price.meta_value;

References: