=== Sellinor Expiration Dates for WooCommerce ===
Contributors: sellinor
Tags: woocommerce, expiration date, product expiry, perishable, inventory
Requires at least: 5.8
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.2.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Track expiration dates for WooCommerce products. Auto-hide or set them out of stock when they expire, and show expiration info to shoppers.

== Description ==

**Sellinor Expiration Dates for WooCommerce** helps store owners manage perishable and time-sensitive inventory by adding expiration date tracking to WooCommerce products.

Set expiration dates on simple products or individual variations, and let the plugin handle the rest — automatically hiding expired products from your catalog or marking them out of stock when they expire.

= Features =

* **Expiration date tracking** — Add expiration dates to any simple or variable product from a dedicated Expiration tab in the product editor.
* **Automatic expiry actions** — Automatically hide expired products, set them to out of stock, or both. Configurable days-before threshold so the action triggers ahead of the actual date.
* **Cart and checkout protection** — When an expiry action is enabled, expired items are blocked from being added to the cart and are removed at checkout, so customers can't purchase products past their date. (With the action set to "Do nothing", no cart guarding is applied.)
* **Product list column** — Sortable, color-coded expiration column in the WooCommerce product list for at-a-glance status.
* **Dashboard widget** — See your soonest-expiring products right from the WordPress dashboard.
* **Reports** — Overview page with an expiration calendar, an at-a-glance product list, and Expired / Expiring soon filter tabs.
* **CSV import/export** — Bulk manage expiration dates via a dedicated CSV import / export page.
* **Activity log** — Track all automatic plugin actions (stock changes, edits, imports); entries older than 90 days are cleaned up automatically.
* **Quick edit & bulk edit** — Update expiration dates directly from the product list without opening each product.
* **Frontend display** — Optionally show the expiration date on the product page, with a configurable display threshold.
* **WooCommerce HPOS compatible** — Full compatibility with High-Performance Order Storage.

= Pro version =

This free plugin is fully functional on its own. A separate **Pro add-on** — available from [sellinor.dev](https://sellinor.dev/expiration-dates-for-woocommerce/) — adds inventory features for stores that sell perishable stock:

* **Batch & lot tracking (FEFO)** — track stock by batch/lot with per-batch expiry dates and earliest-expiry-first deduction.
* **Automatic expiry discounts** — apply tiered discounts as products approach their expiration date.
* **Email alerts & digests** — scheduled emails listing products expiring soon.
* **WooCommerce CSV integration** — read/write expiration dates in WooCommerce's native product importer and exporter.
* **Batch details in orders & emails** — show the exact lot fulfilled on each order.

The Pro add-on is a separate plugin hosted on sellinor.dev and is not required to use this plugin.

= Translations =

The plugin is fully translation-ready. Translations are welcome via the WordPress.org translation platform.

= Documentation =

Full documentation is available at [sellinor.dev/docs/product-expiration-dates/](https://sellinor.dev/docs/product-expiration-dates/).

== Installation ==

1. Upload the `sellinor-expiration-dates-for-woocommerce` folder to the `/wp-content/plugins/` directory, or install the plugin through the WordPress Plugins screen.
2. Activate the plugin through the Plugins screen in WordPress.
3. Make sure WooCommerce is installed and active.
4. Navigate to **Products > Expirations** to configure your settings.
5. Edit any product and open the **Expiration** tab to set an expiration date.

== Frequently Asked Questions ==

= Does this plugin require WooCommerce? =

Yes. WooCommerce must be installed and active for this plugin to work.

= What happens when a product expires? =

You can choose from several actions in the plugin settings: hide the product from the catalog, set it to out of stock, or do nothing (manual control). You can also configure a "days before" threshold so the action triggers before the actual expiration date.

= Can I set different expiration dates for product variations? =

Yes. Each variation can have its own expiration date. If a variation does not have a date set, it inherits the parent product's expiration date. The product is only hidden when all variations are expired.

= Can I import expiration dates in bulk? =

Yes. The plugin includes a dedicated CSV import/export page under **Products > Expirations > Import / Export** where you can download every product with its current expiration date and re-upload the edited file.

= Is the plugin compatible with WooCommerce HPOS? =

Yes. The plugin declares full compatibility with WooCommerce High-Performance Order Storage (HPOS).

= What does the activity log track? =

The log records all automatic plugin actions: stock changes, imports, and manual date edits. Logs older than 90 days are automatically cleaned up.

= Will my data be removed when I uninstall? =

Only if you opt in. By default, settings and the activity log are preserved on uninstall in case you reinstall later. You can enable a "delete all data on uninstall" option in settings if you want a full cleanup.

== Screenshots ==

1. Expiration tab in the product editor
2. Product list with sortable expiration column
3. Dashboard widget showing upcoming expirations
4. Reports page with stat cards and calendar
5. Settings page
6. Import/export page

== Changelog ==

= 1.2.2 - 2026-06-13 =
* Maintenance: version kept in sync with the separately distributed Pro add-on release; no functional changes in the free plugin. (The Pro add-on's one-click "install the free base plugin" helper now falls back to the publisher-hosted download when the WordPress.org listing isn't reachable.)

= 1.2.1 - 2026-06-13 =
* Maintenance: version kept in sync with the separately distributed Pro add-on release; no functional changes in the free plugin.

= 1.2.0 - 2026-06-13 =
* Added: theme-proof on-product display — when a customized theme doesn't fire the standard product summary hook, the expiration date now renders via the add-to-cart form area instead (never duplicated on standard themes).
* Added: an [edfw_expiration_date] shortcode for manual placement in page builders and fully bespoke templates (optional id attribute; suppresses the automatic placement on that product's page; works even with the display toggle off).
* Improved: the "Show on product page" setting now explains placement and points to the shortcode for heavily customized themes.

= 1.1.15 - 2026-06-13 =
* Improved: consistent wording across the Pro hints — every mention now says "the Pro add-on" instead of a bare "Pro" as the sentence subject (product editor, Overview hint, Import/Export note, Pro Features page).

= 1.1.14 - 2026-06-13 =
* Improved: clearer wording on the Overview email hint ("Get this report by email — Pro sends you an expiring-products digest daily, weekly, or monthly.").

= 1.1.13 - 2026-06-12 =
* Improved: the page title and header Upgrade button are now centered by flex layout instead of pixel offsets, so the header renders identically regardless of other plugins' admin styles, fonts, or zoom level.

= 1.1.12 - 2026-06-10 =
* Added: an `edfw_hide_single_date_field` filter so add-ons that derive the expiration date themselves can start the manual date field hidden in the product editor.

= 1.1.11 - 2026-06-10 =
* Maintenance: version kept in sync with the separately distributed Pro add-on release; no functional changes in the free plugin.

= 1.1.10 - 2026-06-10 =
* Fixed: the help "?" icon next to the expiration date field is now vertically centered with the input (WooCommerce's default tip offset is sized for its shorter fields).

= 1.1.9 - 2026-06-10 =
* Improved: every "Upgrade to Pro" button now renders at exactly the same size as the header upgrade button — the preview-panel and Pro Features page buttons previously used larger WordPress button sizes.

= 1.1.8 - 2026-06-10 =
* Improved: the Pro preview panels (Notifications/Discounts) now span the full content width and start flush with the tab bar, exactly like the regular settings cards — previously they were capped at 640px and offset 16px lower.

= 1.1.7 - 2026-06-10 =
* Improved: wider, hard-enforced equal tabs in the settings navigation (200px minimum on every tab, asserted with priority so nothing can shrink the Pro preview tabs below the others).

= 1.1.6 - 2026-06-10 =
* Fixed: the settings tabs now keep their equal widths at every window size — below 960px wide they previously collapsed back to unequal natural widths; the tab rail now scrolls horizontally instead when space runs out.

= 1.1.5 - 2026-06-10 =
* Fixed: the equal-width settings tabs now render identically in every browser — the previous approach relied on a CSS grid behavior that differs between Chrome, Safari, and Firefox.

= 1.1.4 - 2026-06-10 =
* Improved: the settings tabs now render as a segmented control — every tab has the same width with a centered label, on free and Pro installs alike (natural-width pills wrap on small screens).

= 1.1.3 - 2026-06-10 =
* Improved: the settings tab row now keeps every tab at the same height (the PRO preview pills no longer sit ragged against the plain tabs), and the Pro panel icon chip and buttons share the header button's corner radius for a consistent look.

= 1.1.2 - 2026-06-10 =
* Improved: polished the Pro preview panels (icon now sits inline with the section title in a gradient chip), the header Upgrade button aligns with the page title, and all upsell buttons keep their gradient on hover instead of falling back to the admin color scheme.

= 1.1.1 - 2026-06-10 =
* Improved: button icons (Copy, Download, Clear all logs) are now inline SVGs instead of icon-font glyphs — exact geometric centering on every display, no more font-baseline alignment drift.

= 1.1.0 - 2026-06-10 =
* Improved: all admin button icons now render on the Dashicons-native 20px grid with flex centering — sub-native sizes landed glyphs on fractional pixels, which read as misalignment (System Info, Activity Log, and settings buttons).

= 1.0.99 - 2026-06-10 =
* Improved: crisper button icons on the System Info and Activity Log screens (straight "copy" glyph, consistent sizing and optical alignment).

= 1.0.98 - 2026-06-10 =
* Maintenance: version kept in sync with the separately distributed Pro add-on release; no functional changes in the free plugin.

= 1.0.97 - 2026-06-10 =
* Fixed: the Overview's "inventory at risk" query no longer depends on a relaxed MySQL mode, so it works on hosts that enforce ONLY_FULL_GROUP_BY.
* Fixed: changing the "expiring soon" threshold for the first time now refreshes the cached Overview counts immediately.
* Improved: dropping a CSV onto the import box now stages the file for review — the import runs when you click Import CSV, never on the drop itself.
* Improved: the activity-log clear action fails quietly on a missing security token instead of stopping the whole page.

= 1.0.96 - 2026-06-10 =
* Improved: the Overview statistics are now cached briefly and the inventory-value calculation is bounded, so the dashboard loads noticeably faster on large catalogs.
* Improved: dropping a CSV onto the import box now submits through the standard form flow, which is more reliable across browsers.
* Improved: clearing the activity log now redirects after the action, so refreshing the page can no longer re-submit it.
* Security: the CSV importer now also inspects the uploaded file's content (not just its extension) and rejects files that are clearly not CSV.
* Fixed: the uninstall cleanup matches the plugin's own post meta exactly and skips WooCommerce-specific cleanup when WooCommerce's tables are already gone.
* Added: preview pages for the Pro add-on's Notifications and Discounts settings sections (shown only when the separate Pro add-on is not installed).

= 1.0.95 =
* Fixed: the admin "Site timezone" status now reads correctly (e.g. "UTC" or "America/New_York") instead of a doubled "UTCUTC", and the "expiring soon threshold" status reads "1 day" instead of "1 days".

= 1.0.94 =
* Added: the on-product expiration date now also displays on block themes that use WooCommerce's block-based single-product template (for example the default Twenty Twenty-Four / Twenty Twenty-Five themes) — it appears just after the product price. Classic WooCommerce themes are unchanged.

= 1.0.93 =
* Security: the "Expiring products" dashboard widget is now shown only to users who can manage WooCommerce, so other roles can no longer see product stock levels.
* Maintenance: a malformed import row's date value is now truncated in the error notice, removed some dead admin CSS, and minor internal cleanups.

= 1.0.92 =
* Fixed: the "Clear all logs" confirmation prompt now works in translated languages whose text contains an apostrophe (previously the confirmation could be skipped in, for example, French).
* Improved: the dashboard widget skips impossible stored dates, and the active tab and settings section are announced to screen readers.
* Maintenance: small internal cleanups.

= 1.0.91 =
* Improved: the System Info report and the dashboard "with dates"/"expired" counts now ignore impossible stored dates (e.g. a corrupt "2025-02-29"), so every screen agrees.
* Improved: a variation whose stored date is corrupt now shows no date rather than a wrong one on the product page, consistent with the rest of the plugin.
* Maintenance: refreshed the translation template, removed dead code, and fixed a misleading code comment.

= 1.0.90 =
* Fixed: upgrading from a much older version now reliably carries a previously enabled "automatically hide expired products" setting over to the current expiry-action control.
* Maintenance: documentation, internationalization, and accessibility polish.

= 1.0.89 =
* Fixed: changing a variable product to a simple product now clears its leftover synced expiration date so it no longer shows a date it doesn't really have.
* Improved: a clearer cap on long import error lists; small accessibility, consistency, and security-log refinements.
* Maintenance: removed dead code and consolidated duplicated logic across the plugin.

= 1.0.88 =
* Improved: corrupt stored dates are handled consistently across display, reports, and import; the weekday initials in the calendar header now translate independently per day.
* Maintenance: accessibility, internationalization, security, and documentation polish; removed dead code and consolidated duplicated logic.

= 1.0.87 =
* Improved: dashboard report counts and the calendar load more efficiently and consistently exclude impossible stored dates.
* Improved: a corrupt stored date can no longer produce a wrong reformatted date or an empty badge; recipient and settings values are validated and clamped defensively.
* Maintenance: accessibility (announced toasts), documentation, and security log-hygiene fixes; removed dead code.

The full changelog, including the separate Pro add-on, is available at [sellinor.dev](https://sellinor.dev/expiration-dates-for-woocommerce/).
