Plugin Directory

Changeset 3765069


Ignore:
Timestamp:
10/25/2026 06:28:00 PM (2 hours ago)
Author:
fernandot
Message:

Works with Scheduled Sales, Discounts and Omnibus Price History for WooCommerce for the EU Omnibus prior price: with it active, the discount badge is calculated on the lowest price of the last 30 days, and by default the crossed out price is that prior price.

Location:
show-only-lowest-prices-in-woocommerce-variable-products
Files:
13 added
3 edited

Legend:

Unmodified
Added
Removed
  • show-only-lowest-prices-in-woocommerce-variable-products/trunk/changelog.txt

    r3653570 r3765069  
    33For each release, see the entries below. The latest version is always at the top.
    44The current release notes also live in readme.txt under "== Changelog ==".
     5
     6= 2.4.0 =
     7* New: Works with Scheduled Sales, Discounts and Omnibus Price History for WooCommerce, from the same author, for the prior price of the EU Omnibus Directive. With it active, the discount percentage badge is calculated on the prior price (the lowest price of the 30 days before the sale) instead of the regular price, and no badge is shown when the sale price is not lower than the prior price
     8* New: The prior price note of Scheduled Sales, Discounts and Omnibus Price History for WooCommerce goes right after the price, only once, and describes the variation shown, also in the "Up to" mode
     9* New: When Scheduled Sales, Discounts and Omnibus Price History for WooCommerce crosses out the prior price, which it does by default, the price crosses out the prior price instead of the regular one, or nothing when the sale price is not lower than it
     10* Improved: The settings page detects Scheduled Sales, Discounts and Omnibus Price History for WooCommerce, and the FAQ about the Omnibus Directive recommends it first
     11* Improved: Tested up to WooCommerce 11.2
    512
    613= 2.3.0 =
  • show-only-lowest-prices-in-woocommerce-variable-products/trunk/readme.txt

    r3653570 r3765069  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 2.3.0
     7Stable tag: 2.4.0
    88License: GPLv2+
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
    … …  
    2323* **Sales stay visible** - The regular price is kept crossed out next to the sale price, so discounts are not lost in shop and archive pages
    2424* **Discount badge** - Optionally show the discount percentage next to the price
     25* **EU Omnibus friendly** - With Scheduled Sales, Discounts and Omnibus Price History for WooCommerce, the discount badge is calculated on the prior price of the last 30 days, and by default the crossed out price is that prior price
    2526* **Out of stock aware** - Leave sold out variations out of the calculation, so the price you advertise is one your customers can actually buy
    2627* **Where it applies** - Everywhere, shop and archives only, or product pages only
    … …  
    101102= Does this plugin implement the EU Omnibus Directive (lowest price in the last 30 days)? =
    102103
    103 No. This plugin only changes how variable product prices are displayed. It is a presentational feature: it does not track price history and it does not display the prior price (the lowest price applied during the 30 days before a price reduction) required by Directive (EU) 2019/2161, known as the Omnibus Directive. Despite the similar name of some compliance plugins, this one serves a different purpose.
     104Not on its own. It only changes how variable product prices are displayed: it does not track price history or show the prior price (the lowest price applied in the 30 days before a price reduction) required by Directive (EU) 2019/2161.
    104105
    105 If your store needs to comply with those rules, use a plugin built for it, such as Omnibus - show the lowest price, WC Price History or FleekCode - Omnibus Price Tracker. They work alongside this plugin without conflict: this one decides which price replaces the WooCommerce range, and the other one adds the lowest price of the last 30 days. When one of them is active you will see a notice in the settings page confirming it.
     106For that, use [Scheduled Sales, Discounts and Omnibus Price History for WooCommerce](https://wordpress.org/plugins/multiple-sale-prices-scheduler/), from the same author. It records the price history and shows the prior price, and both plugins work together:
    106107
    107 Two things help here. Products on sale keep their regular price crossed out, so a price reduction stays visible instead of showing only the reduced price. And the `ayudawp_lowest_price_html` filter lets you place the price history message exactly where you want it:
     108* The discount percentage badge is calculated on the prior price, as the EU Court of Justice requires (case C-330/23). If the sale price is not lower than the prior price, no badge is shown.
     109* By default it crosses out the prior price instead of the regular one, and so does the price shown by this plugin. If the sale price is not lower than the prior price, nothing is crossed out.
     110* Its prior price note appears right after the price, once, for the variation shown.
     111
     112Omnibus - show the lowest price, WC Price History and FleekCode - Omnibus Price Tracker also work alongside this plugin. The settings page tells you when one of these plugins is active.
     113
     114To place the price history message exactly where you want it, use the `ayudawp_lowest_price_html` filter:
    108115
    109116`add_filter( 'ayudawp_lowest_price_html', function ( $html, $product ) {
    … …  
    123130== Changelog ==
    124131
    125 = 2.3.0 =
    126 * New: Live preview in the settings page, showing how your price will read for three situations at once: variations at different prices, the variation shown on sale, and every variation at the same price. It updates as you change the options and follows the page while you scroll
    127 * Improved: Compatible with WordPress 7.1
     132= 2.4.0 =
     133Works with Scheduled Sales, Discounts and Omnibus Price History for WooCommerce for the EU Omnibus prior price: with it active, the discount badge is calculated on the lowest price of the last 30 days, and by default the crossed out price is that prior price.
     134
     135* New: Works with Scheduled Sales, Discounts and Omnibus Price History for WooCommerce, from the same author, for the prior price of the EU Omnibus Directive. With it active, the discount percentage badge is calculated on the prior price (the lowest price of the 30 days before the sale) instead of the regular price, and no badge is shown when the sale price is not lower than the prior price
     136* New: The prior price note of Scheduled Sales, Discounts and Omnibus Price History for WooCommerce goes right after the price, only once, and describes the variation shown, also in the "Up to" mode
     137* New: When Scheduled Sales, Discounts and Omnibus Price History for WooCommerce crosses out the prior price, which it does by default, the price crosses out the prior price instead of the regular one, or nothing when the sale price is not lower than it
     138* Improved: The settings page detects Scheduled Sales, Discounts and Omnibus Price History for WooCommerce, and the FAQ about the Omnibus Directive recommends it first
     139* Improved: Tested up to WooCommerce 11.2
    128140
    129141For older changelog entries, please check the [changelog.txt](https://plugins.svn.wordpress.org/show-only-lowest-prices-in-woocommerce-variable-products/trunk/changelog.txt) file
    … …  
    131143== Upgrade Notice ==
    132144
    133 = 2.3.0 =
    134 Compatibility release for WordPress 7.1. The settings page now shows a live preview of the price, so you can see how it will read before saving anything.
     145= 2.4.0 =
     146Works with Scheduled Sales, Discounts and Omnibus Price History for WooCommerce for the EU Omnibus prior price: with it active, the discount badge is calculated on the lowest price of the last 30 days, and by default the crossed out price is that prior price.
    135147
    136148== Support ==
  • show-only-lowest-prices-in-woocommerce-variable-products/trunk/show-lowest-woo-variable-prices.php

    r3653570 r3765069  
    55 * Description: Shows only the lowest price and sale in variable WooCommerce products with customizable prefix and advanced options.
    66 * Author: Fernando Tellado
    7  * Version: 2.3.0
     7 * Version: 2.4.0
    88 * Author URI: https://ayudawp.com
    99 * Text Domain: show-only-lowest-prices-in-woocommerce-variable-products
    … …  
    1313 * Requires PHP: 7.4
    1414 * WC requires at least: 4.0
    15  * WC tested up to: 11.0
     15 * WC tested up to: 11.2
    1616 * License: GPLv2+
    1717 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
    … …  
    2424
    2525// Plugin constants.
    26 define( 'AYUDAWP_LOWEST_PRICES_VERSION', '2.3.0' );
     26define( 'AYUDAWP_LOWEST_PRICES_VERSION', '2.4.0' );
    2727define( 'AYUDAWP_LOWEST_PRICES_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    2828define( 'AYUDAWP_LOWEST_PRICES_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
    … …  
    367367
    368368                $space = $options['add_space_after_prefix'] ? ' ' : '';
     369                $note  = '';
    369370
    370371                switch ( $mode ) {
    371372                        case 'highest':
    372                                 $body   = $this->ayudawp_format_price( $max, $max_reg, $options );
    373                                 $body  .= $this->ayudawp_discount_badge( $max, $max_reg, $options );
    374                                 $body   = $this->ayudawp_add_prefix( $body, $options['max_prefix_text'], $space, $same_price, $options );
    375                                 $amount = $max;
     373                                $reference = $this->ayudawp_sale_reference( $max_id, $max, $max_reg );
     374                                $body      = $this->ayudawp_format_price( $max, $reference['strike'], $options );
     375                                $body     .= $this->ayudawp_discount_badge( $max, $reference['badge'], $options );
     376                                $body      = $this->ayudawp_add_prefix( $body, $options['max_prefix_text'], $space, $same_price, $options );
     377                                $amount    = $max;
     378                                $note      = $this->ayudawp_msps_note( $max_id, $max, $max_reg, $product );
    376379                                break;
    377380
    … …  
    397400                        case 'lowest':
    398401                        default:
    399                                 $body   = $this->ayudawp_format_price( $min, $min_reg, $options );
    400                                 $body  .= $this->ayudawp_discount_badge( $min, $min_reg, $options );
    401                                 $body   = $this->ayudawp_add_prefix( $body, $options['prefix_text'], $space, $same_price, $options );
    402                                 $amount = $min;
     402                                $reference = $this->ayudawp_sale_reference( $min_id, $min, $min_reg );
     403                                $body      = $this->ayudawp_format_price( $min, $reference['strike'], $options );
     404                                $body     .= $this->ayudawp_discount_badge( $min, $reference['badge'], $options );
     405                                $body      = $this->ayudawp_add_prefix( $body, $options['prefix_text'], $space, $same_price, $options );
     406                                $amount    = $min;
     407                                $note      = $this->ayudawp_msps_note( $min_id, $min, $min_reg, $product );
    403408                                break;
    404409                }
    … …  
    411416
    412417                $css_class = ! empty( $options['custom_css_class'] ) ? $options['custom_css_class'] : 'ayudawp-lowest-price';
    413                 $html      = '<span class="' . esc_attr( $css_class ) . '">' . $body . '</span>';
     418                $html      = '<span class="' . esc_attr( $css_class ) . '">' . $body . '</span>' . $note;
    414419
    415420                /**
    … …  
    417422                 *
    418423                 * Useful to append extra information next to the price, such as the lowest
    419                  * price of the last 30 days tracked by a price history plugin.
     424                 * price of the last 30 days tracked by a price history plugin. The prior
     425                 * price note of Scheduled Sales, Discounts and Omnibus Price History for WooCommerce, when there
     426                 * is one, is already in it, right after the price.
    420427                 *
    421428                 * @since 2.2.0
    … …  
    455462         *
    456463         * @param string|float $price   Active price.
    457          * @param string|float $regular Regular price of the very same variation.
     464         * @param string|float $regular Price to cross out: the regular price of the very
     465         *                              same variation, or its prior price (see
     466         *                              ayudawp_sale_reference()). Nothing is crossed out
     467         *                              unless it is higher than $price.
    458468         * @param array        $options Plugin options.
    459469         * @return string
    … …  
    472482         *
    473483         * @param string|float $price   Active price.
    474          * @param string|float $regular Regular price of the very same variation.
     484         * @param string|float $regular Price the discount is measured against: the regular
     485         *                              price of the very same variation, or its prior
     486         *                              price (see ayudawp_sale_reference()).
    475487         * @param array        $options Plugin options.
    476488         * @return string Empty string when there is no discount to show.
    … …  
    494506                        )
    495507                ) . '</span>';
     508        }
     509
     510        /**
     511         * Prices a sale of the shown variation is measured against.
     512         *
     513         * Without a price history the regular price is both the one crossed out and
     514         * the one the badge is calculated on. With Scheduled Sales, Discounts and Omnibus Price History for
     515         * WooCommerce active, a prior price lower than the regular one takes over
     516         * the badge: the EU Court of Justice ruled that a reduction announced as a
     517         * percentage is calculated on the prior price (C-330/23, September 2024).
     518         * It is also the price crossed out when that plugin is set to cross out the
     519         * prior price; otherwise the regular price stays crossed out and the prior
     520         * price is left to its note (ayudawp_msps_note()).
     521         *
     522         * @param int          $variation_id Shown variation.
     523         * @param string|float $price        Its active price, for display.
     524         * @param string|float $regular      Its regular price, for display.
     525         * @return array {
     526         *     @type string|float $strike Price to cross out.
     527         *     @type string|float $badge  Price the discount badge is calculated on.
     528         * }
     529         */
     530        private function ayudawp_sale_reference( $variation_id, $price, $regular ) {
     531                $reference = array(
     532                        'strike' => $regular,
     533                        'badge'  => $regular,
     534                );
     535
     536                // Not on sale: there is no reduction to measure.
     537                if ( '' === $regular || (float) $regular <= (float) $price ) {
     538                        return $reference;
     539                }
     540
     541                $prior = $this->ayudawp_get_msps_prior_price( $variation_id );
     542
     543                if ( null === $prior || $prior >= (float) $regular ) {
     544                        return $reference;
     545                }
     546
     547                $reference['badge'] = $prior;
     548
     549                if ( function_exists( 'ayudawp_msps_strikes_prior_price' ) && ayudawp_msps_strikes_prior_price() ) {
     550                        $reference['strike'] = $prior;
     551                }
     552
     553                return $reference;
     554        }
     555
     556        /**
     557         * Prior price of a variation from Scheduled Sales, Discounts and Omnibus Price History for WooCommerce.
     558         *
     559         * That plugin records every price a product had and works out the lowest one
     560         * applied in the days before the current price started, which is the prior
     561         * price of the EU Omnibus Directive. It is asked for the price as the shop
     562         * displays it, with or without tax, because that is how the prices compared
     563         * here come out of get_variation_prices( true ).
     564         *
     565         * Only its public API is used, stable since its version 1.6.0. Earlier
     566         * versions do not have it, and their prior price did not yet count the price
     567         * in effect when the window opened, so nothing changes with them.
     568         *
     569         * @param int $variation_id Variation ID.
     570         * @return float|null Prior price for display, or null when the plugin is not
     571         *                    active or there is no earlier price to compare.
     572         */
     573        private function ayudawp_get_msps_prior_price( $variation_id ) {
     574                if ( ! function_exists( 'ayudawp_msps_get_prior_price' ) ) {
     575                        return null;
     576                }
     577
     578                $prior = ayudawp_msps_get_prior_price( $variation_id, true );
     579
     580                return is_numeric( $prior ) ? round( (float) $prior, wc_get_price_decimals() ) : null;
     581        }
     582
     583        /**
     584         * Prior price note of Scheduled Sales, Discounts and Omnibus Price History for WooCommerce for the
     585         * variation shown.
     586         *
     587         * That plugin adds its note to the price of a variable product by itself,
     588         * for the cheapest variation, unless the price already carries one. Asking
     589         * for it here puts the note of the variation this plugin actually shows,
     590         * which in the "Up to" mode is the most expensive one, and keeps it to one.
     591         * The note comes from that plugin already escaped and following its own
     592         * settings (when to show it, where, and its text).
     593         *
     594         * @param int          $variation_id Shown variation.
     595         * @param string|float $price        Its active price, for display.
     596         * @param string|float $regular      Its regular price, for display.
     597         * @param WC_Product   $product      Variable product being displayed.
     598         * @return string Note HTML, or an empty string.
     599         */
     600        private function ayudawp_msps_note( $variation_id, $price, $regular, $product ) {
     601                if ( ! function_exists( 'ayudawp_msps_get_prior_price_note' ) || '' === $regular || (float) $regular <= (float) $price ) {
     602                        return '';
     603                }
     604
     605                return wp_kses_post( (string) ayudawp_msps_get_prior_price_note( $variation_id, $product->get_id() ) );
    496606        }
    497607
    … …  
    10731183         * These plugins track the lowest price of the last 30 days required by the EU
    10741184         * Omnibus Directive, which this plugin does not do. Detection is done against
    1075          * the active plugins list to avoid guessing class or constant names.
    1076          *
    1077          * @return string Plugin name, or an empty string when none is active.
     1185         * the active plugins list to avoid guessing class or constant names, in the
     1186         * order of the list below: Scheduled Sales, Discounts and Omnibus Price History goes first because
     1187         * it is the one this plugin works with beyond sharing the price.
     1188         *
     1189         * @return string Plugin folder, or an empty string when none is active.
    10781190         */
    10791191        private function ayudawp_get_price_history_plugin() {
    1080                 $known = array(
    1081                         'omnibus'               => 'Omnibus — show the lowest price',
    1082                         'wc-price-history'      => 'WC Price History',
    1083                         'product-price-history' => 'Product Price History for WooCommerce',
    1084                         'fleekcode-omnibus'     => 'FleekCode – Omnibus Price Tracker',
    1085                 );
    1086 
    10871192                $active = (array) get_option( 'active_plugins', array() );
    10881193
    … …  
    10911196                }
    10921197
    1093                 foreach ( $active as $plugin_file ) {
    1094                         $folder = dirname( $plugin_file );
    1095 
    1096                         if ( isset( $known[ $folder ] ) ) {
    1097                                 return $known[ $folder ];
     1198                $active_folders = array_map( 'dirname', $active );
     1199
     1200                foreach ( array_keys( $this->ayudawp_price_history_plugins() ) as $folder ) {
     1201                        if ( in_array( $folder, $active_folders, true ) ) {
     1202                                return $folder;
    10981203                        }
    10991204                }
    … …  
    11031208
    11041209        /**
     1210         * Known price history plugins.
     1211         *
     1212         * @return array Plugin folder => plugin name.
     1213         */
     1214        private function ayudawp_price_history_plugins() {
     1215                return array(
     1216                        'multiple-sale-prices-scheduler' => 'Scheduled Sales, Discounts and Omnibus Price History for WooCommerce',
     1217                        'omnibus'                        => 'Omnibus — show the lowest price',
     1218                        'wc-price-history'               => 'WC Price History',
     1219                        'product-price-history'          => 'Product Price History for WooCommerce',
     1220                        'fleekcode-omnibus'              => 'FleekCode – Omnibus Price Tracker',
     1221                );
     1222        }
     1223
     1224        /**
    11051225         * Notice explaining how this plugin coexists with a price history plugin.
    11061226         */
    11071227        private function ayudawp_render_price_history_notice() {
    1108                 $plugin_name = $this->ayudawp_get_price_history_plugin();
    1109 
    1110                 if ( '' === $plugin_name ) {
     1228                $folder  = $this->ayudawp_get_price_history_plugin();
     1229                $plugins = $this->ayudawp_price_history_plugins();
     1230
     1231                if ( '' === $folder ) {
    11111232                        return;
    11121233                }
    … …  
    11181239                                        /* translators: %s: name of the detected price history plugin. */
    11191240                                        esc_html__( '%s is active on this site. Both plugins work together: this one decides which price replaces the WooCommerce range, and the other one adds the lowest price of the last 30 days required by the EU Omnibus Directive.', 'show-only-lowest-prices-in-woocommerce-variable-products' ),
    1120                                         esc_html( $plugin_name )
     1241                                        esc_html( $plugins[ $folder ] )
    11211242                                );
    11221243                                ?>
    11231244                        </p>
     1245                        <?php if ( 'multiple-sale-prices-scheduler' === $folder ) : ?>
     1246                                <p>
     1247                                        <?php esc_html_e( 'The discount percentage is calculated on that prior price instead of the regular one, as EU rules require. If that plugin is set to cross out the prior price, the price shown here crosses out the prior price too.', 'show-only-lowest-prices-in-woocommerce-variable-products' ); ?>
     1248                                </p>
     1249                        <?php endif; ?>
    11241250                        <p>
    11251251                                <?php esc_html_e( 'If the two messages end up in the wrong order, use the ayudawp_lowest_price_html filter to place them exactly where you want.', 'show-only-lowest-prices-in-woocommerce-variable-products' ); ?>
Note: See TracChangeset for help on using the changeset viewer.