Changelog
Current version: 3.0.0
Combined history for qoliber/omnibus-directive and its two optional
companions. qoliber/mysql-utils and qoliber/hyva-module-registration are
shared dependencies with their own release cycles and are not tracked here.
3.0.0 — 2026-09-27
Omnibus Directive
Added
- A -X% discount badge next to the lowest 30-day price, under
Stores > Configuration > Qoliber > Omnibus Directive > Discount Badge. X is the reduction from the prior price, rounded down so it never overstates the discount; the badge shows only when Show Discount Badge is on and X is at least Minimum Discount (%) (default 10). It renders on product pages, category and search listings, and CatalogWidget listings. Dynamic product prices only: the Static attribute is not the price in effect before the current one, so a badge calculated from it could claim a bigger reduction than the directive allows. qoliber_omnibus_directive_price_log, which records every change of a product's effective price per website and customer group.- A daily cron job,
qoliber_omnibus_directive_expire_stale_reductions(00:15), that ends reductions once they have lasted 30 days. Model\HistoricalPriceDisplay, the single rule for when the figure and the badge are displayed, shared by the Luma price box, the Luma listing renderer and the Hyvä templates.
Changed
- BREAKING In Dynamic mode the index now stores the Omnibus prior price:
the lowest price in effect during the 30 days BEFORE the current price took
effect. It used to include the current price, so a reduction was its own
"lowest price" and the figure was hidden exactly when the directive requires
it. The prior price stays fixed while a reduction lasts and becomes the
current price once that has lasted 30 days. When no earlier price is known,
the current price is used, so no reduction is claimed without evidence. The
index column is still
min_price, so the registry, bulk loader and ElasticSuite read it unchanged. Price history starts when this version is first indexed. - BREAKING With Always Show Historical Price off, the figure now shows whenever the current price differs from it - after a reduction, and after a rise from a lower 30-day price - and hides only when they are equal. It used to show only when the current price was higher.
- The full reindex pages through products by id instead of
LIMIT/OFFSETwithout anORDER BY, which did not guarantee every product was visited. composer.jsondeclaressupport.security.
Removed
- The 30-day date prune of the index table. The index is derived from the price log on every run, and the log is pruned of entries that can no longer reach any window.
Omnibus Directive for Hyvä
Added
- The -X% discount badge on Hyvä product pages and listings, styled with
Tailwind utilities (added to
tailwind-classes.phtmlso they survive the purge).ViewModel\HyvaHistoricalPrice::getDiscountPercent()exposes it, andgetHistoricalPriceData()carries it asdiscount_percent.
Changed
- BREAKING Requires
qoliber/omnibus-directive^3.0: the templates andHyvaHistoricalPriceuseModel\HistoricalPriceDisplay, which 3.0.0 adds. - The product-page template and
HyvaHistoricalPrice::shouldShowHistoricalPrice()now use the sharedModel\HistoricalPriceDisplayrule fromqoliber/omnibus-directive, so Hyvä and Luma decide the same way. With Always Show Historical Price off, the figure shows whenever the current price differs from it, including after a reduction. composer.jsondeclaressupport.security.
Omnibus Directive for ElasticSuite
Changed
- BREAKING Requires
qoliber/omnibus-directive^3.0, released in lockstep with the core module. No code changes: the index this module reads,qoliber_omnibus_directive_min_price_index, keeps its schema, and itsmin_pricenow holds the core module's Omnibus prior price. composer.jsondeclaressupport.security.
2.0.0 — 2026-08-13
Omnibus Directive
Findings F1–F24 refer to the repository's code-review audit, which documents each one in full: the defect, the file and line as originally imported, the failure it caused, the fix, and the test (where one exists) that proves it.
Removed
- BREAKING The Hyvä presentation layer — layouts, templates, and
ViewModel\HyvaHistoricalPrice— moved to the newqoliber/omnibus-directive-hyvapackage. This module no longer renders anything on a Hyvä storefront by itself; stores running Hyvä must also install and enableQoliber_OmnibusDirectiveHyva. The pricing pool wiring, the registry,GetDynamicPrice, andModel\Configurationstay here and are shared by both storefronts. qoliber/coredependency, replaced by a<tab id="qoliber">declaration in this module's ownsystem.xml(see F23 below).
Changed
- BREAKING
Model\Configuration's five getters (isEnabled,getProductPricesType,isShowOnPlp,isShowOnPdp,isAlwaysShow) now accept an optional store code and resolve atScopeInterface::SCOPE_STORE;etc/adminhtml/system.xmlopens the section, its group, and all five fields toshowInWebsite="1" showInStore="1"(F7). A multi-store install can now set the Omnibus configuration per store view, which was not possible before. - BREAKING The price-recording observer was renamed
Observer\UpdateHistoricalPriceAdminhtmlObserver→Observer\UpdateHistoricalPriceObserverand rebound fromcatalog_product_save_after(global, but a no-op — see F2) tocatalog_product_save_before(global).etc/adminhtml/events.xml, which duplicated the binding for the admin area only, was deleted. composer.json:"license"corrected to"proprietary"; explicitphp(~8.1.0||~8.2.0||~8.3.0||~8.4.0||~8.5.0) andmagento/frameworkconstraints added; theqoliber.dev"source"block dropped (F19, F20, F21).
Fixed
- F1
Service\GetDynamicPrice::execute()treated a registry miss as a real price of0.0instead of falling back to a per-product query, becauseHistoricalPriceRegistry::getPrice()(F10) always returned afloat, nevernull. Any product outside a bulk-loaded registry silently lost its historical price. Now branches onisBulkLoaded() && hasPrice(). - F2 The price-history observer was bound to
catalog_product_save_afterglobally and tocatalog_product_save_beforeonly underadminhtml. Onsave_afteritssetData()calls are never persisted, so REST, SOAP,bin/magentoimport, and ERP-driven price changes — most real-world price changes — recorded no history at all. Now bound once, tocatalog_product_save_before, globally. - F3
Model\Indexer\MinPrice::executeList()issued oneSELECTper price row to read the existing minimum (measured: 8278 queries over an 8156-row index). Replaced with one batchedSELECT ... WHERE product_id IN (?)per call (1687 queries after). - F4
getAllProductIdsBatch()hardcodedentity_type_id = 4for thestatusattribute. Now resolved viaMagento\Eav\Model\Config. - F5
deleteOldPrices()ran once per 1000-product batch insideexecuteList()instead of once per reindex. Hoisted toexecute(),executeFull(), andexecuteRow(). - F6 The product-status join filtered
attribute_idbut notstore_id, so a product with a store-scoped status value was yielded once per scope row (12 duplicates measured on this catalogue). Addedcpev.store_id = 0. - F7 See "Changed" above.
- F8
Plugin\Block\Product\AbstractProductPluginwas anaroundplugin that called$subject->getDetailsRenderer('historical_price')with no null guard.getDetailsRenderer()returnsnullwhenever a layout has no renderer-list block at all, which fatals the unguarded call. Converted toafter, added a null/instanceofguard, and an explicitisShowOnPlp()check. - F9
Pricing\Render\HistoricalPriceBox::__construct()used implicit nullable parameters, deprecated as of PHP 8.4. Made the?Typenullability explicit. - F10
HistoricalPriceRegistry::getPrice()cast a missing entry to0.0. Now returns?float;hasPrice()remains the presence check. - F11
view/frontend/templates/hyva/tailwind-classes.phtml(now inqoliber/omnibus-directive-hyva) built a$classessafelist array that was never output — Tailwind's regex file scanner picked it up regardless, so the mechanism worked by accident. Now rendered into a hidden element so intent and mechanism agree. - F12
system.xmlgave three fields the samesortOrder="10". PHP 8's stable sort meant declaration order was preserved regardless, so this was a determinism/hygiene fix, not a live rendering bug. Renumbered 10/20/30/40/50. - F22 Categories with Display Mode "Static block only" render their
product grid through
Magento\CatalogWidget\Block\Product\ProductsList, whose renderer list (category.product.type.widget.details.renderers) this module never targeted — only the native category grid's list was wired. Every such category (7 of this install's: gear, collections, training, men, women, promotions, sale) showed no historical price at all. Addedview/frontend/layout/catalog_widget_product_list.xmlregistering the same renderer on the widget's list. - F23
etc/adminhtml/system.xmlreferenced<tab>qoliber</tab>with no matching<tab id="qoliber">declared anywhere — previously supplied by the now-droppedqoliber/corepackage.Magento\Config\Model\Config\ Structurethrows while assembling the tab tree, which broke Stores > Configuration for every admin user, not only this section. The module now declares its own<tab id="qoliber">and no longer depends on another Qoliber package to be installable. - F24
MinPrice::executeList()computedmin($price['price'], $price['final_price'], $price['min_price']). Composite types (grouped, bundle, downloadable) carryNULLinprice/final_priceincatalog_product_index_price; PHP'smin()ranksNULLbelow any real number, so it was chosen over a genuinemin_price, and MySQL coerced thatNULLto0in theNOT NULLindex column — a permanent, silent "price has never dropped" for every composite product.NULLcandidates are now filtered out beforemin(), and a row is skipped entirely rather than written as a fabricated0when no price data exists.
Known limitations
- F4, F9, F11, F21 have no dedicated falsifying test — each is a
portability, hygiene, or packaging fix with no observable behavioural
difference on this install. See the audit for the reasoning specific to
each. (F13–F18 are documented in
app/code/Qoliber/OmnibusDirectiveElasticSuite/CHANGELOG.md.)
[Unreleased changes prior to 2.0.0 were not tracked in this file.]
Omnibus Directive for Hyvä
First release of this package. It starts at 2.0.0 rather than 1.0.0 so that every module in the Omnibus Directive set carries the same version, which is the convention across Qoliber suites — the Hyvä companion is not versioned independently of the core module it renders for.
Added
- Initial release. The Hyvä presentation layer moves here out of
qoliber/omnibus-directive, which shipped it up to and including 1.0.6. ViewModel\HyvaHistoricalPrice, exposing the historical price to Hyvä templates without pullingHyva\Themeclasses into the core module.- Hyvä layout handles for the product page, category page, search results
page, and the Tailwind safelist template (
hyva_catalog_product_view.xml,hyva_catalog_category_view.xml,hyva_catalogsearch_result_index.xml,hyva_default.xml). - Product list renderer templates (
historical-price.phtmland the Alpine.js varianthistorical-price-alpine.phtml) rendered through the core module's theme-agnosticBlock\Product\List\Renderer\HistoricalPrice.
Notes
- The pricing pool wiring, the registry, the dynamic price service and the
configuration model stay in
qoliber/omnibus-directiveand are shared with the Luma templates; only the Hyvä view layer and itsHyvaHistoricalPriceview model live here.
Omnibus Directive for ElasticSuite
Fixed
- ElasticSearch lookups never executed: the service called
Layer\Resolver::create()at view time, which throws once the catalog controller has created the layer, and a barecatch (\Exception)silently fell back to MySQL. The service now consumes the resolved layer. - The database fallback queried
website_id = 0because a null website id was cast to int. - Fallbacks are logged instead of discarded.
Removed
- BREAKING
GetDynamicPricePlugin, which walked the whole layer collection once per product on a listing page while bypassing the registry. - BREAKING
ElasticSearchHistoricalPriceService::getHistoricalPriceForProduct(), whose only caller was that plugin.
Changed
- Depends on
RequestInterfacerather than the concreteHttprequest. - Requires
qoliber/omnibus-directive^2.0 and PHP 8.1-8.5.
1.0.2 — 2025-10-16
Omnibus Directive for ElasticSuite
Released 2025-10-16.
Fixed
- Fixed ElasticSearchHistoricalPriceService for better compatibility with ElasticSuite
1.0.1 — 2025-09-17
Omnibus Directive for ElasticSuite
Released 2025-09-17.
Changed
- Updated HistoricalPriceData datasource to align with changes in main OmnibusDirective module
- Improved integration with OmnibusDirective configuration changes
1.0.0 — 2025-08-28
Omnibus Directive for ElasticSuite
Released 2025-08-28.
Added
- Initial release of Omnibus Directive ElasticSuite integration module
- Implemented ElasticSearch index integration for historical prices
- Added HistoricalPriceData datasource for Smile ElasticSuite fulltext indexing
- Implemented ElasticSearchHistoricalPriceService for retrieving prices from ElasticSearch
- Added plugin for BulkLoadHistoricalPrices to use ElasticSearch when available
- Added plugin for GetDynamicPrice to leverage ElasticSearch for price retrieval
- Implemented elasticsuite_indices.xml configuration for historical price indexing
- Added historical price fields to ElasticSearch product index:
historical_price- indexed as decimalhistorical_price_date- indexed as date
- Enabled high-performance price retrieval from ElasticSearch instead of MySQL
- Added automatic fallback to database when ElasticSearch is unavailable
- Implemented dependency on smile/elasticsuite and qoliber/omnibus-directive modules
Performance
- Significantly improved historical price loading performance on category pages using ElasticSearch
- Reduced database queries by leveraging ElasticSearch for bulk price operations
- Optimized price data retrieval for large product catalogs
Integration
- Seamless integration with Smile ElasticSuite search engine
- Compatible with OmnibusDirective module's registry and service layer
- Automatic detection and usage of ElasticSearch when module is enabled