{"id":307382,"date":"2026-05-20T21:08:52","date_gmt":"2026-05-20T21:08:52","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/price-history-omnibus-for-woocommerce\/"},"modified":"2026-05-20T21:08:29","modified_gmt":"2026-05-20T21:08:29","slug":"wpnest-price-history-for-woocommerce","status":"publish","type":"plugin","link":"https:\/\/mg.wordpress.org\/plugins\/wpnest-price-history-for-woocommerce\/","author":23491788,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.3","stable_tag":"1.0.3","tested":"6.9.5","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"WPNest Price History for WooCommerce","header_author":"WPNest","header_description":"EU Omnibus directive compliance \u2014 tracks every WooCommerce price change and displays the lowest price from the last 30 days when a product is on sale.","assets_banners_color":"","last_updated":"2026-05-20 21:08:29","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wpnest.pl\/wpnest-price-history-for-woocommerce\/","header_author_uri":"https:\/\/wpnest.pl","rating":0,"author_block_rating":0,"active_installs":0,"downloads":123,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.3":{"tag":"1.0.3","author":"wpnest","date":"2026-05-20 21:08:29"}},"upgrade_notice":{"1.0.3":"<p>Restores the admin product price history chart, which silently failed to render after the Chart.js 4.x upgrade. Recommended.<\/p>","1.0.2":"<p>Security hardening of custom CSS sanitization and fix for cache invalidation. Recommended.<\/p>","1.0.1":"<p>Plugin slug renamed per WordPress.org review. Reinstall under the new slug; old instances should be deactivated and removed.<\/p>","1.0.0":"<p>Initial public release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3540112,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3540112,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":[],"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.3"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3540112,"resolution":"1","location":"assets","locale":"","width":2560,"height":2713},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3540112,"resolution":"2","location":"assets","locale":"","width":2560,"height":1551},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3540112,"resolution":"3","location":"assets","locale":"","width":2560,"height":1289}},"screenshots":{"1":"Settings page \u2014 General (General) tab.","2":"Settings page \u2014 Display (Display) tab with custom CSS.","3":"Frontend notice on a single product page.","4":"Per-product price history chart in the admin metabox."}},"plugin_section":[],"plugin_tags":[14361,209978,217117,5447,286],"plugin_category":[45],"plugin_contributors":[263746],"plugin_business_model":[],"class_list":["post-307382","plugin","type-plugin","status-publish","hentry","plugin_tags-compliance","plugin_tags-omnibus","plugin_tags-price-history","plugin_tags-sale","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_contributors-wpnest","plugin_committers-wpnest"],"banners":[],"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/wpnest-price-history-for-woocommerce\/assets\/icon-128x128.png?rev=3540112","icon_2x":"https:\/\/ps.w.org\/wpnest-price-history-for-woocommerce\/assets\/icon-256x256.png?rev=3540112","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/wpnest-price-history-for-woocommerce\/assets\/screenshot-1.png?rev=3540112","caption":"Settings page \u2014 General (General) tab."},{"src":"https:\/\/ps.w.org\/wpnest-price-history-for-woocommerce\/assets\/screenshot-2.png?rev=3540112","caption":"Settings page \u2014 Display (Display) tab with custom CSS."},{"src":"https:\/\/ps.w.org\/wpnest-price-history-for-woocommerce\/assets\/screenshot-3.png?rev=3540112","caption":"Frontend notice on a single product page."}],"raw_content":"<!--section=description-->\n<p>WPNest Price History for WooCommerce gives WooCommerce stores automatic compliance with the EU Omnibus directive (Directive 2019\/2161, in force across the EU since May 2022, in Poland from January 2023).<\/p>\n\n<p>The plugin records every price change in your store with a timestamp and renders the legally required notice \u2014 \"Najni\u017csza cena z 30 dni przed obni\u017ck\u0105: X\" \u2014 under the price on every product page where a discount is announced.<\/p>\n\n<h4>Key features (Free)<\/h4>\n\n<ul>\n<li><strong>Automatic price capture<\/strong> \u2014 every change made via admin, REST API, WP-CLI, CSV import, or programmatic save is recorded with timestamp.<\/li>\n<li><strong>Daily snapshot failsafe<\/strong> \u2014 a cron job ensures every published product has at least one history row per 24h, even when capture hooks miss.<\/li>\n<li><strong>30-day-low calculation<\/strong> \u2014 uses the legally correct \"Omnibus\" method (lookback from the moment the current sale started). A \"Rolling\" alternative is available for stores that prefer it.<\/li>\n<li><strong>Configurable label<\/strong> \u2014 the notice text is fully customizable per language.<\/li>\n<li><strong>Display options<\/strong> \u2014 single product page (required), shop loop, cart line items.<\/li>\n<li><strong>Variable products<\/strong> \u2014 per-variation tracking with frontend handler that updates the notice when a variation is selected.<\/li>\n<li><strong>Custom CSS<\/strong> \u2014 paste your own styles for the notice without editing files.<\/li>\n<li><strong>Per-product mini-chart<\/strong> in the admin product edit screen.<\/li>\n<li><strong>WP-CLI support<\/strong> \u2014 <code>snapshot<\/code>, <code>cleanup<\/code>, <code>backfill<\/code>.<\/li>\n<li><strong>Backfill on activation<\/strong> \u2014 seeds the history table with current prices so the 30-day window is never empty.<\/li>\n<li><strong>Cleanup cron<\/strong> \u2014 automatically removes entries older than the retention window (default 90 days).<\/li>\n<li><strong>Cache<\/strong> \u2014 transient-based (12h TTL) with automatic invalidation on every price change.<\/li>\n<li><strong>Compatible<\/strong> with HPOS (WooCommerce custom order tables).<\/li>\n<\/ul>\n\n<h4>Compliance basis<\/h4>\n\n<p>This plugin implements requirements from:<\/p>\n\n<ul>\n<li><strong>EU Directive 2019\/2161<\/strong> (\"Omnibus\") \u2014 applicable across all EU member states.<\/li>\n<li><strong>Polish Ustawa o informowaniu o cenach towar\u00f3w i us\u0142ug, art. 4 ust. 2<\/strong> \u2014 domestic transposition, in force since 1 January 2023.<\/li>\n<\/ul>\n\n<p>UOKiK (the Polish Office of Competition and Consumer Protection) actively monitors compliance and may impose fines of up to 10% of annual revenue. The most commonly flagged violation is \"price gymnastics\" \u2014 artificially inflating the regular price shortly before a sale to make the discount appear larger.<\/p>\n\n<h4>Pro version<\/h4>\n\n<p>A Pro extension is available with additional features for shops that want to go beyond the legal minimum: public price history chart on product pages, Omnibus violation detector, compliance dashboard, webhooks (Slack\/Discord), CSV export for audit, tamper-proof hash chain, white-label, and more. See https:\/\/wpnest.pl\/sklep\/prawo-i-formalnosci\/wpnest-price-history-pro\/ for details.<\/p>\n\n<h3>Third-Party Libraries<\/h3>\n\n<p>This plugin bundles the following libraries:<\/p>\n\n<ul>\n<li>Chart.js v4.5.1 \u2014 MIT License, https:\/\/www.chartjs.org\/ \u2014 used to render the per-product price history chart in the admin product edit screen.<\/li>\n<li>chartjs-adapter-date-fns v3.0.0 (bundle including date-fns) \u2014 MIT License, https:\/\/github.com\/chartjs\/chartjs-adapter-date-fns \u2014 date adapter required by Chart.js v3+ for time-scale axes on the price history chart.<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin folder to <code>\/wp-content\/plugins\/<\/code> or install via the Plugins screen.<\/li>\n<li>Activate the plugin through the \"Plugins\" screen in WordPress.<\/li>\n<li>Go to <strong>WooCommerce \u2192 Price History<\/strong> to configure settings.<\/li>\n<li>The plugin starts capturing prices immediately. The backfill process seeds existing products in the background \u2014 typically completes within minutes for shops up to 10k products.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20plugin%20work%20with%20variable%20products%3F\"><h3>Does this plugin work with variable products?<\/h3><\/dt>\n<dd><p>Yes. Each variation has its own price history. The notice on the single product page updates automatically when a customer selects a different variation.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20theme%20overrides%20of%20single-product.php%3F\"><h3>Does it work with theme overrides of single-product.php?<\/h3><\/dt>\n<dd><p>Yes. The plugin uses the <code>woocommerce_get_price_html<\/code> filter as its primary integration point, which fires whenever any code calls <code>$product-&gt;get_price_html()<\/code>. This works in both standard themes and custom themes that bypass <code>woocommerce_single_product_summary<\/code>.<\/p><\/dd>\n<dt id=\"what%20if%20my%20theme%20renders%20prices%20via%20wc_price%28%29%20directly%20%28not%20get_price_html%28%29%29%3F\"><h3>What if my theme renders prices via wc_price() directly (not get_price_html())?<\/h3><\/dt>\n<dd><p>In that rare case, you can call <code>do_action('woocommerce_single_product_summary')<\/code> from your theme template, or use the included <code>omnibus-notice.php<\/code> template override mechanism.<\/p><\/dd>\n<dt id=\"will%20my%20historical%20data%20be%20deleted%20when%20i%20uninstall%20the%20plugin%3F\"><h3>Will my historical data be deleted when I uninstall the plugin?<\/h3><\/dt>\n<dd><p>No, by default. The history table is treated as regulatory-grade data. To explicitly delete it on uninstall, enable \"Delete data on uninstall\" in the plugin settings.<\/p><\/dd>\n<dt id=\"how%20does%20the%20daily%20cleanup%20cron%20decide%20what%20to%20delete%3F\"><h3>How does the daily cleanup cron decide what to delete?<\/h3><\/dt>\n<dd><p>Entries older than the configured retention window (default 90 days) are deleted by <code>wpnest_ph_cleanup<\/code> cron. The retention window is configurable down to a minimum of 30 days.<\/p><\/dd>\n<dt id=\"i%20see%20%22najni%C5%BCsza%20cena%3A%20x%22%20but%20x%20equals%20the%20current%20sale%20price%20%E2%80%94%20is%20that%20wrong%3F\"><h3>I see \"Najni\u017csza cena: X\" but X equals the current sale price \u2014 is that wrong?<\/h3><\/dt>\n<dd><p>This usually means you've recently activated the plugin and there's no historical data yet from before the current sale. The plugin falls back to the rolling window when no pre-sale snapshot exists. Once you accumulate ~31 days of history, the Omnibus method kicks in.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Fixed: the per-product admin price history chart did not render after the Chart.js 4.x upgrade because Chart.js v3+ no longer bundles a date adapter. Added the <code>chartjs-adapter-date-fns<\/code> UMD bundle (MIT, includes date-fns inline) as a dependency of the chart script.<\/li>\n<li>Fixed: chart points were not drawn even when axes rendered, because <code>parsing: false<\/code> was paired with raw MySQL DATETIME strings as the <code>x<\/code> value. Pre-convert each row's <code>captured_at<\/code> (UTC) to a millisecond timestamp before pushing into the dataset.<\/li>\n<li>Disclosed the bundled <code>chartjs-adapter-date-fns<\/code> in the \"Third-Party Libraries\" section.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Hardened custom CSS sanitization: <code>sanitize_textarea_field()<\/code> plus blacklist of CSS-level attack vectors (expression, javascript:, vbscript:, @import, behavior, -moz-binding).<\/li>\n<li>Refactored transient cache key construction to make the <code>wpnest_ph_low_<\/code> prefix unambiguous to static analyzers.<\/li>\n<li>Fixed: per-product cache invalidation now correctly clears transients for both calculation methods (previous version only cleared entries that no longer existed under that key).<\/li>\n<li>Hardened plugin action link: <code>esc_url()<\/code> and <code>esc_html__()<\/code> on the Settings link, matching the Pro link pattern.<\/li>\n<li>Documented why the Omnibus notice output is echoed without an outer <code>wp_kses_post()<\/code> wrap (would strip the <code>data-variation-lows<\/code> JSON attribute needed by the variation handler) \u2014 all values are still escaped internally.<\/li>\n<li>Synced Chart.js enqueue version string with the bundled file (4.5.1).<\/li>\n<li>Disclosed bundled Chart.js library in readme.txt.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Renamed plugin slug to <code>wpnest-price-history-for-woocommerce<\/code> and display name to \"WPNest Price History for WooCommerce\" per WordPress.org review feedback.<\/li>\n<li>Added <code>Requires Plugins: woocommerce<\/code> header for WordPress 6.5+ dependency declaration.<\/li>\n<li>Hardened custom CSS output: <code>wp_strip_all_tags()<\/code> now applied at output in addition to sanitize-on-save.<\/li>\n<li>Updated bundled Chart.js to 4.5.1.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<li>Automatic price capture via <code>woocommerce_update_product<\/code> and <code>woocommerce_update_product_variation<\/code> hooks.<\/li>\n<li>Daily snapshot failsafe cron.<\/li>\n<li>Omnibus and Rolling calculation methods.<\/li>\n<li>Configurable retention window with cleanup cron.<\/li>\n<li>Variable product support with per-variation tracking.<\/li>\n<li>Customizable notice label and position.<\/li>\n<li>Custom CSS field.<\/li>\n<li>Per-product mini-chart in admin.<\/li>\n<li>WP-CLI commands.<\/li>\n<li>HPOS compatible.<\/li>\n<\/ul>","raw_excerpt":"EU Omnibus directive compliance for WooCommerce \u2014 shows the lowest price from the last 30 days when a product is on sale.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/307382","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=307382"}],"author":[{"embeddable":true,"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/wpnest"}],"wp:attachment":[{"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=307382"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=307382"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=307382"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=307382"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=307382"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/mg.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=307382"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}