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.
Magendoo Omnibus Price Tracker
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
All general/* and label/* settings are scoped per website; 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.com)
No changelog yet
The vendor hasn't published a changelog. Tagged releases appear in the Versions tab.
Requires 10
| Package | Constraint |
|---|---|
| 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 |
| php | >=8.1 |
Requires-dev 2
| Package | Constraint |
|---|---|
| phpstan/phpstan | ^1.10 || ^2.0 |
| phpunit/phpunit | ^10.5 |
No QA results yet
QA pipelines haven't run for this version. Compatibility and quality results appear here once the vendor publishes a tagged release that gets ingested.
More from magendoo
View vendorEU Base Price (Grundpreis) display for Magento 2. Shows price per reference unit (kg, litre, m) on product pages, category listings, search results and cart. Compliant with EU Price Indication Directive 98/6/EC and German PAngV.
Shipping Restrictions module
Product labels for Magento 2 - first-class label entities with stable codes, manual assignment via product attribute, rule-computed assignments materialized by an indexer, PLP/PDP badges and GraphQL exposure
Catalog data quality observability for Magento 2 — completeness scoring, filter-blind detection, and gap reporting
Turn an existing module into recurring revenue.
If you already maintain a Magento 2 module on GitHub or GitLab, listing it on Packagento takes about five minutes. We mirror your tags, handle distribution signing, and route paid licenses through Stripe Connect, so you can keep shipping the way you already do.