magendoo / module-omnibus
EU Omnibus Directive (2019/2161) price tracker for Magento 2. Records price changes with timestamps, computes the lowest prior price in the disclosure window, and displays it on PDP and PLP, with async queue processing for bulk operations.
Package info
github.com/magendooro/module-omnibus
Type:magento2-module
pkg:composer/magendoo/module-omnibus
Fund package maintenance!
Requires
- php: >=8.1
- magento/framework: >=103.0.0
- magento/module-backend: >=102.0.0
- magento/module-catalog: >=104.0.0
- magento/module-catalog-rule: >=101.2.0
- magento/module-eav: >=102.1.0
- magento/module-sales-rule: >=101.2.0
- magento/module-store: >=101.0.0
- magento/module-tax: >=100.4.0
- magento/module-ui: >=101.2.0
Requires (Dev)
- phpstan/phpstan: ^1.10 || ^2.0
- phpunit/phpunit: ^10.5
This package is auto-updated.
Last update: 2026-07-21 18:41:01 UTC
README
A Magento 2 module that records product price history and computes the lowest price in a configurable window, satisfying the requirements of the EU Omnibus Directive 2019/2161 (Article 6a).
The directive requires that any price reduction must show the lowest price from the preceding 30 days — measured not from today, but from the moment the reduction began. This module implements that anchored-window algorithm correctly.
Requirements
| Dependency | Version |
|---|---|
| PHP | ≥ 8.1 |
| Magento Open Source / Adobe Commerce | 2.4.4 – 2.4.x |
| MySQL / MariaDB | 8.0 / 10.6+ |
No RabbitMQ required: all queue topics are pinned to Magento's MySQL-backed db queue connection (queue_message table), so the async pipeline works on any standard Magento installation. Consumers run through Magento cron (consumers_runner) or a process manager — see Async processing.
Installation
The module is distributed via its GitHub repository (it is not published on Packagist).
Via Composer (recommended)
Add the repository to your project's composer.json:
"repositories": [ { "type": "vcs", "url": "https://github.com/magendooro/module-omnibus" } ]
Install and enable:
composer require magendoo/module-omnibus:^2.0 bin/magento module:enable Magendoo_Omnibus bin/magento setup:upgrade bin/magento setup:di:compile bin/magento cache:flush
Manual installation
Copy the module directory to app/code/Magendoo/Omnibus/, then run the same setup:upgrade / setup:di:compile / cache:flush sequence.
Quick Start
After installation, navigate to:
Stores → Configuration → Magendoo Extensions → Omnibus Price Tracker
- Set Enable Price History to Yes
- Set Period in Days to 30 (minimum required by the Directive; increase for a wider safety margin)
- Set Display Mode to Discounted Products Only (recommended for compliance)
- Set Display Omnibus Prices On to Product Pages Only or Product and Category Pages — the default is None, which records history but renders nothing on the storefront
- Click Save Config → the module queues a full catalog recompute on the
magendoo.omnibus.recomputequeue (make sure consumers are running) - Seed an initial baseline so products have history to compute from:
bin/magento omnibus:history:restore --force
The omnibus_price product attribute is then populated and the label renders on the configured surfaces.
Configuration Reference
general/* settings are scoped per website. label/* settings are scoped per store view so the label can be translated for each language (store view is Magento's locale boundary); an untranslated store view inherits the website/default value. cleanup/* and cron/* are global.
| Path | Default | Description |
|---|---|---|
general/enabled |
No | Master toggle. Disabling stops capture and display but does not delete existing history. |
general/period_in_days |
30 | Window length in days. Hard minimum: 30 (enforced at save time and again at read time). |
general/display_mode |
discounted_only |
discounted_only — show only during active reductions. all_products — show for all products (rolling window). |
general/display_on |
none |
Where to render the label: none, pdp_only, or pdp_and_plp. Must be changed from the default for any storefront output. |
general/hide_if_matches |
Yes | Suppress the label when the Omnibus price equals the current price. |
general/show_history_on_product |
No | Adds a notice on the admin product edit page pointing to the price history grid. |
general/hide_for_customer_groups |
(none) | Suppress the label for specific customer groups (history is still recorded). |
label/template |
The lowest price from the last {days} days: {omnibus_price} |
Label text. Variables: {days}, {omnibus_price}, {percentage}. Allowed HTML tags: span i u b strong em. |
label/display_format |
text |
text (inline) or tooltip. |
label/percentage_difference |
disabled |
Show % change between current price and Omnibus price via {percentage}. |
cleanup/enabled |
Yes | Automatic deletion of old history records by the cleanup cron. |
cleanup/period_in_days |
400 | Delete records older than this many days. Must be ≥ period_in_days + 30. The newest pre-cutoff record per product/website/group is always kept (carry-in). |
cron/sync_schedule |
0 2 * * * |
Drift-sync cron: queues captures for products whose current effective price (rule / active special / regular) drifted from the last history entry. |
cron/cleanup_schedule |
0 3 * * * |
Cleanup cron: deletes records older than the retention window. |
All recorded_at timestamps are stored in UTC.
CLI Commands
All three commands refuse to run (exit non-zero) while magendoo_omnibus/general/enabled is 0.
# Recompute omnibus_price for all products on all websites bin/magento omnibus:prices:recompute # Recompute for a single product (all websites) bin/magento omnibus:prices:recompute --product-id=42 # Recompute for a single product on a specific website bin/magento omnibus:prices:recompute --product-id=42 --website-id=2 # Delete history older than the configured retention period, or a given date. # The newest pre-cutoff record per product/website/group is always preserved. bin/magento omnibus:history:clean bin/magento omnibus:history:clean --before=2025-01-01 bin/magento omnibus:history:clean --before="2025-01-01 12:00:00" # Wipe and rebuild history (fresh snapshot + full recompute). # Prints the actual inserted row count, per-product coverage, and how many # products ended up with a stored omnibus_price value. # Requires confirmation unless --force is passed. bin/magento omnibus:history:restore bin/magento omnibus:history:restore --force bin/magento omnibus:history:restore --force --website-id=1
Admin Interface
Marketing → Omnibus → Price Change History
The price history grid displays every recorded price change across your catalog. Columns: ID, SKU, Website, Customer Group, Price Type, Price (excl. tax), Price (incl. tax), Source, Recorded At, Actions.
Filterable columns include SKU (text), Website, Customer Group, Price Type, Source, and Recorded At (date range).
- Row action: Delete — POST with confirmation dialog.
- Mass action: Delete selected — POST.
- Toolbar button: Restore History — wipes the history table and rebuilds it from a full catalog snapshot (POST with confirmation; only visible to admins holding the
manageACL resource). Refuses to run while the module is disabled globally or for any website: the capture/recompute services no-op when disabled, so the wipe would never be re-seeded.
All destructive endpoints accept POST only; GET requests are rejected.
ACL resources
| Resource | Grants |
|---|---|
Magendoo_Omnibus::view_history |
View the price history grid |
Magendoo_Omnibus::delete_history |
Delete single records / mass delete |
Magendoo_Omnibus::manage |
Restore History (wipe + rebuild) |
Magendoo_Omnibus::configuration |
Stores → Configuration section |
Product attributes
| Attribute | Where | Purpose |
|---|---|---|
omnibus_price |
Product edit → Prices group (read-only value, website scope) | The computed lowest prior price. |
omnibus_hide |
Product edit → General group (Yes/No) | Set to Yes to suppress the storefront label for this product. |
Async processing
Bulk capture and recompute work runs through two message queue topics on the MySQL (db) queue connection:
magendoo.omnibus.capture— price capture for single products (catalog rule changes, imports, drift sync)magendoo.omnibus.recompute—omnibus_pricerecompute batches (config changes)
Magento's cron consumers_runner starts the consumers automatically. To run them under a process manager instead:
bin/magento queue:consumers:start magendoo.omnibus.capture bin/magento queue:consumers:start magendoo.omnibus.recompute
A single product save in admin/API is captured synchronously (so the product page is correct immediately); everything else is queued.
How It Works
See docs/how-it-works.md for a full explanation of the anchored-window algorithm (including the carry-in rule), the price resolution chain, and the complete event capture pipeline.
Architecture
See docs/architecture.md for the module structure, database schema, DI wiring, queue topology, and cron schedule.
Testing
See docs/testing.md for the PHPUnit suite inventory and manual verification checklist.
Roadmap
Reserved for future versions (constants exist in Api/Data/PriceHistoryInterface, but no code writes them yet):
- Per-customer-group price tracking (
tier/groupprice types) - REST API write sources (
api,api_overridesource labels) - Cart price rule (checkout promotion) disclosure — the v1.x
salesrule.is_omnibuscolumn was removed in v2.0.0 and may return as a separate feature
Changelog
v2.0.0 — 2026-07-18
Major refactor. Breaking changes — review before upgrading from v1.x:
Removed
- Config field
general/display_child_products(never implemented), constantOmnibusConfigInterface::XML_PATH_DISPLAY_CHILD_PRODUCTS, methodConfig::isDisplayChildProducts() - Methods
Config::getSyncCronSchedule()/Config::getCleanupCronSchedule()(schedules are read byetc/crontab.xmldirectly) - DB column
salesrule.is_omnibus— dropped onsetup:upgrade; any data in that column is lost — and thesales_rule_form.xmlcheckbox that wrote it - Redundant index
MGDOO_OMNIBUS_HIST_PROD_WEB_TIME_IDX(the unique key covers the same prefix) - The
magendoo.omnibus.cleanupqueue topic and its consumer (cleanup deletes directly, no queue round-trip) - Admin grid filter options that were never written: sources
API/API Override, price typesTier/Group(interface constants remain reserved)
Fixed / changed
- Window query now applies the carry-in rule: the price in force before the window opened is part of the minimum, so a product with a long-stable price gets a correct reference price
- History retention (
omnibus:history:cleanand the cleanup cron) always keeps the newest pre-cutoff row per product/website/customer group — the future carry-in - Catalog rule changes, imports, config changes, and drift sync now publish to the message queue instead of doing bulk work inline; product save stays synchronous
- Drift-sync cron pre-filters with a single SQL query per website and queues only genuinely drifted products
- Config-change recompute is gated on the changed paths (
enabled,period_in_days,display_mode) - PLP label implemented (pricing render plugin,
pdp_and_plpmode); previously advertised but missing - Special price date handling: a
special_from_dateof today is active from the start of the day - All timestamps generated in UTC via the framework date library
- Admin: ACL split into view/delete/manage/configuration; destructive endpoints POST-only; Restore button hidden without the
manageresource - CLI commands exit with an error when the module is disabled; restore prints real inserted/coverage counts
- Data patches are re-runnable (skip attribute creation when the attribute exists)
Notes
- Product deletion also deletes that product's price history (
ON DELETE CASCADEon the history table). If you need audit history to survive product deletion, export before deleting products. composer.jsonversion 2.0.0; module description updated to match actual behavior.
v1.0.0 — 2026-04-16
Initial release:
- Price capture from admin save, catalog rules, Magento Import, config change, nightly cron
- Anchored 30-day window algorithm
- Async queue processing (MySQL-backed)
- Admin price history grid with filters, mass-delete, and restore
- CLI commands,
omnibus_priceandomnibus_hideEAV product attributes - PHPUnit unit test suite
License
MIT — see the license header in each source file. Copyright © Magendoo (https://magendoo.ro)